action_core/
env.rs

1use parking_lot::Mutex;
2use std::collections::HashMap;
3use std::ffi::{OsStr, OsString};
4use std::sync::Arc;
5
6#[derive(Debug, Default)]
7pub struct EnvMap {
8    inner: Arc<Mutex<HashMap<OsString, OsString>>>,
9}
10
11impl FromIterator<(OsString, OsString)> for EnvMap {
12    fn from_iter<I: IntoIterator<Item = (OsString, OsString)>>(iter: I) -> Self {
13        Self::new(HashMap::from_iter(iter))
14    }
15}
16
17impl EnvMap {
18    #[must_use]
19    pub fn new(inner: HashMap<OsString, OsString>) -> Self {
20        let inner = Arc::new(Mutex::new(inner));
21        Self { inner }
22    }
23
24    /// Parses environment from reader.
25    ///
26    /// # Errors
27    /// If the input cannot be parsed as a `HashMap<String, String>`.
28    #[cfg(feature = "serde")]
29    pub fn from_reader(reader: impl std::io::Read) -> Result<Self, serde_yaml::Error> {
30        Ok(Self::new(serde_yaml::from_reader(reader)?))
31    }
32}
33
34#[cfg(feature = "serde")]
35impl std::str::FromStr for EnvMap {
36    type Err = serde_yaml::Error;
37
38    fn from_str(env: &str) -> Result<Self, Self::Err> {
39        Ok(Self::new(serde_yaml::from_str(env)?))
40    }
41}
42
43pub trait Read {
44    /// Get value from environment.
45    ///
46    /// # Errors
47    /// When the environment variable is not present.
48    fn get<K>(&self, key: K) -> Option<OsString>
49    where
50        K: AsRef<OsStr>;
51}
52
53pub trait Write {
54    /// Set value for environment.
55    fn set<K, V>(&self, key: K, value: V)
56    where
57        K: AsRef<OsStr>,
58        V: AsRef<OsStr>;
59}
60
61impl Read for EnvMap {
62    fn get<K>(&self, key: K) -> Option<OsString>
63    where
64        K: AsRef<OsStr>,
65    {
66        self.inner.lock().get(key.as_ref()).cloned()
67    }
68}
69
70impl Write for EnvMap {
71    fn set<K, V>(&self, key: K, value: V)
72    where
73        K: AsRef<OsStr>,
74        V: AsRef<OsStr>,
75    {
76        self.inner
77            .lock()
78            .insert(key.as_ref().to_os_string(), value.as_ref().to_os_string());
79    }
80}
81
82#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
83pub struct OsEnv;
84
85impl Read for OsEnv {
86    fn get<K>(&self, key: K) -> Option<OsString>
87    where
88        K: AsRef<OsStr>,
89    {
90        std::env::var_os(key)
91    }
92}
93
94impl Write for OsEnv {
95    fn set<K, V>(&self, key: K, value: V)
96    where
97        K: AsRef<OsStr>,
98        V: AsRef<OsStr>,
99    {
100        unsafe {
101            std::env::set_var(key, value);
102        }
103    }
104}
105
106pub trait Parse {
107    type Error: std::error::Error;
108
109    /// Parses environment from a string.
110    ///
111    /// # Errors
112    /// If the input cannot be parsed as a `HashMap<String, String>`.
113    fn from_str(config: &str) -> Result<HashMap<String, String>, Self::Error>;
114
115    /// Parses environment from a reader.
116    ///
117    /// # Errors
118    /// If the input cannot be parsed as a `HashMap<String, String>`.
119    fn from_reader(reader: impl std::io::Read) -> Result<HashMap<String, String>, Self::Error>;
120}
121
122#[cfg(test)]
123mod tests {
124    use super::{EnvMap, Read, Write};
125    use similar_asserts::assert_eq as sim_assert_eq;
126
127    #[test]
128    fn get_env_map() {
129        let input_name = "SOME_NAME";
130        let env = EnvMap::from_iter([(input_name.into(), "SET".into())]);
131        sim_assert_eq!(env.get(input_name), Some("SET".into()));
132    }
133
134    #[test]
135    fn set_env_map() {
136        let input_name = "SOME_NAME";
137        let env = EnvMap::default();
138        env.set(input_name, "SET");
139        sim_assert_eq!(env.get(input_name), Some("SET".into()));
140    }
141}