Skip to main content

eure_document/
parse.rs

1//! FromEure trait for parsing Rust types from Eure documents.
2
3extern crate alloc;
4
5pub mod object_key;
6pub mod record;
7pub mod tuple;
8pub mod union;
9pub mod variant_path;
10
11use alloc::borrow::{Cow, ToOwned};
12use alloc::string::String;
13use indexmap::{IndexMap, IndexSet};
14pub use object_key::ParseObjectKey;
15pub use record::RecordParser;
16pub use tuple::TupleParser;
17pub use union::UnionParser;
18pub use variant_path::VariantPath;
19
20use alloc::format;
21use alloc::rc::Rc;
22use core::cell::RefCell;
23use num_bigint::BigInt;
24
25use core::marker::PhantomData;
26use std::collections::{BTreeMap, HashMap, HashSet};
27
28use crate::{
29    document::node::{Node, NodeArray},
30    identifier::IdentifierError,
31    prelude_internal::*,
32    value::ValueKind,
33};
34
35// =============================================================================
36// AccessedSet
37// =============================================================================
38
39/// Snapshot of accessed state (fields, extensions).
40pub type AccessedSnapshot = (HashSet<String>, HashSet<Identifier>);
41
42/// Tracks accessed fields and extensions with snapshot/rollback support for union parsing.
43///
44/// The internal state is a stack where the last item is always the current working state.
45/// Items before it are snapshots (save points) for rollback.
46///
47/// Invariant: stack is never empty.
48///
49/// # Stack visualization
50/// ```text
51/// Initial:        [current]
52/// push_snapshot:  [snapshot, current]  (snapshot = copy of current)
53/// modify:         [snapshot, current'] (current' has changes)
54/// restore:        [snapshot, snapshot] (current reset to snapshot)
55/// pop_restore:    [snapshot]           (current removed, snapshot is new current)
56/// pop_no_restore: [current']           (snapshot removed, keep modified current)
57/// ```
58#[derive(Debug, Clone)]
59pub struct AccessedSet(Rc<RefCell<Vec<AccessedSnapshot>>>);
60
61impl AccessedSet {
62    /// Create a new empty set.
63    pub fn new() -> Self {
64        // Start with one empty state (the current working state)
65        Self(Rc::new(RefCell::new(vec![(
66            HashSet::new(),
67            HashSet::new(),
68        )])))
69    }
70
71    /// Add a field to the accessed set.
72    pub fn add_field(&self, field: impl Into<String>) {
73        self.0
74            .borrow_mut()
75            .last_mut()
76            .unwrap()
77            .0
78            .insert(field.into());
79    }
80
81    /// Add an extension to the accessed set.
82    pub fn add_ext(&self, ext: Identifier) {
83        self.0.borrow_mut().last_mut().unwrap().1.insert(ext);
84    }
85
86    /// Check if a field has been accessed.
87    pub fn has_field(&self, field: &str) -> bool {
88        self.0.borrow().last().unwrap().0.contains(field)
89    }
90
91    /// Check if an extension has been accessed.
92    pub fn has_ext(&self, ext: &Identifier) -> bool {
93        self.0.borrow().last().unwrap().1.contains(ext)
94    }
95
96    /// Get all accessed extensions.
97    pub fn get_accessed_exts(&self) -> HashSet<Identifier> {
98        self.0.borrow().last().unwrap().1.clone()
99    }
100
101    /// Push a snapshot (call at start of union parsing).
102    /// Inserts a copy of current BEFORE current, so current can be modified.
103    pub fn push_snapshot(&self) {
104        let mut stack = self.0.borrow_mut();
105        let snapshot = stack.last().unwrap().clone();
106        let len = stack.len();
107        stack.insert(len - 1, snapshot);
108        // Stack: [..., current] → [..., snapshot, current]
109    }
110
111    /// Restore current to the snapshot (call when variant fails).
112    /// Resets current (last) to match the snapshot (second-to-last).
113    pub fn restore_to_current_snapshot(&self) {
114        let mut stack = self.0.borrow_mut();
115        if stack.len() >= 2 {
116            let snapshot = stack[stack.len() - 2].clone();
117            *stack.last_mut().unwrap() = snapshot;
118        }
119    }
120
121    /// Capture the current state (for non-priority variants).
122    pub fn capture_current_state(&self) -> AccessedSnapshot {
123        self.0.borrow().last().unwrap().clone()
124    }
125
126    /// Restore to a previously captured state.
127    pub fn restore_to_state(&self, state: AccessedSnapshot) {
128        *self.0.borrow_mut().last_mut().unwrap() = state;
129    }
130
131    /// Pop and restore (call when union fails/ambiguous).
132    /// Removes current, snapshot becomes new current.
133    pub fn pop_and_restore(&self) {
134        let mut stack = self.0.borrow_mut();
135        if stack.len() >= 2 {
136            stack.pop(); // Remove current, snapshot is now current
137        }
138    }
139
140    /// Pop without restore (call when union succeeds).
141    /// Removes the snapshot, keeps current.
142    pub fn pop_without_restore(&self) {
143        let mut stack = self.0.borrow_mut();
144        if stack.len() >= 2 {
145            let snapshot_idx = stack.len() - 2;
146            stack.remove(snapshot_idx); // Remove snapshot, keep current
147        }
148    }
149}
150
151impl Default for AccessedSet {
152    fn default() -> Self {
153        Self::new()
154    }
155}
156
157// =============================================================================
158// ParserScope
159// =============================================================================
160
161/// Scope for flatten parsing - indicates whether we're in record or extension mode.
162///
163/// This determines what `parse_record_or_ext()` iterates over for catch-all types like HashMap.
164#[derive(Debug, Clone, Copy, PartialEq, Eq)]
165pub enum ParserScope {
166    /// Record scope - iterates record fields (from `rec.flatten()`)
167    Record,
168    /// Extension scope - iterates extensions (from `ext.flatten_ext()`)
169    Extension,
170}
171
172// =============================================================================
173// FlattenContext
174// =============================================================================
175
176/// Context for flatten parsing - wraps AccessedSet with snapshot/rollback support.
177///
178/// When parsing flattened types, all levels share a single `AccessedSet` owned
179/// by the root parser. Child parsers add to this shared set, and only the root
180/// parser actually validates with `deny_unknown_fields()`.
181///
182/// # Example
183///
184/// ```ignore
185/// // Root parser owns the accessed set
186/// let mut rec1 = ctx.parse_record()?;  // accessed = {} (owned)
187/// rec1.field("a");  // accessed = {a}
188///
189/// // Child parser shares and adds to the same set
190/// let ctx2 = rec1.flatten();
191/// let mut rec2 = ctx2.parse_record()?;  // shares accessed via Rc
192/// rec2.field("b");  // accessed = {a, b}
193/// rec2.deny_unknown_fields()?;  // NO-OP (child)
194///
195/// rec1.field("c");  // accessed = {a, b, c}
196/// rec1.deny_unknown_fields()?;  // VALIDATES (root)
197/// ```
198#[derive(Debug, Clone)]
199pub struct FlattenContext {
200    accessed: AccessedSet,
201    scope: ParserScope,
202}
203
204impl FlattenContext {
205    /// Create a FlattenContext from an existing AccessedSet with the given scope.
206    pub fn new(accessed: AccessedSet, scope: ParserScope) -> Self {
207        Self { accessed, scope }
208    }
209
210    /// Get the parser scope.
211    pub fn scope(&self) -> ParserScope {
212        self.scope
213    }
214
215    /// Get the underlying AccessedSet (for sharing with RecordParser).
216    pub fn accessed_set(&self) -> &AccessedSet {
217        &self.accessed
218    }
219
220    /// Add a field to the accessed set.
221    pub fn add_field(&self, field: impl Into<String>) {
222        self.accessed.add_field(field);
223    }
224
225    /// Add an extension to the accessed set.
226    pub fn add_ext(&self, ext: Identifier) {
227        self.accessed.add_ext(ext);
228    }
229
230    /// Check if a field has been accessed.
231    pub fn has_field(&self, field: &str) -> bool {
232        self.accessed.has_field(field)
233    }
234
235    /// Check if an extension has been accessed.
236    pub fn has_ext(&self, ext: &Identifier) -> bool {
237        self.accessed.has_ext(ext)
238    }
239
240    /// Push snapshot (at start of union parsing).
241    pub fn push_snapshot(&self) {
242        self.accessed.push_snapshot();
243    }
244
245    /// Restore to current snapshot (when variant fails).
246    pub fn restore_to_current_snapshot(&self) {
247        self.accessed.restore_to_current_snapshot();
248    }
249
250    /// Capture current state (for non-priority variants).
251    pub fn capture_current_state(&self) -> AccessedSnapshot {
252        self.accessed.capture_current_state()
253    }
254
255    /// Restore to a captured state (when selecting a non-priority variant).
256    pub fn restore_to_state(&self, state: AccessedSnapshot) {
257        self.accessed.restore_to_state(state);
258    }
259
260    /// Pop and restore (when union fails/ambiguous).
261    pub fn pop_and_restore(&self) {
262        self.accessed.pop_and_restore();
263    }
264
265    /// Pop without restore (when union succeeds).
266    pub fn pop_without_restore(&self) {
267        self.accessed.pop_without_restore();
268    }
269}
270
271// =============================================================================
272// ParseContext
273// =============================================================================
274
275/// Context for parsing Eure documents.
276///
277/// Encapsulates the document, current node, and variant path state.
278/// Similar to `DocumentConstructor` for writing, but for reading.
279#[derive(Clone, Debug)]
280pub struct ParseContext<'doc> {
281    doc: &'doc EureDocument,
282    node_id: NodeId,
283    variant_path: Option<VariantPath>,
284    /// Flatten context for tracking shared accessed fields/extensions.
285    /// If Some, this context is a flattened child - deny_unknown_* is no-op.
286    /// If None, this is a root context.
287    flatten_ctx: Option<FlattenContext>,
288    /// Tracks accessed fields and extensions.
289    accessed: AccessedSet,
290}
291
292impl<'doc> ParseContext<'doc> {
293    /// Create a new parse context at the given node.
294    pub fn new(doc: &'doc EureDocument, node_id: NodeId) -> Self {
295        Self {
296            doc,
297            node_id,
298            variant_path: None,
299            flatten_ctx: None,
300            accessed: AccessedSet::new(),
301        }
302    }
303
304    /// Create a context with a flatten context (for flatten parsing).
305    ///
306    /// When a flatten context is present:
307    /// - `deny_unknown_fields()` and `deny_unknown_extensions()` become no-ops
308    /// - All field/extension accesses are recorded in the shared `FlattenContext`
309    pub fn with_flatten_ctx(
310        doc: &'doc EureDocument,
311        node_id: NodeId,
312        flatten_ctx: FlattenContext,
313    ) -> Self {
314        // Share accessed set from flatten context
315        let accessed = flatten_ctx.accessed_set().clone();
316        Self {
317            doc,
318            node_id,
319            variant_path: None,
320            flatten_ctx: Some(flatten_ctx),
321            accessed,
322        }
323    }
324
325    /// Get the flatten context, if present.
326    pub fn flatten_ctx(&self) -> Option<&FlattenContext> {
327        self.flatten_ctx.as_ref()
328    }
329
330    /// Check if this context is flattened (has a flatten context).
331    ///
332    /// When flattened, `deny_unknown_fields()` and `deny_unknown_extensions()` are no-ops.
333    pub fn is_flattened(&self) -> bool {
334        self.flatten_ctx.is_some()
335    }
336
337    /// Get the parser scope, if in a flatten context.
338    ///
339    /// Returns `Some(ParserScope::Record)` if from `rec.flatten()`,
340    /// `Some(ParserScope::Extension)` if from `ext.flatten_ext()`,
341    /// `None` if not in a flatten context.
342    pub fn parser_scope(&self) -> Option<ParserScope> {
343        self.flatten_ctx.as_ref().map(|fc| fc.scope())
344    }
345
346    /// Get the current node ID.
347    pub fn node_id(&self) -> NodeId {
348        self.node_id
349    }
350
351    /// Get the document reference (internal use only).
352    pub(crate) fn doc(&self) -> &'doc EureDocument {
353        self.doc
354    }
355
356    /// Get the current node.
357    pub fn node(&self) -> &'doc Node {
358        self.doc.node(self.node_id)
359    }
360
361    /// Create a new context at a different node (clears variant path and flatten context).
362    pub(crate) fn at(&self, node_id: NodeId) -> Self {
363        Self {
364            doc: self.doc,
365            node_id,
366            variant_path: None,
367            flatten_ctx: None,
368            accessed: AccessedSet::new(),
369        }
370    }
371
372    /// Create a flattened version of this context for shared access tracking.
373    ///
374    /// When you need both record parsing and extension parsing to share
375    /// access tracking (so deny_unknown_* works correctly), use this method
376    /// to create a shared context first.
377    ///
378    /// # Scope
379    ///
380    /// This method always sets `ParserScope::Record`. When alternating between
381    /// `flatten()` and `flatten_ext()`, the scope is updated each time:
382    ///
383    /// ```ignore
384    /// ctx.flatten()       // scope = Record
385    ///    .flatten_ext()   // scope = Extension
386    ///    .flatten()       // scope = Record (updated, not inherited)
387    /// ```
388    ///
389    /// # AccessedSet Sharing
390    ///
391    /// The `AccessedSet` is shared across all contexts in the flatten chain
392    /// (via `Rc`). This ensures that field/extension accesses are tracked
393    /// regardless of which scope they were accessed from, and the root parser
394    /// can validate everything with `deny_unknown_*`.
395    ///
396    /// # Example
397    ///
398    /// ```ignore
399    /// // Both record and extension parsers share the same AccessedSet
400    /// let ctx = ctx.flatten();
401    /// // ... parse extensions with ctx.parse_ext(...) ...
402    /// let mut rec = ctx.parse_record()?;
403    /// // ... parse fields ...
404    /// rec.deny_unknown_fields()?;  // Validates both fields and extensions
405    /// ```
406    pub fn flatten(&self) -> Self {
407        // Always create a NEW FlattenContext with Record scope.
408        // We cannot just clone the existing FlattenContext because that would
409        // preserve the old scope. Instead, we create a new one with the correct
410        // scope while sharing the AccessedSet (via Rc clone).
411        let flatten_ctx = match &self.flatten_ctx {
412            Some(fc) => FlattenContext::new(fc.accessed_set().clone(), ParserScope::Record),
413            None => FlattenContext::new(self.accessed.clone(), ParserScope::Record),
414        };
415        Self {
416            doc: self.doc,
417            node_id: self.node_id,
418            variant_path: self.variant_path.clone(),
419            flatten_ctx: Some(flatten_ctx),
420            accessed: self.accessed.clone(),
421        }
422    }
423
424    /// Parse the current node as type T.
425    pub fn parse<T: FromEure<'doc, T>>(&self) -> Result<T, T::Error> {
426        T::parse(self)
427    }
428
429    /// Parse the current node as type T using a marker/strategy type M.
430    ///
431    /// This is used for parsing remote types where `M` implements
432    /// `FromEure<'doc, T>` but `T` doesn't implement `FromEure` itself.
433    pub fn parse_via<M, T>(&self) -> Result<T, M::Error>
434    where
435        M: FromEure<'doc, T>,
436    {
437        M::parse(self)
438    }
439
440    pub fn parse_with<T: DocumentParser<'doc>>(
441        &self,
442        mut parser: T,
443    ) -> Result<T::Output, T::Error> {
444        parser.parse(self)
445    }
446
447    /// Get a union parser for the current node.
448    ///
449    /// Returns error if `$variant` extension has invalid type or syntax.
450    pub fn parse_union<T, E>(&self) -> Result<UnionParser<'doc, '_, T, E>, E>
451    where
452        E: UnionParseError,
453    {
454        UnionParser::new(self).map_err(Into::into)
455    }
456
457    /// Parse the current node as a record.
458    ///
459    /// Returns error if variant path is not empty.
460    pub fn parse_record(&self) -> Result<RecordParser<'doc>, ParseError> {
461        self.ensure_no_variant_path()?;
462        RecordParser::new(self)
463    }
464
465    /// Parse the current node as a tuple.
466    ///
467    /// Returns error if variant path is not empty.
468    pub fn parse_tuple(&self) -> Result<TupleParser<'doc>, ParseError> {
469        self.ensure_no_variant_path()?;
470        TupleParser::new(self)
471    }
472
473    /// Parse the current node as a primitive value.
474    ///
475    /// Returns `NotPrimitive` error if the node is not a primitive.
476    /// Returns `UnexpectedVariantPath` error if variant path is not empty.
477    pub fn parse_primitive(&self) -> Result<&'doc PrimitiveValue, ParseError> {
478        self.ensure_no_variant_path()?;
479        match &self.node().content {
480            NodeValue::Primitive(p) => Ok(p),
481            _ => Err(ParseError {
482                node_id: self.node_id(),
483                kind: ParseErrorKind::NotPrimitive {
484                    actual: self.node().content.value_kind(),
485                },
486            }),
487        }
488    }
489
490    // =========================================================================
491    // Extension parsing methods
492    // =========================================================================
493
494    /// Get the AccessedSet for this context.
495    pub(crate) fn accessed(&self) -> &AccessedSet {
496        &self.accessed
497    }
498
499    /// Create a standalone document from the current node's subtree,
500    /// excluding extensions that have been marked as accessed.
501    ///
502    /// This is useful for literal comparison in schema validation, where
503    /// extensions like `$variant` have been consumed by union resolution
504    /// and should not be part of the literal value.
505    pub fn node_subtree_to_document_excluding_accessed(&self) -> EureDocument {
506        let mut doc = self.doc.node_subtree_to_document(self.node_id);
507        let root_id = doc.get_root_id();
508        let accessed_exts = self.accessed.get_accessed_exts();
509        for ext in accessed_exts {
510            doc.node_mut(root_id).extensions.remove_fast(&ext);
511        }
512        doc
513    }
514
515    /// Mark an extension as accessed.
516    fn mark_ext_accessed(&self, ident: Identifier) {
517        self.accessed.add_ext(ident);
518    }
519
520    /// Get a required extension field.
521    ///
522    /// Returns `ParseErrorKind::MissingExtension` if the extension is not present.
523    pub fn parse_ext<T>(&self, name: &str) -> Result<T, T::Error>
524    where
525        T: FromEure<'doc>,
526        T::Error: From<ParseError>,
527    {
528        self.parse_ext_with(name, T::parse)
529    }
530
531    /// Get a required extension field with a custom parser.
532    pub fn parse_ext_with<T>(&self, name: &str, mut parser: T) -> Result<T::Output, T::Error>
533    where
534        T: DocumentParser<'doc>,
535        T::Error: From<ParseError>,
536    {
537        let ident: Identifier = name.parse().map_err(|e| ParseError {
538            node_id: self.node_id,
539            kind: ParseErrorKind::InvalidIdentifier(e),
540        })?;
541        self.mark_ext_accessed(ident.clone());
542        let ext_node_id = self
543            .node()
544            .extensions
545            .get(&ident)
546            .ok_or_else(|| ParseError {
547                node_id: self.node_id,
548                kind: ParseErrorKind::MissingExtension(name.to_string()),
549            })?;
550        let ctx = ParseContext::new(self.doc, *ext_node_id);
551        parser.parse(&ctx)
552    }
553
554    /// Get an optional extension field.
555    ///
556    /// Returns `Ok(None)` if the extension is not present.
557    pub fn parse_ext_optional<T>(&self, name: &str) -> Result<Option<T>, T::Error>
558    where
559        T: FromEure<'doc>,
560        T::Error: From<ParseError>,
561    {
562        self.parse_ext_optional_with(name, T::parse)
563    }
564
565    /// Get an optional extension field with a custom parser.
566    ///
567    /// Returns `Ok(None)` if the extension is not present.
568    pub fn parse_ext_optional_with<T>(
569        &self,
570        name: &str,
571        mut parser: T,
572    ) -> Result<Option<T::Output>, T::Error>
573    where
574        T: DocumentParser<'doc>,
575        T::Error: From<ParseError>,
576    {
577        let ident: Identifier = name.parse().map_err(|e| ParseError {
578            node_id: self.node_id,
579            kind: ParseErrorKind::InvalidIdentifier(e),
580        })?;
581        self.mark_ext_accessed(ident.clone());
582        match self.node().extensions.get(&ident) {
583            Some(ext_node_id) => {
584                let ctx = ParseContext::new(self.doc, *ext_node_id);
585                Ok(Some(parser.parse(&ctx)?))
586            }
587            None => Ok(None),
588        }
589    }
590
591    /// Get the parse context for an extension field without parsing it.
592    ///
593    /// Use this when you need access to the extension's NodeId or want to defer parsing.
594    /// Returns `ParseErrorKind::MissingExtension` if the extension is not present.
595    pub fn ext(&self, name: &str) -> Result<ParseContext<'doc>, ParseError> {
596        let ident: Identifier = name.parse().map_err(|e| ParseError {
597            node_id: self.node_id,
598            kind: ParseErrorKind::InvalidIdentifier(e),
599        })?;
600        self.mark_ext_accessed(ident.clone());
601        let ext_node_id =
602            self.node()
603                .extensions
604                .get(&ident)
605                .copied()
606                .ok_or_else(|| ParseError {
607                    node_id: self.node_id,
608                    kind: ParseErrorKind::MissingExtension(name.to_string()),
609                })?;
610        Ok(ParseContext::new(self.doc, ext_node_id))
611    }
612
613    /// Get the parse context for an optional extension field without parsing it.
614    ///
615    /// Use this when you need access to the extension's NodeId or want to defer parsing.
616    /// Returns `None` if the extension is not present.
617    pub fn ext_optional(&self, name: &str) -> Option<ParseContext<'doc>> {
618        let ident: Identifier = name.parse().ok()?;
619        self.mark_ext_accessed(ident.clone());
620        self.node()
621            .extensions
622            .get(&ident)
623            .map(|&node_id| ParseContext::new(self.doc, node_id))
624    }
625
626    /// Finish parsing with Deny policy (error if unknown extensions exist).
627    ///
628    /// **Flatten behavior**: If this context is in a flatten chain (has flatten_ctx),
629    /// this is a no-op. Only root parsers validate.
630    pub fn deny_unknown_extensions(&self) -> Result<(), ParseError> {
631        // If child (in any flatten context), no-op - parent will validate
632        if self.flatten_ctx.is_some() {
633            return Ok(());
634        }
635
636        // Root parser - validate using accessed set
637        for (ident, _) in self.node().extensions.iter() {
638            if !self.accessed.has_ext(ident) {
639                return Err(ParseError {
640                    node_id: self.node_id,
641                    kind: ParseErrorKind::UnknownExtension(ident.clone()),
642                });
643            }
644        }
645        Ok(())
646    }
647
648    /// Get an iterator over unknown extensions (for custom handling).
649    ///
650    /// Returns (identifier, context) pairs for extensions that haven't been accessed.
651    pub fn unknown_extensions(
652        &self,
653    ) -> impl Iterator<Item = (&'doc Identifier, ParseContext<'doc>)> + '_ {
654        let doc = self.doc;
655        // Clone the accessed set for filtering - we need the current state
656        let accessed = self.accessed.clone();
657        self.node()
658            .extensions
659            .iter()
660            .filter_map(move |(ident, &node_id)| {
661                if !accessed.has_ext(ident) {
662                    Some((ident, ParseContext::new(doc, node_id)))
663                } else {
664                    None
665                }
666            })
667    }
668
669    /// Create a flatten context for child parsers in Extension scope.
670    ///
671    /// This creates a FlattenContext initialized with the current accessed extensions,
672    /// and returns a ParseContext that children can use. Children created from this
673    /// context will:
674    /// - Add their accessed extensions to the shared FlattenContext
675    /// - Have deny_unknown_extensions() be a no-op
676    ///
677    /// The root parser should call deny_unknown_extensions() after all children are done.
678    ///
679    /// # Scope
680    ///
681    /// This method always sets `ParserScope::Extension`. When alternating between
682    /// `flatten()` and `flatten_ext()`, the scope is updated each time:
683    ///
684    /// ```ignore
685    /// ctx.flatten()       // scope = Record
686    ///    .flatten_ext()   // scope = Extension (updated, not inherited)
687    ///    .flatten()       // scope = Record
688    /// ```
689    ///
690    /// # AccessedSet Sharing
691    ///
692    /// The `AccessedSet` is shared across all contexts in the flatten chain
693    /// (via `Rc`). See [`flatten()`](Self::flatten) for details.
694    pub fn flatten_ext(&self) -> ParseContext<'doc> {
695        // Always create a NEW FlattenContext with Extension scope.
696        // We cannot just clone the existing FlattenContext because that would
697        // preserve the old scope. Instead, we create a new one with the correct
698        // scope while sharing the AccessedSet (via Rc clone).
699        let flatten_ctx = match &self.flatten_ctx {
700            Some(fc) => FlattenContext::new(fc.accessed_set().clone(), ParserScope::Extension),
701            None => FlattenContext::new(self.accessed.clone(), ParserScope::Extension),
702        };
703
704        ParseContext::with_flatten_ctx(self.doc, self.node_id, flatten_ctx)
705    }
706
707    /// Check if the current node is null.
708    pub fn is_null(&self) -> bool {
709        matches!(
710            &self.node().content,
711            NodeValue::Primitive(PrimitiveValue::Null)
712        )
713    }
714
715    /// Create a child context with the remaining variant path.
716    pub(crate) fn with_variant_rest(&self, rest: Option<VariantPath>) -> Self {
717        Self {
718            doc: self.doc,
719            node_id: self.node_id,
720            variant_path: rest,
721            flatten_ctx: self.flatten_ctx.clone(),
722            accessed: self.accessed.clone(),
723        }
724    }
725
726    /// Get the current variant path.
727    pub(crate) fn variant_path(&self) -> Option<&VariantPath> {
728        self.variant_path.as_ref()
729    }
730
731    /// Check that no variant path remains, error otherwise.
732    fn ensure_no_variant_path(&self) -> Result<(), ParseError> {
733        if let Some(vp) = &self.variant_path
734            && !vp.is_empty()
735        {
736            return Err(ParseError {
737                node_id: self.node_id,
738                kind: ParseErrorKind::UnexpectedVariantPath(vp.clone()),
739            });
740        }
741        Ok(())
742    }
743
744    fn unexpected_kind(&self, expected: ValueKind) -> ParseError {
745        ParseError {
746            node_id: self.node_id(),
747            kind: ParseErrorKind::TypeMismatch {
748                expected,
749                actual: self.node().content.value_kind(),
750            },
751        }
752    }
753}
754
755// =============================================================================
756// FromEure trait
757// =============================================================================
758
759/// Trait for parsing Rust types from Eure documents.
760///
761/// Types implementing this trait can be constructed from [`EureDocument`]
762/// via [`ParseContext`].
763///
764/// # Type Parameters
765///
766/// - `'doc`: The document lifetime, allowing zero-copy parsing for references
767/// - `T`: The target type to parse (defaults to `Self`)
768///
769/// When `T = Self` (the default), this is standard parsing.
770/// When `T != Self`, `Self` acts as a "strategy" type for parsing remote types.
771/// This follows the same pattern as `PartialEq<Rhs = Self>`.
772///
773/// # Remote Type Support
774///
775/// The `T` parameter enables parsing external crate types that can't implement
776/// `FromEure` directly (due to Rust's orphan rule). Define a marker type and
777/// implement `FromEure<'doc, RemoteType>` for it:
778///
779/// ```ignore
780/// struct DurationDef;
781///
782/// impl<'doc> FromEure<'doc, std::time::Duration> for DurationDef {
783///     type Error = ParseError;
784///     fn parse(ctx: &ParseContext<'doc>) -> Result<std::time::Duration, Self::Error> {
785///         let rec = ctx.parse_record()?;
786///         let secs: u64 = rec.parse_field("secs")?;
787///         let nanos: u32 = rec.parse_field("nanos")?;
788///         rec.deny_unknown_fields()?;
789///         Ok(std::time::Duration::new(secs, nanos))
790///     }
791/// }
792/// ```
793///
794/// Container types (`Option<M>`, `Vec<M>`, etc.) automatically support remote types:
795/// if `M: FromEure<'doc, T>`, then `Option<M>: FromEure<'doc, Option<T>>`.
796///
797/// # Examples
798///
799/// ```ignore
800/// // Reference type - borrows from document
801/// impl<'doc> FromEure<'doc> for &'doc str { ... }
802///
803/// // Owned type - no lifetime dependency
804/// impl FromEure<'_> for String { ... }
805/// ```
806#[diagnostic::on_unimplemented(
807    message = "`{Self}` cannot be parsed from Eure document",
808    label = "this type does not implement `FromEure`",
809    note = "consider adding `#[derive(FromEure)]` to `{Self}`"
810)]
811pub trait FromEure<'doc, T = Self>: Sized {
812    /// The error type returned by parsing.
813    type Error;
814
815    /// Parse a value of type T from the given parse context.
816    fn parse(ctx: &ParseContext<'doc>) -> Result<T, Self::Error>;
817}
818
819/// Additional behavior required by [`ParseContext::parse_union`].
820///
821/// Union parsing needs to synthesize a "no matching variant" error after all
822/// variants have been tried. Some callers want that as a plain [`ParseError`],
823/// while others use a higher-level error type and treat it as a control signal.
824pub trait UnionParseError: From<ParseError> {
825    /// Returns the underlying [`ParseError`] when one exists.
826    fn as_parse_error(&self) -> Option<&ParseError>;
827
828    /// Constructs the error returned when no union variant matches.
829    fn from_no_matching_variant(
830        node_id: NodeId,
831        variant: Option<String>,
832        best_match: Option<BestParseVariantMatch>,
833        _failures: &[(String, Self)],
834    ) -> Self {
835        ParseError {
836            node_id,
837            kind: ParseErrorKind::NoMatchingVariant {
838                variant,
839                best_match: best_match.map(Box::new),
840            },
841        }
842        .into()
843    }
844}
845
846impl UnionParseError for ParseError {
847    fn as_parse_error(&self) -> Option<&ParseError> {
848        Some(self)
849    }
850}
851
852/// The nearest matching variant for a failed parse-time union attempt.
853#[derive(Debug, Clone, PartialEq)]
854pub struct BestParseVariantMatch {
855    /// Name of the variant that came closest to matching.
856    pub variant_name: String,
857    /// The error produced by that variant.
858    pub error: Box<ParseError>,
859}
860
861#[derive(Debug, thiserror::Error, Clone, PartialEq)]
862#[error("parse error: {kind}")]
863pub struct ParseError {
864    pub node_id: NodeId,
865    pub kind: ParseErrorKind,
866}
867
868/// Error type for parsing failures.
869#[derive(Debug, thiserror::Error, Clone, PartialEq)]
870pub enum ParseErrorKind {
871    /// Unexpected uninitialized value.
872    #[error("unexpected uninitialized value")]
873    UnexpectedHole,
874
875    /// Type mismatch between expected and actual value.
876    #[error("type mismatch: expected {expected}, got {actual}")]
877    TypeMismatch {
878        expected: ValueKind,
879        actual: ValueKind,
880    },
881
882    /// Required field is missing.
883    #[error("missing field: {0}")]
884    MissingField(String),
885
886    /// Required extension is missing.
887    #[error("missing extension: ${0}")]
888    MissingExtension(String),
889
890    /// Unknown variant in a union type.
891    #[error("unknown variant: {0}")]
892    UnknownVariant(String),
893
894    /// Value is out of valid range.
895    #[error("value out of range: {0}")]
896    OutOfRange(String),
897
898    /// Invalid value pattern or format.
899    ///
900    /// Used for validation errors in types like regex, URL, UUID, etc.
901    /// - `kind`: Type of validation (e.g., "regex", "url", "uuid", "pattern: <expected>")
902    /// - `reason`: Human-readable error message explaining the failure
903    #[error("invalid {kind}: {reason}")]
904    InvalidPattern { kind: String, reason: String },
905
906    /// Nested parse error with path context.
907    #[error("at {path}: {source}")]
908    Nested {
909        path: String,
910        #[source]
911        source: Box<ParseErrorKind>,
912    },
913
914    /// Invalid identifier.
915    #[error("invalid identifier: {0}")]
916    InvalidIdentifier(#[from] IdentifierError),
917
918    /// Unexpected tuple length.
919    #[error("unexpected tuple length: expected {expected}, got {actual}")]
920    UnexpectedTupleLength { expected: usize, actual: usize },
921
922    /// Unknown field in record.
923    #[error("unknown field: {0}")]
924    UnknownField(String),
925
926    /// Unknown extension on node.
927    #[error("unknown extension: ${0}")]
928    UnknownExtension(Identifier),
929
930    /// Invalid key type in record (expected string).
931    #[error("invalid key type in record: expected string key, got {0:?}")]
932    InvalidKeyType(crate::value::ObjectKey),
933
934    /// No variant matched in union type.
935    #[error("{}", format_no_matching_variant(variant, best_match))]
936    NoMatchingVariant {
937        /// Variant name extracted (if any).
938        variant: Option<String>,
939        /// Best matching variant, when available.
940        best_match: Option<Box<BestParseVariantMatch>>,
941    },
942
943    /// Conflicting variant tags: $variant and repr extracted different variant names.
944    #[error("conflicting variant tags: $variant = {explicit}, repr = {repr}")]
945    ConflictingVariantTags { explicit: String, repr: String },
946
947    /// Multiple variants matched with no priority to resolve.
948    #[error("ambiguous union: {0:?}")]
949    AmbiguousUnion(Vec<String>),
950
951    /// Literal value mismatch.
952    #[error("literal value mismatch: expected {expected}, got {actual}")]
953    // FIXME: Use EureDocument instead of String?
954    LiteralMismatch { expected: String, actual: String },
955
956    /// Variant path provided but type is not a union.
957    #[error("unexpected variant path: {0}")]
958    UnexpectedVariantPath(VariantPath),
959
960    /// $variant extension has invalid type (not a string).
961    #[error("$variant must be a string, got {0}")]
962    InvalidVariantType(ValueKind),
963
964    /// $variant extension has invalid path syntax.
965    #[error("invalid $variant path syntax: {0}")]
966    InvalidVariantPath(String),
967
968    /// Tried to parse record fields while in extension flatten scope.
969    /// This happens when using #[eure(flatten_ext)] with a type that calls parse_record().
970    #[error(
971        "cannot parse record in extension scope: use #[eure(flatten)] instead of #[eure(flatten_ext)]"
972    )]
973    RecordInExtensionScope,
974
975    /// Unexpected array length.
976    #[error("unexpected array length: expected {expected}, got {actual}")]
977    UnexpectedArrayLength { expected: usize, actual: usize },
978
979    /// Expected a primitive value but got a non-primitive node.
980    #[error("expected primitive value, got {actual}")]
981    NotPrimitive { actual: ValueKind },
982}
983
984impl ParseErrorKind {
985    /// Wrap this error with path context.
986    pub fn at(self, path: impl Into<String>) -> Self {
987        ParseErrorKind::Nested {
988            path: path.into(),
989            source: Box::new(self),
990        }
991    }
992}
993
994fn format_no_matching_variant(
995    variant: &Option<String>,
996    best_match: &Option<Box<BestParseVariantMatch>>,
997) -> String {
998    let mut message = "no matching variant".to_string();
999    if let Some(variant) = variant {
1000        message.push_str(&format!(" (variant: {variant})"));
1001    }
1002    if let Some(best_match) = best_match {
1003        message.push_str(&format!(
1004            " (based on nearest variant '{}')",
1005            best_match.variant_name
1006        ));
1007    }
1008    message
1009}
1010
1011fn unwrap_best_variant_error<E: UnionParseError>(error: E) -> E {
1012    if let Some(ParseError {
1013        kind:
1014            ParseErrorKind::NoMatchingVariant {
1015                best_match: Some(best_match),
1016                ..
1017            },
1018        ..
1019    }) = error.as_parse_error()
1020    {
1021        return E::from(best_match.error.as_ref().clone());
1022    }
1023    error
1024}
1025
1026impl<'doc> EureDocument {
1027    /// Parse a value of type T from the given node.
1028    pub fn parse<T: FromEure<'doc, T>>(&'doc self, node_id: NodeId) -> Result<T, T::Error> {
1029        self.parse_with(node_id, T::parse)
1030    }
1031
1032    /// Parse a value of type T from the given node using a marker/strategy type M.
1033    ///
1034    /// This is used for parsing remote types where `M` implements
1035    /// `FromEure<'doc, T>` but `T` doesn't implement `FromEure` itself.
1036    pub fn parse_via<M, T>(&'doc self, node_id: NodeId) -> Result<T, M::Error>
1037    where
1038        M: FromEure<'doc, T>,
1039    {
1040        let ctx = self.parse_context(node_id);
1041        M::parse(&ctx)
1042    }
1043
1044    pub fn parse_with<T: DocumentParser<'doc>>(
1045        &'doc self,
1046        node_id: NodeId,
1047        mut parser: T,
1048    ) -> Result<T::Output, T::Error> {
1049        parser.parse(&self.parse_context(node_id))
1050    }
1051
1052    /// Create a parse context at the given node.
1053    pub fn parse_context(&'doc self, node_id: NodeId) -> ParseContext<'doc> {
1054        ParseContext::new(self, node_id)
1055    }
1056
1057    /// Parse a node as a record.
1058    ///
1059    /// Convenience method equivalent to `doc.parse_context(node_id).parse_record()`.
1060    pub fn parse_record(&'doc self, node_id: NodeId) -> Result<RecordParser<'doc>, ParseError> {
1061        RecordParser::from_doc_and_node(self, node_id)
1062    }
1063
1064    /// Create a parse context for extension parsing.
1065    ///
1066    /// Convenience method equivalent to `doc.parse_context(node_id)`.
1067    /// Use the returned context's `parse_ext()`, `ext()`, etc. methods.
1068    pub fn parse_extension_context(&'doc self, node_id: NodeId) -> ParseContext<'doc> {
1069        ParseContext::new(self, node_id)
1070    }
1071
1072    /// Parse a node as a tuple.
1073    ///
1074    /// Convenience method equivalent to `doc.parse_context(node_id).parse_tuple()`.
1075    pub fn parse_tuple(&'doc self, node_id: NodeId) -> Result<TupleParser<'doc>, ParseError> {
1076        TupleParser::from_doc_and_node(self, node_id)
1077    }
1078}
1079
1080impl<'doc> FromEure<'doc> for EureDocument {
1081    type Error = ParseError;
1082
1083    fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
1084        Ok(ctx.doc().node_subtree_to_document(ctx.node_id()))
1085    }
1086}
1087
1088impl<'doc> FromEure<'doc> for &'doc str {
1089    type Error = ParseError;
1090
1091    fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
1092        if let PrimitiveValue::Text(text) = ctx.parse_primitive()? {
1093            return Ok(text.as_str());
1094        }
1095        Err(ctx.unexpected_kind(ValueKind::Text))
1096    }
1097}
1098
1099impl FromEure<'_> for String {
1100    type Error = ParseError;
1101
1102    fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
1103        ctx.parse::<&str>().map(String::from)
1104    }
1105}
1106
1107impl FromEure<'_> for crate::layout::LayoutStyle {
1108    type Error = ParseError;
1109
1110    fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
1111        let value: &str = ctx.parse()?;
1112        match value {
1113            "auto" => Ok(crate::layout::LayoutStyle::Auto),
1114            "passthrough" => Ok(crate::layout::LayoutStyle::Passthrough),
1115            "section" => Ok(crate::layout::LayoutStyle::Section),
1116            "nested" => Ok(crate::layout::LayoutStyle::Nested),
1117            "binding" => Ok(crate::layout::LayoutStyle::Binding),
1118            "section-binding" => Ok(crate::layout::LayoutStyle::SectionBinding),
1119            "section-root-binding" => Ok(crate::layout::LayoutStyle::SectionRootBinding),
1120            other => Err(ParseError {
1121                node_id: ctx.node_id(),
1122                kind: ParseErrorKind::UnknownVariant(other.to_string()),
1123            }),
1124        }
1125    }
1126}
1127
1128#[diagnostic::do_not_recommend]
1129impl<'doc, T> FromEure<'doc> for Cow<'static, T>
1130where
1131    T: ToOwned + ?Sized,
1132    T::Owned: FromEure<'doc>,
1133{
1134    type Error = <T::Owned as FromEure<'doc>>::Error;
1135
1136    fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
1137        <T::Owned as FromEure<'doc>>::parse(ctx).map(Cow::Owned)
1138    }
1139}
1140
1141impl FromEure<'_> for Text {
1142    type Error = ParseError;
1143
1144    fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
1145        if let PrimitiveValue::Text(text) = ctx.parse_primitive()? {
1146            return Ok(text.clone());
1147        }
1148        Err(ctx.unexpected_kind(ValueKind::Text))
1149    }
1150}
1151
1152impl FromEure<'_> for bool {
1153    type Error = ParseError;
1154
1155    fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
1156        if let PrimitiveValue::Bool(b) = ctx.parse_primitive()? {
1157            return Ok(*b);
1158        }
1159        Err(ctx.unexpected_kind(ValueKind::Bool))
1160    }
1161}
1162
1163impl FromEure<'_> for BigInt {
1164    type Error = ParseError;
1165
1166    fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
1167        if let PrimitiveValue::Integer(i) = ctx.parse_primitive()? {
1168            return Ok(i.clone());
1169        }
1170        Err(ctx.unexpected_kind(ValueKind::Integer))
1171    }
1172}
1173
1174impl FromEure<'_> for f32 {
1175    type Error = ParseError;
1176
1177    fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
1178        if let PrimitiveValue::F32(f) = ctx.parse_primitive()? {
1179            return Ok(*f);
1180        }
1181        Err(ctx.unexpected_kind(ValueKind::F32))
1182    }
1183}
1184
1185impl FromEure<'_> for f64 {
1186    type Error = ParseError;
1187
1188    fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
1189        match ctx.parse_primitive()? {
1190            PrimitiveValue::F32(f) => Ok(*f as f64),
1191            PrimitiveValue::F64(f) => Ok(*f),
1192            _ => Err(ctx.unexpected_kind(ValueKind::F64)),
1193        }
1194    }
1195}
1196
1197macro_rules! impl_from_eure_int {
1198    ($($ty:ty),*) => {
1199        $(
1200            impl FromEure<'_> for $ty {
1201                type Error = ParseError;
1202
1203                fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
1204                    let value: BigInt = ctx.parse()?;
1205                    <$ty>::try_from(&value).map_err(|_| ParseError {
1206                        node_id: ctx.node_id(),
1207                        kind: ParseErrorKind::OutOfRange(
1208                            format!("value {} out of {} range", value, stringify!($ty)),
1209                        ),
1210                    })
1211                }
1212            }
1213        )*
1214    };
1215}
1216
1217impl_from_eure_int!(
1218    u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
1219);
1220
1221impl<'doc> FromEure<'doc> for &'doc PrimitiveValue {
1222    type Error = ParseError;
1223
1224    fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
1225        ctx.parse_primitive()
1226    }
1227}
1228
1229impl FromEure<'_> for PrimitiveValue {
1230    type Error = ParseError;
1231
1232    fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
1233        ctx.parse::<&PrimitiveValue>().cloned()
1234    }
1235}
1236
1237impl FromEure<'_> for Identifier {
1238    type Error = ParseError;
1239
1240    fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
1241        if let PrimitiveValue::Text(text) = ctx.parse_primitive()? {
1242            return text
1243                .content
1244                .parse()
1245                .map_err(ParseErrorKind::InvalidIdentifier)
1246                .map_err(|kind| ParseError {
1247                    node_id: ctx.node_id(),
1248                    kind,
1249                });
1250        }
1251        Err(ctx.unexpected_kind(ValueKind::Text))
1252    }
1253}
1254
1255impl<'doc> FromEure<'doc> for &'doc NodeArray {
1256    type Error = ParseError;
1257
1258    fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
1259        ctx.ensure_no_variant_path()?;
1260        match &ctx.node().content {
1261            NodeValue::Array(array) => Ok(array),
1262            _ => Err(ctx.unexpected_kind(ValueKind::Array)),
1263        }
1264    }
1265}
1266
1267/// `Vec<M>` parses `Vec<T>` using M's FromEure implementation.
1268///
1269/// When `M = T`, this is standard `Vec<T>` parsing.
1270/// When `M ≠ T`, M acts as a strategy type for parsing remote type T.
1271#[diagnostic::do_not_recommend]
1272impl<'doc, M, T> FromEure<'doc, Vec<T>> for Vec<M>
1273where
1274    M: FromEure<'doc, T>,
1275    M::Error: From<ParseError>,
1276{
1277    type Error = M::Error;
1278
1279    fn parse(ctx: &ParseContext<'doc>) -> Result<Vec<T>, Self::Error> {
1280        ctx.ensure_no_variant_path()?;
1281        match &ctx.node().content {
1282            NodeValue::Array(array) => array
1283                .iter()
1284                .map(|item| M::parse(&ctx.at(*item)))
1285                .collect::<Result<Vec<_>, _>>(),
1286            _ => Err(ctx.unexpected_kind(ValueKind::Array).into()),
1287        }
1288    }
1289}
1290
1291/// `[M; N]` parses `[T; N]` using M's FromEure implementation.
1292///
1293/// When `M = T`, this is standard fixed-size array parsing.
1294/// When `M ≠ T`, M acts as a strategy type for parsing remote type T.
1295#[diagnostic::do_not_recommend]
1296impl<'doc, M, T, const N: usize> FromEure<'doc, [T; N]> for [M; N]
1297where
1298    M: FromEure<'doc, T>,
1299    M::Error: From<ParseError>,
1300{
1301    type Error = M::Error;
1302
1303    fn parse(ctx: &ParseContext<'doc>) -> Result<[T; N], Self::Error> {
1304        ctx.ensure_no_variant_path()?;
1305        match &ctx.node().content {
1306            NodeValue::Array(array) => {
1307                let node_ids: [NodeId; N] = array.try_into_array().ok_or_else(|| ParseError {
1308                    node_id: ctx.node_id(),
1309                    kind: ParseErrorKind::UnexpectedArrayLength {
1310                        expected: N,
1311                        actual: array.len(),
1312                    },
1313                })?;
1314                let mut parsed = Vec::with_capacity(N);
1315                for id in node_ids {
1316                    parsed.push(M::parse(&ctx.at(id))?);
1317                }
1318                let parsed: [T; N] = parsed
1319                    .try_into()
1320                    .unwrap_or_else(|_| unreachable!("length was asserted previously"));
1321                Ok(parsed)
1322            }
1323            _ => Err(ctx.unexpected_kind(ValueKind::Array).into()),
1324        }
1325    }
1326}
1327
1328/// `IndexSet<M>` parses `IndexSet<T>` using M's FromEure implementation.
1329#[diagnostic::do_not_recommend]
1330impl<'doc, M, T> FromEure<'doc, IndexSet<T>> for IndexSet<M>
1331where
1332    M: FromEure<'doc, T>,
1333    T: Eq + std::hash::Hash,
1334    M::Error: From<ParseError>,
1335{
1336    type Error = M::Error;
1337    fn parse(ctx: &ParseContext<'doc>) -> Result<IndexSet<T>, Self::Error> {
1338        ctx.ensure_no_variant_path()?;
1339        match &ctx.node().content {
1340            NodeValue::Array(array) => array
1341                .iter()
1342                .map(|item| M::parse(&ctx.at(*item)))
1343                .collect::<Result<IndexSet<_>, _>>(),
1344            _ => Err(ctx.unexpected_kind(ValueKind::Array).into()),
1345        }
1346    }
1347}
1348
1349macro_rules! parse_tuple {
1350    ($n:expr, $($var:ident),*) => {
1351        #[diagnostic::do_not_recommend]
1352        impl<'doc, $($var),*, Err> FromEure<'doc> for ($($var),*,)
1353            where $($var: FromEure<'doc, Error = Err>),*,
1354            Err: From<ParseError>,
1355        {
1356            type Error = Err;
1357
1358            fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
1359                ctx.ensure_no_variant_path()?;
1360                let tuple = match &ctx.node().content {
1361                    NodeValue::Tuple(tuple) => tuple,
1362                    _ => return Err(ctx.unexpected_kind(ValueKind::Tuple).into()),
1363                };
1364                if tuple.len() != $n {
1365                    return Err(ParseError { node_id: ctx.node_id(), kind: ParseErrorKind::UnexpectedTupleLength { expected: $n, actual: tuple.len() } }.into());
1366                }
1367                let mut iter = tuple.iter();
1368                Ok(($($var::parse(&ctx.at(*iter.next().unwrap()))?),*,))
1369            }
1370        }
1371    }
1372}
1373
1374parse_tuple!(1, A);
1375parse_tuple!(2, A, B);
1376parse_tuple!(3, A, B, C);
1377parse_tuple!(4, A, B, C, D);
1378parse_tuple!(5, A, B, C, D, E);
1379parse_tuple!(6, A, B, C, D, E, F);
1380parse_tuple!(7, A, B, C, D, E, F, G);
1381parse_tuple!(8, A, B, C, D, E, F, G, H);
1382parse_tuple!(9, A, B, C, D, E, F, G, H, I);
1383parse_tuple!(10, A, B, C, D, E, F, G, H, I, J);
1384parse_tuple!(11, A, B, C, D, E, F, G, H, I, J, K);
1385parse_tuple!(12, A, B, C, D, E, F, G, H, I, J, K, L);
1386parse_tuple!(13, A, B, C, D, E, F, G, H, I, J, K, L, M);
1387parse_tuple!(14, A, B, C, D, E, F, G, H, I, J, K, L, M, N);
1388parse_tuple!(15, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
1389parse_tuple!(16, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
1390
1391/// Internal macro for parsing maps.
1392/// $M is the marker type, $T is the target type.
1393/// When $M = $T, this is standard parsing.
1394/// When $M ≠ $T, $M acts as a strategy type for parsing remote type $T.
1395macro_rules! parse_map {
1396    ($ctx:ident, $M:ty, $T:ty) => {{
1397        $ctx.ensure_no_variant_path()?;
1398
1399        // Check scope: Extension scope iterates extensions, otherwise record fields
1400        if $ctx.parser_scope() == Some(ParserScope::Extension) {
1401            // Extension scope: iterate UNACCESSED extensions only
1402            let node = $ctx.node();
1403            let flatten_ctx = $ctx.flatten_ctx();
1404            let accessed = flatten_ctx.map(|fc| fc.accessed_set());
1405            node.extensions
1406                .iter()
1407                .filter(|(ident, _)| {
1408                    // Only include extensions not already accessed
1409                    accessed.map_or(true, |a| !a.has_ext(ident))
1410                })
1411                .map(|(ident, &node_id)| {
1412                    // Mark extension as accessed so deny_unknown_extensions won't complain
1413                    if let Some(fc) = &flatten_ctx {
1414                        fc.add_ext((*ident).clone());
1415                    }
1416                    Ok((
1417                        K::from_extension_ident(ident).map_err(|kind| ParseError {
1418                            node_id: $ctx.node_id(),
1419                            kind,
1420                        })?,
1421                        <$M as FromEure<'doc, $T>>::parse(&$ctx.at(node_id))?,
1422                    ))
1423                })
1424                .collect::<Result<_, _>>()
1425        } else {
1426            // Record scope or no scope: iterate record fields
1427            let map = match &$ctx.node().content {
1428                NodeValue::Map(map) => map,
1429                _ => {
1430                    return Err($ctx.unexpected_kind(ValueKind::Map).into());
1431                }
1432            };
1433            // If in flatten context with Record scope, only iterate UNACCESSED fields
1434            let flatten_ctx = $ctx
1435                .flatten_ctx()
1436                .filter(|fc| fc.scope() == ParserScope::Record);
1437            let accessed = flatten_ctx.map(|fc| fc.accessed_set().clone());
1438            map.iter()
1439                .filter(|(key, _)| {
1440                    match &accessed {
1441                        Some(acc) => match key {
1442                            ObjectKey::String(s) => !acc.has_field(s),
1443                            _ => true, // Non-string keys are always included
1444                        },
1445                        None => true, // No flatten context means include all
1446                    }
1447                })
1448                .map(|(key, value)| {
1449                    // Mark field as accessed so deny_unknown_fields won't complain
1450                    if let Some(fc) = &flatten_ctx {
1451                        if let ObjectKey::String(s) = key {
1452                            fc.add_field(s);
1453                        }
1454                    }
1455                    Ok((
1456                        K::from_object_key(key).map_err(|kind| ParseError {
1457                            node_id: $ctx.node_id(),
1458                            kind,
1459                        })?,
1460                        <$M as FromEure<'doc, $T>>::parse(&$ctx.at(*value))?,
1461                    ))
1462                })
1463                .collect::<Result<_, _>>()
1464        }
1465    }};
1466}
1467
1468/// `Map<K, M>` parses `Map<K, T>` using M's FromEure implementation.
1469#[diagnostic::do_not_recommend]
1470impl<'doc, K, M, T> FromEure<'doc, Map<K, T>> for Map<K, M>
1471where
1472    K: ParseObjectKey<'doc>,
1473    M: FromEure<'doc, T>,
1474    M::Error: From<ParseError>,
1475{
1476    type Error = M::Error;
1477
1478    fn parse(ctx: &ParseContext<'doc>) -> Result<Map<K, T>, Self::Error> {
1479        parse_map!(ctx, M, T)
1480    }
1481}
1482
1483/// `BTreeMap<K, M>` parses `BTreeMap<K, T>` using M's FromEure implementation.
1484#[diagnostic::do_not_recommend]
1485impl<'doc, K, M, T> FromEure<'doc, BTreeMap<K, T>> for BTreeMap<K, M>
1486where
1487    K: ParseObjectKey<'doc>,
1488    M: FromEure<'doc, T>,
1489    M::Error: From<ParseError>,
1490{
1491    type Error = M::Error;
1492    fn parse(ctx: &ParseContext<'doc>) -> Result<BTreeMap<K, T>, Self::Error> {
1493        parse_map!(ctx, M, T)
1494    }
1495}
1496
1497/// `HashMap<K, M>` parses `HashMap<K, T>` using M's FromEure implementation.
1498#[diagnostic::do_not_recommend]
1499impl<'doc, K, M, T> FromEure<'doc, HashMap<K, T>> for HashMap<K, M>
1500where
1501    K: ParseObjectKey<'doc>,
1502    M: FromEure<'doc, T>,
1503    M::Error: From<ParseError>,
1504{
1505    type Error = M::Error;
1506    fn parse(ctx: &ParseContext<'doc>) -> Result<HashMap<K, T>, Self::Error> {
1507        parse_map!(ctx, M, T)
1508    }
1509}
1510
1511/// `IndexMap<K, M>` parses `IndexMap<K, T>` using M's FromEure implementation.
1512#[diagnostic::do_not_recommend]
1513impl<'doc, K, M, T> FromEure<'doc, IndexMap<K, T>> for IndexMap<K, M>
1514where
1515    K: ParseObjectKey<'doc>,
1516    M: FromEure<'doc, T>,
1517    M::Error: From<ParseError>,
1518{
1519    type Error = M::Error;
1520    fn parse(ctx: &ParseContext<'doc>) -> Result<IndexMap<K, T>, Self::Error> {
1521        parse_map!(ctx, M, T)
1522    }
1523}
1524
1525impl FromEure<'_> for regex::Regex {
1526    type Error = ParseError;
1527
1528    fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
1529        let pattern: &str = ctx.parse()?;
1530        regex::Regex::new(pattern).map_err(|e| ParseError {
1531            node_id: ctx.node_id(),
1532            kind: ParseErrorKind::InvalidPattern {
1533                kind: format!("regex '{}'", pattern),
1534                reason: e.to_string(),
1535            },
1536        })
1537    }
1538}
1539
1540/// `Option<M>` parses `Option<T>` using M's FromEure implementation.
1541///
1542/// When `M = T` (same type), this is standard `Option<T>` parsing.
1543/// When `M ≠ T`, M acts as a strategy type for parsing remote type T.
1544///
1545/// - `$variant: some` -> parse T via M
1546/// - `$variant: none` -> None
1547/// - No `$variant` and value is null -> None
1548/// - No `$variant` and value is not null -> try parsing as T (Some)
1549#[diagnostic::do_not_recommend]
1550impl<'doc, M, T> FromEure<'doc, Option<T>> for Option<M>
1551where
1552    M: FromEure<'doc, T>,
1553    M::Error: UnionParseError,
1554{
1555    type Error = M::Error;
1556
1557    fn parse(ctx: &ParseContext<'doc>) -> Result<Option<T>, Self::Error> {
1558        ctx.parse_union::<Option<T>, M::Error>()?
1559            .variant("some", (M::parse).map(Some))
1560            .variant("none", |ctx: &ParseContext<'_>| {
1561                if ctx.is_null() {
1562                    Ok(None)
1563                } else {
1564                    Err(ParseError {
1565                        node_id: ctx.node_id(),
1566                        kind: ParseErrorKind::TypeMismatch {
1567                            expected: ValueKind::Null,
1568                            actual: ctx.node().content.value_kind(),
1569                        },
1570                    }
1571                    .into())
1572                }
1573            })
1574            .parse()
1575            .map_err(unwrap_best_variant_error)
1576    }
1577}
1578
1579/// `Result<MT, ME>` parses `Result<T, E>` using MT and ME's FromEure implementations.
1580///
1581/// When `MT = T` and `ME = E` (same types), this is standard `Result` parsing.
1582/// When different, MT and ME act as strategy types for parsing remote types.
1583///
1584/// - `$variant: ok` -> parse T via MT
1585/// - `$variant: err` -> parse E via ME
1586/// - No `$variant` -> try Ok first, then Err (priority-based)
1587#[diagnostic::do_not_recommend]
1588impl<'doc, MT, T, ME, E, Err> FromEure<'doc, Result<T, E>> for Result<MT, ME>
1589where
1590    MT: FromEure<'doc, T, Error = Err>,
1591    ME: FromEure<'doc, E, Error = Err>,
1592    Err: UnionParseError,
1593{
1594    type Error = Err;
1595
1596    fn parse(ctx: &ParseContext<'doc>) -> Result<Result<T, E>, Self::Error> {
1597        ctx.parse_union::<Result<T, E>, Self::Error>()?
1598            .variant("ok", (MT::parse).map(Ok))
1599            .variant("err", (ME::parse).map(Err))
1600            .parse()
1601            .map_err(unwrap_best_variant_error)
1602    }
1603}
1604
1605#[diagnostic::do_not_recommend]
1606impl<'doc> FromEure<'doc> for () {
1607    type Error = ParseError;
1608    fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
1609        ctx.parse_tuple()?.finish()
1610    }
1611}
1612
1613impl<'doc> FromEure<'doc> for NodeId {
1614    type Error = ParseError;
1615    fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
1616        Ok(ctx.node_id())
1617    }
1618}
1619
1620pub trait DocumentParser<'doc> {
1621    type Output;
1622    type Error;
1623    fn parse(&mut self, ctx: &ParseContext<'doc>) -> Result<Self::Output, Self::Error>;
1624}
1625
1626pub struct AlwaysParser<T, E>(T, PhantomData<E>);
1627
1628impl<T, E> AlwaysParser<T, E> {
1629    pub fn new(value: T) -> AlwaysParser<T, E> {
1630        Self(value, PhantomData)
1631    }
1632}
1633
1634impl<'doc, T, E> DocumentParser<'doc> for AlwaysParser<T, E>
1635where
1636    T: Clone,
1637{
1638    type Output = T;
1639    type Error = E;
1640    fn parse(&mut self, _ctx: &ParseContext<'doc>) -> Result<Self::Output, Self::Error> {
1641        Ok(self.0.clone())
1642    }
1643}
1644
1645impl<'doc, T, F, E> DocumentParser<'doc> for F
1646where
1647    F: FnMut(&ParseContext<'doc>) -> Result<T, E>,
1648{
1649    type Output = T;
1650    type Error = E;
1651    fn parse(&mut self, ctx: &ParseContext<'doc>) -> Result<Self::Output, Self::Error> {
1652        (*self)(ctx)
1653    }
1654}
1655
1656pub struct LiteralParser<T>(pub T);
1657
1658impl<'doc, T, E> DocumentParser<'doc> for LiteralParser<T>
1659where
1660    T: FromEure<'doc, Error = E> + PartialEq + core::fmt::Debug,
1661    E: From<ParseError>,
1662{
1663    type Output = T;
1664    type Error = E;
1665    fn parse(&mut self, ctx: &ParseContext<'doc>) -> Result<Self::Output, Self::Error> {
1666        let value: T = ctx.parse::<T>()?;
1667        if value == self.0 {
1668            Ok(value)
1669        } else {
1670            Err(ParseError {
1671                node_id: ctx.node_id(),
1672                kind: ParseErrorKind::LiteralMismatch {
1673                    expected: format!("{:?}", self.0),
1674                    actual: format!("{:?}", value),
1675                },
1676            }
1677            .into())
1678        }
1679    }
1680}
1681
1682/// A parser that matches a specific string literal as an enum variant name.
1683///
1684/// Similar to [`LiteralParser`], but returns [`ParseErrorKind::UnknownVariant`]
1685/// on mismatch instead of [`ParseErrorKind::LiteralMismatch`]. This provides
1686/// better error messages when parsing unit enum variants as string literals.
1687pub struct VariantLiteralParser(pub &'static str);
1688
1689impl<'doc> DocumentParser<'doc> for VariantLiteralParser {
1690    type Output = &'static str;
1691    type Error = ParseError;
1692    fn parse(&mut self, ctx: &ParseContext<'doc>) -> Result<Self::Output, Self::Error> {
1693        let value: &str = ctx.parse()?;
1694        if value == self.0 {
1695            Ok(self.0)
1696        } else {
1697            Err(ParseError {
1698                node_id: ctx.node_id(),
1699                kind: ParseErrorKind::UnknownVariant(value.to_string()),
1700            })
1701        }
1702    }
1703}
1704
1705pub struct MapParser<T, F> {
1706    parser: T,
1707    mapper: F,
1708}
1709
1710impl<'doc, T, O, F> DocumentParser<'doc> for MapParser<T, F>
1711where
1712    T: DocumentParser<'doc>,
1713    F: FnMut(T::Output) -> O,
1714{
1715    type Output = O;
1716    type Error = T::Error;
1717    fn parse(&mut self, ctx: &ParseContext<'doc>) -> Result<Self::Output, Self::Error> {
1718        self.parser.parse(ctx).map(|value| (self.mapper)(value))
1719    }
1720}
1721
1722pub struct AndThenParser<T, F> {
1723    parser: T,
1724    mapper: F,
1725}
1726
1727impl<'doc, T, O, F, E> DocumentParser<'doc> for AndThenParser<T, F>
1728where
1729    T: DocumentParser<'doc, Error = E>,
1730    F: Fn(T::Output) -> Result<O, E>,
1731{
1732    type Output = O;
1733    type Error = E;
1734    fn parse(&mut self, ctx: &ParseContext<'doc>) -> Result<Self::Output, Self::Error> {
1735        let value = self.parser.parse(ctx)?;
1736        (self.mapper)(value)
1737    }
1738}
1739
1740pub trait DocumentParserExt<'doc>: DocumentParser<'doc> + Sized {
1741    fn map<O, F>(self, mapper: F) -> MapParser<Self, F>
1742    where
1743        F: Fn(Self::Output) -> O,
1744    {
1745        MapParser {
1746            parser: self,
1747            mapper,
1748        }
1749    }
1750
1751    fn and_then<O, F>(self, mapper: F) -> AndThenParser<Self, F>
1752    where
1753        F: Fn(Self::Output) -> Result<O, Self::Error>,
1754    {
1755        AndThenParser {
1756            parser: self,
1757            mapper,
1758        }
1759    }
1760}
1761
1762impl<'doc, T> DocumentParserExt<'doc> for T where T: DocumentParser<'doc> {}
1763
1764#[cfg(test)]
1765mod tests {
1766    use super::*;
1767    use crate::document::node::NodeValue;
1768    use crate::eure;
1769    use crate::identifier::Identifier;
1770    use crate::text::Text;
1771    use crate::value::ObjectKey;
1772    use num_bigint::BigInt;
1773
1774    fn identifier(s: &str) -> Identifier {
1775        s.parse().unwrap()
1776    }
1777
1778    /// Create a document with a single field that has a $variant extension
1779    fn create_record_with_variant(
1780        field_name: &str,
1781        value: NodeValue,
1782        variant: &str,
1783    ) -> EureDocument {
1784        let mut doc = EureDocument::new();
1785        let root_id = doc.get_root_id();
1786
1787        // Add field
1788        let field_id = doc
1789            .add_map_child(ObjectKey::String(field_name.to_string()), root_id)
1790            .unwrap()
1791            .node_id;
1792        doc.node_mut(field_id).content = value;
1793
1794        // Add $variant extension
1795        let variant_node_id = doc
1796            .add_extension(identifier("variant"), field_id)
1797            .unwrap()
1798            .node_id;
1799        doc.node_mut(variant_node_id).content =
1800            NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext(variant.to_string())));
1801
1802        doc
1803    }
1804
1805    #[test]
1806    fn test_option_some_tagged() {
1807        let doc = create_record_with_variant(
1808            "value",
1809            NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(42))),
1810            "some",
1811        );
1812        let root_id = doc.get_root_id();
1813        let rec = doc.parse_record(root_id).unwrap();
1814        let value: Option<i32> = rec.parse_field("value").unwrap();
1815        assert_eq!(value, Some(42));
1816    }
1817
1818    #[test]
1819    fn test_option_none_tagged() {
1820        let doc =
1821            create_record_with_variant("value", NodeValue::Primitive(PrimitiveValue::Null), "none");
1822        let root_id = doc.get_root_id();
1823        let rec = doc.parse_record(root_id).unwrap();
1824        let value: Option<i32> = rec.parse_field("value").unwrap();
1825        assert_eq!(value, None);
1826    }
1827
1828    #[test]
1829    fn test_option_some_untagged() {
1830        // Without $variant, non-null value is Some
1831        let doc = eure!({ value = 42 });
1832        let root_id = doc.get_root_id();
1833        let rec = doc.parse_record(root_id).unwrap();
1834        let value: Option<i32> = rec.parse_field("value").unwrap();
1835        assert_eq!(value, Some(42));
1836    }
1837
1838    #[test]
1839    fn test_option_none_untagged() {
1840        // Without $variant, null is None
1841        let doc = eure!({ value = null });
1842        let root_id = doc.get_root_id();
1843        let rec = doc.parse_record(root_id).unwrap();
1844        let value: Option<i32> = rec.parse_field("value").unwrap();
1845        assert_eq!(value, None);
1846    }
1847
1848    #[test]
1849    fn test_result_ok_tagged() {
1850        let doc = create_record_with_variant(
1851            "value",
1852            NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(42))),
1853            "ok",
1854        );
1855        let root_id = doc.get_root_id();
1856        let rec = doc.parse_record(root_id).unwrap();
1857        let value: Result<i32, String> = rec.parse_field("value").unwrap();
1858        assert_eq!(value, Ok(42));
1859    }
1860
1861    #[test]
1862    fn test_result_err_tagged() {
1863        let doc = create_record_with_variant(
1864            "value",
1865            NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext(
1866                "error message".to_string(),
1867            ))),
1868            "err",
1869        );
1870        let root_id = doc.get_root_id();
1871        let rec = doc.parse_record(root_id).unwrap();
1872        let value: Result<i32, String> = rec.parse_field("value").unwrap();
1873        assert_eq!(value, Err("error message".to_string()));
1874    }
1875
1876    #[test]
1877    fn test_nested_result_option_ok_some() {
1878        // $variant: ok.some - Result<Option<i32>, String>
1879        let doc = create_record_with_variant(
1880            "value",
1881            NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(42))),
1882            "ok.some",
1883        );
1884        let root_id = doc.get_root_id();
1885        let rec = doc.parse_record(root_id).unwrap();
1886        let value: Result<Option<i32>, String> = rec.parse_field("value").unwrap();
1887        assert_eq!(value, Ok(Some(42)));
1888    }
1889
1890    #[test]
1891    fn test_nested_result_option_ok_none() {
1892        // $variant: ok.none - Result<Option<i32>, String>
1893        let doc = create_record_with_variant(
1894            "value",
1895            NodeValue::Primitive(PrimitiveValue::Null),
1896            "ok.none",
1897        );
1898        let root_id = doc.get_root_id();
1899        let rec = doc.parse_record(root_id).unwrap();
1900        let value: Result<Option<i32>, String> = rec.parse_field("value").unwrap();
1901        assert_eq!(value, Ok(None));
1902    }
1903
1904    #[test]
1905    fn test_nested_result_option_err() {
1906        // $variant: err - Result<Option<i32>, String>
1907        let doc = create_record_with_variant(
1908            "value",
1909            NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext("error".to_string()))),
1910            "err",
1911        );
1912        let root_id = doc.get_root_id();
1913        let rec = doc.parse_record(root_id).unwrap();
1914        let value: Result<Option<i32>, String> = rec.parse_field("value").unwrap();
1915        assert_eq!(value, Err("error".to_string()));
1916    }
1917
1918    #[test]
1919    fn test_deeply_nested_option_option() {
1920        // $variant: some.some - Option<Option<i32>>
1921        let doc = create_record_with_variant(
1922            "value",
1923            NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(42))),
1924            "some.some",
1925        );
1926        let root_id = doc.get_root_id();
1927        let rec = doc.parse_record(root_id).unwrap();
1928        let value: Option<Option<i32>> = rec.parse_field("value").unwrap();
1929        assert_eq!(value, Some(Some(42)));
1930    }
1931
1932    #[test]
1933    fn test_deeply_nested_option_none() {
1934        // $variant: some.none - Option<Option<i32>> inner None
1935        let doc = create_record_with_variant(
1936            "value",
1937            NodeValue::Primitive(PrimitiveValue::Null),
1938            "some.none",
1939        );
1940        let root_id = doc.get_root_id();
1941        let rec = doc.parse_record(root_id).unwrap();
1942        let value: Option<Option<i32>> = rec.parse_field("value").unwrap();
1943        assert_eq!(value, Some(None));
1944    }
1945
1946    #[test]
1947    fn test_outer_none() {
1948        // $variant: none - Option<Option<i32>> outer None
1949        let doc =
1950            create_record_with_variant("value", NodeValue::Primitive(PrimitiveValue::Null), "none");
1951        let root_id = doc.get_root_id();
1952        let rec = doc.parse_record(root_id).unwrap();
1953        let value: Option<Option<i32>> = rec.parse_field("value").unwrap();
1954        assert_eq!(value, None);
1955    }
1956
1957    // =========================================================================
1958    // BUG: parse_map! doesn't mark fields as accessed
1959    // =========================================================================
1960
1961    /// BUG: When parsing IndexMap via flatten, fields are not marked as accessed.
1962    /// This causes deny_unknown_fields() to report them as unknown.
1963    #[test]
1964    fn test_flatten_indexmap_marks_fields_as_accessed() {
1965        use indexmap::IndexMap;
1966
1967        let doc = eure!({
1968            name = "test"
1969            foo = "bar"
1970            baz = "qux"
1971        });
1972
1973        let root_id = doc.get_root_id();
1974        let rec = doc.parse_record(root_id).unwrap();
1975
1976        // Parse "name" as a regular field
1977        let _name: String = rec.parse_field("name").unwrap();
1978
1979        // Parse remaining fields via flatten into IndexMap
1980        let extra: IndexMap<String, String> = rec.flatten().parse().unwrap();
1981
1982        // Verify IndexMap captured the extra fields
1983        assert_eq!(extra.get("foo"), Some(&"bar".to_string()));
1984        assert_eq!(extra.get("baz"), Some(&"qux".to_string()));
1985
1986        // BUG: This fails with UnknownField("foo") because parse_map! doesn't
1987        // mark "foo" and "baz" as accessed when parsing into IndexMap
1988        rec.deny_unknown_fields().unwrap();
1989    }
1990
1991    // =========================================================================
1992    // Remote type support tests
1993    // =========================================================================
1994
1995    /// A "remote" type that we can't implement FromEure for directly.
1996    #[derive(Debug, PartialEq)]
1997    struct RemoteDuration {
1998        secs: u64,
1999        nanos: u32,
2000    }
2001
2002    /// Marker type that implements FromEure<'doc, RemoteDuration>.
2003    struct RemoteDurationDef;
2004
2005    impl<'doc> FromEure<'doc, RemoteDuration> for RemoteDurationDef {
2006        type Error = ParseError;
2007
2008        fn parse(ctx: &ParseContext<'doc>) -> Result<RemoteDuration, Self::Error> {
2009            let rec = ctx.parse_record()?;
2010            let secs: u64 = rec.parse_field("secs")?;
2011            let nanos: u32 = rec.parse_field("nanos")?;
2012            rec.deny_unknown_fields()?;
2013            Ok(RemoteDuration { secs, nanos })
2014        }
2015    }
2016
2017    #[test]
2018    fn test_remote_type_basic_parsing() {
2019        let doc = eure!({ secs = 10, nanos = 500 });
2020        let root_id = doc.get_root_id();
2021
2022        // Use parse_via to parse RemoteDuration via RemoteDurationDef
2023        let duration: RemoteDuration = doc.parse_via::<RemoteDurationDef, _>(root_id).unwrap();
2024
2025        assert_eq!(
2026            duration,
2027            RemoteDuration {
2028                secs: 10,
2029                nanos: 500
2030            }
2031        );
2032    }
2033
2034    #[test]
2035    fn test_remote_type_in_option() {
2036        // When the marker type implements FromEure<T>,
2037        // Option<Marker> implements FromEure<Option<T>>
2038        let doc = eure!({ secs = 5, nanos = 0 });
2039        let root_id = doc.get_root_id();
2040
2041        // Parse Option<RemoteDuration> via Option<RemoteDurationDef>
2042        let duration: Option<RemoteDuration> = doc
2043            .parse_via::<Option<RemoteDurationDef>, _>(root_id)
2044            .unwrap();
2045
2046        assert_eq!(duration, Some(RemoteDuration { secs: 5, nanos: 0 }));
2047    }
2048
2049    #[test]
2050    fn test_remote_type_in_option_none() {
2051        let doc = eure!({ = null });
2052        let root_id = doc.get_root_id();
2053
2054        let duration: Option<RemoteDuration> = doc
2055            .parse_via::<Option<RemoteDurationDef>, _>(root_id)
2056            .unwrap();
2057
2058        assert_eq!(duration, None);
2059    }
2060
2061    #[test]
2062    fn test_remote_type_in_vec() {
2063        let doc = eure!({
2064            items[] { secs = 1, nanos = 0 }
2065            items[] { secs = 2, nanos = 100 }
2066        });
2067        let root_id = doc.get_root_id();
2068        let rec = doc.parse_record(root_id).unwrap();
2069        let items_ctx = rec.field("items").unwrap();
2070
2071        // Parse Vec<RemoteDuration> via Vec<RemoteDurationDef>
2072        let durations: Vec<RemoteDuration> =
2073            items_ctx.parse_via::<Vec<RemoteDurationDef>, _>().unwrap();
2074
2075        assert_eq!(
2076            durations,
2077            vec![
2078                RemoteDuration { secs: 1, nanos: 0 },
2079                RemoteDuration {
2080                    secs: 2,
2081                    nanos: 100
2082                },
2083            ]
2084        );
2085    }
2086
2087    #[test]
2088    fn test_remote_type_in_indexmap() {
2089        let doc = eure!({
2090            short { secs = 1, nanos = 0 }
2091            long { secs = 10, nanos = 0 }
2092        });
2093        let root_id = doc.get_root_id();
2094
2095        // Parse IndexMap<String, RemoteDuration> via IndexMap<String, RemoteDurationDef>
2096        let durations: IndexMap<String, RemoteDuration> = doc
2097            .parse_via::<IndexMap<String, RemoteDurationDef>, _>(root_id)
2098            .unwrap();
2099
2100        assert_eq!(durations.len(), 2);
2101        assert_eq!(
2102            durations.get("short"),
2103            Some(&RemoteDuration { secs: 1, nanos: 0 })
2104        );
2105        assert_eq!(
2106            durations.get("long"),
2107            Some(&RemoteDuration { secs: 10, nanos: 0 })
2108        );
2109    }
2110
2111    #[test]
2112    fn test_remote_type_in_nested_containers() {
2113        // Test Option<Vec<RemoteDuration>>
2114        let doc = eure!({
2115            items[] { secs = 1, nanos = 0 }
2116        });
2117        let root_id = doc.get_root_id();
2118        let rec = doc.parse_record(root_id).unwrap();
2119        let items_ctx = rec.field("items").unwrap();
2120
2121        // Parse Option<Vec<RemoteDuration>> via Option<Vec<RemoteDurationDef>>
2122        let durations: Option<Vec<RemoteDuration>> = items_ctx
2123            .parse_via::<Option<Vec<RemoteDurationDef>>, _>()
2124            .unwrap();
2125
2126        assert_eq!(durations, Some(vec![RemoteDuration { secs: 1, nanos: 0 }]));
2127    }
2128
2129    #[test]
2130    fn test_parse_context_parse_via() {
2131        let doc = eure!({ secs = 42, nanos = 123 });
2132        let root_id = doc.get_root_id();
2133        let ctx = doc.parse_context(root_id);
2134
2135        // Use parse_via on ParseContext
2136        let duration: RemoteDuration = ctx.parse_via::<RemoteDurationDef, _>().unwrap();
2137
2138        assert_eq!(
2139            duration,
2140            RemoteDuration {
2141                secs: 42,
2142                nanos: 123
2143            }
2144        );
2145    }
2146
2147    // =========================================================================
2148    // Fixed-size array tests
2149    // =========================================================================
2150
2151    #[test]
2152    fn test_array_basic_parsing() {
2153        let doc = eure!({ items = [1, 2, 3] });
2154        let root_id = doc.get_root_id();
2155        let rec = doc.parse_record(root_id).unwrap();
2156        let items: [i32; 3] = rec.parse_field("items").unwrap();
2157        assert_eq!(items, [1, 2, 3]);
2158    }
2159
2160    #[test]
2161    fn test_array_empty() {
2162        let doc = eure!({ items = [] });
2163        let root_id = doc.get_root_id();
2164        let rec = doc.parse_record(root_id).unwrap();
2165        let items: [i32; 0] = rec.parse_field("items").unwrap();
2166        assert_eq!(items, []);
2167    }
2168
2169    #[test]
2170    fn test_array_length_mismatch_too_few() {
2171        let doc = eure!({ items = [1, 2] });
2172        let root_id = doc.get_root_id();
2173        let rec = doc.parse_record(root_id).unwrap();
2174        let result: Result<[i32; 3], _> = rec.parse_field("items");
2175        assert!(result.is_err());
2176        let err = result.unwrap_err();
2177        assert!(matches!(
2178            err.kind,
2179            ParseErrorKind::UnexpectedArrayLength {
2180                expected: 3,
2181                actual: 2
2182            }
2183        ));
2184    }
2185
2186    #[test]
2187    fn test_array_length_mismatch_too_many() {
2188        let doc = eure!({ items = [1, 2, 3, 4] });
2189        let root_id = doc.get_root_id();
2190        let rec = doc.parse_record(root_id).unwrap();
2191        let result: Result<[i32; 3], _> = rec.parse_field("items");
2192        assert!(result.is_err());
2193        let err = result.unwrap_err();
2194        assert!(matches!(
2195            err.kind,
2196            ParseErrorKind::UnexpectedArrayLength {
2197                expected: 3,
2198                actual: 4
2199            }
2200        ));
2201    }
2202
2203    #[test]
2204    fn test_array_nested_types() {
2205        let doc = eure!({ items = ["a", "b"] });
2206        let root_id = doc.get_root_id();
2207        let rec = doc.parse_record(root_id).unwrap();
2208        let items: [String; 2] = rec.parse_field("items").unwrap();
2209        assert_eq!(items, ["a".to_string(), "b".to_string()]);
2210    }
2211
2212    #[test]
2213    fn test_array_in_option() {
2214        let doc = eure!({ items = [1, 2, 3] });
2215        let root_id = doc.get_root_id();
2216        let rec = doc.parse_record(root_id).unwrap();
2217        let items: Option<[i32; 3]> = rec.parse_field("items").unwrap();
2218        assert_eq!(items, Some([1, 2, 3]));
2219    }
2220
2221    #[test]
2222    fn test_array_of_arrays() {
2223        let doc = eure!({ matrix = [[1, 2], [3, 4]] });
2224        let root_id = doc.get_root_id();
2225        let rec = doc.parse_record(root_id).unwrap();
2226        let matrix: [[i32; 2]; 2] = rec.parse_field("matrix").unwrap();
2227        assert_eq!(matrix, [[1, 2], [3, 4]]);
2228    }
2229
2230    #[test]
2231    fn test_array_remote_type() {
2232        let doc = eure!({
2233            items[] { secs = 1, nanos = 0 }
2234            items[] { secs = 2, nanos = 100 }
2235        });
2236        let root_id = doc.get_root_id();
2237        let rec = doc.parse_record(root_id).unwrap();
2238        let items_ctx = rec.field("items").unwrap();
2239
2240        // Parse [RemoteDuration; 2] via [RemoteDurationDef; 2]
2241        let durations: [RemoteDuration; 2] =
2242            items_ctx.parse_via::<[RemoteDurationDef; 2], _>().unwrap();
2243
2244        assert_eq!(
2245            durations,
2246            [
2247                RemoteDuration { secs: 1, nanos: 0 },
2248                RemoteDuration {
2249                    secs: 2,
2250                    nanos: 100
2251                },
2252            ]
2253        );
2254    }
2255
2256    // =========================================================================
2257    // Cow tests
2258    // =========================================================================
2259
2260    #[test]
2261    fn test_cow_static_str_from_eure() {
2262        use alloc::borrow::Cow;
2263
2264        let doc = eure!({ name = "hello" });
2265        let root_id = doc.get_root_id();
2266        let rec = doc.parse_record(root_id).unwrap();
2267        let value: Cow<'static, str> = rec.parse_field("name").unwrap();
2268        assert_eq!(value, Cow::<str>::Owned("hello".to_string()));
2269    }
2270}