1use std::borrow::Cow;
2
3#[derive(Clone, Default, Debug, PartialEq, Eq)]
9pub struct ConfigParameters {
10 values: String,
11}
12
13impl ConfigParameters {
14 pub fn new() -> Self {
15 let values = std::env::var("GIT_CONFIG_PARAMETERS").unwrap_or_else(|_| Default::default());
16 Self { values }
17 }
18
19 pub fn iter(&self) -> ConfigParametersIter<'_> {
20 self.into_iter()
21 }
22}
23
24impl<'s> IntoIterator for &'s ConfigParameters {
25 type Item = (Cow<'s, str>, Option<Cow<'s, str>>);
26 type IntoIter = ConfigParametersIter<'s>;
27
28 fn into_iter(self) -> Self::IntoIter {
29 Self::IntoIter::new(&self.values)
30 }
31}
32
33#[derive(Clone, Default, Debug, PartialEq, Eq)]
39pub struct ConfigParametersIter<'s> {
40 values: &'s str,
41}
42
43impl<'s> ConfigParametersIter<'s> {
44 pub fn new(values: &'s str) -> Self {
45 Self { values }
46 }
47
48 pub fn iter(&self) -> impl Iterator<Item = (Cow<'_, str>, Cow<'_, str>)> + '_ {
49 None.into_iter()
50 }
51}
52
53impl<'s> Iterator for ConfigParametersIter<'s> {
54 type Item = (Cow<'s, str>, Option<Cow<'s, str>>);
55
56 fn next(&mut self) -> Option<Self::Item> {
57 self.values = self.values.trim_start();
59 let key = crate::quote::sq_dequote(&mut self.values).ok()?;
60
61 if let Some(values) = self.values.strip_prefix('=') {
62 self.values = values;
64
65 if self.values.is_empty() {
66 Some((key, None))
67 } else if let Some(values) = self.values.strip_prefix(' ') {
68 self.values = values;
69 Some((key, None))
70 } else {
71 let value = crate::quote::sq_dequote(&mut self.values).ok()?;
72 Some((key, Some(value)))
73 }
74 } else {
75 if self.values.is_empty() {
77 Some(parse_parameter_cow(key))
78 } else if let Some(values) = self.values.strip_prefix(' ') {
79 self.values = values;
80 Some(parse_parameter_cow(key))
81 } else {
82 self.values = "";
83 None
84 }
85 }
86 }
87}
88
89#[cfg(test)]
90mod test_env {
91 use super::*;
92
93 #[test]
94 fn empty() {
95 let fixture = "";
96 let config = ConfigParametersIter::new(fixture);
97 let actual: Vec<_> = config.collect();
98 assert_eq!(actual, vec![]);
99 }
100
101 #[test]
102 fn test_old() {
103 let fixture = "'delta.plus-style=green'";
104 let config = ConfigParametersIter::new(fixture);
105 let actual: Vec<_> = config.collect();
106 assert_eq!(
107 actual,
108 vec![(
109 Cow::Borrowed("delta.plus-style"),
110 Some(Cow::Borrowed("green"))
111 )]
112 );
113 }
114
115 #[test]
116 fn test_old_bool() {
117 let fixture = "'delta.plus-style'";
118 let config = ConfigParametersIter::new(fixture);
119 let actual: Vec<_> = config.collect();
120 assert_eq!(actual, vec![(Cow::Borrowed("delta.plus-style"), None)]);
121 }
122
123 #[test]
124 fn test_old_multiple() {
125 let fixture = "'delta.plus-style=green' 'delta.plus-style' 'delta.plus-style=green'";
126 let config = ConfigParametersIter::new(fixture);
127 let actual: Vec<_> = config.collect();
128 assert_eq!(
129 actual,
130 vec![
131 (
132 Cow::Borrowed("delta.plus-style"),
133 Some(Cow::Borrowed("green"))
134 ),
135 (Cow::Borrowed("delta.plus-style"), None),
136 (
137 Cow::Borrowed("delta.plus-style"),
138 Some(Cow::Borrowed("green"))
139 ),
140 ]
141 );
142 }
143
144 #[test]
145 fn test_new() {
146 let fixture = "'delta.plus-style'='green'";
147 let config = ConfigParametersIter::new(fixture);
148 let actual: Vec<_> = config.collect();
149 assert_eq!(
150 actual,
151 vec![(
152 Cow::Borrowed("delta.plus-style"),
153 Some(Cow::Borrowed("green"))
154 )]
155 );
156 }
157
158 #[test]
159 fn test_new_bool() {
160 let fixture = "'delta.plus-style'=";
161 let config = ConfigParametersIter::new(fixture);
162 let actual: Vec<_> = config.collect();
163 assert_eq!(actual, vec![(Cow::Borrowed("delta.plus-style"), None)]);
164 }
165
166 #[test]
167 fn test_new_multiple() {
168 let fixture = "'delta.plus-style'='green' 'delta.plus-style'= 'delta.plus-style'='green'";
169 let config = ConfigParametersIter::new(fixture);
170 let actual: Vec<_> = config.collect();
171 assert_eq!(
172 actual,
173 vec![
174 (
175 Cow::Borrowed("delta.plus-style"),
176 Some(Cow::Borrowed("green"))
177 ),
178 (Cow::Borrowed("delta.plus-style"), None),
179 (
180 Cow::Borrowed("delta.plus-style"),
181 Some(Cow::Borrowed("green"))
182 ),
183 ]
184 );
185 }
186}
187
188pub fn parse_parameter(arg: &str) -> (&str, Option<&str>) {
192 arg.split_once('=')
215 .map(|(k, v)| (k, Some(v)))
216 .unwrap_or((arg, None))
217}
218
219fn parse_parameter_cow(arg: Cow<'_, str>) -> (Cow<'_, str>, Option<Cow<'_, str>>) {
220 match arg {
221 Cow::Borrowed(arg) => {
222 let (key, value) = parse_parameter(arg);
223 (Cow::Borrowed(key), value.map(Cow::Borrowed))
224 }
225 Cow::Owned(arg) => {
226 let (key, value) = parse_parameter(arg.as_str());
227 (
228 Cow::Owned(key.to_owned()),
229 value.map(|v| Cow::Owned(v.to_owned())),
230 )
231 }
232 }
233}
234
235#[cfg(test)]
236mod test_parse_parameter {
237 use super::*;
238
239 #[test]
240 fn basic() {
241 let fixture = "key=value";
242 let expected = ("key", Some("value"));
243 let actual = parse_parameter(fixture);
244 assert_eq!(actual, expected);
245 }
246
247 #[test]
248 fn implied_bool() {
249 let fixture = "key";
250 let expected = ("key", None);
251 let actual = parse_parameter(fixture);
252 assert_eq!(actual, expected);
253 }
254
255 #[test]
256 fn multiple_eq() {
257 let fixture = "section.subsection=with=equals.key=value";
258 let expected = ("section.subsection", Some("with=equals.key=value"));
259 let actual = parse_parameter(fixture);
260 assert_eq!(actual, expected);
261 }
262}