1use super::*;
2
3#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
30pub struct Positional {
31 pub overrides: Vec<(String, String)>,
33 pub search_directory: Option<String>,
35 pub arguments: Vec<String>,
37}
38
39impl Positional {
40 pub fn from_values<'values>(values: Option<impl IntoIterator<Item = &'values str>>) -> Self {
41 let mut overrides = Vec::new();
42 let mut search_directory = None;
43 let mut arguments = Vec::new();
44
45 if let Some(values) = values {
46 for value in values {
47 if search_directory.is_none() && arguments.is_empty() {
48 if let Some(o) = Self::override_from_value(value) {
49 overrides.push(o);
50 } else if value == "." || value == ".." {
51 search_directory = Some(value.to_owned());
52 } else if let Some(i) = value.rfind('/') {
53 let (dir, tail) = value.split_at(i + 1);
54
55 search_directory = Some(dir.to_owned());
56
57 if !tail.is_empty() {
58 arguments.push(tail.to_owned());
59 }
60 } else {
61 arguments.push(value.to_owned());
62 }
63 } else {
64 arguments.push(value.to_owned());
65 }
66 }
67 }
68
69 Self {
70 overrides,
71 search_directory,
72 arguments,
73 }
74 }
75
76 fn override_from_value(value: &str) -> Option<(String, String)> {
78 let equals = value.find('=')?;
79
80 let (identifier, equals_value) = value.split_at(equals);
81
82 let value = &equals_value[1..];
84
85 if Lexer::is_identifier(identifier) {
86 Some((identifier.to_owned(), value.to_owned()))
87 } else {
88 None
89 }
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96
97 use pretty_assertions::assert_eq;
98
99 macro_rules! test {
100 {
101 name: $name:ident,
102 values: $vals:expr,
103 overrides: $overrides:expr,
104 search_directory: $search_directory:expr,
105 arguments: $arguments:expr,
106 } => {
107 #[test]
108 fn $name() {
109 assert_eq! (
110 Positional::from_values(Some($vals.iter().cloned())),
111 Positional {
112 overrides: $overrides
113 .iter()
114 .cloned()
115 .map(|(key, value): (&str, &str)| (key.to_owned(), value.to_owned()))
116 .collect(),
117 search_directory: $search_directory.map(str::to_owned),
118 arguments: $arguments.iter().cloned().map(str::to_owned).collect(),
119 },
120 )
121 }
122 }
123 }
124
125 test! {
126 name: no_values,
127 values: [],
128 overrides: [],
129 search_directory: None,
130 arguments: [],
131 }
132
133 test! {
134 name: arguments_only,
135 values: ["foo", "bar"],
136 overrides: [],
137 search_directory: None,
138 arguments: ["foo", "bar"],
139 }
140
141 test! {
142 name: all_overrides,
143 values: ["foo=bar", "bar=foo"],
144 overrides: [("foo", "bar"), ("bar", "foo")],
145 search_directory: None,
146 arguments: [],
147 }
148
149 test! {
150 name: override_not_name,
151 values: ["foo=bar", "bar.=foo"],
152 overrides: [("foo", "bar")],
153 search_directory: None,
154 arguments: ["bar.=foo"],
155 }
156
157 test! {
158 name: no_overrides,
159 values: ["the-dir/", "baz", "bzzd"],
160 overrides: [],
161 search_directory: Some("the-dir/"),
162 arguments: ["baz", "bzzd"],
163 }
164
165 test! {
166 name: no_search_directory,
167 values: ["foo=bar", "bar=foo", "baz", "bzzd"],
168 overrides: [("foo", "bar"), ("bar", "foo")],
169 search_directory: None,
170 arguments: ["baz", "bzzd"],
171 }
172
173 test! {
174 name: no_arguments,
175 values: ["foo=bar", "bar=foo", "the-dir/"],
176 overrides: [("foo", "bar"), ("bar", "foo")],
177 search_directory: Some("the-dir/"),
178 arguments: [],
179 }
180
181 test! {
182 name: all_dot,
183 values: ["foo=bar", "bar=foo", ".", "garnor"],
184 overrides: [("foo", "bar"), ("bar", "foo")],
185 search_directory: Some("."),
186 arguments: ["garnor"],
187 }
188
189 test! {
190 name: all_dot_dot,
191 values: ["foo=bar", "bar=foo", "..", "garnor"],
192 overrides: [("foo", "bar"), ("bar", "foo")],
193 search_directory: Some(".."),
194 arguments: ["garnor"],
195 }
196
197 test! {
198 name: all_slash,
199 values: ["foo=bar", "bar=foo", "/", "garnor"],
200 overrides: [("foo", "bar"), ("bar", "foo")],
201 search_directory: Some("/"),
202 arguments: ["garnor"],
203 }
204
205 test! {
206 name: search_directory_after_argument,
207 values: ["foo=bar", "bar=foo", "baz", "bzzd", "bar/"],
208 overrides: [("foo", "bar"), ("bar", "foo")],
209 search_directory: None,
210 arguments: ["baz", "bzzd", "bar/"],
211 }
212
213 test! {
214 name: override_after_search_directory,
215 values: ["..", "a=b"],
216 overrides: [],
217 search_directory: Some(".."),
218 arguments: ["a=b"],
219 }
220
221 test! {
222 name: override_after_argument,
223 values: ["a", "a=b"],
224 overrides: [],
225 search_directory: None,
226 arguments: ["a", "a=b"],
227 }
228}