1 use winnow::combinator::cut_err;
2 use winnow::combinator::delimited;
3 use winnow::combinator::separated0;
4 use winnow::token::one_of;
5 use winnow::trace::trace;
6 
7 use crate::key::Key;
8 use crate::parser::error::CustomError;
9 use crate::parser::key::key;
10 use crate::parser::prelude::*;
11 use crate::parser::trivia::ws;
12 use crate::parser::value::value;
13 use crate::table::TableKeyValue;
14 use crate::{InlineTable, InternalString, Item, RawString, Value};
15 
16 use indexmap::map::Entry;
17 
18 // ;; Inline Table
19 
20 // inline-table = inline-table-open inline-table-keyvals inline-table-close
inline_table<'i>( check: RecursionCheck, ) -> impl Parser<Input<'i>, InlineTable, ContextError>21 pub(crate) fn inline_table<'i>(
22     check: RecursionCheck,
23 ) -> impl Parser<Input<'i>, InlineTable, ContextError> {
24     trace("inline-table", move |input: &mut Input<'i>| {
25         delimited(
26             INLINE_TABLE_OPEN,
27             cut_err(inline_table_keyvals(check).try_map(|(kv, p)| table_from_pairs(kv, p))),
28             cut_err(INLINE_TABLE_CLOSE)
29                 .context(StrContext::Label("inline table"))
30                 .context(StrContext::Expected(StrContextValue::CharLiteral('}'))),
31         )
32         .parse_next(input)
33     })
34 }
35 
table_from_pairs( v: Vec<(Vec<Key>, TableKeyValue)>, preamble: RawString, ) -> Result<InlineTable, CustomError>36 fn table_from_pairs(
37     v: Vec<(Vec<Key>, TableKeyValue)>,
38     preamble: RawString,
39 ) -> Result<InlineTable, CustomError> {
40     let mut root = InlineTable::new();
41     root.set_preamble(preamble);
42     // Assuming almost all pairs will be directly in `root`
43     root.items.reserve(v.len());
44 
45     for (path, kv) in v {
46         let table = descend_path(&mut root, &path)?;
47 
48         // "Likewise, using dotted keys to redefine tables already defined in [table] form is not allowed"
49         let mixed_table_types = table.is_dotted() == path.is_empty();
50         if mixed_table_types {
51             return Err(CustomError::DuplicateKey {
52                 key: kv.key.get().into(),
53                 table: None,
54             });
55         }
56 
57         let key: InternalString = kv.key.get_internal().into();
58         match table.items.entry(key) {
59             Entry::Vacant(o) => {
60                 o.insert(kv);
61             }
62             Entry::Occupied(o) => {
63                 return Err(CustomError::DuplicateKey {
64                     key: o.key().as_str().into(),
65                     table: None,
66                 });
67             }
68         }
69     }
70     Ok(root)
71 }
72 
descend_path<'a>( mut table: &'a mut InlineTable, path: &'a [Key], ) -> Result<&'a mut InlineTable, CustomError>73 fn descend_path<'a>(
74     mut table: &'a mut InlineTable,
75     path: &'a [Key],
76 ) -> Result<&'a mut InlineTable, CustomError> {
77     let dotted = !path.is_empty();
78     for (i, key) in path.iter().enumerate() {
79         let entry = table.entry_format(key).or_insert_with(|| {
80             let mut new_table = InlineTable::new();
81             new_table.set_implicit(dotted);
82             new_table.set_dotted(dotted);
83 
84             Value::InlineTable(new_table)
85         });
86         match *entry {
87             Value::InlineTable(ref mut sweet_child_of_mine) => {
88                 // Since tables cannot be defined more than once, redefining such tables using a
89                 // [table] header is not allowed. Likewise, using dotted keys to redefine tables
90                 // already defined in [table] form is not allowed.
91                 if dotted && !sweet_child_of_mine.is_implicit() {
92                     return Err(CustomError::DuplicateKey {
93                         key: key.get().into(),
94                         table: None,
95                     });
96                 }
97                 table = sweet_child_of_mine;
98             }
99             ref v => {
100                 return Err(CustomError::extend_wrong_type(path, i, v.type_name()));
101             }
102         }
103     }
104     Ok(table)
105 }
106 
107 // inline-table-open  = %x7B ws     ; {
108 pub(crate) const INLINE_TABLE_OPEN: u8 = b'{';
109 // inline-table-close = ws %x7D     ; }
110 const INLINE_TABLE_CLOSE: u8 = b'}';
111 // inline-table-sep   = ws %x2C ws  ; , Comma
112 const INLINE_TABLE_SEP: u8 = b',';
113 // keyval-sep = ws %x3D ws ; =
114 pub(crate) const KEYVAL_SEP: u8 = b'=';
115 
116 // inline-table-keyvals = [ inline-table-keyvals-non-empty ]
117 // inline-table-keyvals-non-empty =
118 // ( key keyval-sep val inline-table-sep inline-table-keyvals-non-empty ) /
119 // ( key keyval-sep val )
120 
inline_table_keyvals<'i>( check: RecursionCheck, ) -> impl Parser<Input<'i>, (Vec<(Vec<Key>, TableKeyValue)>, RawString), ContextError>121 fn inline_table_keyvals<'i>(
122     check: RecursionCheck,
123 ) -> impl Parser<Input<'i>, (Vec<(Vec<Key>, TableKeyValue)>, RawString), ContextError> {
124     move |input: &mut Input<'i>| {
125         let check = check.recursing(input)?;
126         (
127             separated0(keyval(check), INLINE_TABLE_SEP),
128             ws.span().map(RawString::with_span),
129         )
130             .parse_next(input)
131     }
132 }
133 
keyval<'i>( check: RecursionCheck, ) -> impl Parser<Input<'i>, (Vec<Key>, TableKeyValue), ContextError>134 fn keyval<'i>(
135     check: RecursionCheck,
136 ) -> impl Parser<Input<'i>, (Vec<Key>, TableKeyValue), ContextError> {
137     move |input: &mut Input<'i>| {
138         (
139             key,
140             cut_err((
141                 one_of(KEYVAL_SEP)
142                     .context(StrContext::Expected(StrContextValue::CharLiteral('.')))
143                     .context(StrContext::Expected(StrContextValue::CharLiteral('='))),
144                 (ws.span(), value(check), ws.span()),
145             )),
146         )
147             .map(|(key, (_, v))| {
148                 let mut path = key;
149                 let key = path.pop().expect("grammar ensures at least 1");
150 
151                 let (pre, v, suf) = v;
152                 let pre = RawString::with_span(pre);
153                 let suf = RawString::with_span(suf);
154                 let v = v.decorated(pre, suf);
155                 (
156                     path,
157                     TableKeyValue {
158                         key,
159                         value: Item::Value(v),
160                     },
161                 )
162             })
163             .parse_next(input)
164     }
165 }
166 
167 #[cfg(test)]
168 #[cfg(feature = "parse")]
169 #[cfg(feature = "display")]
170 mod test {
171     use super::*;
172 
173     #[test]
inline_tables()174     fn inline_tables() {
175         let inputs = [
176             r#"{}"#,
177             r#"{   }"#,
178             r#"{a = 1e165}"#,
179             r#"{ hello = "world", a = 1}"#,
180             r#"{ hello.world = "a" }"#,
181         ];
182         for input in inputs {
183             dbg!(input);
184             let mut parsed = inline_table(Default::default()).parse(new_input(input));
185             if let Ok(parsed) = &mut parsed {
186                 parsed.despan(input);
187             }
188             assert_eq!(parsed.map(|a| a.to_string()), Ok(input.to_owned()));
189         }
190     }
191 
192     #[test]
invalid_inline_tables()193     fn invalid_inline_tables() {
194         let invalid_inputs = [r#"{a = 1e165"#, r#"{ hello = "world", a = 2, hello = 1}"#];
195         for input in invalid_inputs {
196             dbg!(input);
197             let mut parsed = inline_table(Default::default()).parse(new_input(input));
198             if let Ok(parsed) = &mut parsed {
199                 parsed.despan(input);
200             }
201             assert!(parsed.is_err());
202         }
203     }
204 }
205