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