1use 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#[derive(Debug, Clone, Default)]
17pub struct Config {
18 pub env: HashMap<OsString, OsString>,
20 pub cwd: Option<PathBuf>,
22 pub stdin: Option<StdioConfig>,
24 pub stdout: Option<StdioConfig>,
26 pub stderr: Option<StdioConfig>,
28}
29
30#[derive(Debug, Clone, Copy, Default)]
32pub enum StdioConfig {
33 #[default]
35 Inherit,
36 Piped,
38 Null,
40 Fd(u32),
42}
43
44impl Config {
45 pub fn new() -> Self {
47 Self::default()
48 }
49
50 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 #[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 #[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 #[must_use]
91 pub fn stdin(mut self, config: StdioConfig) -> Self {
92 self.stdin = Some(config);
93 self
94 }
95
96 #[must_use]
98 pub fn stdout(mut self, config: StdioConfig) -> Self {
99 self.stdout = Some(config);
100 self
101 }
102
103 #[must_use]
105 pub fn stderr(mut self, config: StdioConfig) -> Self {
106 self.stderr = Some(config);
107 self
108 }
109}
110
111pub trait ConfigExt {
113 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}