use std::collections::HashMap;
use std::path::PathBuf;
use std::time::Duration;
#[derive(Debug, Clone)]
pub struct Command {
pub command_line: String,
pub working_dir: Option<PathBuf>,
pub env: HashMap<String, String>,
pub timeout: Option<Duration>,
pub capture_output: bool,
}
impl Command {
pub fn new(command_line: impl Into<String>) -> Self {
Self {
command_line: command_line.into(),
working_dir: None,
env: HashMap::new(),
timeout: None,
capture_output: true,
}
}
pub fn working_dir(mut self, dir: impl Into<PathBuf>) -> Self {
self.working_dir = Some(dir.into());
self
}
pub fn env(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.env.insert(key.into(), value.into());
self
}
pub fn envs<I, K, V>(mut self, vars: I) -> Self
where
I: IntoIterator<Item = (K, V)>,
K: Into<String>,
V: Into<String>,
{
for (k, v) in vars {
self.env.insert(k.into(), v.into());
}
self
}
pub fn timeout(mut self, duration: Duration) -> Self {
self.timeout = Some(duration);
self
}
pub fn capture_output(mut self, capture: bool) -> Self {
self.capture_output = capture;
self
}
}
impl Default for Command {
fn default() -> Self {
Self::new("")
}
}
#[derive(Debug, Default)]
pub struct CommandBuilder {
command_line: Option<String>,
working_dir: Option<PathBuf>,
env: HashMap<String, String>,
timeout: Option<Duration>,
capture_output: bool,
}
impl CommandBuilder {
pub fn new() -> Self {
Self {
capture_output: true,
..Default::default()
}
}
pub fn command_line(mut self, cmd: impl Into<String>) -> Self {
self.command_line = Some(cmd.into());
self
}
pub fn working_dir(mut self, dir: impl Into<PathBuf>) -> Self {
self.working_dir = Some(dir.into());
self
}
pub fn env(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.env.insert(key.into(), value.into());
self
}
pub fn timeout(mut self, duration: Duration) -> Self {
self.timeout = Some(duration);
self
}
pub fn capture_output(mut self, capture: bool) -> Self {
self.capture_output = capture;
self
}
pub fn build(self) -> Option<Command> {
self.command_line.map(|cmd| Command {
command_line: cmd,
working_dir: self.working_dir,
env: self.env,
timeout: self.timeout,
capture_output: self.capture_output,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_command_new() {
let cmd = Command::new("ls -la");
assert_eq!(cmd.command_line, "ls -la");
assert!(cmd.working_dir.is_none());
assert!(cmd.env.is_empty());
assert!(cmd.timeout.is_none());
assert!(cmd.capture_output);
}
#[test]
fn test_command_builder_chain() {
let cmd = Command::new("cargo build")
.working_dir("/project")
.env("RUST_LOG", "debug")
.timeout(Duration::from_secs(60))
.capture_output(true);
assert_eq!(cmd.command_line, "cargo build");
assert_eq!(cmd.working_dir, Some(PathBuf::from("/project")));
assert_eq!(cmd.env.get("RUST_LOG"), Some(&"debug".to_string()));
assert_eq!(cmd.timeout, Some(Duration::from_secs(60)));
}
#[test]
fn test_command_envs() {
let vars = [("KEY1", "val1"), ("KEY2", "val2")];
let cmd = Command::new("echo").envs(vars);
assert_eq!(cmd.env.len(), 2);
assert_eq!(cmd.env.get("KEY1"), Some(&"val1".to_string()));
assert_eq!(cmd.env.get("KEY2"), Some(&"val2".to_string()));
}
#[test]
fn test_command_builder_build() {
let cmd = CommandBuilder::new()
.command_line("pwd")
.working_dir("/tmp")
.build();
assert!(cmd.is_some());
let cmd = cmd.unwrap();
assert_eq!(cmd.command_line, "pwd");
assert_eq!(cmd.working_dir, Some(PathBuf::from("/tmp")));
}
#[test]
fn test_command_builder_empty() {
let cmd = CommandBuilder::new().build();
assert!(cmd.is_none());
}
}