Skip to main content

bare_script/
config.rs

1//! Configuration for command execution.
2//!
3//! This module provides configuration types for command execution.
4
5use std::collections::HashMap;
6use std::ffi::{OsStr, OsString};
7use std::path::PathBuf;
8
9fn from_env_vars() -> HashMap<OsString, OsString> {
10    std::env::vars()
11        .map(|(k, v)| (k.into(), v.into()))
12        .collect()
13}
14
15/// Configuration for command execution.
16#[derive(Debug, Clone, Default)]
17pub struct Config {
18    /// Default environment variables.
19    pub env: HashMap<OsString, OsString>,
20    /// Default working directory.
21    pub cwd: Option<PathBuf>,
22    /// Default stdin configuration.
23    pub stdin: Option<StdioConfig>,
24    /// Default stdout configuration.
25    pub stdout: Option<StdioConfig>,
26    /// Default stderr configuration.
27    pub stderr: Option<StdioConfig>,
28}
29
30/// Standard I/O configuration.
31#[derive(Debug, Clone, Copy, Default)]
32pub enum StdioConfig {
33    /// Inherit from parent.
34    #[default]
35    Inherit,
36    /// Pipe (capture).
37    Piped,
38    /// Null/Discard.
39    Null,
40    /// Specific file descriptor.
41    Fd(u32),
42}
43
44impl Config {
45    /// Creates a new empty configuration.
46    pub fn new() -> Self {
47        Self::default()
48    }
49
50    /// Creates a configuration from environment variables.
51    ///
52    /// # Errors
53    ///
54    /// Returns an error if the working directory is invalid.
55    pub fn from_env() -> std::io::Result<Self> {
56        let cwd = std::env::current_dir()?;
57        let env = from_env_vars();
58
59        Ok(Self {
60            env,
61            cwd: Some(cwd),
62            stdin: None,
63            stdout: None,
64            stderr: None,
65        })
66    }
67
68    /// Sets an environment variable.
69    #[must_use]
70    pub fn env<K, V>(mut self, key: K, value: V) -> Self
71    where
72        K: AsRef<OsStr>,
73        V: AsRef<OsStr>,
74    {
75        drop(
76            self.env
77                .insert(key.as_ref().to_os_string(), value.as_ref().to_os_string()),
78        );
79        self
80    }
81
82    /// Sets the working directory.
83    #[must_use]
84    pub fn cwd<D: Into<PathBuf>>(mut self, dir: D) -> Self {
85        self.cwd = Some(dir.into());
86        self
87    }
88
89    /// Sets stdin configuration.
90    #[must_use]
91    pub fn stdin(mut self, config: StdioConfig) -> Self {
92        self.stdin = Some(config);
93        self
94    }
95
96    /// Sets stdout configuration.
97    #[must_use]
98    pub fn stdout(mut self, config: StdioConfig) -> Self {
99        self.stdout = Some(config);
100        self
101    }
102
103    /// Sets stderr configuration.
104    #[must_use]
105    pub fn stderr(mut self, config: StdioConfig) -> Self {
106        self.stderr = Some(config);
107        self
108    }
109}
110
111/// Extension trait for applying configuration to CommandBuilder.
112pub trait ConfigExt {
113    /// Applies this configuration to a command builder.
114    fn with_config(self, config: &Config) -> Self;
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120
121    #[test]
122    fn test_config_new() {
123        let config = Config::new();
124        assert!(config.env.is_empty());
125        assert!(config.cwd.is_none());
126    }
127
128    #[test]
129    fn test_config_env() {
130        let config = Config::new().env("KEY", "value").env("OTHER", "123");
131
132        let key = OsString::from("KEY");
133        assert_eq!(
134            config.env.get(&key).map(|s| s.to_str()),
135            Some(Some("value"))
136        );
137    }
138
139    #[test]
140    fn test_config_cwd() {
141        let config = Config::new().cwd("/tmp");
142        assert!(config.cwd.is_some());
143    }
144}