1use std::borrow::Cow;
2
3#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
13pub struct ConfigEnv<E: Env> {
14 e: E,
15}
16
17impl ConfigEnv<StdEnv> {
18 pub fn new() -> Self {
19 Self { e: StdEnv }
20 }
21}
22
23impl ConfigEnv<NoEnv> {
24 pub fn empty() -> Self {
25 Self { e: NoEnv }
26 }
27}
28
29impl<E: Env> ConfigEnv<E> {
30 pub fn with_env(e: E) -> Self {
31 Self { e }
32 }
33
34 pub fn iter(&self) -> ConfigEnvIter<'_, E> {
35 self.into_iter()
36 }
37}
38
39impl<'e, E: Env> IntoIterator for &'e ConfigEnv<E> {
40 type Item = (Cow<'e, str>, Cow<'e, str>);
41 type IntoIter = ConfigEnvIter<'e, E>;
42
43 fn into_iter(self) -> Self::IntoIter {
44 let i = 0;
45 let max = self
46 .e
47 .var("GIT_CONFIG_COUNT")
48 .ok()
49 .and_then(|m| m.parse().ok())
50 .unwrap_or(0);
51 Self::IntoIter { e: &self.e, max, i }
52 }
53}
54
55impl<K, V> FromIterator<(K, V)> for ConfigEnv<std::collections::HashMap<String, String>>
56where
57 K: Into<String>,
58 V: Into<String>,
59{
60 fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
61 let e = iter
62 .into_iter()
63 .map(|(k, v)| (k.into(), v.into()))
64 .collect();
65 Self { e }
66 }
67}
68
69#[derive(Copy, Clone, Debug, PartialEq, Eq)]
71pub struct ConfigEnvIter<'e, E: Env> {
72 e: &'e E,
73 max: usize,
74 i: usize,
75}
76
77impl<'e, E: Env> Iterator for ConfigEnvIter<'e, E> {
78 type Item = (Cow<'e, str>, Cow<'e, str>);
79
80 fn next(&mut self) -> Option<Self::Item> {
81 while self.i < self.max {
83 let key_key = format!("GIT_CONFIG_KEY_{}", self.i);
84 let value_key = format!("GIT_CONFIG_VALUE_{}", self.i);
85 self.i += 1;
86 if let (Ok(key), Ok(value)) = (self.e.var(&key_key), self.e.var(&value_key)) {
87 return Some((key, value));
88 }
89 }
90 None
91 }
92}
93
94pub trait Env {
96 fn var(&self, key: &str) -> Result<Cow<'_, str>, std::env::VarError>;
97}
98
99#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
101pub struct StdEnv;
102
103impl Env for StdEnv {
104 fn var(&self, key: &str) -> Result<Cow<'_, str>, std::env::VarError> {
105 std::env::var(key).map(Cow::Owned)
106 }
107}
108
109#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
111pub struct NoEnv;
112
113impl Env for NoEnv {
114 fn var(&self, _key: &str) -> Result<Cow<'_, str>, std::env::VarError> {
115 Err(std::env::VarError::NotPresent)
116 }
117}
118
119impl Env for std::collections::HashMap<String, String> {
120 fn var(&self, key: &str) -> Result<Cow<'_, str>, std::env::VarError> {
121 self.get(key)
122 .map(|v| v.as_str())
123 .map(Cow::Borrowed)
124 .ok_or(std::env::VarError::NotPresent)
125 }
126}
127
128#[cfg(test)]
129mod test {
130 use super::*;
131
132 #[test]
133 fn implicitly_empty() {
134 let c = ConfigEnv::empty();
135 assert_eq!(c.iter().collect::<Vec<_>>(), vec![]);
136 }
137
138 #[test]
139 fn explicitly_empty() {
140 let c: ConfigEnv<_> = vec![("GIT_CONFIG_COUNT", "0")].into_iter().collect();
141 assert_eq!(c.iter().collect::<Vec<_>>(), vec![]);
142 }
143
144 #[test]
145 fn bad_count() {
146 let c: ConfigEnv<_> = vec![("GIT_CONFIG_COUNT", "")].into_iter().collect();
147 assert_eq!(c.iter().collect::<Vec<_>>(), vec![]);
148
149 let c: ConfigEnv<_> = vec![("GIT_CONFIG_COUNT", "-1")].into_iter().collect();
150 assert_eq!(c.iter().collect::<Vec<_>>(), vec![]);
151
152 let c: ConfigEnv<_> = vec![("GIT_CONFIG_COUNT", "County McCountFace")]
153 .into_iter()
154 .collect();
155 assert_eq!(c.iter().collect::<Vec<_>>(), vec![]);
156 }
157
158 #[test]
159 fn single() {
160 let c: ConfigEnv<_> = vec![
161 ("GIT_CONFIG_COUNT", "1"),
162 ("GIT_CONFIG_KEY_0", "key"),
163 ("GIT_CONFIG_VALUE_0", "value"),
164 ]
165 .into_iter()
166 .collect();
167 assert_eq!(
168 c.iter().collect::<Vec<_>>(),
169 vec![(Cow::Borrowed("key"), Cow::Borrowed("value"))]
170 );
171 }
172
173 #[test]
174 fn multiple() {
175 let c: ConfigEnv<_> = vec![
176 ("GIT_CONFIG_COUNT", "3"),
177 ("GIT_CONFIG_KEY_0", "key"),
178 ("GIT_CONFIG_VALUE_0", "value"),
179 ("GIT_CONFIG_KEY_1", "one_key"),
180 ("GIT_CONFIG_VALUE_1", "one_value"),
181 ("GIT_CONFIG_KEY_2", "two_key"),
182 ("GIT_CONFIG_VALUE_2", "two_value"),
183 ]
184 .into_iter()
185 .collect();
186 assert_eq!(
187 c.iter().collect::<Vec<_>>(),
188 vec![
189 (Cow::Borrowed("key"), Cow::Borrowed("value")),
190 (Cow::Borrowed("one_key"), Cow::Borrowed("one_value")),
191 (Cow::Borrowed("two_key"), Cow::Borrowed("two_value")),
192 ]
193 );
194 }
195}