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 #[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 fn get<K>(&self, key: K) -> Option<OsString>
49 where
50 K: AsRef<OsStr>;
51}
52
53pub trait Write {
54 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 fn from_str(config: &str) -> Result<HashMap<String, String>, Self::Error>;
114
115 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}