1use crate::symbol::symbol_colon;
2use winnow::ascii::{multispace0, multispace1};
3use winnow::combinator::fail;
4use winnow::error::{StrContext, StrContextValue};
5use winnow::token::{literal, take_till, take_while};
6use winnow::{ModalResult as WResult, Parser};
7
8pub fn take_var_name<'a>(input: &mut &'a str) -> WResult<&'a str> {
9 let _ = multispace0.parse_next(input)?;
10 take_while(1.., ('0'..='9', 'A'..='Z', 'a'..='z', ['_', '.'])).parse_next(input)
11}
12
13pub fn take_json_path<'a>(input: &mut &'a str) -> WResult<&'a str> {
14 let _ = multispace0.parse_next(input)?;
15 take_while(
16 1..,
17 ('0'..='9', 'A'..='Z', 'a'..='z', ['_', '.', '/', '[', ']']),
18 )
19 .parse_next(input)
20}
21
22pub fn take_wild_key<'a>(input: &mut &'a str) -> WResult<&'a str> {
23 let _ = multispace0.parse_next(input)?;
24 take_while(
25 1..,
26 (
27 '0'..='9',
28 'A'..='Z',
29 'a'..='z',
30 ['_', '.', '*', '/', '[', ']'],
31 ),
32 )
33 .parse_next(input)
34}
35
36pub fn take_path<'a>(input: &mut &'a str) -> WResult<&'a str> {
37 let _ = multispace0.parse_next(input)?;
38 take_while(1.., ('0'..='9', 'A'..='Z', 'a'..='z', ['_', '.', '/'], '-')).parse_next(input)
39}
40
41pub fn take_string<'a>(input: &mut &'a str) -> WResult<&'a str> {
42 let _ = multispace0.parse_next(input)?;
43 take_while(1.., |c: char| {
44 c.is_alphanumeric() || matches!(c, '_' | '.' | '/' | '-')
45 })
46 .parse_next(input)
47}
48
49pub fn take_obj_path<'a>(input: &mut &'a str) -> WResult<&'a str> {
50 let _ = multispace0.parse_next(input)?;
51 let key = take_while(1.., ('0'..='9', 'A'..='Z', 'a'..='z', ['_', '/'])).parse_next(input)?;
52 let _ = multispace1.parse_next(input)?;
53 Ok(key)
54}
55
56pub fn take_obj_wild_path<'a>(input: &mut &'a str) -> WResult<&'a str> {
57 let _ = multispace0.parse_next(input)?;
58 let key =
59 take_while(1.., ('0'..='9', 'A'..='Z', 'a'..='z', ['_', '/', '*'])).parse_next(input)?;
60 let _ = multispace1.parse_next(input)?;
61 Ok(key)
62}
63
64pub fn take_key_pair<'a>(input: &mut &'a str) -> WResult<(&'a str, &'a str)> {
65 let _ = multispace0.parse_next(input)?;
66 let key = take_while(1.., ('0'..='9', 'A'..='Z', 'a'..='z', ['_', '.'])).parse_next(input)?;
67 symbol_colon.parse_next(input)?;
68 let _ = multispace0.parse_next(input)?;
69 let val = take_while(1.., ('0'..='9', 'A'..='Z', 'a'..='z', ['_', '.'])).parse_next(input)?;
70 Ok((key, val))
71}
72
73pub fn take_key_val<'a>(input: &mut &'a str) -> WResult<(&'a str, &'a str)> {
74 let _ = multispace0.parse_next(input)?;
75 let key = take_while(1.., ('0'..='9', 'A'..='Z', 'a'..='z', ['_', '.'])).parse_next(input)?;
76 symbol_colon.parse_next(input)?;
77 let _ = multispace0.parse_next(input)?;
78 let val = take_till(1.., |c| c == ',' || c == ';').parse_next(input)?;
79 Ok((key, val))
80}
81pub fn take_empty(input: &mut &str) -> WResult<()> {
82 let _ = multispace0.parse_next(input)?;
83 Ok(())
84}
85
86pub fn take_parentheses_val(data: &mut &str) -> WResult<String> {
87 use crate::scope::ScopeEval;
88 use winnow::token::take;
89
90 let _ = multispace0.parse_next(data)?;
91
92 let scope_len = ScopeEval::len(data, '(', ')');
94
95 if scope_len < 2 {
96 return fail
97 .context(StrContext::Label("syntax"))
98 .context(StrContext::Expected(StrContextValue::Description(
99 "need match '(x)', lack '(' or unbalanced parentheses",
100 )))
101 .parse_next(data);
102 }
103
104 literal("(")
106 .context(StrContext::Label("syntax"))
107 .context(StrContext::Expected(StrContextValue::Description(
108 "need match '(x)', lack '('",
109 )))
110 .parse_next(data)?;
111
112 let content = take(scope_len - 2).parse_next(data)?;
114
115 literal(")")
117 .context(StrContext::Label("syntax"))
118 .context(StrContext::Expected(StrContextValue::Description(
119 "need match '(x)', lack ')'",
120 )))
121 .parse_next(data)?;
122
123 let trimmed = content.trim();
124
125 let result = if (trimmed.starts_with('\'') && trimmed.ends_with('\''))
127 || (trimmed.starts_with('"') && trimmed.ends_with('"'))
128 {
129 if trimmed.len() >= 2 {
131 let unquoted = &trimmed[1..trimmed.len() - 1];
132 unquoted
134 .replace("\\n", "\n")
135 .replace("\\r", "\r")
136 .replace("\\t", "\t")
137 .replace("\\\\", "\\")
138 .replace("\\'", "'")
139 .replace("\\\"", "\"")
140 } else {
141 String::new()
142 }
143 } else {
144 trimmed.to_string()
146 };
147
148 Ok(result)
149}
150
151pub fn take_parentheses_scope<'a>(data: &mut &'a str) -> WResult<(&'a str, &'a str)> {
152 let _ = multispace0.parse_next(data)?;
153 literal("(").parse_next(data)?;
154 let beg = take_till(0.., |x| x == ',').parse_next(data)?;
155 literal(",").parse_next(data)?;
156 let _ = multispace0.parse_next(data)?;
157 let end = take_till(0.., |x| x == ')').parse_next(data)?;
158 literal(")").parse_next(data)?;
159 Ok((beg, end))
160}
161
162#[cfg(test)]
163mod tests {
164 use crate::atom::{take_parentheses_val, take_var_name};
165 use winnow::{ModalResult as WResult, Parser};
166
167 mod var_name {
168 use super::*;
169
170 #[test]
171 fn valid_simple_name() -> WResult<()> {
172 let mut data = "x";
173 let key = take_var_name.parse_next(&mut data)?;
174 assert_eq!(key, "x");
175 Ok(())
176 }
177
178 #[test]
179 fn valid_name_followed_by_parens() -> WResult<()> {
180 let mut data = "x(10)";
181 let key = take_var_name.parse_next(&mut data)?;
182 assert_eq!(key, "x");
183 assert_eq!(data, "(10)");
184 Ok(())
185 }
186
187 #[test]
188 fn valid_alphanumeric() -> WResult<()> {
189 let mut data = "x10(10)";
190 let key = take_var_name.parse_next(&mut data)?;
191 assert_eq!(key, "x10");
192 Ok(())
193 }
194
195 #[test]
196 fn valid_with_leading_space() -> WResult<()> {
197 let mut data = " x10 (10)";
198 let key = take_var_name.parse_next(&mut data)?;
199 assert_eq!(key, "x10");
200 Ok(())
201 }
202
203 #[test]
204 fn valid_with_underscore() -> WResult<()> {
205 let mut data = " x_1 (10)";
206 let key = take_var_name.parse_next(&mut data)?;
207 assert_eq!(key, "x_1");
208 Ok(())
209 }
210
211 #[test]
212 fn valid_with_dot() -> WResult<()> {
213 let mut data = "foo.bar.baz";
214 let key = take_var_name.parse_next(&mut data)?;
215 assert_eq!(key, "foo.bar.baz");
216 Ok(())
217 }
218
219 #[test]
220 fn valid_complex_path() -> WResult<()> {
221 let mut data = "user_data.profile.name_field";
222 let key = take_var_name.parse_next(&mut data)?;
223 assert_eq!(key, "user_data.profile.name_field");
224 Ok(())
225 }
226
227 #[test]
228 fn invalid_empty_input() {
229 let mut data = "";
230 let result = take_var_name.parse_next(&mut data);
231 assert!(result.is_err(), "Should fail on empty input");
232 }
233
234 #[test]
235 fn invalid_starts_with_special() {
236 let mut data = "@invalid";
237 let result = take_var_name.parse_next(&mut data);
238 assert!(result.is_err(), "Should fail on special characters");
239 }
240
241 #[test]
242 fn invalid_only_whitespace() {
243 let mut data = " ";
244 let result = take_var_name.parse_next(&mut data);
245 assert!(result.is_err(), "Should fail on only whitespace");
246 }
247 }
248
249 mod parentheses_val {
250 use super::*;
251
252 #[test]
253 fn valid_simple_value() -> WResult<()> {
254 let mut data = "(hello)";
255 let val = take_parentheses_val.parse_next(&mut data)?;
256 assert_eq!(val, "hello");
257 Ok(())
258 }
259
260 #[test]
261 fn valid_nested_parens() -> WResult<()> {
262 let mut data = "(outer(inner)value)";
263 let val = take_parentheses_val.parse_next(&mut data)?;
264 assert_eq!(val, "outer(inner)value");
265 Ok(())
266 }
267
268 #[test]
269 fn valid_deeply_nested() -> WResult<()> {
270 let mut data = "(a(b(c)d)e)";
271 let val = take_parentheses_val.parse_next(&mut data)?;
272 assert_eq!(val, "a(b(c)d)e");
273 Ok(())
274 }
275
276 #[test]
277 fn valid_with_leading_space() -> WResult<()> {
278 let mut data = " (value)";
279 let val = take_parentheses_val.parse_next(&mut data)?;
280 assert_eq!(val, "value");
281 Ok(())
282 }
283
284 #[test]
285 fn valid_trims_internal_space() -> WResult<()> {
286 let mut data = "( value )";
287 let val = take_parentheses_val.parse_next(&mut data)?;
288 assert_eq!(val, "value");
289 Ok(())
290 }
291
292 #[test]
293 fn invalid_missing_open_paren() {
294 let mut data = "value)";
295 let result = take_parentheses_val.parse_next(&mut data);
296 assert!(result.is_err(), "Should fail on missing '('");
297 }
298
299 #[test]
300 fn invalid_missing_close_paren() {
301 let mut data = "(value";
302 let result = take_parentheses_val.parse_next(&mut data);
303 assert!(result.is_err(), "Should fail on missing ')'");
304 }
305
306 #[test]
307 fn invalid_unbalanced_nested() {
308 let mut data = "(value"; let result = take_parentheses_val.parse_next(&mut data);
310 assert!(result.is_err(), "Should fail on unbalanced parentheses");
311 }
312
313 #[test]
314 fn invalid_empty_input() {
315 let mut data = "";
316 let result = take_parentheses_val.parse_next(&mut data);
317 assert!(result.is_err(), "Should fail on empty input");
318 }
319
320 #[test]
321 fn quoted_single_quotes() -> WResult<()> {
322 let mut data = "('hello world')";
323 let val = take_parentheses_val.parse_next(&mut data)?;
324 assert_eq!(val, "hello world");
325 Ok(())
326 }
327
328 #[test]
329 fn quoted_double_quotes() -> WResult<()> {
330 let mut data = r#"("hello world")"#;
331 let val = take_parentheses_val.parse_next(&mut data)?;
332 assert_eq!(val, "hello world");
333 Ok(())
334 }
335
336 #[test]
337 fn quoted_with_escape_sequences() -> WResult<()> {
338 let mut data = r#"('hello\nworld\ttab')"#;
339 let val = take_parentheses_val.parse_next(&mut data)?;
340 assert_eq!(val, "hello\nworld\ttab");
341 Ok(())
342 }
343
344 #[test]
345 fn quoted_with_escaped_quotes() -> WResult<()> {
346 let mut data = r#"('it\'s working')"#;
347 let val = take_parentheses_val.parse_next(&mut data)?;
348 assert_eq!(val, "it's working");
349 Ok(())
350 }
351
352 #[test]
353 fn unquoted_backward_compatible() -> WResult<()> {
354 let mut data = "(hello)";
355 let val = take_parentheses_val.parse_next(&mut data)?;
356 assert_eq!(val, "hello");
357 Ok(())
358 }
359
360 #[test]
361 fn unquoted_with_special_chars() -> WResult<()> {
362 let mut data = "(1.0.0)";
363 let val = take_parentheses_val.parse_next(&mut data)?;
364 assert_eq!(val, "1.0.0");
365 Ok(())
366 }
367 }
368}