kagi-vault 0.1.2

Encrypted secrets and environment variable manager for teams — a secure, team-ready dotenv alternative with per-service isolation
use crate::domain::error::DomainError;
use crate::domain::runner::CommandRunner;
use std::process::Command;

pub struct SystemCommandRunner;

impl SystemCommandRunner {
    pub fn new() -> Self {
        Self
    }
}

impl CommandRunner for SystemCommandRunner {
    fn run(
        &self,
        env_vars: &[(String, String)],
        command: &str,
        args: &[String],
    ) -> Result<i32, DomainError> {
        let mut cmd = Command::new(command);
        cmd.args(args);
        for (key, value) in env_vars {
            cmd.env(key, value);
        }
        let status = cmd.status()?;
        Ok(status.code().unwrap_or(1))
    }
}

#[cfg(test)]
pub mod mock {
    use crate::domain::error::DomainError;
    use crate::domain::runner::CommandRunner;
    use std::sync::{Arc, Mutex};

    type CallRecord = (Vec<(String, String)>, String, Vec<String>);

    #[derive(Default, Clone)]
    pub struct MockCommandRunner {
        pub calls: Arc<Mutex<Vec<CallRecord>>>,
        pub exit_code: i32,
    }

    impl CommandRunner for MockCommandRunner {
        fn run(
            &self,
            env_vars: &[(String, String)],
            command: &str,
            args: &[String],
        ) -> Result<i32, DomainError> {
            self.calls.lock().unwrap().push((
                env_vars.to_vec(),
                command.to_string(),
                args.to_vec(),
            ));
            Ok(self.exit_code)
        }
    }
}