toml_edit/parser/
array.rs

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