toml_edit/parser/
array.rs

1use winnow::combinator::cut_err;
2use winnow::combinator::delimited;
3use winnow::combinator::opt;
4use winnow::combinator::peek;
5use winnow::combinator::separated;
6use winnow::combinator::trace;
7
8use crate::parser::trivia::ws_comment_newline;
9use crate::parser::value::value;
10use crate::{Array, Item, RawString};
11
12use crate::parser::prelude::*;
13
14// ;; Array
15
16// array = array-open array-values array-close
17pub(crate) fn array<'i>(input: &mut Input<'i>) -> ModalResult<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  ; [
34pub(crate) const ARRAY_OPEN: u8 = b'[';
35// array-close = ws-newline %x5D  ; ]
36const ARRAY_CLOSE: u8 = b']';
37// array-sep = ws %x2C ws  ; , Comma
38const 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 ]
42pub(crate) fn array_values(input: &mut Input<'_>) -> ModalResult<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
60pub(crate) fn array_value(input: &mut Input<'_>) -> ModalResult<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")]
72mod test {
73    use super::*;
74
75    #[test]
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]
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}