1use winnow::combinator::cut_err;
2use winnow::combinator::delimited;
3use winnow::combinator::separated0;
4use winnow::token::one_of;
5use winnow::trace::trace;
6
7use crate::key::Key;
8use crate::parser::errors::CustomError;
9use crate::parser::key::key;
10use crate::parser::prelude::*;
11use crate::parser::trivia::ws;
12use crate::parser::value::value;
13use crate::table::TableKeyValue;
14use crate::{InlineTable, InternalString, Item, RawString, Value};
15
16use indexmap::map::Entry;
17
18// ;; Inline Table
19
20// inline-table = inline-table-open inline-table-keyvals inline-table-close
21pub(crate) fn inline_table<'i>(
22 check: RecursionCheck,
23) -> impl Parser<Input<'i>, InlineTable, ContextError> {
24 trace(name:"inline-table", parser:move |input: &mut Input<'i>| {
25 delimitedimpl Parser, …>(
26 INLINE_TABLE_OPEN,
27 second:cut_err(inline_table_keyvals(check).try_map(|(kv, p)| table_from_pairs(kv, p))),
28 third: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
36fn 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
73fn 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 ; {
108pub(crate) const INLINE_TABLE_OPEN: u8 = b'{';
109// inline-table-close = ws %x7D ; }
110const INLINE_TABLE_CLOSE: u8 = b'}';
111// inline-table-sep = ws %x2C ws ; , Comma
112const INLINE_TABLE_SEP: u8 = b',';
113// keyval-sep = ws %x3D ws ; =
114pub(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
121fn 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: RecursionCheck = check.recursing(input)?;
126 (
127 separated0(parser:keyval(check), INLINE_TABLE_SEP),
128 ws.span().map(RawString::with_span),
129 )
130 .parse_next(input)
131 }
132}
133
134fn 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)]
168mod test {
169 use super::*;
170
171 #[test]
172 fn inline_tables() {
173 let inputs = [
174 r#"{}"#,
175 r#"{ }"#,
176 r#"{a = 1e165}"#,
177 r#"{ hello = "world", a = 1}"#,
178 r#"{ hello.world = "a" }"#,
179 ];
180 for input in inputs {
181 dbg!(input);
182 let mut parsed = inline_table(Default::default()).parse(new_input(input));
183 if let Ok(parsed) = &mut parsed {
184 parsed.despan(input);
185 }
186 assert_eq!(parsed.map(|a| a.to_string()), Ok(input.to_owned()));
187 }
188 }
189
190 #[test]
191 fn invalid_inline_tables() {
192 let invalid_inputs = [r#"{a = 1e165"#, r#"{ hello = "world", a = 2, hello = 1}"#];
193 for input in invalid_inputs {
194 dbg!(input);
195 let mut parsed = inline_table(Default::default()).parse(new_input(input));
196 if let Ok(parsed) = &mut parsed {
197 parsed.despan(input);
198 }
199 assert!(parsed.is_err());
200 }
201 }
202}
203