toml_edit/parser/
inline_table.rs1use nom8::bytes::one_of;
2use nom8::combinator::cut;
3use nom8::multi::separated_list0;
4use nom8::sequence::delimited;
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
17pub(crate) fn inline_table(
21 check: RecursionCheck,
22) -> impl FnMut(Input<'_>) -> IResult<Input<'_>, InlineTable, ParserError<'_>> {
23 move |input| {
24 delimited(
25 INLINE_TABLE_OPEN,
26 cut(inline_table_keyvals(check).map_res(|(kv, p)| table_from_pairs(kv, p))),
27 cut(INLINE_TABLE_CLOSE)
28 .context(Context::Expression("inline table"))
29 .context(Context::Expected(ParserValue::CharLiteral('}'))),
30 )
31 .parse(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::new();
40 root.set_preamble(preamble);
41 root.items.reserve(v.len());
43
44 for (path, kv) in v {
45 let table = descend_path(&mut root, &path)?;
46 let key: InternalString = kv.key.get_internal().into();
47 match table.items.entry(key) {
48 Entry::Vacant(o) => {
49 o.insert(kv);
50 }
51 Entry::Occupied(o) => {
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, key) in path.iter().enumerate() {
67 let entry = table.entry_format(key).or_insert_with(|| {
68 let mut new_table = InlineTable::new();
69 new_table.set_dotted(true);
70
71 Value::InlineTable(new_table)
72 });
73 match *entry {
74 Value::InlineTable(ref mut sweet_child_of_mine) => {
75 table = sweet_child_of_mine;
76 }
77 ref v => {
78 return Err(CustomError::extend_wrong_type(path, i, v.type_name()));
79 }
80 }
81 }
82 Ok(table)
83}
84
85pub(crate) const INLINE_TABLE_OPEN: u8 = b'{';
87const INLINE_TABLE_CLOSE: u8 = b'}';
89const INLINE_TABLE_SEP: u8 = b',';
91pub(crate) const KEYVAL_SEP: u8 = b'=';
93
94fn inline_table_keyvals(
100 check: RecursionCheck,
101) -> impl FnMut(
102 Input<'_>,
103) -> IResult<Input<'_>, (Vec<(Vec<Key>, TableKeyValue)>, RawString), ParserError<'_>> {
104 move |input| {
105 let check = check.recursing(input)?;
106 (
107 separated_list0(INLINE_TABLE_SEP, keyval(check)),
108 ws.span().map(RawString::with_span),
109 )
110 .parse(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((
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(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())
163 .parse(new_input(input))
164 .finish();
165 if let Ok(parsed) = &mut parsed {
166 parsed.despan(input);
167 }
168 assert_eq!(parsed.map(|a| a.to_string()), Ok(input.to_owned()));
169 }
170 }
171
172 #[test]
173 fn invalid_inline_tables() {
174 let invalid_inputs = [r#"{a = 1e165"#, r#"{ hello = "world", a = 2, hello = 1}"#];
175 for input in invalid_inputs {
176 dbg!(input);
177 let mut parsed = inline_table(Default::default())
178 .parse(new_input(input))
179 .finish();
180 if let Ok(parsed) = &mut parsed {
181 parsed.despan(input);
182 }
183 assert!(parsed.is_err());
184 }
185 }
186}