Skip to main content

facet_toml/
parser.rs

1//! Streaming TOML parser implementing the FormatParser trait.
2//!
3//! The key challenge with TOML is "table reopening" - fields for the same struct
4//! can appear at different points in the document:
5//!
6//! ```toml
7//! [foo.bar]
8//! x = 1
9//!
10//! [foo.baz]
11//! z = 3
12//!
13//! [foo.bar]  # reopening!
14//! y = 2
15//! ```
16//!
17//! This parser handles this by treating `StructEnd` and `SequenceEnd` as
18//! "navigating up the graph" rather than "we're done forever". The same applies
19//! to array tables - they can be interleaved with other tables:
20//!
21//! ```toml
22//! [[servers]]
23//! name = "alpha"
24//!
25//! [database]
26//! host = "localhost"
27//!
28//! [[servers]]  # reopening the array!
29//! name = "beta"
30//! ```
31//!
32//! The deserializer with `Partial` in deferred mode handles fields/elements
33//! arriving out of order. No buffering or pre-scanning needed.
34
35extern crate alloc;
36
37use alloc::{
38    borrow::Cow,
39    collections::VecDeque,
40    string::{String, ToString},
41    vec::Vec,
42};
43
44use facet_format::{
45    ContainerKind, FieldEvidence, FieldKey, FieldLocationHint, FormatParser, ParseEvent,
46    ProbeStream, ScalarValue,
47};
48use toml_parser::{
49    ErrorSink, Raw, Source,
50    decoder::ScalarKind,
51    parser::{Event, EventKind, RecursionGuard, parse_document},
52};
53
54use crate::{TomlError, TomlErrorKind};
55
56// ============================================================================
57// Error collection for parsing
58// ============================================================================
59
60/// Collects parse errors from the TOML parser
61struct ParseErrorCollector {
62    error: Option<String>,
63}
64
65impl ParseErrorCollector {
66    const fn new() -> Self {
67        Self { error: None }
68    }
69
70    const fn take_error(&mut self) -> Option<String> {
71        self.error.take()
72    }
73}
74
75impl ErrorSink for ParseErrorCollector {
76    fn report_error(&mut self, error: toml_parser::ParseError) {
77        if self.error.is_none() {
78            self.error = Some(error.description().to_string());
79        }
80    }
81}
82
83// ============================================================================
84// Path tracking
85// ============================================================================
86
87/// Kind of a path segment - determines what events to emit when navigating.
88#[derive(Debug, Clone, Copy, PartialEq, Eq)]
89enum SegmentKind {
90    /// Standard table `[foo]` - emits StructStart/StructEnd
91    Table,
92    /// Array table element `[[foo]]` - the array itself
93    Array,
94    /// Element inside an array table - emits StructStart/StructEnd
95    ArrayElement,
96}
97
98/// A segment in the current document path.
99#[derive(Debug, Clone)]
100struct PathSegment<'de> {
101    name: Cow<'de, str>,
102    kind: SegmentKind,
103}
104
105// ============================================================================
106// TOML Parser
107// ============================================================================
108
109/// Streaming TOML parser backed by `toml_parser`.
110///
111/// This parser translates TOML's event stream into the `ParseEvent` format
112/// expected by `facet-format`'s deserializer.
113pub struct TomlParser<'de> {
114    /// Original input string.
115    input: &'de str,
116    /// Pre-parsed events from toml_parser.
117    events: Vec<Event>,
118    /// Current position in the event stream.
119    pos: usize,
120    /// Current path in the document with segment kinds.
121    current_path: Vec<PathSegment<'de>>,
122    /// Pending events to emit (navigation when tables change).
123    pending_events: VecDeque<ParseEvent<'de>>,
124    /// Cached event for peek_event().
125    event_peek: Option<ParseEvent<'de>>,
126    /// Whether we've emitted the initial StructStart for the root.
127    root_started: bool,
128    /// Whether we've emitted the final StructEnd for the root.
129    root_ended: bool,
130    /// Stack tracking nested inline containers (inline tables and arrays).
131    /// Each entry is (is_inline_table, deferred_struct_ends) where:
132    /// - is_inline_table: true for inline table, false for array
133    /// - deferred_struct_ends: number of StructEnd events to emit when this container closes
134    inline_stack: Vec<(bool, usize)>,
135    /// The span of the most recently consumed scalar value (for error reporting).
136    last_scalar_span: Option<facet_reflect::Span>,
137}
138
139impl<'de> TomlParser<'de> {
140    /// Create a new TOML parser from a string slice.
141    pub fn new(input: &'de str) -> Result<Self, TomlError> {
142        let source = Source::new(input);
143        let tokens: Vec<_> = source.lex().collect();
144        let mut events: Vec<Event> = Vec::new();
145        let mut guarded = RecursionGuard::new(&mut events, 128);
146        let mut error_collector = ParseErrorCollector::new();
147
148        parse_document(&tokens, &mut guarded, &mut error_collector);
149
150        if let Some(err_msg) = error_collector.take_error() {
151            return Err(TomlError::without_span(TomlErrorKind::Parse(err_msg)));
152        }
153
154        Ok(Self {
155            input,
156            events,
157            pos: 0,
158            current_path: Vec::new(),
159            pending_events: VecDeque::new(),
160            event_peek: None,
161            root_started: false,
162            root_ended: false,
163            inline_stack: Vec::new(),
164            last_scalar_span: None,
165        })
166    }
167
168    /// Get the original input string.
169    pub const fn input(&self) -> &'de str {
170        self.input
171    }
172
173    /// Check if an event should be skipped (whitespace, comment, newline).
174    #[inline]
175    fn should_skip(event: &Event) -> bool {
176        matches!(
177            event.kind(),
178            EventKind::Whitespace | EventKind::Comment | EventKind::Newline
179        )
180    }
181
182    /// Peek at the next raw event (skipping whitespace/comments).
183    fn peek_raw(&self) -> Option<&Event> {
184        let mut pos = self.pos;
185        while pos < self.events.len() {
186            let event = &self.events[pos];
187            if !Self::should_skip(event) {
188                return Some(event);
189            }
190            pos += 1;
191        }
192        None
193    }
194
195    /// Consume the next raw event (skipping whitespace/comments).
196    fn next_raw(&mut self) -> Option<&Event> {
197        while self.pos < self.events.len() {
198            let event = &self.events[self.pos];
199            self.pos += 1;
200            if !Self::should_skip(event) {
201                return Some(event);
202            }
203        }
204        None
205    }
206
207    /// Get the string slice for an event's span.
208    fn get_span_str(&self, event: &Event) -> &'de str {
209        let span = event.span();
210        &self.input[span.start()..span.end()]
211    }
212
213    /// Create a Raw from an event for scalar decoding.
214    fn raw_from_event(&self, event: &Event) -> Raw<'de> {
215        let span = event.span();
216        Raw::new_unchecked(
217            &self.input[span.start()..span.end()],
218            event.encoding(),
219            span,
220        )
221    }
222
223    /// Decode a raw TOML value into the appropriate scalar.
224    fn decode_scalar(&self, event: &Event) -> Result<ScalarValue<'de>, TomlError> {
225        let raw = self.raw_from_event(event);
226        let mut output: Cow<'de, str> = Cow::Borrowed("");
227        let kind = raw.decode_scalar(&mut output, &mut ());
228
229        match kind {
230            ScalarKind::String => {
231                // Use the decoded output (handles escapes, quotes, etc.)
232                Ok(ScalarValue::Str(output))
233            }
234            ScalarKind::Boolean(b) => Ok(ScalarValue::Bool(b)),
235            ScalarKind::Integer(radix) => {
236                // Remove underscores for parsing
237                let clean: String = output.chars().filter(|c| *c != '_').collect();
238                let n: i64 = i64::from_str_radix(&clean, radix.value()).map_err(|e| {
239                    TomlError::without_span(TomlErrorKind::InvalidValue {
240                        message: e.to_string(),
241                    })
242                })?;
243                Ok(ScalarValue::I64(n))
244            }
245            ScalarKind::Float => {
246                let clean: String = output.chars().filter(|c| *c != '_').collect();
247                // Handle special float values
248                let f: f64 = match clean.as_str() {
249                    "inf" | "+inf" => f64::INFINITY,
250                    "-inf" => f64::NEG_INFINITY,
251                    "nan" | "+nan" | "-nan" => f64::NAN,
252                    _ => clean.parse().map_err(|e: core::num::ParseFloatError| {
253                        TomlError::without_span(TomlErrorKind::InvalidValue {
254                            message: e.to_string(),
255                        })
256                    })?,
257                };
258                Ok(ScalarValue::F64(f))
259            }
260            ScalarKind::DateTime => {
261                // Keep as string, let facet-reflect handle datetime types
262                Ok(ScalarValue::Str(output))
263            }
264        }
265    }
266
267    /// Parse a dotted key from the current position until we hit a delimiter.
268    /// Returns the components and advances past any key separators.
269    fn parse_dotted_key(&mut self) -> Vec<Cow<'de, str>> {
270        let mut parts = Vec::new();
271
272        loop {
273            let Some(event) = self.peek_raw() else {
274                break;
275            };
276
277            match event.kind() {
278                EventKind::SimpleKey => {
279                    let key = self.decode_key(event);
280                    self.next_raw(); // consume the key
281                    parts.push(key);
282                }
283                EventKind::KeySep => {
284                    // Dot separator - consume and continue
285                    self.next_raw();
286                }
287                _ => break,
288            }
289        }
290
291        parts
292    }
293
294    /// Decode a key from an event.
295    fn decode_key(&self, event: &Event) -> Cow<'de, str> {
296        let raw = self.raw_from_event(event);
297        let mut output: Cow<'de, str> = Cow::Borrowed("");
298        raw.decode_key(&mut output, &mut ());
299        output
300    }
301
302    /// Emit the "end" event for a path segment based on its kind.
303    const fn end_event_for_segment(segment: &PathSegment<'_>) -> ParseEvent<'static> {
304        match segment.kind {
305            SegmentKind::Table => ParseEvent::StructEnd,
306            SegmentKind::Array => ParseEvent::SequenceEnd,
307            SegmentKind::ArrayElement => ParseEvent::StructEnd,
308        }
309    }
310
311    /// Compute navigation events to move from current path to target path.
312    ///
313    /// For standard tables `[foo.bar]`, target segments are all `Table` kind.
314    /// For array tables `[[foo.bar]]`, the last segment is `Array` + `ArrayElement`.
315    ///
316    /// Special handling: When inside an array element (Array + ArrayElement pair),
317    /// and the target path starts with the array's name, we stay in the current
318    /// array element rather than exiting it. This handles cases like:
319    /// ```toml
320    /// [[item]]
321    /// foo = 1
322    /// [item.nested_item]  # nested_item is inside the current item element
323    /// bar = 2
324    /// ```
325    fn compute_navigation_to_table(
326        &self,
327        target_names: &[Cow<'de, str>],
328    ) -> (Vec<ParseEvent<'de>>, Vec<PathSegment<'de>>) {
329        let mut events = Vec::new();
330
331        // Find how many segments match, with special handling for Array+ArrayElement pairs.
332        // An Array+ArrayElement pair in current_path corresponds to ONE segment in target_names.
333        let mut current_idx = 0;
334        let mut target_idx = 0;
335
336        while current_idx < self.current_path.len() && target_idx < target_names.len() {
337            let seg = &self.current_path[current_idx];
338            let target_name = &target_names[target_idx];
339
340            if seg.name != *target_name {
341                break;
342            }
343
344            current_idx += 1;
345
346            // If this was an Array segment and the next is its ArrayElement, include both
347            // (but only advance target_idx once - both segments correspond to one target name)
348            if matches!(seg.kind, SegmentKind::Array) && current_idx < self.current_path.len() {
349                let next_seg = &self.current_path[current_idx];
350                if matches!(next_seg.kind, SegmentKind::ArrayElement) && next_seg.name == seg.name {
351                    current_idx += 1;
352                }
353            }
354
355            target_idx += 1;
356        }
357
358        // Pop up to common ancestor - emit end events in reverse order
359        for segment in self.current_path[current_idx..].iter().rev() {
360            events.push(Self::end_event_for_segment(segment));
361        }
362
363        // Navigate down to target - all segments are Tables for [table.path]
364        let mut new_path: Vec<PathSegment<'de>> = self.current_path[..current_idx].to_vec();
365        for name in &target_names[target_idx..] {
366            events.push(ParseEvent::FieldKey(FieldKey::new(
367                name.clone(),
368                FieldLocationHint::KeyValue,
369            )));
370            events.push(ParseEvent::StructStart(ContainerKind::Object));
371            new_path.push(PathSegment {
372                name: name.clone(),
373                kind: SegmentKind::Table,
374            });
375        }
376
377        (events, new_path)
378    }
379
380    /// Compute navigation events to move to an array table `[[path]]`.
381    ///
382    /// Array tables are special: the last segment becomes Array + ArrayElement,
383    /// meaning we emit FieldKey, SequenceStart, StructStart.
384    ///
385    /// There are two cases to handle:
386    /// 1. `[[item]]` after `[[item]]` - same array, new element. We must exit the
387    ///    old element and re-enter the array with a new element.
388    /// 2. `[[item.subarray]]` after `[[item]]` - nested array. We stay in the
389    ///    current array element and add a nested array inside it.
390    ///
391    /// The distinction is whether the target path goes DEEPER than just matching
392    /// the current array context.
393    fn compute_navigation_to_array_table(
394        &self,
395        target_names: &[Cow<'de, str>],
396    ) -> (Vec<ParseEvent<'de>>, Vec<PathSegment<'de>>) {
397        let mut events = Vec::new();
398
399        // Find how many segments match, with special handling for Array+ArrayElement pairs.
400        let mut current_idx = 0;
401        let mut target_idx = 0;
402
403        while current_idx < self.current_path.len() && target_idx < target_names.len() {
404            let seg = &self.current_path[current_idx];
405            let target_name = &target_names[target_idx];
406
407            if seg.name != *target_name {
408                break;
409            }
410
411            // Check if this is an Array segment
412            if matches!(seg.kind, SegmentKind::Array) {
413                // Check if we're navigating DEEPER (more target segments after this)
414                // or just reopening the same array (this is the last target segment)
415                let more_targets_after = target_idx + 1 < target_names.len();
416
417                if more_targets_after {
418                    // Nested path like [[item.subarray]] - stay in the array element
419                    current_idx += 1;
420                    // Skip the ArrayElement too if it follows
421                    if current_idx < self.current_path.len() {
422                        let next_seg = &self.current_path[current_idx];
423                        if matches!(next_seg.kind, SegmentKind::ArrayElement)
424                            && next_seg.name == seg.name
425                        {
426                            current_idx += 1;
427                        }
428                    }
429                    target_idx += 1;
430                } else {
431                    // Same array, new element like [[item]] then [[item]]
432                    // Stop here - we need to exit and re-enter this array
433                    break;
434                }
435            } else if matches!(seg.kind, SegmentKind::ArrayElement) {
436                // Skip ArrayElement if we encounter it directly (should be handled with its Array)
437                break;
438            } else {
439                // Table segment - include in common prefix
440                current_idx += 1;
441                target_idx += 1;
442            }
443        }
444
445        // Pop up to common ancestor
446        for segment in self.current_path[current_idx..].iter().rev() {
447            events.push(Self::end_event_for_segment(segment));
448        }
449
450        // Navigate down - all but last are Tables, last is Array + ArrayElement
451        let mut new_path: Vec<PathSegment<'de>> = self.current_path[..current_idx].to_vec();
452
453        if target_names.len() > target_idx {
454            // Navigate to parent tables first
455            for name in &target_names[target_idx..target_names.len() - 1] {
456                events.push(ParseEvent::FieldKey(FieldKey::new(
457                    name.clone(),
458                    FieldLocationHint::KeyValue,
459                )));
460                events.push(ParseEvent::StructStart(ContainerKind::Object));
461                new_path.push(PathSegment {
462                    name: name.clone(),
463                    kind: SegmentKind::Table,
464                });
465            }
466
467            // Last segment is the array table
468            let array_name = target_names.last().unwrap();
469            events.push(ParseEvent::FieldKey(FieldKey::new(
470                array_name.clone(),
471                FieldLocationHint::KeyValue,
472            )));
473            events.push(ParseEvent::SequenceStart(ContainerKind::Array));
474            events.push(ParseEvent::StructStart(ContainerKind::Object));
475
476            new_path.push(PathSegment {
477                name: array_name.clone(),
478                kind: SegmentKind::Array,
479            });
480            new_path.push(PathSegment {
481                name: array_name.clone(),
482                kind: SegmentKind::ArrayElement,
483            });
484        }
485
486        (events, new_path)
487    }
488
489    /// Produce the next parse event.
490    fn produce_event(&mut self) -> Result<Option<ParseEvent<'de>>, TomlError> {
491        // First, drain any pending navigation events
492        if let Some(event) = self.pending_events.pop_front() {
493            return Ok(Some(event));
494        }
495
496        // If we're inside inline containers, handle them specially
497        if !self.inline_stack.is_empty() {
498            return self.produce_inline_event();
499        }
500
501        // Emit root StructStart if we haven't yet
502        if !self.root_started {
503            self.root_started = true;
504            return Ok(Some(ParseEvent::StructStart(ContainerKind::Object)));
505        }
506
507        // Get next raw event
508        let Some(event) = self.peek_raw() else {
509            // EOF - emit end events for remaining path elements, then root
510            if self.root_ended {
511                return Ok(None);
512            }
513
514            // Pop all remaining path segments
515            for segment in self.current_path.iter().rev() {
516                self.pending_events
517                    .push_back(Self::end_event_for_segment(segment));
518            }
519            self.current_path.clear();
520
521            // Final StructEnd for root
522            self.pending_events.push_back(ParseEvent::StructEnd);
523            self.root_ended = true;
524
525            return Ok(self.pending_events.pop_front());
526        };
527
528        match event.kind() {
529            EventKind::StdTableOpen => {
530                // Standard table header [table.path]
531                self.next_raw(); // consume StdTableOpen
532                let path = self.parse_dotted_key();
533
534                // Consume the StdTableClose
535                if let Some(close) = self.peek_raw()
536                    && matches!(close.kind(), EventKind::StdTableClose)
537                {
538                    self.next_raw();
539                }
540
541                // Compute navigation from current path to new table path
542                let (nav_events, new_path) = self.compute_navigation_to_table(&path);
543                for e in nav_events {
544                    self.pending_events.push_back(e);
545                }
546                self.current_path = new_path;
547
548                // If no navigation events were generated, recurse to get next actual event
549                if self.pending_events.is_empty() {
550                    return self.produce_event();
551                }
552
553                Ok(self.pending_events.pop_front())
554            }
555
556            EventKind::ArrayTableOpen => {
557                // Array table header [[table.path]]
558                self.next_raw(); // consume ArrayTableOpen
559                let path = self.parse_dotted_key();
560
561                // Consume the ArrayTableClose
562                if let Some(close) = self.peek_raw()
563                    && matches!(close.kind(), EventKind::ArrayTableClose)
564                {
565                    self.next_raw();
566                }
567
568                // Compute navigation to array table (handles reopening)
569                let (nav_events, new_path) = self.compute_navigation_to_array_table(&path);
570                for e in nav_events {
571                    self.pending_events.push_back(e);
572                }
573                self.current_path = new_path;
574
575                Ok(self.pending_events.pop_front())
576            }
577
578            EventKind::SimpleKey => {
579                // Key-value pair
580                let key_parts = self.parse_dotted_key();
581
582                // Consume the KeyValSep (=)
583                if let Some(sep) = self.peek_raw()
584                    && matches!(sep.kind(), EventKind::KeyValSep)
585                {
586                    self.next_raw();
587                }
588
589                // For dotted keys like `foo.bar.baz = 1`, emit navigation events
590                // to nested structs, then the final key
591                if key_parts.len() > 1 {
592                    // Navigate into nested structs
593                    for name in &key_parts[..key_parts.len() - 1] {
594                        self.pending_events
595                            .push_back(ParseEvent::FieldKey(FieldKey::new(
596                                name.clone(),
597                                FieldLocationHint::KeyValue,
598                            )));
599                        self.pending_events
600                            .push_back(ParseEvent::StructStart(ContainerKind::Object));
601                    }
602
603                    // Emit the final key
604                    let final_key = key_parts.last().unwrap();
605                    self.pending_events
606                        .push_back(ParseEvent::FieldKey(FieldKey::new(
607                            final_key.clone(),
608                            FieldLocationHint::KeyValue,
609                        )));
610
611                    // Track inline stack depth before parsing value
612                    let inline_depth_before = self.inline_stack.len();
613
614                    // Parse the value
615                    self.parse_value_into_pending()?;
616
617                    // Check if we entered an inline container (array or inline table)
618                    let entered_inline_container = self.inline_stack.len() > inline_depth_before;
619
620                    if entered_inline_container {
621                        // Defer the StructEnd events until the inline container closes
622                        let num_deferred = key_parts.len() - 1;
623                        if let Some((_, deferred_closes)) = self.inline_stack.last_mut() {
624                            *deferred_closes += num_deferred;
625                        }
626                    } else {
627                        // Navigate back out of nested structs immediately (for scalar values)
628                        for _ in 0..key_parts.len() - 1 {
629                            self.pending_events.push_back(ParseEvent::StructEnd);
630                        }
631                    }
632
633                    Ok(self.pending_events.pop_front())
634                } else {
635                    // Simple key
636                    let key = key_parts.into_iter().next().unwrap();
637                    self.pending_events
638                        .push_back(ParseEvent::FieldKey(FieldKey::new(
639                            key,
640                            FieldLocationHint::KeyValue,
641                        )));
642
643                    // Parse the value
644                    self.parse_value_into_pending()?;
645
646                    Ok(self.pending_events.pop_front())
647                }
648            }
649
650            EventKind::Error => {
651                let span_str = self.get_span_str(event);
652                Err(TomlError::without_span(TomlErrorKind::Parse(
653                    span_str.to_string(),
654                )))
655            }
656
657            _ => {
658                // Skip unexpected events
659                self.next_raw();
660                self.produce_event()
661            }
662        }
663    }
664
665    /// Parse a value and add its events to pending_events.
666    fn parse_value_into_pending(&mut self) -> Result<(), TomlError> {
667        let Some(event) = self.peek_raw() else {
668            return Err(TomlError::without_span(TomlErrorKind::UnexpectedEof {
669                expected: "value",
670            }));
671        };
672
673        match event.kind() {
674            EventKind::Scalar => {
675                let scalar = self.decode_scalar(event)?;
676                // Track span for error reporting
677                let span = event.span();
678                self.last_scalar_span = Some(facet_reflect::Span::new(
679                    span.start(),
680                    span.end() - span.start(),
681                ));
682                self.next_raw();
683                self.pending_events.push_back(ParseEvent::Scalar(scalar));
684            }
685
686            EventKind::InlineTableOpen => {
687                self.next_raw();
688                self.pending_events
689                    .push_back(ParseEvent::StructStart(ContainerKind::Object));
690                self.inline_stack.push((true, 0)); // true = inline table, 0 deferred closes
691            }
692
693            EventKind::ArrayOpen => {
694                self.next_raw();
695                self.pending_events
696                    .push_back(ParseEvent::SequenceStart(ContainerKind::Array));
697                self.inline_stack.push((false, 0)); // false = array, 0 deferred closes
698            }
699
700            _ => {
701                return Err(TomlError::without_span(TomlErrorKind::UnexpectedType {
702                    expected: "value",
703                    got: "unexpected token",
704                }));
705            }
706        }
707
708        Ok(())
709    }
710
711    /// Produce events while inside inline containers (inline tables or arrays).
712    fn produce_inline_event(&mut self) -> Result<Option<ParseEvent<'de>>, TomlError> {
713        // Check pending events first
714        if let Some(event) = self.pending_events.pop_front() {
715            return Ok(Some(event));
716        }
717
718        let (is_inline_table, _deferred_closes) = *self.inline_stack.last().unwrap();
719
720        let Some(event) = self.peek_raw() else {
721            return Err(TomlError::without_span(TomlErrorKind::UnexpectedEof {
722                expected: if is_inline_table { "}" } else { "]" },
723            }));
724        };
725
726        match event.kind() {
727            EventKind::InlineTableClose if is_inline_table => {
728                self.next_raw();
729                let (_, deferred_closes) = self.inline_stack.pop().unwrap();
730                // Emit the StructEnd for the inline table
731                self.pending_events.push_back(ParseEvent::StructEnd);
732                // Then emit any deferred StructEnd events from dotted keys
733                for _ in 0..deferred_closes {
734                    self.pending_events.push_back(ParseEvent::StructEnd);
735                }
736                Ok(self.pending_events.pop_front())
737            }
738
739            EventKind::ArrayClose if !is_inline_table => {
740                self.next_raw();
741                let (_, deferred_closes) = self.inline_stack.pop().unwrap();
742                // Emit the SequenceEnd for the array
743                self.pending_events.push_back(ParseEvent::SequenceEnd);
744                // Then emit any deferred StructEnd events from dotted keys
745                for _ in 0..deferred_closes {
746                    self.pending_events.push_back(ParseEvent::StructEnd);
747                }
748                Ok(self.pending_events.pop_front())
749            }
750
751            EventKind::ValueSep => {
752                // Comma separator - skip and continue
753                self.next_raw();
754                self.produce_inline_event()
755            }
756
757            EventKind::SimpleKey if is_inline_table => {
758                // Key in inline table
759                let key_parts = self.parse_dotted_key();
760
761                // Consume KeyValSep
762                if let Some(sep) = self.peek_raw()
763                    && matches!(sep.kind(), EventKind::KeyValSep)
764                {
765                    self.next_raw();
766                }
767
768                // Handle dotted keys
769                if key_parts.len() > 1 {
770                    for name in &key_parts[..key_parts.len() - 1] {
771                        self.pending_events
772                            .push_back(ParseEvent::FieldKey(FieldKey::new(
773                                name.clone(),
774                                FieldLocationHint::KeyValue,
775                            )));
776                        self.pending_events
777                            .push_back(ParseEvent::StructStart(ContainerKind::Object));
778                    }
779
780                    let final_key = key_parts.last().unwrap();
781                    self.pending_events
782                        .push_back(ParseEvent::FieldKey(FieldKey::new(
783                            final_key.clone(),
784                            FieldLocationHint::KeyValue,
785                        )));
786
787                    // Track inline stack depth before parsing value
788                    let inline_depth_before = self.inline_stack.len();
789
790                    self.parse_value_into_pending()?;
791
792                    // Check if we entered an inline container (array or inline table)
793                    let entered_inline_container = self.inline_stack.len() > inline_depth_before;
794
795                    if entered_inline_container {
796                        // Defer the StructEnd events until the inline container closes
797                        let num_deferred = key_parts.len() - 1;
798                        if let Some((_, deferred_closes)) = self.inline_stack.last_mut() {
799                            *deferred_closes += num_deferred;
800                        }
801                    } else {
802                        // Navigate back out of nested structs immediately (for scalar values)
803                        for _ in 0..key_parts.len() - 1 {
804                            self.pending_events.push_back(ParseEvent::StructEnd);
805                        }
806                    }
807
808                    Ok(self.pending_events.pop_front())
809                } else {
810                    let key = key_parts.into_iter().next().unwrap();
811                    self.pending_events
812                        .push_back(ParseEvent::FieldKey(FieldKey::new(
813                            key,
814                            FieldLocationHint::KeyValue,
815                        )));
816                    self.parse_value_into_pending()?;
817                    Ok(self.pending_events.pop_front())
818                }
819            }
820
821            EventKind::Scalar if !is_inline_table => {
822                // Value in array
823                let scalar = self.decode_scalar(event)?;
824                // Track span for error reporting
825                let span = event.span();
826                self.last_scalar_span = Some(facet_reflect::Span::new(
827                    span.start(),
828                    span.end() - span.start(),
829                ));
830                self.next_raw();
831                Ok(Some(ParseEvent::Scalar(scalar)))
832            }
833
834            EventKind::InlineTableOpen if !is_inline_table => {
835                // Inline table inside array
836                self.next_raw();
837                self.inline_stack.push((true, 0));
838                Ok(Some(ParseEvent::StructStart(ContainerKind::Object)))
839            }
840
841            EventKind::ArrayOpen if !is_inline_table => {
842                // Nested array
843                self.next_raw();
844                self.inline_stack.push((false, 0));
845                Ok(Some(ParseEvent::SequenceStart(ContainerKind::Array)))
846            }
847
848            _ => {
849                // Skip unexpected
850                self.next_raw();
851                self.produce_inline_event()
852            }
853        }
854    }
855
856    /// Skip the current value (used for skip_value).
857    ///
858    /// This operates at the parse event level, not the raw TOML token level.
859    /// It must handle:
860    /// - Scalars: consume one Scalar event
861    /// - Structs: consume StructStart, all contents, and StructEnd
862    /// - Sequences: consume SequenceStart, all contents, and SequenceEnd
863    fn skip_current_value(&mut self) -> Result<(), TomlError> {
864        // Peek at the next parse event (not raw token)
865        let Some(event) = self.produce_event()? else {
866            return Ok(());
867        };
868
869        match event {
870            ParseEvent::Scalar(_) => {
871                // Scalar value - already consumed by produce_event
872                Ok(())
873            }
874            ParseEvent::StructStart(_) => {
875                // Need to skip the entire struct
876                let mut depth = 1;
877                while depth > 0 {
878                    let Some(event) = self.produce_event()? else {
879                        return Err(TomlError::without_span(TomlErrorKind::UnexpectedEof {
880                            expected: "struct end",
881                        }));
882                    };
883                    match event {
884                        ParseEvent::StructStart(_) => depth += 1,
885                        ParseEvent::StructEnd => depth -= 1,
886                        _ => {}
887                    }
888                }
889                Ok(())
890            }
891            ParseEvent::SequenceStart(_) => {
892                // Need to skip the entire sequence
893                let mut depth = 1;
894                while depth > 0 {
895                    let Some(event) = self.produce_event()? else {
896                        return Err(TomlError::without_span(TomlErrorKind::UnexpectedEof {
897                            expected: "sequence end",
898                        }));
899                    };
900                    match event {
901                        ParseEvent::SequenceStart(_) => depth += 1,
902                        ParseEvent::SequenceEnd => depth -= 1,
903                        _ => {}
904                    }
905                }
906                Ok(())
907            }
908            _ => {
909                // Unexpected event type - shouldn't happen in well-formed input
910                Ok(())
911            }
912        }
913    }
914
915    /// Build probe evidence by scanning ahead.
916    fn build_probe(&self) -> Result<Vec<FieldEvidence<'de>>, TomlError> {
917        let mut evidence = Vec::new();
918        let mut pos = self.pos;
919
920        // Skip to find field keys at current level
921        while pos < self.events.len() {
922            let event = &self.events[pos];
923
924            if Self::should_skip(event) {
925                pos += 1;
926                continue;
927            }
928
929            match event.kind() {
930                EventKind::SimpleKey => {
931                    let key = self.decode_key(event);
932                    pos += 1;
933
934                    // Skip to value
935                    while pos < self.events.len() {
936                        let e = &self.events[pos];
937                        if !Self::should_skip(e) {
938                            break;
939                        }
940                        pos += 1;
941                    }
942
943                    // Skip KeySep (dots) and additional key parts
944                    while pos < self.events.len() {
945                        let e = &self.events[pos];
946                        if Self::should_skip(e) {
947                            pos += 1;
948                            continue;
949                        }
950                        if matches!(e.kind(), EventKind::KeySep | EventKind::SimpleKey) {
951                            pos += 1;
952                            continue;
953                        }
954                        break;
955                    }
956
957                    // Skip KeyValSep (=)
958                    if pos < self.events.len() {
959                        let e = &self.events[pos];
960                        if matches!(e.kind(), EventKind::KeyValSep) {
961                            pos += 1;
962                        }
963                    }
964
965                    // Skip whitespace to value
966                    while pos < self.events.len() {
967                        let e = &self.events[pos];
968                        if !Self::should_skip(e) {
969                            break;
970                        }
971                        pos += 1;
972                    }
973
974                    // Try to get scalar value
975                    let scalar_value = if pos < self.events.len() {
976                        let e = &self.events[pos];
977                        if matches!(e.kind(), EventKind::Scalar) {
978                            self.decode_scalar(e).ok()
979                        } else {
980                            None
981                        }
982                    } else {
983                        None
984                    };
985
986                    if let Some(sv) = scalar_value {
987                        evidence.push(FieldEvidence::with_scalar_value(
988                            key,
989                            FieldLocationHint::KeyValue,
990                            None,
991                            sv,
992                        ));
993                    } else {
994                        evidence.push(FieldEvidence::new(key, FieldLocationHint::KeyValue, None));
995                    }
996                }
997
998                EventKind::StdTableOpen
999                | EventKind::ArrayTableOpen
1000                | EventKind::InlineTableClose
1001                | EventKind::ArrayClose => {
1002                    // Stop scanning at table boundaries or container ends
1003                    break;
1004                }
1005
1006                _ => {
1007                    pos += 1;
1008                }
1009            }
1010        }
1011
1012        Ok(evidence)
1013    }
1014}
1015
1016impl<'de> FormatParser<'de> for TomlParser<'de> {
1017    type Error = TomlError;
1018    type Probe<'a>
1019        = TomlProbe<'de>
1020    where
1021        Self: 'a;
1022
1023    fn next_event(&mut self) -> Result<Option<ParseEvent<'de>>, Self::Error> {
1024        if let Some(event) = self.event_peek.take() {
1025            return Ok(Some(event));
1026        }
1027        self.produce_event()
1028    }
1029
1030    fn peek_event(&mut self) -> Result<Option<ParseEvent<'de>>, Self::Error> {
1031        if let Some(event) = self.event_peek.clone() {
1032            return Ok(Some(event));
1033        }
1034        let event = self.produce_event()?;
1035        if let Some(ref e) = event {
1036            self.event_peek = Some(e.clone());
1037        }
1038        Ok(event)
1039    }
1040
1041    fn skip_value(&mut self) -> Result<(), Self::Error> {
1042        debug_assert!(
1043            self.event_peek.is_none(),
1044            "skip_value called while an event is buffered"
1045        );
1046        self.skip_current_value()
1047    }
1048
1049    fn begin_probe(&mut self) -> Result<Self::Probe<'_>, Self::Error> {
1050        let evidence = self.build_probe()?;
1051        Ok(TomlProbe { evidence, idx: 0 })
1052    }
1053
1054    fn capture_raw(&mut self) -> Result<Option<&'de str>, Self::Error> {
1055        // TOML doesn't support raw capture (unlike JSON)
1056        self.skip_value()?;
1057        Ok(None)
1058    }
1059
1060    fn current_span(&self) -> Option<facet_reflect::Span> {
1061        self.last_scalar_span
1062    }
1063}
1064
1065/// Probe stream for TOML.
1066pub struct TomlProbe<'de> {
1067    evidence: Vec<FieldEvidence<'de>>,
1068    idx: usize,
1069}
1070
1071impl<'de> ProbeStream<'de> for TomlProbe<'de> {
1072    type Error = TomlError;
1073
1074    fn next(&mut self) -> Result<Option<FieldEvidence<'de>>, Self::Error> {
1075        if self.idx >= self.evidence.len() {
1076            Ok(None)
1077        } else {
1078            let ev = self.evidence[self.idx].clone();
1079            self.idx += 1;
1080            Ok(Some(ev))
1081        }
1082    }
1083}
1084
1085#[cfg(test)]
1086mod tests {
1087    use super::*;
1088    use crate::from_str;
1089
1090    /// Helper to collect all events from a parser
1091    fn collect_events<'de>(parser: &mut TomlParser<'de>) -> Vec<ParseEvent<'de>> {
1092        let mut events = Vec::new();
1093        while let Ok(Some(event)) = parser.next_event() {
1094            events.push(event);
1095        }
1096        events
1097    }
1098
1099    /// Helper to format events for debugging
1100    fn format_events(events: &[ParseEvent<'_>]) -> String {
1101        events
1102            .iter()
1103            .map(|e| format!("{:?}", e))
1104            .collect::<Vec<_>>()
1105            .join("\n")
1106    }
1107
1108    #[test]
1109    fn test_simple_key_value() {
1110        let input = r#"
1111name = "test"
1112value = 42
1113"#;
1114        let mut parser = TomlParser::new(input).unwrap();
1115
1116        // StructStart (root)
1117        assert!(matches!(
1118            parser.next_event().unwrap(),
1119            Some(ParseEvent::StructStart(ContainerKind::Object))
1120        ));
1121
1122        // FieldKey("name")
1123        assert!(matches!(
1124            parser.next_event().unwrap(),
1125            Some(ParseEvent::FieldKey(key)) if key.name.as_deref() == Some("name")
1126        ));
1127
1128        // Scalar("test")
1129        assert!(matches!(
1130            parser.next_event().unwrap(),
1131            Some(ParseEvent::Scalar(ScalarValue::Str(s))) if s == "test"
1132        ));
1133
1134        // FieldKey("value")
1135        assert!(matches!(
1136            parser.next_event().unwrap(),
1137            Some(ParseEvent::FieldKey(key)) if key.name.as_deref() == Some("value")
1138        ));
1139
1140        // Scalar(42)
1141        assert!(matches!(
1142            parser.next_event().unwrap(),
1143            Some(ParseEvent::Scalar(ScalarValue::I64(42)))
1144        ));
1145
1146        // StructEnd (root)
1147        assert!(matches!(
1148            parser.next_event().unwrap(),
1149            Some(ParseEvent::StructEnd)
1150        ));
1151
1152        // EOF
1153        assert!(parser.next_event().unwrap().is_none());
1154    }
1155
1156    #[test]
1157    fn test_table_header() {
1158        let input = r#"
1159[server]
1160host = "localhost"
1161port = 8080
1162"#;
1163        let mut parser = TomlParser::new(input).unwrap();
1164        let events = collect_events(&mut parser);
1165
1166        // Expected: StructStart, FieldKey(server), StructStart, FieldKey(host), Scalar,
1167        //           FieldKey(port), Scalar, StructEnd, StructEnd
1168        assert!(matches!(&events[0], ParseEvent::StructStart(_)));
1169        assert!(
1170            matches!(&events[1], ParseEvent::FieldKey(k) if k.name.as_deref() == Some("server"))
1171        );
1172        assert!(matches!(&events[2], ParseEvent::StructStart(_)));
1173        assert!(matches!(&events[3], ParseEvent::FieldKey(k) if k.name.as_deref() == Some("host")));
1174        assert!(matches!(&events[4], ParseEvent::Scalar(ScalarValue::Str(s)) if s == "localhost"));
1175        assert!(matches!(&events[5], ParseEvent::FieldKey(k) if k.name.as_deref() == Some("port")));
1176        assert!(matches!(
1177            &events[6],
1178            ParseEvent::Scalar(ScalarValue::I64(8080))
1179        ));
1180        assert!(matches!(&events[7], ParseEvent::StructEnd)); // server
1181        assert!(matches!(&events[8], ParseEvent::StructEnd)); // root
1182    }
1183
1184    #[test]
1185    fn test_array_table() {
1186        let input = r#"
1187[[servers]]
1188name = "alpha"
1189
1190[[servers]]
1191name = "beta"
1192"#;
1193        let mut parser = TomlParser::new(input).unwrap();
1194        let events = collect_events(&mut parser);
1195
1196        // Expected sequence:
1197        // StructStart (root)
1198        // FieldKey(servers), SequenceStart, StructStart (element 0)
1199        // FieldKey(name), Scalar(alpha)
1200        // StructEnd (element 0), SequenceEnd
1201        // FieldKey(servers), SequenceStart, StructStart (element 1) <- REOPEN
1202        // FieldKey(name), Scalar(beta)
1203        // StructEnd (element 1), SequenceEnd
1204        // StructEnd (root)
1205
1206        let event_str = format_events(&events);
1207        eprintln!("Events:\n{}", event_str);
1208
1209        assert!(matches!(&events[0], ParseEvent::StructStart(_))); // root
1210        assert!(
1211            matches!(&events[1], ParseEvent::FieldKey(k) if k.name.as_deref() == Some("servers"))
1212        );
1213        assert!(matches!(&events[2], ParseEvent::SequenceStart(_)));
1214        assert!(matches!(&events[3], ParseEvent::StructStart(_))); // element 0
1215        assert!(matches!(&events[4], ParseEvent::FieldKey(k) if k.name.as_deref() == Some("name")));
1216        assert!(matches!(&events[5], ParseEvent::Scalar(ScalarValue::Str(s)) if s == "alpha"));
1217        assert!(matches!(&events[6], ParseEvent::StructEnd)); // element 0
1218        assert!(matches!(&events[7], ParseEvent::SequenceEnd)); // servers array (navigate up)
1219
1220        // Reopen servers array
1221        assert!(
1222            matches!(&events[8], ParseEvent::FieldKey(k) if k.name.as_deref() == Some("servers"))
1223        );
1224        assert!(matches!(&events[9], ParseEvent::SequenceStart(_)));
1225        assert!(matches!(&events[10], ParseEvent::StructStart(_))); // element 1
1226        assert!(
1227            matches!(&events[11], ParseEvent::FieldKey(k) if k.name.as_deref() == Some("name"))
1228        );
1229        assert!(matches!(&events[12], ParseEvent::Scalar(ScalarValue::Str(s)) if s == "beta"));
1230    }
1231
1232    #[test]
1233    fn test_interleaved_array_table() {
1234        // This is the tricky case: array table elements interleaved with other tables
1235        let input = r#"
1236[[servers]]
1237name = "alpha"
1238
1239[database]
1240host = "localhost"
1241
1242[[servers]]
1243name = "beta"
1244"#;
1245        let mut parser = TomlParser::new(input).unwrap();
1246        let events = collect_events(&mut parser);
1247
1248        let event_str = format_events(&events);
1249        eprintln!("Interleaved events:\n{}", event_str);
1250
1251        // The key verification: we should see servers array opened, closed,
1252        // then database, then servers reopened
1253        let mut saw_servers_first = false;
1254        let mut saw_database = false;
1255        let mut saw_servers_second = false;
1256        let mut servers_count = 0;
1257
1258        for event in events.iter() {
1259            if let ParseEvent::FieldKey(k) = event {
1260                if k.name.as_deref() == Some("servers") {
1261                    servers_count += 1;
1262                    if !saw_database {
1263                        saw_servers_first = true;
1264                    } else {
1265                        saw_servers_second = true;
1266                    }
1267                } else if k.name.as_deref() == Some("database") {
1268                    saw_database = true;
1269                }
1270            }
1271        }
1272
1273        assert!(saw_servers_first, "Should see servers before database");
1274        assert!(saw_database, "Should see database");
1275        assert!(
1276            saw_servers_second,
1277            "Should see servers reopened after database"
1278        );
1279        assert_eq!(servers_count, 2, "Should have two FieldKey(servers) events");
1280    }
1281
1282    #[test]
1283    fn test_table_reopening() {
1284        // Standard table reopening (not array table)
1285        let input = r#"
1286[foo.bar]
1287x = 1
1288
1289[foo.baz]
1290z = 3
1291
1292[foo.bar]
1293y = 2
1294"#;
1295        let mut parser = TomlParser::new(input).unwrap();
1296        let events = collect_events(&mut parser);
1297
1298        let event_str = format_events(&events);
1299        eprintln!("Table reopen events:\n{}", event_str);
1300
1301        // Count how many times we see FieldKey("bar")
1302        let bar_count = events
1303            .iter()
1304            .filter(|e| matches!(e, ParseEvent::FieldKey(k) if k.name.as_deref() == Some("bar")))
1305            .count();
1306
1307        assert_eq!(bar_count, 2, "Should see bar twice (reopened)");
1308    }
1309
1310    #[test]
1311    fn test_dotted_key() {
1312        let input = r#"
1313foo.bar.baz = 1
1314"#;
1315        let mut parser = TomlParser::new(input).unwrap();
1316        let events = collect_events(&mut parser);
1317
1318        let event_str = format_events(&events);
1319        eprintln!("Dotted key events:\n{}", event_str);
1320
1321        // Expected: StructStart, FieldKey(foo), StructStart, FieldKey(bar), StructStart,
1322        //           FieldKey(baz), Scalar(1), StructEnd, StructEnd, StructEnd
1323        assert!(matches!(&events[0], ParseEvent::StructStart(_))); // root
1324        assert!(matches!(&events[1], ParseEvent::FieldKey(k) if k.name.as_deref() == Some("foo")));
1325        assert!(matches!(&events[2], ParseEvent::StructStart(_)));
1326        assert!(matches!(&events[3], ParseEvent::FieldKey(k) if k.name.as_deref() == Some("bar")));
1327        assert!(matches!(&events[4], ParseEvent::StructStart(_)));
1328        assert!(matches!(&events[5], ParseEvent::FieldKey(k) if k.name.as_deref() == Some("baz")));
1329        assert!(matches!(
1330            &events[6],
1331            ParseEvent::Scalar(ScalarValue::I64(1))
1332        ));
1333        // Three StructEnds for the nested structs, plus root
1334        assert!(matches!(&events[7], ParseEvent::StructEnd));
1335        assert!(matches!(&events[8], ParseEvent::StructEnd));
1336        assert!(matches!(&events[9], ParseEvent::StructEnd));
1337    }
1338
1339    #[test]
1340    fn test_inline_table() {
1341        let input = r#"
1342server = { host = "localhost", port = 8080 }
1343"#;
1344        let mut parser = TomlParser::new(input).unwrap();
1345        let events = collect_events(&mut parser);
1346
1347        let event_str = format_events(&events);
1348        eprintln!("Inline table events:\n{}", event_str);
1349
1350        assert!(matches!(&events[0], ParseEvent::StructStart(_))); // root
1351        assert!(
1352            matches!(&events[1], ParseEvent::FieldKey(k) if k.name.as_deref() == Some("server"))
1353        );
1354        assert!(matches!(&events[2], ParseEvent::StructStart(_))); // inline table
1355        assert!(matches!(&events[3], ParseEvent::FieldKey(k) if k.name.as_deref() == Some("host")));
1356        assert!(matches!(&events[4], ParseEvent::Scalar(ScalarValue::Str(s)) if s == "localhost"));
1357        assert!(matches!(&events[5], ParseEvent::FieldKey(k) if k.name.as_deref() == Some("port")));
1358        assert!(matches!(
1359            &events[6],
1360            ParseEvent::Scalar(ScalarValue::I64(8080))
1361        ));
1362        assert!(matches!(&events[7], ParseEvent::StructEnd)); // inline table
1363        assert!(matches!(&events[8], ParseEvent::StructEnd)); // root
1364    }
1365
1366    #[test]
1367    fn test_inline_array() {
1368        let input = r#"
1369numbers = [1, 2, 3]
1370"#;
1371        let mut parser = TomlParser::new(input).unwrap();
1372        let events = collect_events(&mut parser);
1373
1374        let event_str = format_events(&events);
1375        eprintln!("Inline array events:\n{}", event_str);
1376
1377        assert!(matches!(&events[0], ParseEvent::StructStart(_))); // root
1378        assert!(
1379            matches!(&events[1], ParseEvent::FieldKey(k) if k.name.as_deref() == Some("numbers"))
1380        );
1381        assert!(matches!(&events[2], ParseEvent::SequenceStart(_)));
1382        assert!(matches!(
1383            &events[3],
1384            ParseEvent::Scalar(ScalarValue::I64(1))
1385        ));
1386        assert!(matches!(
1387            &events[4],
1388            ParseEvent::Scalar(ScalarValue::I64(2))
1389        ));
1390        assert!(matches!(
1391            &events[5],
1392            ParseEvent::Scalar(ScalarValue::I64(3))
1393        ));
1394        assert!(matches!(&events[6], ParseEvent::SequenceEnd));
1395        assert!(matches!(&events[7], ParseEvent::StructEnd)); // root
1396    }
1397
1398    // ========================================================================
1399    // Deserialization tests (full pipeline)
1400    // ========================================================================
1401
1402    #[test]
1403    fn test_deserialize_simple_struct() {
1404        #[derive(Debug, PartialEq, facet::Facet)]
1405        struct Config {
1406            name: String,
1407            port: i64,
1408            enabled: bool,
1409        }
1410
1411        let input = r#"
1412name = "myapp"
1413port = 8080
1414enabled = true
1415"#;
1416        let config: Config = from_str(input).unwrap();
1417        assert_eq!(config.name, "myapp");
1418        assert_eq!(config.port, 8080);
1419        assert!(config.enabled);
1420    }
1421
1422    #[test]
1423    fn test_deserialize_nested_table() {
1424        #[derive(Debug, PartialEq, facet::Facet)]
1425        struct Config {
1426            server: Server,
1427        }
1428
1429        #[derive(Debug, PartialEq, facet::Facet)]
1430        struct Server {
1431            host: String,
1432            port: i64,
1433        }
1434
1435        let input = r#"
1436[server]
1437host = "localhost"
1438port = 3000
1439"#;
1440        let config: Config = from_str(input).unwrap();
1441        assert_eq!(config.server.host, "localhost");
1442        assert_eq!(config.server.port, 3000);
1443    }
1444
1445    #[test]
1446    fn test_deserialize_array_table() {
1447        #[derive(Debug, PartialEq, facet::Facet)]
1448        struct Config {
1449            servers: Vec<Server>,
1450        }
1451
1452        #[derive(Debug, PartialEq, facet::Facet)]
1453        struct Server {
1454            name: String,
1455        }
1456
1457        let input = r#"
1458[[servers]]
1459name = "alpha"
1460
1461[[servers]]
1462name = "beta"
1463
1464[[servers]]
1465name = "gamma"
1466"#;
1467        let config: Config = from_str(input).unwrap();
1468        assert_eq!(config.servers.len(), 3);
1469        assert_eq!(config.servers[0].name, "alpha");
1470        assert_eq!(config.servers[1].name, "beta");
1471        assert_eq!(config.servers[2].name, "gamma");
1472    }
1473
1474    #[test]
1475    fn test_deserialize_interleaved_array_table() {
1476        #[derive(Debug, PartialEq, facet::Facet)]
1477        struct Config {
1478            servers: Vec<Server>,
1479            database: Database,
1480        }
1481
1482        #[derive(Debug, PartialEq, facet::Facet)]
1483        struct Server {
1484            name: String,
1485        }
1486
1487        #[derive(Debug, PartialEq, facet::Facet)]
1488        struct Database {
1489            host: String,
1490        }
1491
1492        let input = r#"
1493[[servers]]
1494name = "alpha"
1495
1496[database]
1497host = "localhost"
1498
1499[[servers]]
1500name = "beta"
1501"#;
1502        let config: Config = from_str(input).unwrap();
1503        assert_eq!(config.servers.len(), 2);
1504        assert_eq!(config.servers[0].name, "alpha");
1505        assert_eq!(config.servers[1].name, "beta");
1506        assert_eq!(config.database.host, "localhost");
1507    }
1508
1509    #[test]
1510    fn test_issue_1399_array_of_tables_only_parses_last_entry() {
1511        // Regression test for #1399: array-of-tables should collect all entries, not just the last one
1512        // The bug is specifically with Option<Vec<T>>, not Vec<T>
1513        #[derive(Debug, PartialEq, facet::Facet)]
1514        struct Lockfile {
1515            version: Option<u32>,
1516            package: Option<Vec<Package>>,
1517        }
1518
1519        #[derive(Debug, PartialEq, facet::Facet)]
1520        struct Package {
1521            name: String,
1522            version: String,
1523        }
1524
1525        let input = r#"
1526version = 4
1527
1528[[package]]
1529name = "myapp"
1530version = "0.1.0"
1531
1532[[package]]
1533name = "aho-corasick"
1534version = "1.1.2"
1535"#;
1536        let lockfile: Lockfile = from_str(input).unwrap();
1537
1538        assert_eq!(lockfile.version, Some(4));
1539
1540        let packages = lockfile.package.expect("package field should be Some");
1541        assert_eq!(
1542            packages.len(),
1543            2,
1544            "Should parse both package entries, not just the last one"
1545        );
1546
1547        assert_eq!(packages[0].name, "myapp");
1548        assert_eq!(packages[0].version, "0.1.0");
1549
1550        assert_eq!(packages[1].name, "aho-corasick");
1551        assert_eq!(packages[1].version, "1.1.2");
1552    }
1553
1554    #[test]
1555    fn test_deserialize_inline_table() {
1556        #[derive(Debug, PartialEq, facet::Facet)]
1557        struct Config {
1558            point: Point,
1559        }
1560
1561        #[derive(Debug, PartialEq, facet::Facet)]
1562        struct Point {
1563            x: i64,
1564            y: i64,
1565        }
1566
1567        let input = r#"point = { x = 10, y = 20 }"#;
1568        let config: Config = from_str(input).unwrap();
1569        assert_eq!(config.point.x, 10);
1570        assert_eq!(config.point.y, 20);
1571    }
1572
1573    #[test]
1574    fn test_deserialize_inline_array() {
1575        #[derive(Debug, PartialEq, facet::Facet)]
1576        struct Config {
1577            values: Vec<i64>,
1578        }
1579
1580        let input = r#"values = [1, 2, 3, 4, 5]"#;
1581        let config: Config = from_str(input).unwrap();
1582        assert_eq!(config.values, vec![1, 2, 3, 4, 5]);
1583    }
1584
1585    #[test]
1586    fn test_deserialize_dotted_key() {
1587        #[derive(Debug, PartialEq, facet::Facet)]
1588        struct Config {
1589            foo: Foo,
1590        }
1591
1592        #[derive(Debug, PartialEq, facet::Facet)]
1593        struct Foo {
1594            bar: Bar,
1595        }
1596
1597        #[derive(Debug, PartialEq, facet::Facet)]
1598        struct Bar {
1599            baz: i64,
1600        }
1601
1602        let input = r#"foo.bar.baz = 42"#;
1603        let config: Config = from_str(input).unwrap();
1604        assert_eq!(config.foo.bar.baz, 42);
1605    }
1606
1607    // Table reopening: TOML allows fields for the same struct to appear at different
1608    // points in the document. This works because facet-toml uses deferred mode, which
1609    // stores frames when we navigate away and restores them when we re-enter.
1610    #[test]
1611    fn test_deserialize_table_reopening() {
1612        #[derive(Debug, PartialEq, facet::Facet)]
1613        struct Config {
1614            foo: Foo,
1615        }
1616
1617        #[derive(Debug, PartialEq, facet::Facet)]
1618        struct Foo {
1619            bar: Bar,
1620            baz: Baz,
1621        }
1622
1623        #[derive(Debug, PartialEq, facet::Facet)]
1624        struct Bar {
1625            x: i64,
1626            y: i64,
1627        }
1628
1629        #[derive(Debug, PartialEq, facet::Facet)]
1630        struct Baz {
1631            z: i64,
1632        }
1633
1634        let input = r#"
1635[foo.bar]
1636x = 1
1637
1638[foo.baz]
1639z = 3
1640
1641[foo.bar]
1642y = 2
1643"#;
1644        let config: Config = from_str(input).unwrap();
1645        assert_eq!(config.foo.bar.x, 1);
1646        assert_eq!(config.foo.bar.y, 2);
1647        assert_eq!(config.foo.baz.z, 3);
1648    }
1649}