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