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