2021-09-02 19:22:15 +02:00
|
|
|
//! Command construction, configuration, and tracking.
|
2021-08-19 10:44:02 +02:00
|
|
|
|
2022-06-16 17:36:08 +02:00
|
|
|
use std::fmt;
|
|
|
|
|
|
|
|
use tokio::process::Command as TokioCommand;
|
|
|
|
use tracing::trace;
|
|
|
|
|
|
|
|
use crate::error::RuntimeError;
|
2021-08-22 14:28:20 +02:00
|
|
|
|
2021-09-02 19:22:15 +02:00
|
|
|
#[doc(inline)]
|
2022-06-16 17:36:08 +02:00
|
|
|
pub use process::Process;
|
2021-08-22 14:28:20 +02:00
|
|
|
|
2021-09-02 19:22:15 +02:00
|
|
|
#[doc(inline)]
|
|
|
|
pub use supervisor::Supervisor;
|
2021-08-16 17:09:22 +02:00
|
|
|
|
2021-09-02 19:22:15 +02:00
|
|
|
mod process;
|
|
|
|
mod supervisor;
|
2022-06-16 17:36:08 +02:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests;
|
|
|
|
|
|
|
|
/// A command to execute.
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
|
|
|
pub enum Command {
|
|
|
|
/// A raw command which will be executed as-is.
|
|
|
|
Exec {
|
|
|
|
/// The program to run.
|
|
|
|
prog: String,
|
|
|
|
|
|
|
|
/// The arguments to pass.
|
|
|
|
args: Vec<String>,
|
|
|
|
},
|
|
|
|
|
|
|
|
/// A shelled command line.
|
|
|
|
Shell {
|
|
|
|
/// The shell to run.
|
|
|
|
shell: Shell,
|
|
|
|
|
|
|
|
/// Additional options or arguments to pass to the shell.
|
|
|
|
///
|
|
|
|
/// These will be inserted before the `-c` (or equivalent) option immediately preceding the
|
|
|
|
/// command line string.
|
|
|
|
args: Vec<String>,
|
|
|
|
|
|
|
|
/// The command line to pass to the shell.
|
|
|
|
command: String,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Shell to use to run shelled commands.
|
|
|
|
///
|
|
|
|
/// `Cmd` and `Powershell` are special-cased because they have different calling conventions. Also
|
|
|
|
/// `Cmd` is only available in Windows, while `Powershell` is also available on unices (provided the
|
|
|
|
/// end-user has it installed, of course).
|
|
|
|
///
|
|
|
|
/// There is no default implemented: as consumer of this library you are encouraged to set your own
|
|
|
|
/// default as makes sense in your application / for your platform.
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
|
|
|
pub enum Shell {
|
|
|
|
/// Use the given string as a unix shell invocation.
|
|
|
|
///
|
2022-06-17 00:55:25 +02:00
|
|
|
/// This is invoked with `-c` followed by the command.
|
2022-06-16 17:36:08 +02:00
|
|
|
Unix(String),
|
|
|
|
|
|
|
|
/// Use the Windows CMD.EXE shell.
|
|
|
|
///
|
2022-06-17 00:55:25 +02:00
|
|
|
/// This is `cmd.exe` invoked with `/C` followed by the command.
|
2022-06-16 17:36:08 +02:00
|
|
|
#[cfg(windows)]
|
|
|
|
Cmd,
|
|
|
|
|
|
|
|
/// Use Powershell, on Windows or elsewhere.
|
|
|
|
///
|
2022-06-17 00:55:25 +02:00
|
|
|
/// This is `powershell.exe` invoked with `-Command` followed by the command on Windows.
|
|
|
|
/// On unices, it is equivalent to `Unix("pwsh")`.
|
2022-06-16 17:36:08 +02:00
|
|
|
Powershell,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Command {
|
|
|
|
/// Obtain a [`tokio::process::Command`] from a [`Command`].
|
|
|
|
///
|
|
|
|
/// Behaves as described in the [`Command`] and [`Shell`] documentation.
|
|
|
|
///
|
|
|
|
/// # Errors
|
|
|
|
///
|
|
|
|
/// - Errors if the `command` of a `Command::Shell` is empty.
|
|
|
|
/// - Errors if the `shell` of a `Shell::Unix(shell)` is empty.
|
|
|
|
pub fn to_spawnable(&self) -> Result<TokioCommand, RuntimeError> {
|
|
|
|
trace!(cmd=?self, "constructing command");
|
|
|
|
|
|
|
|
match self {
|
2023-01-06 14:53:49 +01:00
|
|
|
Self::Exec { prog, args } => {
|
2022-06-16 17:36:08 +02:00
|
|
|
let mut c = TokioCommand::new(prog);
|
|
|
|
c.args(args);
|
|
|
|
Ok(c)
|
|
|
|
}
|
|
|
|
|
2023-01-06 14:53:49 +01:00
|
|
|
Self::Shell {
|
2022-06-16 17:36:08 +02:00
|
|
|
shell,
|
|
|
|
args,
|
|
|
|
command,
|
|
|
|
} => {
|
|
|
|
let (shcmd, shcliopt) = match shell {
|
|
|
|
#[cfg(windows)]
|
|
|
|
Shell::Cmd => ("cmd.exe", "/C"),
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
Shell::Powershell => ("powershell.exe", "-Command"),
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
Shell::Powershell => ("pwsh", "-c"),
|
|
|
|
|
|
|
|
Shell::Unix(cmd) => {
|
|
|
|
if cmd.is_empty() {
|
|
|
|
return Err(RuntimeError::CommandShellEmptyShell);
|
|
|
|
}
|
|
|
|
|
|
|
|
(cmd.as_str(), "-c")
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if command.is_empty() {
|
|
|
|
return Err(RuntimeError::CommandShellEmptyCommand);
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut c = TokioCommand::new(shcmd);
|
|
|
|
c.args(args);
|
|
|
|
c.arg(shcliopt).arg(command);
|
|
|
|
Ok(c)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for Command {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
match self {
|
2023-01-06 14:53:49 +01:00
|
|
|
Self::Exec { prog, args } => {
|
|
|
|
write!(f, "{prog}")?;
|
2022-06-16 17:36:08 +02:00
|
|
|
for arg in args {
|
2023-01-06 14:53:49 +01:00
|
|
|
write!(f, " {arg}")?;
|
2022-06-16 17:36:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2023-01-06 14:53:49 +01:00
|
|
|
Self::Shell { command, .. } => {
|
|
|
|
write!(f, "{command}")
|
2022-06-16 17:36:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|