1 use winnow::combinator::cut_err;
2 use winnow::combinator::delimited;
3 use winnow::combinator::opt;
4 use winnow::combinator::separated1;
5 use winnow::trace::trace;
6 
7 use crate::parser::trivia::ws_comment_newline;
8 use crate::parser::value::value;
9 use crate::{Array, Item, RawString, Value};
10 
11 use crate::parser::prelude::*;
12 
13 // ;; Array
14 
15 // array = array-open array-values array-close
array<'i>(check: RecursionCheck) -> impl Parser<Input<'i>, Array, ContextError>16 pub(crate) fn array<'i>(check: RecursionCheck) -> impl Parser<Input<'i>, Array, ContextError> {
17     trace("array", move |input: &mut Input<'i>| {
18         delimited(
19             ARRAY_OPEN,
20             cut_err(array_values(check)),
21             cut_err(ARRAY_CLOSE)
22                 .context(StrContext::Label("array"))
23                 .context(StrContext::Expected(StrContextValue::CharLiteral(']'))),
24         )
25         .parse_next(input)
26     })
27 }
28 
29 // note: we're omitting ws and newlines here, because
30 // they should be part of the formatted values
31 // array-open  = %x5B ws-newline  ; [
32 pub(crate) const ARRAY_OPEN: u8 = b'[';
33 // array-close = ws-newline %x5D  ; ]
34 const ARRAY_CLOSE: u8 = b']';
35 // array-sep = ws %x2C ws  ; , Comma
36 const ARRAY_SEP: u8 = b',';
37 
38 // note: this rule is modified
39 // array-values = [ ( array-value array-sep array-values ) /
40 //                  array-value / ws-comment-newline ]
array_values<'i>( check: RecursionCheck, ) -> impl Parser<Input<'i>, Array, ContextError>41 pub(crate) fn array_values<'i>(
42     check: RecursionCheck,
43 ) -> impl Parser<Input<'i>, Array, ContextError> {
44     move |input: &mut Input<'i>| {
45         let check = check.recursing(input)?;
46         (
47             opt(
48                 (separated1(array_value(check), ARRAY_SEP), opt(ARRAY_SEP)).map(
49                     |(v, trailing): (Vec<Value>, Option<u8>)| {
50                         (
51                             Array::with_vec(v.into_iter().map(Item::Value).collect()),
52                             trailing.is_some(),
53                         )
54                     },
55                 ),
56             ),
57             ws_comment_newline.span(),
58         )
59             .try_map::<_, _, std::str::Utf8Error>(|(array, trailing)| {
60                 let (mut array, comma) = array.unwrap_or_default();
61                 array.set_trailing_comma(comma);
62                 array.set_trailing(RawString::with_span(trailing));
63                 Ok(array)
64             })
65             .parse_next(input)
66     }
67 }
68 
array_value<'i>( check: RecursionCheck, ) -> impl Parser<Input<'i>, Value, ContextError>69 pub(crate) fn array_value<'i>(
70     check: RecursionCheck,
71 ) -> impl Parser<Input<'i>, Value, ContextError> {
72     move |input: &mut Input<'i>| {
73         (
74             ws_comment_newline.span(),
75             value(check),
76             ws_comment_newline.span(),
77         )
78             .map(|(ws1, v, ws2)| v.decorated(RawString::with_span(ws1), RawString::with_span(ws2)))
79             .parse_next(input)
80     }
81 }
82 
83 #[cfg(test)]
84 #[cfg(feature = "parse")]
85 #[cfg(feature = "display")]
86 mod test {
87     use super::*;
88 
89     #[test]
arrays()90     fn arrays() {
91         let inputs = [
92             r#"[]"#,
93             r#"[   ]"#,
94             r#"[
95   1, 2, 3
96 ]"#,
97             r#"[
98   1,
99   2, # this is ok
100 ]"#,
101             r#"[# comment
102 # comment2
103 
104 
105    ]"#,
106             r#"[# comment
107 # comment2
108       1
109 
110 #sd
111 ,
112 # comment3
113 
114    ]"#,
115             r#"[1]"#,
116             r#"[1,]"#,
117             r#"[ "all", 'strings', """are the same""", '''type''']"#,
118             r#"[ 100, -2,]"#,
119             r#"[1, 2, 3]"#,
120             r#"[1.1, 2.1, 3.1]"#,
121             r#"["a", "b", "c"]"#,
122             r#"[ [ 1, 2 ], [3, 4, 5] ]"#,
123             r#"[ [ 1, 2 ], ["a", "b", "c"] ]"#,
124             r#"[ { x = 1, a = "2" }, {a = "a",b = "b",     c =    "c"} ]"#,
125         ];
126         for input in inputs {
127             dbg!(input);
128             let mut parsed = array(Default::default()).parse(new_input(input));
129             if let Ok(parsed) = &mut parsed {
130                 parsed.despan(input);
131             }
132             assert_eq!(parsed.map(|a| a.to_string()), Ok(input.to_owned()));
133         }
134     }
135 
136     #[test]
invalid_arrays()137     fn invalid_arrays() {
138         let invalid_inputs = [r#"["#, r#"[,]"#, r#"[,2]"#, r#"[1e165,,]"#];
139         for input in invalid_inputs {
140             dbg!(input);
141             let mut parsed = array(Default::default()).parse(new_input(input));
142             if let Ok(parsed) = &mut parsed {
143                 parsed.despan(input);
144             }
145             assert!(parsed.is_err());
146         }
147     }
148 }
149