Skip to main content

toml_edit/parser/
inline_table.rs

1use crate::key::Key;
2use crate::parser::array::on_array;
3use crate::parser::key::on_key;
4use crate::parser::prelude::*;
5use crate::parser::value::on_scalar;
6use crate::repr::Decor;
7use crate::{InlineTable, Item, RawString, Value};
8
9use indexmap::map::Entry;
10
11/// ```abnf
12/// ;; Inline Table
13///
14/// inline-table = inline-table-open [ inline-table-keyvals ] ws-comment-newline inline-table-close
15/// ```
16pub(crate) fn on_inline_table(
17    open_event: &toml_parser::parser::Event,
18    input: &mut Input<'_>,
19    source: toml_parser::Source<'_>,
20    errors: &mut dyn ErrorSink,
21) -> Value {
22    #[cfg(feature = "debug")]
23    let _scope = TraceScope::new("inline_table::on_inline_table");
24    let mut result = InlineTable::new();
25
26    let mut state = State::default();
27    state.open(open_event);
28    while let Some(event) = input.next_token() {
29        match event.kind() {
30            EventKind::StdTableOpen
31            | EventKind::ArrayTableOpen
32            | EventKind::StdTableClose
33            | EventKind::ArrayClose
34            | EventKind::ArrayTableClose
35            | EventKind::KeySep => {
36                #[cfg(feature = "debug")]
37                trace(
38                    &format!("unexpected {event:?}"),
39                    anstyle::AnsiColor::Red.on_default(),
40                );
41                break;
42            }
43            EventKind::Error => {
44                #[cfg(feature = "debug")]
45                trace(
46                    &format!("unexpected {event:?}"),
47                    anstyle::AnsiColor::Red.on_default(),
48                );
49                continue;
50            }
51            EventKind::SimpleKey => {
52                let (path, key) = on_key(event, input, source, errors);
53                state.capture_key(event, path, key);
54            }
55            EventKind::KeyValSep => {
56                state.finish_key(event);
57            }
58            EventKind::InlineTableOpen => {
59                let value = on_inline_table(event, input, source, errors);
60                state.capture_value(event, value);
61            }
62            EventKind::ArrayOpen => {
63                let value = on_array(event, input, source, errors);
64                state.capture_value(event, value);
65            }
66            EventKind::Scalar => {
67                let value = on_scalar(event, source, errors);
68                state.capture_value(event, value);
69            }
70            EventKind::ValueSep => {
71                state.finish_value(event, &mut result, errors);
72                state.sep_value(event);
73            }
74            EventKind::Whitespace | EventKind::Comment | EventKind::Newline => {
75                state.whitespace(event);
76            }
77            EventKind::InlineTableClose => {
78                state.finish_value(event, &mut result, errors);
79                state.close(open_event, event, &mut result);
80                break;
81            }
82        }
83    }
84
85    Value::InlineTable(result)
86}
87
88#[derive(Default)]
89struct State {
90    current_prefix: Option<toml_parser::Span>,
91    current_key: Option<(Vec<Key>, Key)>,
92    seen_keyval_sep: bool,
93    current_value: Option<Value>,
94    trailing_start: Option<usize>,
95    current_suffix: Option<toml_parser::Span>,
96}
97
98impl State {
99    fn open(&mut self, open_event: &toml_parser::parser::Event) {
100        self.trailing_start = Some(open_event.span().end());
101    }
102
103    fn whitespace(&mut self, event: &toml_parser::parser::Event) {
104        #[cfg(feature = "debug")]
105        let _scope = TraceScope::new("inline_table::whitespace");
106        let decor = if self.is_prefix() {
107            self.current_prefix.get_or_insert(event.span())
108        } else {
109            self.current_suffix.get_or_insert(event.span())
110        };
111        *decor = decor.append(event.span());
112    }
113
114    fn is_prefix(&self) -> bool {
115        if self.seen_keyval_sep {
116            self.current_value.is_none()
117        } else {
118            self.current_key.is_none()
119        }
120    }
121
122    fn capture_key(
123        &mut self,
124        event: &toml_parser::parser::Event,
125        path: Vec<Key>,
126        key: Option<Key>,
127    ) {
128        #[cfg(feature = "debug")]
129        let _scope = TraceScope::new("inline_table::capture_key");
130        self.trailing_start = None;
131        self.current_prefix
132            .get_or_insert_with(|| event.span().before());
133        if let Some(key) = key {
134            self.current_key = Some((path, key));
135        }
136    }
137
138    fn finish_key(&mut self, event: &toml_parser::parser::Event) {
139        #[cfg(feature = "debug")]
140        let _scope = TraceScope::new("inline_table::finish_key");
141        self.seen_keyval_sep = true;
142        if let Some(last_key) = self.current_key.as_mut().map(|(_, k)| k) {
143            let prefix = self
144                .current_prefix
145                .take()
146                .expect("setting a key should set a prefix");
147            let suffix = self
148                .current_suffix
149                .take()
150                .unwrap_or_else(|| event.span().before());
151            let prefix = RawString::with_span(prefix.start()..prefix.end());
152            let suffix = RawString::with_span(suffix.start()..suffix.end());
153            let leaf_decor = Decor::new(prefix, suffix);
154            *last_key.leaf_decor_mut() = leaf_decor;
155        }
156    }
157
158    fn capture_value(&mut self, event: &toml_parser::parser::Event, value: Value) {
159        #[cfg(feature = "debug")]
160        let _scope = TraceScope::new("inline_table::capture_value");
161        self.current_prefix
162            .get_or_insert_with(|| event.span().before());
163        self.current_value = Some(value);
164    }
165
166    fn finish_value(
167        &mut self,
168        event: &toml_parser::parser::Event,
169        result: &mut InlineTable,
170        errors: &mut dyn ErrorSink,
171    ) {
172        #[cfg(feature = "debug")]
173        let _scope = TraceScope::new("inline_table::finish_value");
174        self.seen_keyval_sep = false;
175        if let (Some((path, key)), Some(mut value)) =
176            (self.current_key.take(), self.current_value.take())
177        {
178            let prefix = self
179                .current_prefix
180                .take()
181                .expect("setting a value should set a prefix");
182            let suffix = self
183                .current_suffix
184                .take()
185                .unwrap_or_else(|| event.span().before());
186            let Some(table) = descend_path(result, &path, true, errors) else {
187                return;
188            };
189
190            let decor = value.decor_mut();
191            decor.set_prefix(RawString::with_span(prefix.start()..prefix.end()));
192            decor.set_suffix(RawString::with_span(suffix.start()..suffix.end()));
193
194            // "Likewise, using dotted keys to redefine tables already defined in [table] form is not allowed"
195            let mixed_table_types = table.is_dotted() == path.is_empty();
196            if mixed_table_types {
197                #[cfg(feature = "debug")]
198                trace(
199                    &format!("table.dotted={}", table.is_dotted()),
200                    anstyle::AnsiColor::Red.on_default(),
201                );
202                #[cfg(feature = "debug")]
203                trace(
204                    &format!("path.is_empty={}", path.is_empty()),
205                    anstyle::AnsiColor::Red.on_default(),
206                );
207                let key_span = get_key_span(&key).unwrap_or_else(|| event.span());
208                errors.report_error(ParseError::new("duplicate key").with_unexpected(key_span));
209            } else {
210                let key_span = get_key_span(&key).unwrap_or_else(|| event.span());
211                match table.items.entry(key) {
212                    Entry::Vacant(o) => {
213                        o.insert(Item::Value(value));
214                    }
215                    Entry::Occupied(o) => {
216                        let old_span = get_key_span(o.key()).unwrap_or_else(|| event.span());
217                        errors.report_error(
218                            ParseError::new("duplicate key")
219                                .with_unexpected(key_span)
220                                .with_context(old_span),
221                        );
222                    }
223                }
224            }
225        }
226    }
227
228    fn sep_value(&mut self, event: &toml_parser::parser::Event) {
229        self.trailing_start = Some(event.span().end());
230    }
231
232    fn close(
233        &mut self,
234        open_event: &toml_parser::parser::Event,
235        close_event: &toml_parser::parser::Event,
236        result: &mut InlineTable,
237    ) {
238        #[cfg(feature = "debug")]
239        let _scope = TraceScope::new("inline_table::close");
240        let trailing_comma = self.trailing_start.is_some() && !result.is_empty();
241        let span = open_event.span().append(close_event.span());
242        let trailing_start = self
243            .trailing_start
244            .unwrap_or_else(|| close_event.span().start());
245        let trailing_end = close_event.span().start();
246
247        result.set_trailing_comma(trailing_comma);
248        result.set_trailing(RawString::with_span(trailing_start..trailing_end));
249        result.span = Some(span.start()..span.end());
250    }
251}
252
253fn descend_path<'a>(
254    mut table: &'a mut InlineTable,
255    path: &'a [Key],
256    dotted: bool,
257    errors: &mut dyn ErrorSink,
258) -> Option<&'a mut InlineTable> {
259    #[cfg(feature = "debug")]
260    let _scope = TraceScope::new("inline_table::descend_path");
261    #[cfg(feature = "debug")]
262    trace(
263        &format!(
264            "path={:?}",
265            path.iter().map(|k| k.get()).collect::<Vec<_>>()
266        ),
267        anstyle::AnsiColor::Blue.on_default(),
268    );
269    for key in path.iter() {
270        #[cfg(feature = "debug")]
271        trace(
272            &format!("path[_]={key:?}"),
273            anstyle::AnsiColor::Blue.on_default(),
274        );
275        table = match table.entry_format(key) {
276            crate::InlineEntry::Vacant(entry) => {
277                let mut new_table = InlineTable::new();
278                new_table.span = key.span();
279                new_table.set_implicit(true);
280                new_table.set_dotted(dotted);
281                entry
282                    .insert(Value::InlineTable(new_table))
283                    .as_inline_table_mut()
284                    .unwrap()
285            }
286            crate::InlineEntry::Occupied(entry) => {
287                match entry.into_mut() {
288                    Value::InlineTable(sweet_child_of_mine) => {
289                        // Since tables cannot be defined more than once, redefining such tables using a
290                        // [table] header is not allowed. Likewise, using dotted keys to redefine tables
291                        // already defined in [table] form is not allowed.
292                        let mixed_table_types = dotted && !sweet_child_of_mine.is_implicit();
293                        if mixed_table_types {
294                            #[cfg(feature = "debug")]
295                            trace(
296                                &format!("dotted={dotted}"),
297                                anstyle::AnsiColor::Red.on_default(),
298                            );
299                            #[cfg(feature = "debug")]
300                            trace(
301                                &format!(
302                                    "sweet_child_of_mine.is_implicit={}",
303                                    sweet_child_of_mine.is_implicit()
304                                ),
305                                anstyle::AnsiColor::Red.on_default(),
306                            );
307                            let key_span = get_key_span(key).expect("all keys have spans");
308                            errors.report_error(
309                                ParseError::new("duplicate key").with_unexpected(key_span),
310                            );
311                            return None;
312                        }
313                        sweet_child_of_mine
314                    }
315                    existing => {
316                        let key_span = get_key_span(key).expect("all keys have spans");
317                        errors.report_error(
318                            ParseError::new(format!(
319                                "cannot extend value of type {} with a dotted key",
320                                existing.type_name()
321                            ))
322                            .with_unexpected(key_span),
323                        );
324                        return None;
325                    }
326                }
327            }
328        };
329    }
330    Some(table)
331}
332
333fn get_key_span(key: &Key) -> Option<toml_parser::Span> {
334    key.as_repr()
335        .and_then(|r| r.span())
336        .map(|s| toml_parser::Span::new_unchecked(s.start, s.end))
337}