1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
use std::{env, process};
use tracing::debug;
#[cfg(debug_assertions)]
pub mod debug;
#[derive(Debug)]
struct Metadata<'a> {
env_key: &'a str,
program: &'a str,
args: &'a [&'a str],
}
#[cfg(windows)]
static DEFAULT_METADATA: Metadata = Metadata {
env_key: "COMSPEC",
program: "cmd.exe",
args: &["/D", "/S", "/C"],
};
#[cfg(unix)]
static DEFAULT_METADATA: Metadata = Metadata {
env_key: "SHELL",
program: "/bin/sh",
args: &["-c"],
};
fn parse_program() -> String {
env::var(DEFAULT_METADATA.env_key).unwrap_or_else(|e| {
debug!(
default_program = DEFAULT_METADATA.program,
env_key = DEFAULT_METADATA.env_key,
error = ?e,
"Failed to get shell environment variable, falling back to default program."
);
DEFAULT_METADATA.program.to_string()
})
}
/// Sheller is a builder for `std::process::Command` that sets the shell program and arguments.
///
/// Please see the `Sheller::new` method for more information.
#[derive(Debug)]
pub struct Sheller<'a> {
program: String,
args: Vec<&'a str>,
script: &'a str,
}
impl Default for Sheller<'_> {
fn default() -> Self {
Self {
program: parse_program(),
args: DEFAULT_METADATA.args.to_vec(),
script: "",
}
}
}
impl<'a> Sheller<'a> {
/// Create a new `Sheller` with the given `script` and platform-specific defaults.
///
/// # Platform-specific defaults
///
/// ## Windows
///
/// When `target_family` is `windows`.
///
/// Set the `COMSPEC` environment variable to `program`, and if the environment variable is not set, use `cmd.exe` as the fallback program.
///
/// Also set the `args` to `["/D", "/S", "/C"]`.
///
/// ## Unix
///
/// When `target_family` is `unix`.
///
/// Set the `SHELL` environment variable to `program`, and if the environment variable is not set, use `/bin/sh` as the fallback program.
///
/// Also set the `args` to `["-c"]`.
///
/// # Arguments
///
/// * `script` - The shell script to run. This is dependent on the shell program.
///
/// # Examples
///
/// ```
/// use sheller::Sheller;
/// let sheller = Sheller::new("echo hello");
/// ```
#[must_use]
pub fn new(script: &'a str) -> Self {
Self {
script,
..Default::default()
}
}
/// Returns `std::process::Command` with the shell program and arguments set.
///
/// # Examples
///
/// ```
/// use sheller::Sheller;
/// let sheller = Sheller::new("echo hello");
/// let command = sheller.build();
/// ```
#[must_use]
pub fn build(self) -> process::Command {
let mut command = process::Command::new(&self.program);
command.args(&self.args);
command.arg(self.script);
command
}
}