eure_document/
parse.rs

1//! ParseDocument 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 indexmap::{IndexMap, IndexSet};
12pub use object_key::ParseObjectKey;
13pub use record::RecordParser;
14pub use tuple::TupleParser;
15pub use union::UnionParser;
16pub use variant_path::VariantPath;
17// UnionTagMode is defined in this module and exported automatically
18
19use alloc::format;
20use alloc::rc::Rc;
21use core::cell::RefCell;
22use num_bigint::BigInt;
23
24use core::marker::PhantomData;
25use std::collections::{BTreeMap, HashMap, HashSet};
26
27use crate::{
28    data_model::VariantRepr,
29    document::node::{Node, NodeArray},
30    identifier::IdentifierError,
31    prelude_internal::*,
32    value::ValueKind,
33};
34
35// =============================================================================
36// UnionTagMode
37// =============================================================================
38
39/// Mode for union tag resolution.
40///
41/// This determines how variant tags are resolved during union parsing:
42/// - `Eure`: Use `$variant` extension and untagged matching (for native Eure documents)
43/// - `Repr`: Use only `VariantRepr` patterns (for JSON/YAML imports)
44///
45/// These modes are mutually exclusive to avoid false positives.
46#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
47pub enum UnionTagMode {
48    /// Eure mode: Use `$variant` extension or untagged matching.
49    ///
50    /// This is the default mode for native Eure documents.
51    /// - If `$variant` extension is present, use it to determine the variant
52    /// - Otherwise, use untagged matching (try all variants)
53    /// - `VariantRepr` is ignored in this mode
54    #[default]
55    Eure,
56
57    /// Repr mode: Use only `VariantRepr` patterns.
58    ///
59    /// This mode is for documents imported from JSON/YAML.
60    /// - Extract variant tag using `VariantRepr` (External, Internal, Adjacent)
61    /// - `$variant` extension is ignored in this mode
62    /// - If repr doesn't extract a tag, error (no untagged fallback)
63    Repr,
64}
65
66// =============================================================================
67// AccessedSet
68// =============================================================================
69
70/// Snapshot of accessed state (fields, extensions).
71pub type AccessedSnapshot = (HashSet<String>, HashSet<Identifier>);
72
73/// Tracks accessed fields and extensions with snapshot/rollback support for union parsing.
74///
75/// The internal state is a stack where the last item is always the current working state.
76/// Items before it are snapshots (save points) for rollback.
77///
78/// Invariant: stack is never empty.
79///
80/// # Stack visualization
81/// ```text
82/// Initial:        [current]
83/// push_snapshot:  [snapshot, current]  (snapshot = copy of current)
84/// modify:         [snapshot, current'] (current' has changes)
85/// restore:        [snapshot, snapshot] (current reset to snapshot)
86/// pop_restore:    [snapshot]           (current removed, snapshot is new current)
87/// pop_no_restore: [current']           (snapshot removed, keep modified current)
88/// ```
89#[derive(Debug, Clone)]
90pub struct AccessedSet(Rc<RefCell<Vec<AccessedSnapshot>>>);
91
92impl AccessedSet {
93    /// Create a new empty set.
94    pub fn new() -> Self {
95        // Start with one empty state (the current working state)
96        Self(Rc::new(RefCell::new(vec![(
97            HashSet::new(),
98            HashSet::new(),
99        )])))
100    }
101
102    /// Add a field to the accessed set.
103    pub fn add_field(&self, field: impl Into<String>) {
104        self.0
105            .borrow_mut()
106            .last_mut()
107            .unwrap()
108            .0
109            .insert(field.into());
110    }
111
112    /// Add an extension to the accessed set.
113    pub fn add_ext(&self, ext: Identifier) {
114        self.0.borrow_mut().last_mut().unwrap().1.insert(ext);
115    }
116
117    /// Check if a field has been accessed.
118    pub fn has_field(&self, field: &str) -> bool {
119        self.0.borrow().last().unwrap().0.contains(field)
120    }
121
122    /// Check if an extension has been accessed.
123    pub fn has_ext(&self, ext: &Identifier) -> bool {
124        self.0.borrow().last().unwrap().1.contains(ext)
125    }
126
127    /// Push a snapshot (call at start of union parsing).
128    /// Inserts a copy of current BEFORE current, so current can be modified.
129    pub fn push_snapshot(&self) {
130        let mut stack = self.0.borrow_mut();
131        let snapshot = stack.last().unwrap().clone();
132        let len = stack.len();
133        stack.insert(len - 1, snapshot);
134        // Stack: [..., current] → [..., snapshot, current]
135    }
136
137    /// Restore current to the snapshot (call when variant fails).
138    /// Resets current (last) to match the snapshot (second-to-last).
139    pub fn restore_to_current_snapshot(&self) {
140        let mut stack = self.0.borrow_mut();
141        if stack.len() >= 2 {
142            let snapshot = stack[stack.len() - 2].clone();
143            *stack.last_mut().unwrap() = snapshot;
144        }
145    }
146
147    /// Capture the current state (for non-priority variants).
148    pub fn capture_current_state(&self) -> AccessedSnapshot {
149        self.0.borrow().last().unwrap().clone()
150    }
151
152    /// Restore to a previously captured state.
153    pub fn restore_to_state(&self, state: AccessedSnapshot) {
154        *self.0.borrow_mut().last_mut().unwrap() = state;
155    }
156
157    /// Pop and restore (call when union fails/ambiguous).
158    /// Removes current, snapshot becomes new current.
159    pub fn pop_and_restore(&self) {
160        let mut stack = self.0.borrow_mut();
161        if stack.len() >= 2 {
162            stack.pop(); // Remove current, snapshot is now current
163        }
164    }
165
166    /// Pop without restore (call when union succeeds).
167    /// Removes the snapshot, keeps current.
168    pub fn pop_without_restore(&self) {
169        let mut stack = self.0.borrow_mut();
170        if stack.len() >= 2 {
171            let snapshot_idx = stack.len() - 2;
172            stack.remove(snapshot_idx); // Remove snapshot, keep current
173        }
174    }
175}
176
177impl Default for AccessedSet {
178    fn default() -> Self {
179        Self::new()
180    }
181}
182
183// =============================================================================
184// ParserScope
185// =============================================================================
186
187/// Scope for flatten parsing - indicates whether we're in record or extension mode.
188///
189/// This determines what `parse_record_or_ext()` iterates over for catch-all types like HashMap.
190#[derive(Debug, Clone, Copy, PartialEq, Eq)]
191pub enum ParserScope {
192    /// Record scope - iterates record fields (from `rec.flatten()`)
193    Record,
194    /// Extension scope - iterates extensions (from `ext.flatten_ext()`)
195    Extension,
196}
197
198// =============================================================================
199// FlattenContext
200// =============================================================================
201
202/// Context for flatten parsing - wraps AccessedSet with snapshot/rollback support.
203///
204/// When parsing flattened types, all levels share a single `AccessedSet` owned
205/// by the root parser. Child parsers add to this shared set, and only the root
206/// parser actually validates with `deny_unknown_fields()`.
207///
208/// # Example
209///
210/// ```ignore
211/// // Root parser owns the accessed set
212/// let mut rec1 = ctx.parse_record()?;  // accessed = {} (owned)
213/// rec1.field("a");  // accessed = {a}
214///
215/// // Child parser shares and adds to the same set
216/// let ctx2 = rec1.flatten();
217/// let mut rec2 = ctx2.parse_record()?;  // shares accessed via Rc
218/// rec2.field("b");  // accessed = {a, b}
219/// rec2.deny_unknown_fields()?;  // NO-OP (child)
220///
221/// rec1.field("c");  // accessed = {a, b, c}
222/// rec1.deny_unknown_fields()?;  // VALIDATES (root)
223/// ```
224#[derive(Debug, Clone)]
225pub struct FlattenContext {
226    accessed: AccessedSet,
227    scope: ParserScope,
228}
229
230impl FlattenContext {
231    /// Create a FlattenContext from an existing AccessedSet with the given scope.
232    pub fn new(accessed: AccessedSet, scope: ParserScope) -> Self {
233        Self { accessed, scope }
234    }
235
236    /// Get the parser scope.
237    pub fn scope(&self) -> ParserScope {
238        self.scope
239    }
240
241    /// Get the underlying AccessedSet (for sharing with RecordParser).
242    pub fn accessed_set(&self) -> &AccessedSet {
243        &self.accessed
244    }
245
246    /// Add a field to the accessed set.
247    pub fn add_field(&self, field: impl Into<String>) {
248        self.accessed.add_field(field);
249    }
250
251    /// Add an extension to the accessed set.
252    pub fn add_ext(&self, ext: Identifier) {
253        self.accessed.add_ext(ext);
254    }
255
256    /// Check if a field has been accessed.
257    pub fn has_field(&self, field: &str) -> bool {
258        self.accessed.has_field(field)
259    }
260
261    /// Check if an extension has been accessed.
262    pub fn has_ext(&self, ext: &Identifier) -> bool {
263        self.accessed.has_ext(ext)
264    }
265
266    /// Push snapshot (at start of union parsing).
267    pub fn push_snapshot(&self) {
268        self.accessed.push_snapshot();
269    }
270
271    /// Restore to current snapshot (when variant fails).
272    pub fn restore_to_current_snapshot(&self) {
273        self.accessed.restore_to_current_snapshot();
274    }
275
276    /// Capture current state (for non-priority variants).
277    pub fn capture_current_state(&self) -> AccessedSnapshot {
278        self.accessed.capture_current_state()
279    }
280
281    /// Restore to a captured state (when selecting a non-priority variant).
282    pub fn restore_to_state(&self, state: AccessedSnapshot) {
283        self.accessed.restore_to_state(state);
284    }
285
286    /// Pop and restore (when union fails/ambiguous).
287    pub fn pop_and_restore(&self) {
288        self.accessed.pop_and_restore();
289    }
290
291    /// Pop without restore (when union succeeds).
292    pub fn pop_without_restore(&self) {
293        self.accessed.pop_without_restore();
294    }
295}
296
297// =============================================================================
298// ParseContext
299// =============================================================================
300
301/// Context for parsing Eure documents.
302///
303/// Encapsulates the document, current node, and variant path state.
304/// Similar to `DocumentConstructor` for writing, but for reading.
305#[derive(Clone, Debug)]
306pub struct ParseContext<'doc> {
307    doc: &'doc EureDocument,
308    node_id: NodeId,
309    variant_path: Option<VariantPath>,
310    /// Flatten context for tracking shared accessed fields/extensions.
311    /// If Some, this context is a flattened child - deny_unknown_* is no-op.
312    /// If None, this is a root context.
313    flatten_ctx: Option<FlattenContext>,
314    /// Mode for union tag resolution.
315    union_tag_mode: UnionTagMode,
316    /// Tracks accessed fields and extensions.
317    accessed: AccessedSet,
318}
319
320impl<'doc> ParseContext<'doc> {
321    /// Create a new parse context at the given node.
322    pub fn new(doc: &'doc EureDocument, node_id: NodeId) -> Self {
323        Self {
324            doc,
325            node_id,
326            variant_path: None,
327            flatten_ctx: None,
328            union_tag_mode: UnionTagMode::default(),
329            accessed: AccessedSet::new(),
330        }
331    }
332
333    /// Create a new parse context with the specified union tag mode.
334    pub fn with_union_tag_mode(
335        doc: &'doc EureDocument,
336        node_id: NodeId,
337        mode: UnionTagMode,
338    ) -> Self {
339        Self {
340            doc,
341            node_id,
342            variant_path: None,
343            flatten_ctx: None,
344            union_tag_mode: mode,
345            accessed: AccessedSet::new(),
346        }
347    }
348
349    /// Create a context with a flatten context (for flatten parsing).
350    ///
351    /// When a flatten context is present:
352    /// - `deny_unknown_fields()` and `deny_unknown_extensions()` become no-ops
353    /// - All field/extension accesses are recorded in the shared `FlattenContext`
354    pub fn with_flatten_ctx(
355        doc: &'doc EureDocument,
356        node_id: NodeId,
357        flatten_ctx: FlattenContext,
358        mode: UnionTagMode,
359    ) -> Self {
360        // Share accessed set from flatten context
361        let accessed = flatten_ctx.accessed_set().clone();
362        Self {
363            doc,
364            node_id,
365            variant_path: None,
366            flatten_ctx: Some(flatten_ctx),
367            union_tag_mode: mode,
368            accessed,
369        }
370    }
371
372    /// Get the flatten context, if present.
373    pub fn flatten_ctx(&self) -> Option<&FlattenContext> {
374        self.flatten_ctx.as_ref()
375    }
376
377    /// Check if this context is flattened (has a flatten context).
378    ///
379    /// When flattened, `deny_unknown_fields()` and `deny_unknown_extensions()` are no-ops.
380    pub fn is_flattened(&self) -> bool {
381        self.flatten_ctx.is_some()
382    }
383
384    /// Get the parser scope, if in a flatten context.
385    ///
386    /// Returns `Some(ParserScope::Record)` if from `rec.flatten()`,
387    /// `Some(ParserScope::Extension)` if from `ext.flatten_ext()`,
388    /// `None` if not in a flatten context.
389    pub fn parser_scope(&self) -> Option<ParserScope> {
390        self.flatten_ctx.as_ref().map(|fc| fc.scope())
391    }
392
393    /// Get the current node ID.
394    pub fn node_id(&self) -> NodeId {
395        self.node_id
396    }
397
398    /// Get the document reference (internal use only).
399    pub(crate) fn doc(&self) -> &'doc EureDocument {
400        self.doc
401    }
402
403    /// Get the current node.
404    pub fn node(&self) -> &'doc Node {
405        self.doc.node(self.node_id)
406    }
407
408    /// Create a new context at a different node (clears variant path and flatten context).
409    pub(crate) fn at(&self, node_id: NodeId) -> Self {
410        Self {
411            doc: self.doc,
412            node_id,
413            variant_path: None,
414            flatten_ctx: None,
415            union_tag_mode: self.union_tag_mode,
416            accessed: AccessedSet::new(),
417        }
418    }
419
420    /// Create a flattened version of this context for shared access tracking.
421    ///
422    /// When you need both record parsing and extension parsing to share
423    /// access tracking (so deny_unknown_* works correctly), use this method
424    /// to create a shared context first.
425    ///
426    /// # Example
427    ///
428    /// ```ignore
429    /// // Both record and extension parsers share the same AccessedSet
430    /// let ctx = ctx.flatten();
431    /// // ... parse extensions with ctx.parse_ext(...) ...
432    /// let mut rec = ctx.parse_record()?;
433    /// // ... parse fields ...
434    /// rec.deny_unknown_fields()?;  // Validates both fields and extensions
435    /// ```
436    pub fn flatten(&self) -> Self {
437        // If already has flatten context, reuse it
438        if let Some(fc) = &self.flatten_ctx {
439            return Self {
440                doc: self.doc,
441                node_id: self.node_id,
442                variant_path: self.variant_path.clone(),
443                flatten_ctx: Some(fc.clone()),
444                union_tag_mode: self.union_tag_mode,
445                accessed: self.accessed.clone(),
446            };
447        }
448
449        // Create new flatten context with shared AccessedSet (Record scope for general use)
450        let flatten_ctx = FlattenContext::new(self.accessed.clone(), ParserScope::Record);
451        Self {
452            doc: self.doc,
453            node_id: self.node_id,
454            variant_path: self.variant_path.clone(),
455            flatten_ctx: Some(flatten_ctx),
456            union_tag_mode: self.union_tag_mode,
457            accessed: self.accessed.clone(),
458        }
459    }
460
461    /// Get the union tag mode.
462    pub fn union_tag_mode(&self) -> UnionTagMode {
463        self.union_tag_mode
464    }
465
466    /// Parse the current node as type T.
467    pub fn parse<T: ParseDocument<'doc>>(&self) -> Result<T, T::Error> {
468        T::parse(self)
469    }
470
471    pub fn parse_with<T: DocumentParser<'doc>>(
472        &self,
473        mut parser: T,
474    ) -> Result<T::Output, T::Error> {
475        parser.parse(self)
476    }
477
478    /// Get a union parser for the current node with the specified variant representation.
479    ///
480    /// Returns error if `$variant` extension has invalid type or syntax.
481    ///
482    /// # Arguments
483    ///
484    /// * `repr` - The variant representation to use. Use `VariantRepr::default()` for Untagged.
485    pub fn parse_union<T, E>(&self, repr: VariantRepr) -> Result<UnionParser<'doc, '_, T, E>, E>
486    where
487        E: From<ParseError>,
488    {
489        UnionParser::new(self, repr).map_err(Into::into)
490    }
491
492    /// Parse the current node as a record.
493    ///
494    /// Returns error if variant path is not empty.
495    pub fn parse_record(&self) -> Result<RecordParser<'doc>, ParseError> {
496        self.ensure_no_variant_path()?;
497        RecordParser::new(self)
498    }
499
500    /// Parse the current node as a tuple.
501    ///
502    /// Returns error if variant path is not empty.
503    pub fn parse_tuple(&self) -> Result<TupleParser<'doc>, ParseError> {
504        self.ensure_no_variant_path()?;
505        TupleParser::new(self)
506    }
507
508    /// Parse the current node as a primitive value.
509    ///
510    /// Returns error if variant path is not empty.
511    pub fn parse_primitive(&self) -> Result<&'doc PrimitiveValue, ParseError> {
512        self.ensure_no_variant_path()?;
513        match &self.node().content {
514            NodeValue::Primitive(p) => Ok(p),
515            value => Err(ParseError {
516                node_id: self.node_id,
517                kind: handle_unexpected_node_value(value),
518            }),
519        }
520    }
521
522    // =========================================================================
523    // Extension parsing methods
524    // =========================================================================
525
526    /// Get the AccessedSet for this context.
527    pub(crate) fn accessed(&self) -> &AccessedSet {
528        &self.accessed
529    }
530
531    /// Mark an extension as accessed.
532    fn mark_ext_accessed(&self, ident: Identifier) {
533        self.accessed.add_ext(ident);
534    }
535
536    /// Get a required extension field.
537    ///
538    /// Returns `ParseErrorKind::MissingExtension` if the extension is not present.
539    pub fn parse_ext<T>(&self, name: &str) -> Result<T, T::Error>
540    where
541        T: ParseDocument<'doc>,
542        T::Error: From<ParseError>,
543    {
544        self.parse_ext_with(name, T::parse)
545    }
546
547    /// Get a required extension field with a custom parser.
548    pub fn parse_ext_with<T>(&self, name: &str, mut parser: T) -> Result<T::Output, T::Error>
549    where
550        T: DocumentParser<'doc>,
551        T::Error: From<ParseError>,
552    {
553        let ident: Identifier = name.parse().map_err(|e| ParseError {
554            node_id: self.node_id,
555            kind: ParseErrorKind::InvalidIdentifier(e),
556        })?;
557        self.mark_ext_accessed(ident.clone());
558        let ext_node_id = self
559            .node()
560            .extensions
561            .get(&ident)
562            .ok_or_else(|| ParseError {
563                node_id: self.node_id,
564                kind: ParseErrorKind::MissingExtension(name.to_string()),
565            })?;
566        let ctx = ParseContext::with_union_tag_mode(self.doc, *ext_node_id, self.union_tag_mode);
567        parser.parse(&ctx)
568    }
569
570    /// Get an optional extension field.
571    ///
572    /// Returns `Ok(None)` if the extension is not present.
573    pub fn parse_ext_optional<T>(&self, name: &str) -> Result<Option<T>, T::Error>
574    where
575        T: ParseDocument<'doc>,
576        T::Error: From<ParseError>,
577    {
578        self.parse_ext_optional_with(name, T::parse)
579    }
580
581    /// Get an optional extension field with a custom parser.
582    ///
583    /// Returns `Ok(None)` if the extension is not present.
584    pub fn parse_ext_optional_with<T>(
585        &self,
586        name: &str,
587        mut parser: T,
588    ) -> Result<Option<T::Output>, T::Error>
589    where
590        T: DocumentParser<'doc>,
591        T::Error: From<ParseError>,
592    {
593        let ident: Identifier = name.parse().map_err(|e| ParseError {
594            node_id: self.node_id,
595            kind: ParseErrorKind::InvalidIdentifier(e),
596        })?;
597        self.mark_ext_accessed(ident.clone());
598        match self.node().extensions.get(&ident) {
599            Some(ext_node_id) => {
600                let ctx =
601                    ParseContext::with_union_tag_mode(self.doc, *ext_node_id, self.union_tag_mode);
602                Ok(Some(parser.parse(&ctx)?))
603            }
604            None => Ok(None),
605        }
606    }
607
608    /// Get the parse context for an extension field without parsing it.
609    ///
610    /// Use this when you need access to the extension's NodeId or want to defer parsing.
611    /// Returns `ParseErrorKind::MissingExtension` if the extension is not present.
612    pub fn ext(&self, name: &str) -> Result<ParseContext<'doc>, ParseError> {
613        let ident: Identifier = name.parse().map_err(|e| ParseError {
614            node_id: self.node_id,
615            kind: ParseErrorKind::InvalidIdentifier(e),
616        })?;
617        self.mark_ext_accessed(ident.clone());
618        let ext_node_id =
619            self.node()
620                .extensions
621                .get(&ident)
622                .copied()
623                .ok_or_else(|| ParseError {
624                    node_id: self.node_id,
625                    kind: ParseErrorKind::MissingExtension(name.to_string()),
626                })?;
627        Ok(ParseContext::with_union_tag_mode(
628            self.doc,
629            ext_node_id,
630            self.union_tag_mode,
631        ))
632    }
633
634    /// Get the parse context for an optional extension field without parsing it.
635    ///
636    /// Use this when you need access to the extension's NodeId or want to defer parsing.
637    /// Returns `None` if the extension is not present.
638    pub fn ext_optional(&self, name: &str) -> Option<ParseContext<'doc>> {
639        let ident: Identifier = name.parse().ok()?;
640        self.mark_ext_accessed(ident.clone());
641        self.node().extensions.get(&ident).map(|&node_id| {
642            ParseContext::with_union_tag_mode(self.doc, node_id, self.union_tag_mode)
643        })
644    }
645
646    /// Finish parsing with Deny policy (error if unknown extensions exist).
647    ///
648    /// **Flatten behavior**: If this context is in a flatten chain (has flatten_ctx),
649    /// this is a no-op. Only root parsers validate.
650    pub fn deny_unknown_extensions(&self) -> Result<(), ParseError> {
651        // If child (in any flatten context), no-op - parent will validate
652        if self.flatten_ctx.is_some() {
653            return Ok(());
654        }
655
656        // Root parser - validate using accessed set
657        for (ident, _) in self.node().extensions.iter() {
658            if !self.accessed.has_ext(ident) {
659                return Err(ParseError {
660                    node_id: self.node_id,
661                    kind: ParseErrorKind::UnknownExtension(ident.clone()),
662                });
663            }
664        }
665        Ok(())
666    }
667
668    /// Get an iterator over unknown extensions (for custom handling).
669    ///
670    /// Returns (identifier, context) pairs for extensions that haven't been accessed.
671    pub fn unknown_extensions(
672        &self,
673    ) -> impl Iterator<Item = (&'doc Identifier, ParseContext<'doc>)> + '_ {
674        let doc = self.doc;
675        let mode = self.union_tag_mode;
676        // Clone the accessed set for filtering - we need the current state
677        let accessed = self.accessed.clone();
678        self.node()
679            .extensions
680            .iter()
681            .filter_map(move |(ident, &node_id)| {
682                if !accessed.has_ext(ident) {
683                    Some((ident, ParseContext::with_union_tag_mode(doc, node_id, mode)))
684                } else {
685                    None
686                }
687            })
688    }
689
690    /// Create a flatten context for child parsers in Extension scope.
691    ///
692    /// This creates a FlattenContext initialized with the current accessed extensions,
693    /// and returns a ParseContext that children can use. Children created from this
694    /// context will:
695    /// - Add their accessed extensions to the shared FlattenContext
696    /// - Have deny_unknown_extensions() be a no-op
697    ///
698    /// The root parser should call deny_unknown_extensions() after all children are done.
699    pub fn flatten_ext(&self) -> ParseContext<'doc> {
700        // Create or reuse flatten context with Extension scope
701        let flatten_ctx = match &self.flatten_ctx {
702            Some(fc) => fc.clone(),
703            None => {
704                // Root: create new FlattenContext wrapping our AccessedSet with Extension scope
705                FlattenContext::new(self.accessed.clone(), ParserScope::Extension)
706            }
707        };
708
709        ParseContext::with_flatten_ctx(self.doc, self.node_id, flatten_ctx, self.union_tag_mode)
710    }
711
712    /// Check if the current node is null.
713    pub fn is_null(&self) -> bool {
714        matches!(
715            &self.node().content,
716            NodeValue::Primitive(PrimitiveValue::Null)
717        )
718    }
719
720    /// Create a child context with the remaining variant path.
721    pub(crate) fn with_variant_rest(&self, rest: Option<VariantPath>) -> Self {
722        Self {
723            doc: self.doc,
724            node_id: self.node_id,
725            variant_path: rest,
726            flatten_ctx: self.flatten_ctx.clone(),
727            union_tag_mode: self.union_tag_mode,
728            accessed: self.accessed.clone(),
729        }
730    }
731
732    /// Get the current variant path.
733    pub(crate) fn variant_path(&self) -> Option<&VariantPath> {
734        self.variant_path.as_ref()
735    }
736
737    /// Check that no variant path remains, error otherwise.
738    fn ensure_no_variant_path(&self) -> Result<(), ParseError> {
739        if let Some(vp) = &self.variant_path
740            && !vp.is_empty()
741        {
742            return Err(ParseError {
743                node_id: self.node_id,
744                kind: ParseErrorKind::UnexpectedVariantPath(vp.clone()),
745            });
746        }
747        Ok(())
748    }
749}
750
751// =============================================================================
752// ParseDocument trait
753// =============================================================================
754
755/// Trait for parsing Rust types from Eure documents.
756///
757/// Types implementing this trait can be constructed from [`EureDocument`]
758/// via [`ParseContext`].
759///
760/// # Lifetime Parameter
761///
762/// The `'doc` lifetime ties the parsed output to the document's lifetime,
763/// allowing zero-copy parsing for reference types like `&'doc str`.
764///
765/// # Examples
766///
767/// ```ignore
768/// // Reference type - borrows from document
769/// impl<'doc> ParseDocument<'doc> for &'doc str { ... }
770///
771/// // Owned type - no lifetime dependency
772/// impl ParseDocument<'_> for String { ... }
773/// ```
774pub trait ParseDocument<'doc>: Sized {
775    /// The error type returned by parsing.
776    type Error;
777
778    /// Parse a value of this type from the given parse context.
779    fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error>;
780}
781
782fn handle_unexpected_node_value(node_value: &NodeValue) -> ParseErrorKind {
783    match node_value {
784        NodeValue::Hole(_) => ParseErrorKind::UnexpectedHole,
785        value => value
786            .value_kind()
787            .map(|actual| ParseErrorKind::TypeMismatch {
788                expected: ValueKind::Text,
789                actual,
790            })
791            .unwrap_or_else(|| ParseErrorKind::UnexpectedHole),
792    }
793}
794
795#[derive(Debug, thiserror::Error, Clone, PartialEq)]
796#[error("parse error: {kind}")]
797pub struct ParseError {
798    pub node_id: NodeId,
799    pub kind: ParseErrorKind,
800}
801
802/// Error type for parsing failures.
803#[derive(Debug, thiserror::Error, Clone, PartialEq)]
804pub enum ParseErrorKind {
805    /// Unexpected uninitialized value.
806    #[error("unexpected uninitialized value")]
807    UnexpectedHole,
808
809    /// Type mismatch between expected and actual value.
810    #[error("type mismatch: expected {expected}, got {actual}")]
811    TypeMismatch {
812        expected: ValueKind,
813        actual: ValueKind,
814    },
815
816    /// Required field is missing.
817    #[error("missing field: {0}")]
818    MissingField(String),
819
820    /// Required extension is missing.
821    #[error("missing extension: ${0}")]
822    MissingExtension(String),
823
824    /// Unknown variant in a union type.
825    #[error("unknown variant: {0}")]
826    UnknownVariant(String),
827
828    /// Value is out of valid range.
829    #[error("value out of range: {0}")]
830    OutOfRange(String),
831
832    /// Invalid value pattern or format.
833    ///
834    /// Used for validation errors in types like regex, URL, UUID, etc.
835    /// - `kind`: Type of validation (e.g., "regex", "url", "uuid", "pattern: <expected>")
836    /// - `reason`: Human-readable error message explaining the failure
837    #[error("invalid {kind}: {reason}")]
838    InvalidPattern { kind: String, reason: String },
839
840    /// Nested parse error with path context.
841    #[error("at {path}: {source}")]
842    Nested {
843        path: String,
844        #[source]
845        source: Box<ParseErrorKind>,
846    },
847
848    /// Invalid identifier.
849    #[error("invalid identifier: {0}")]
850    InvalidIdentifier(#[from] IdentifierError),
851
852    /// Unexpected tuple length.
853    #[error("unexpected tuple length: expected {expected}, got {actual}")]
854    UnexpectedTupleLength { expected: usize, actual: usize },
855
856    /// Unknown field in record.
857    #[error("unknown field: {0}")]
858    UnknownField(String),
859
860    /// Unknown extension on node.
861    #[error("unknown extension: ${0}")]
862    UnknownExtension(Identifier),
863
864    /// Invalid key type in record (expected string).
865    #[error("invalid key type in record: expected string key, got {0:?}")]
866    InvalidKeyType(crate::value::ObjectKey),
867
868    /// No variant matched in union type.
869    #[error("no matching variant{}", variant.as_ref().map(|v| format!(" (variant: {})", v)).unwrap_or_default())]
870    NoMatchingVariant {
871        /// Variant name extracted (if any).
872        variant: Option<String>,
873    },
874
875    /// Conflicting variant tags: $variant and repr extracted different variant names.
876    #[error("conflicting variant tags: $variant = {explicit}, repr = {repr}")]
877    ConflictingVariantTags { explicit: String, repr: String },
878
879    /// Multiple variants matched with no priority to resolve.
880    #[error("ambiguous union: {0:?}")]
881    AmbiguousUnion(Vec<String>),
882
883    /// Literal value mismatch.
884    #[error("literal value mismatch: expected {expected}, got {actual}")]
885    // FIXME: Use EureDocument instead of String?
886    LiteralMismatch { expected: String, actual: String },
887
888    /// Variant path provided but type is not a union.
889    #[error("unexpected variant path: {0}")]
890    UnexpectedVariantPath(VariantPath),
891
892    /// $variant extension has invalid type (not a string).
893    #[error("$variant must be a string, got {0}")]
894    InvalidVariantType(ValueKind),
895
896    /// $variant extension has invalid path syntax.
897    #[error("invalid $variant path syntax: {0}")]
898    InvalidVariantPath(String),
899
900    /// Tried to parse record fields while in extension flatten scope.
901    /// This happens when using #[eure(flatten_ext)] with a type that calls parse_record().
902    #[error(
903        "cannot parse record in extension scope: use #[eure(flatten)] instead of #[eure(flatten_ext)]"
904    )]
905    RecordInExtensionScope,
906}
907
908impl ParseErrorKind {
909    /// Wrap this error with path context.
910    pub fn at(self, path: impl Into<String>) -> Self {
911        ParseErrorKind::Nested {
912            path: path.into(),
913            source: Box::new(self),
914        }
915    }
916}
917
918impl<'doc> EureDocument {
919    /// Parse a value of type T from the given node.
920    pub fn parse<T: ParseDocument<'doc>>(&'doc self, node_id: NodeId) -> Result<T, T::Error> {
921        self.parse_with(node_id, T::parse)
922    }
923
924    pub fn parse_with<T: DocumentParser<'doc>>(
925        &'doc self,
926        node_id: NodeId,
927        mut parser: T,
928    ) -> Result<T::Output, T::Error> {
929        parser.parse(&self.parse_context(node_id))
930    }
931
932    /// Create a parse context at the given node.
933    pub fn parse_context(&'doc self, node_id: NodeId) -> ParseContext<'doc> {
934        ParseContext::new(self, node_id)
935    }
936
937    /// Parse a node as a record.
938    ///
939    /// Convenience method equivalent to `doc.parse_context(node_id).parse_record()`.
940    pub fn parse_record(&'doc self, node_id: NodeId) -> Result<RecordParser<'doc>, ParseError> {
941        RecordParser::from_doc_and_node(self, node_id)
942    }
943
944    /// Create a parse context for extension parsing.
945    ///
946    /// Convenience method equivalent to `doc.parse_context(node_id)`.
947    /// Use the returned context's `parse_ext()`, `ext()`, etc. methods.
948    pub fn parse_extension_context(&'doc self, node_id: NodeId) -> ParseContext<'doc> {
949        ParseContext::new(self, node_id)
950    }
951
952    /// Parse a node as a tuple.
953    ///
954    /// Convenience method equivalent to `doc.parse_context(node_id).parse_tuple()`.
955    pub fn parse_tuple(&'doc self, node_id: NodeId) -> Result<TupleParser<'doc>, ParseError> {
956        TupleParser::from_doc_and_node(self, node_id)
957    }
958}
959
960impl<'doc> ParseDocument<'doc> for EureDocument {
961    type Error = ParseError;
962
963    fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
964        Ok(ctx.doc().node_subtree_to_document(ctx.node_id()))
965    }
966}
967
968impl<'doc> ParseDocument<'doc> for &'doc str {
969    type Error = ParseError;
970
971    fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
972        match ctx.parse_primitive()? {
973            PrimitiveValue::Text(text) => Ok(text.as_str()),
974            _ => Err(ParseError {
975                node_id: ctx.node_id(),
976                kind: ParseErrorKind::TypeMismatch {
977                    expected: ValueKind::Text,
978                    actual: ctx.node().content.value_kind().unwrap_or(ValueKind::Null),
979                },
980            }),
981        }
982    }
983}
984
985impl ParseDocument<'_> for String {
986    type Error = ParseError;
987
988    fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
989        ctx.parse::<&str>().map(String::from)
990    }
991}
992
993impl ParseDocument<'_> for Text {
994    type Error = ParseError;
995
996    fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
997        match ctx.parse_primitive()? {
998            PrimitiveValue::Text(text) => Ok(text.clone()),
999            _ => Err(ParseError {
1000                node_id: ctx.node_id(),
1001                kind: ParseErrorKind::TypeMismatch {
1002                    expected: ValueKind::Text,
1003                    actual: ctx.node().content.value_kind().unwrap_or(ValueKind::Null),
1004                },
1005            }),
1006        }
1007    }
1008}
1009
1010impl ParseDocument<'_> for bool {
1011    type Error = ParseError;
1012
1013    fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
1014        match ctx.parse_primitive()? {
1015            PrimitiveValue::Bool(b) => Ok(*b),
1016            _ => Err(ParseError {
1017                node_id: ctx.node_id(),
1018                kind: ParseErrorKind::TypeMismatch {
1019                    expected: ValueKind::Bool,
1020                    actual: ctx.node().content.value_kind().unwrap_or(ValueKind::Null),
1021                },
1022            }),
1023        }
1024    }
1025}
1026
1027impl ParseDocument<'_> for BigInt {
1028    type Error = ParseError;
1029
1030    fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
1031        match ctx.parse_primitive()? {
1032            PrimitiveValue::Integer(i) => Ok(i.clone()),
1033            _ => Err(ParseError {
1034                node_id: ctx.node_id(),
1035                kind: ParseErrorKind::TypeMismatch {
1036                    expected: ValueKind::Integer,
1037                    actual: ctx.node().content.value_kind().unwrap_or(ValueKind::Null),
1038                },
1039            }),
1040        }
1041    }
1042}
1043
1044impl ParseDocument<'_> for f32 {
1045    type Error = ParseError;
1046
1047    fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
1048        match ctx.parse_primitive()? {
1049            PrimitiveValue::F32(f) => Ok(*f),
1050            _ => Err(ParseError {
1051                node_id: ctx.node_id(),
1052                kind: ParseErrorKind::TypeMismatch {
1053                    expected: ValueKind::F32,
1054                    actual: ctx.node().content.value_kind().unwrap_or(ValueKind::Null),
1055                },
1056            }),
1057        }
1058    }
1059}
1060
1061impl ParseDocument<'_> for f64 {
1062    type Error = ParseError;
1063
1064    fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
1065        match ctx.parse_primitive()? {
1066            // Accept both F32 (with conversion) and F64
1067            PrimitiveValue::F32(f) => Ok(*f as f64),
1068            PrimitiveValue::F64(f) => Ok(*f),
1069            _ => Err(ParseError {
1070                node_id: ctx.node_id(),
1071                kind: ParseErrorKind::TypeMismatch {
1072                    expected: ValueKind::F64,
1073                    actual: ctx.node().content.value_kind().unwrap_or(ValueKind::Null),
1074                },
1075            }),
1076        }
1077    }
1078}
1079
1080impl ParseDocument<'_> for u32 {
1081    type Error = ParseError;
1082
1083    fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
1084        let value: BigInt = ctx.parse()?;
1085        u32::try_from(&value).map_err(|_| ParseError {
1086            node_id: ctx.node_id(),
1087            kind: ParseErrorKind::OutOfRange(format!("value {} out of u32 range", value)),
1088        })
1089    }
1090}
1091
1092impl ParseDocument<'_> for i32 {
1093    type Error = ParseError;
1094
1095    fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
1096        let value: BigInt = ctx.parse()?;
1097        i32::try_from(&value).map_err(|_| ParseError {
1098            node_id: ctx.node_id(),
1099            kind: ParseErrorKind::OutOfRange(format!("value {} out of i32 range", value)),
1100        })
1101    }
1102}
1103
1104impl ParseDocument<'_> for i64 {
1105    type Error = ParseError;
1106
1107    fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
1108        let value: BigInt = ctx.parse()?;
1109        i64::try_from(&value).map_err(|_| ParseError {
1110            node_id: ctx.node_id(),
1111            kind: ParseErrorKind::OutOfRange(format!("value {} out of i64 range", value)),
1112        })
1113    }
1114}
1115
1116impl ParseDocument<'_> for u64 {
1117    type Error = ParseError;
1118
1119    fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
1120        let value: BigInt = ctx.parse()?;
1121        u64::try_from(&value).map_err(|_| ParseError {
1122            node_id: ctx.node_id(),
1123            kind: ParseErrorKind::OutOfRange(format!("value {} out of u64 range", value)),
1124        })
1125    }
1126}
1127
1128impl ParseDocument<'_> for usize {
1129    type Error = ParseError;
1130
1131    fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
1132        let value: BigInt = ctx.parse()?;
1133        usize::try_from(&value).map_err(|_| ParseError {
1134            node_id: ctx.node_id(),
1135            kind: ParseErrorKind::OutOfRange(format!("value {} out of usize range", value)),
1136        })
1137    }
1138}
1139
1140impl<'doc> ParseDocument<'doc> for &'doc PrimitiveValue {
1141    type Error = ParseError;
1142
1143    fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
1144        ctx.parse_primitive()
1145    }
1146}
1147
1148impl ParseDocument<'_> for PrimitiveValue {
1149    type Error = ParseError;
1150
1151    fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
1152        ctx.parse::<&PrimitiveValue>().cloned()
1153    }
1154}
1155
1156impl ParseDocument<'_> for Identifier {
1157    type Error = ParseError;
1158
1159    fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
1160        match ctx.parse_primitive()? {
1161            PrimitiveValue::Text(text) => text
1162                .content
1163                .parse()
1164                .map_err(ParseErrorKind::InvalidIdentifier)
1165                .map_err(|kind| ParseError {
1166                    node_id: ctx.node_id(),
1167                    kind,
1168                }),
1169            _ => Err(ParseError {
1170                node_id: ctx.node_id(),
1171                kind: ParseErrorKind::TypeMismatch {
1172                    expected: ValueKind::Text,
1173                    actual: ctx.node().content.value_kind().unwrap_or(ValueKind::Null),
1174                },
1175            }),
1176        }
1177    }
1178}
1179
1180impl<'doc> ParseDocument<'doc> for &'doc NodeArray {
1181    type Error = ParseError;
1182
1183    fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
1184        ctx.ensure_no_variant_path()?;
1185        match &ctx.node().content {
1186            NodeValue::Array(array) => Ok(array),
1187            value => Err(ParseError {
1188                node_id: ctx.node_id(),
1189                kind: handle_unexpected_node_value(value),
1190            }),
1191        }
1192    }
1193}
1194
1195impl<'doc, T> ParseDocument<'doc> for Vec<T>
1196where
1197    T: ParseDocument<'doc>,
1198    T::Error: From<ParseError>,
1199{
1200    type Error = T::Error;
1201
1202    fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
1203        ctx.ensure_no_variant_path()?;
1204        match &ctx.node().content {
1205            NodeValue::Array(array) => array
1206                .iter()
1207                .map(|item| T::parse(&ctx.at(*item)))
1208                .collect::<Result<Vec<_>, _>>(),
1209            value => Err(ParseError {
1210                node_id: ctx.node_id(),
1211                kind: handle_unexpected_node_value(value),
1212            }
1213            .into()),
1214        }
1215    }
1216}
1217
1218impl<'doc, T> ParseDocument<'doc> for IndexSet<T>
1219where
1220    T: ParseDocument<'doc> + Eq + std::hash::Hash,
1221    T::Error: From<ParseError>,
1222{
1223    type Error = T::Error;
1224    fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
1225        ctx.ensure_no_variant_path()?;
1226        match &ctx.node().content {
1227            NodeValue::Array(array) => array
1228                .iter()
1229                .map(|item| T::parse(&ctx.at(*item)))
1230                .collect::<Result<IndexSet<_>, _>>(),
1231            value => Err(ParseError {
1232                node_id: ctx.node_id(),
1233                kind: handle_unexpected_node_value(value),
1234            }
1235            .into()),
1236        }
1237    }
1238}
1239
1240macro_rules! parse_tuple {
1241    ($n:expr, $($var:ident),*) => {
1242        impl<'doc, $($var),*, Err> ParseDocument<'doc> for ($($var),*,)
1243            where $($var: ParseDocument<'doc, Error = Err>),*,
1244            Err: From<ParseError>,
1245        {
1246            type Error = Err;
1247
1248            fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
1249                ctx.ensure_no_variant_path()?;
1250                let tuple = match &ctx.node().content {
1251                    NodeValue::Tuple(tuple) => tuple,
1252                    value => return Err(ParseError { node_id: ctx.node_id(), kind: handle_unexpected_node_value(value) }.into()),
1253                };
1254                if tuple.len() != $n {
1255                    return Err(ParseError { node_id: ctx.node_id(), kind: ParseErrorKind::UnexpectedTupleLength { expected: $n, actual: tuple.len() } }.into());
1256                }
1257                let mut iter = tuple.iter();
1258                Ok(($($var::parse(&ctx.at(*iter.next().unwrap()))?),*,))
1259            }
1260        }
1261    }
1262}
1263
1264parse_tuple!(1, A);
1265parse_tuple!(2, A, B);
1266parse_tuple!(3, A, B, C);
1267parse_tuple!(4, A, B, C, D);
1268parse_tuple!(5, A, B, C, D, E);
1269parse_tuple!(6, A, B, C, D, E, F);
1270parse_tuple!(7, A, B, C, D, E, F, G);
1271parse_tuple!(8, A, B, C, D, E, F, G, H);
1272parse_tuple!(9, A, B, C, D, E, F, G, H, I);
1273parse_tuple!(10, A, B, C, D, E, F, G, H, I, J);
1274parse_tuple!(11, A, B, C, D, E, F, G, H, I, J, K);
1275parse_tuple!(12, A, B, C, D, E, F, G, H, I, J, K, L);
1276parse_tuple!(13, A, B, C, D, E, F, G, H, I, J, K, L, M);
1277parse_tuple!(14, A, B, C, D, E, F, G, H, I, J, K, L, M, N);
1278parse_tuple!(15, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
1279parse_tuple!(16, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
1280
1281macro_rules! parse_map {
1282    ($ctx:ident) => {{
1283        $ctx.ensure_no_variant_path()?;
1284
1285        // Check scope: Extension scope iterates extensions, otherwise record fields
1286        if $ctx.parser_scope() == Some(ParserScope::Extension) {
1287            // Extension scope: iterate UNACCESSED extensions only
1288            let node = $ctx.node();
1289            let accessed = $ctx.flatten_ctx().map(|fc| fc.accessed_set());
1290            node.extensions
1291                .iter()
1292                .filter(|(ident, _)| {
1293                    // Only include extensions not already accessed
1294                    accessed.map_or(true, |a| !a.has_ext(ident))
1295                })
1296                .map(|(ident, &node_id)| {
1297                    Ok((
1298                        K::from_extension_ident(ident).map_err(|kind| ParseError {
1299                            node_id: $ctx.node_id(),
1300                            kind,
1301                        })?,
1302                        T::parse(&$ctx.at(node_id))?,
1303                    ))
1304                })
1305                .collect::<Result<_, _>>()
1306        } else {
1307            // Record scope or no scope: iterate record fields
1308            let map = match &$ctx.node().content {
1309                NodeValue::Map(map) => map,
1310                value => {
1311                    return Err(ParseError {
1312                        node_id: $ctx.node_id(),
1313                        kind: handle_unexpected_node_value(value),
1314                    }
1315                    .into());
1316                }
1317            };
1318            // If in flatten context with Record scope, only iterate UNACCESSED fields
1319            let accessed = $ctx
1320                .flatten_ctx()
1321                .filter(|fc| fc.scope() == ParserScope::Record)
1322                .map(|fc| fc.accessed_set().clone());
1323            map.iter()
1324                .filter(|(key, _)| {
1325                    match &accessed {
1326                        Some(acc) => match key {
1327                            ObjectKey::String(s) => !acc.has_field(s),
1328                            _ => true, // Non-string keys are always included
1329                        },
1330                        None => true, // No flatten context means include all
1331                    }
1332                })
1333                .map(|(key, value)| {
1334                    Ok((
1335                        K::from_object_key(key).map_err(|kind| ParseError {
1336                            node_id: $ctx.node_id(),
1337                            kind,
1338                        })?,
1339                        T::parse(&$ctx.at(*value))?,
1340                    ))
1341                })
1342                .collect::<Result<_, _>>()
1343        }
1344    }};
1345}
1346
1347impl<'doc, K, T> ParseDocument<'doc> for Map<K, T>
1348where
1349    K: ParseObjectKey<'doc>,
1350    T: ParseDocument<'doc>,
1351    T::Error: From<ParseError>,
1352{
1353    type Error = T::Error;
1354
1355    fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
1356        parse_map!(ctx)
1357    }
1358}
1359
1360impl<'doc, K, T> ParseDocument<'doc> for BTreeMap<K, T>
1361where
1362    K: ParseObjectKey<'doc>,
1363    T: ParseDocument<'doc>,
1364    T::Error: From<ParseError>,
1365{
1366    type Error = T::Error;
1367    fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
1368        parse_map!(ctx)
1369    }
1370}
1371
1372impl<'doc, K, T> ParseDocument<'doc> for HashMap<K, T>
1373where
1374    K: ParseObjectKey<'doc>,
1375    T: ParseDocument<'doc>,
1376    T::Error: From<ParseError>,
1377{
1378    type Error = T::Error;
1379    fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
1380        parse_map!(ctx)
1381    }
1382}
1383
1384impl<'doc, K, T> ParseDocument<'doc> for IndexMap<K, T>
1385where
1386    K: ParseObjectKey<'doc>,
1387    T: ParseDocument<'doc>,
1388    T::Error: From<ParseError>,
1389{
1390    type Error = T::Error;
1391    fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
1392        parse_map!(ctx)
1393    }
1394}
1395
1396impl ParseDocument<'_> for regex::Regex {
1397    type Error = ParseError;
1398
1399    fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
1400        let pattern: &str = ctx.parse()?;
1401        regex::Regex::new(pattern).map_err(|e| ParseError {
1402            node_id: ctx.node_id(),
1403            kind: ParseErrorKind::InvalidPattern {
1404                kind: format!("regex '{}'", pattern),
1405                reason: e.to_string(),
1406            },
1407        })
1408    }
1409}
1410
1411/// `Option<T>` is a union with variants `some` and `none`.
1412///
1413/// - `$variant: some` -> parse T
1414/// - `$variant: none` -> None
1415/// - No `$variant` and value is null -> None
1416/// - No `$variant` and value is not null -> try parsing as T (Some)
1417impl<'doc, T> ParseDocument<'doc> for Option<T>
1418where
1419    T: ParseDocument<'doc>,
1420    T::Error: From<ParseError>,
1421{
1422    type Error = T::Error;
1423
1424    fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
1425        ctx.parse_union::<Option<T>, T::Error>(VariantRepr::default())?
1426            .variant("some", (T::parse).map(Some))
1427            .variant("none", |ctx: &ParseContext<'_>| {
1428                if ctx.is_null() {
1429                    Ok(None)
1430                } else {
1431                    Err(ParseError {
1432                        node_id: ctx.node_id(),
1433                        kind: ParseErrorKind::TypeMismatch {
1434                            expected: ValueKind::Null,
1435                            actual: ctx.node().content.value_kind().unwrap_or(ValueKind::Null),
1436                        },
1437                    }
1438                    .into())
1439                }
1440            })
1441            .parse()
1442    }
1443}
1444
1445/// `Result<T, E>` is a union with variants `ok` and `err`.
1446///
1447/// - `$variant: ok` -> parse T as Ok
1448/// - `$variant: err` -> parse E as Err
1449/// - No `$variant` -> try Ok first, then Err (priority-based)
1450impl<'doc, T, E, Err> ParseDocument<'doc> for Result<T, E>
1451where
1452    T: ParseDocument<'doc, Error = Err>,
1453    E: ParseDocument<'doc, Error = Err>,
1454    Err: From<ParseError>,
1455{
1456    type Error = Err;
1457
1458    fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
1459        ctx.parse_union::<Self, Self::Error>(VariantRepr::default())?
1460            .variant("ok", (T::parse).map(Ok))
1461            .variant("err", (E::parse).map(Err))
1462            .parse()
1463    }
1464}
1465
1466impl ParseDocument<'_> for crate::data_model::VariantRepr {
1467    type Error = ParseError;
1468
1469    fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
1470        use crate::data_model::VariantRepr;
1471
1472        // Check if it's a simple string value
1473        if let Ok(value) = ctx.parse::<&str>() {
1474            return match value {
1475                "external" => Ok(VariantRepr::External),
1476                "untagged" => Ok(VariantRepr::Untagged),
1477                _ => Err(ParseError {
1478                    node_id: ctx.node_id(),
1479                    kind: ParseErrorKind::UnknownVariant(value.to_string()),
1480                }),
1481            };
1482        }
1483
1484        // Otherwise, it should be a record with tag/content fields
1485        let rec = ctx.parse_record()?;
1486
1487        let tag = rec.parse_field_optional::<String>("tag")?;
1488        let content = rec.parse_field_optional::<String>("content")?;
1489
1490        rec.allow_unknown_fields()?;
1491
1492        match (tag, content) {
1493            (Some(tag), Some(content)) => Ok(VariantRepr::Adjacent { tag, content }),
1494            (Some(tag), None) => Ok(VariantRepr::Internal { tag }),
1495            (None, None) => Ok(VariantRepr::External),
1496            (None, Some(_)) => Err(ParseError {
1497                node_id: ctx.node_id(),
1498                kind: ParseErrorKind::MissingField(
1499                    "tag (required when content is present)".to_string(),
1500                ),
1501            }),
1502        }
1503    }
1504}
1505
1506impl<'doc> ParseDocument<'doc> for () {
1507    type Error = ParseError;
1508    fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
1509        ctx.parse_tuple()?.finish()
1510    }
1511}
1512
1513impl<'doc> ParseDocument<'doc> for NodeId {
1514    type Error = ParseError;
1515    fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
1516        Ok(ctx.node_id())
1517    }
1518}
1519
1520pub trait DocumentParser<'doc> {
1521    type Output;
1522    type Error;
1523    fn parse(&mut self, ctx: &ParseContext<'doc>) -> Result<Self::Output, Self::Error>;
1524}
1525
1526pub struct AlwaysParser<T, E>(T, PhantomData<E>);
1527
1528impl<T, E> AlwaysParser<T, E> {
1529    pub fn new(value: T) -> AlwaysParser<T, E> {
1530        Self(value, PhantomData)
1531    }
1532}
1533
1534impl<'doc, T, E> DocumentParser<'doc> for AlwaysParser<T, E>
1535where
1536    T: Clone,
1537{
1538    type Output = T;
1539    type Error = E;
1540    fn parse(&mut self, _ctx: &ParseContext<'doc>) -> Result<Self::Output, Self::Error> {
1541        Ok(self.0.clone())
1542    }
1543}
1544
1545impl<'doc, T, F, E> DocumentParser<'doc> for F
1546where
1547    F: FnMut(&ParseContext<'doc>) -> Result<T, E>,
1548{
1549    type Output = T;
1550    type Error = E;
1551    fn parse(&mut self, ctx: &ParseContext<'doc>) -> Result<Self::Output, Self::Error> {
1552        (*self)(ctx)
1553    }
1554}
1555
1556pub struct LiteralParser<T>(pub T);
1557
1558impl<'doc, T, E> DocumentParser<'doc> for LiteralParser<T>
1559where
1560    T: ParseDocument<'doc, Error = E> + PartialEq + core::fmt::Debug,
1561    E: From<ParseError>,
1562{
1563    type Output = T;
1564    type Error = E;
1565    fn parse(&mut self, ctx: &ParseContext<'doc>) -> Result<Self::Output, Self::Error> {
1566        let value: T = ctx.parse::<T>()?;
1567        if value == self.0 {
1568            Ok(value)
1569        } else {
1570            Err(ParseError {
1571                node_id: ctx.node_id(),
1572                kind: ParseErrorKind::LiteralMismatch {
1573                    expected: format!("{:?}", self.0),
1574                    actual: format!("{:?}", value),
1575                },
1576            }
1577            .into())
1578        }
1579    }
1580}
1581
1582/// A parser that matches a specific string literal as an enum variant name.
1583///
1584/// Similar to [`LiteralParser`], but returns [`ParseErrorKind::UnknownVariant`]
1585/// on mismatch instead of [`ParseErrorKind::LiteralMismatch`]. This provides
1586/// better error messages when parsing unit enum variants as string literals.
1587pub struct VariantLiteralParser(pub &'static str);
1588
1589impl<'doc> DocumentParser<'doc> for VariantLiteralParser {
1590    type Output = &'static str;
1591    type Error = ParseError;
1592    fn parse(&mut self, ctx: &ParseContext<'doc>) -> Result<Self::Output, Self::Error> {
1593        let value: &str = ctx.parse()?;
1594        if value == self.0 {
1595            Ok(self.0)
1596        } else {
1597            Err(ParseError {
1598                node_id: ctx.node_id(),
1599                kind: ParseErrorKind::UnknownVariant(value.to_string()),
1600            })
1601        }
1602    }
1603}
1604
1605pub struct MapParser<T, F> {
1606    parser: T,
1607    mapper: F,
1608}
1609
1610impl<'doc, T, O, F> DocumentParser<'doc> for MapParser<T, F>
1611where
1612    T: DocumentParser<'doc>,
1613    F: FnMut(T::Output) -> O,
1614{
1615    type Output = O;
1616    type Error = T::Error;
1617    fn parse(&mut self, ctx: &ParseContext<'doc>) -> Result<Self::Output, Self::Error> {
1618        self.parser.parse(ctx).map(|value| (self.mapper)(value))
1619    }
1620}
1621
1622pub struct AndThenParser<T, F> {
1623    parser: T,
1624    mapper: F,
1625}
1626
1627impl<'doc, T, O, F, E> DocumentParser<'doc> for AndThenParser<T, F>
1628where
1629    T: DocumentParser<'doc, Error = E>,
1630    F: Fn(T::Output) -> Result<O, E>,
1631{
1632    type Output = O;
1633    type Error = E;
1634    fn parse(&mut self, ctx: &ParseContext<'doc>) -> Result<Self::Output, Self::Error> {
1635        let value = self.parser.parse(ctx)?;
1636        (self.mapper)(value)
1637    }
1638}
1639
1640pub trait DocumentParserExt<'doc>: DocumentParser<'doc> + Sized {
1641    fn map<O, F>(self, mapper: F) -> MapParser<Self, F>
1642    where
1643        F: Fn(Self::Output) -> O,
1644    {
1645        MapParser {
1646            parser: self,
1647            mapper,
1648        }
1649    }
1650
1651    fn and_then<O, F>(self, mapper: F) -> AndThenParser<Self, F>
1652    where
1653        F: Fn(Self::Output) -> Result<O, Self::Error>,
1654    {
1655        AndThenParser {
1656            parser: self,
1657            mapper,
1658        }
1659    }
1660}
1661
1662impl<'doc, T> DocumentParserExt<'doc> for T where T: DocumentParser<'doc> {}
1663
1664#[cfg(test)]
1665mod tests {
1666    use super::*;
1667    use crate::document::node::NodeValue;
1668    use crate::eure;
1669    use crate::identifier::Identifier;
1670    use crate::text::Text;
1671    use crate::value::ObjectKey;
1672    use num_bigint::BigInt;
1673
1674    fn identifier(s: &str) -> Identifier {
1675        s.parse().unwrap()
1676    }
1677
1678    /// Create a document with a single field that has a $variant extension
1679    fn create_record_with_variant(
1680        field_name: &str,
1681        value: NodeValue,
1682        variant: &str,
1683    ) -> EureDocument {
1684        let mut doc = EureDocument::new();
1685        let root_id = doc.get_root_id();
1686
1687        // Add field
1688        let field_id = doc
1689            .add_map_child(ObjectKey::String(field_name.to_string()), root_id)
1690            .unwrap()
1691            .node_id;
1692        doc.node_mut(field_id).content = value;
1693
1694        // Add $variant extension
1695        let variant_node_id = doc
1696            .add_extension(identifier("variant"), field_id)
1697            .unwrap()
1698            .node_id;
1699        doc.node_mut(variant_node_id).content =
1700            NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext(variant.to_string())));
1701
1702        doc
1703    }
1704
1705    #[test]
1706    fn test_option_some_tagged() {
1707        let doc = create_record_with_variant(
1708            "value",
1709            NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(42))),
1710            "some",
1711        );
1712        let root_id = doc.get_root_id();
1713        let rec = doc.parse_record(root_id).unwrap();
1714        let value: Option<i32> = rec.parse_field("value").unwrap();
1715        assert_eq!(value, Some(42));
1716    }
1717
1718    #[test]
1719    fn test_option_none_tagged() {
1720        let doc =
1721            create_record_with_variant("value", NodeValue::Primitive(PrimitiveValue::Null), "none");
1722        let root_id = doc.get_root_id();
1723        let rec = doc.parse_record(root_id).unwrap();
1724        let value: Option<i32> = rec.parse_field("value").unwrap();
1725        assert_eq!(value, None);
1726    }
1727
1728    #[test]
1729    fn test_option_some_untagged() {
1730        // Without $variant, non-null value is Some
1731        let doc = eure!({ value = 42 });
1732        let root_id = doc.get_root_id();
1733        let rec = doc.parse_record(root_id).unwrap();
1734        let value: Option<i32> = rec.parse_field("value").unwrap();
1735        assert_eq!(value, Some(42));
1736    }
1737
1738    #[test]
1739    fn test_option_none_untagged() {
1740        // Without $variant, null is None
1741        let doc = eure!({ value = null });
1742        let root_id = doc.get_root_id();
1743        let rec = doc.parse_record(root_id).unwrap();
1744        let value: Option<i32> = rec.parse_field("value").unwrap();
1745        assert_eq!(value, None);
1746    }
1747
1748    #[test]
1749    fn test_result_ok_tagged() {
1750        let doc = create_record_with_variant(
1751            "value",
1752            NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(42))),
1753            "ok",
1754        );
1755        let root_id = doc.get_root_id();
1756        let rec = doc.parse_record(root_id).unwrap();
1757        let value: Result<i32, String> = rec.parse_field("value").unwrap();
1758        assert_eq!(value, Ok(42));
1759    }
1760
1761    #[test]
1762    fn test_result_err_tagged() {
1763        let doc = create_record_with_variant(
1764            "value",
1765            NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext(
1766                "error message".to_string(),
1767            ))),
1768            "err",
1769        );
1770        let root_id = doc.get_root_id();
1771        let rec = doc.parse_record(root_id).unwrap();
1772        let value: Result<i32, String> = rec.parse_field("value").unwrap();
1773        assert_eq!(value, Err("error message".to_string()));
1774    }
1775
1776    #[test]
1777    fn test_nested_result_option_ok_some() {
1778        // $variant: ok.some - Result<Option<i32>, String>
1779        let doc = create_record_with_variant(
1780            "value",
1781            NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(42))),
1782            "ok.some",
1783        );
1784        let root_id = doc.get_root_id();
1785        let rec = doc.parse_record(root_id).unwrap();
1786        let value: Result<Option<i32>, String> = rec.parse_field("value").unwrap();
1787        assert_eq!(value, Ok(Some(42)));
1788    }
1789
1790    #[test]
1791    fn test_nested_result_option_ok_none() {
1792        // $variant: ok.none - Result<Option<i32>, String>
1793        let doc = create_record_with_variant(
1794            "value",
1795            NodeValue::Primitive(PrimitiveValue::Null),
1796            "ok.none",
1797        );
1798        let root_id = doc.get_root_id();
1799        let rec = doc.parse_record(root_id).unwrap();
1800        let value: Result<Option<i32>, String> = rec.parse_field("value").unwrap();
1801        assert_eq!(value, Ok(None));
1802    }
1803
1804    #[test]
1805    fn test_nested_result_option_err() {
1806        // $variant: err - Result<Option<i32>, String>
1807        let doc = create_record_with_variant(
1808            "value",
1809            NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext("error".to_string()))),
1810            "err",
1811        );
1812        let root_id = doc.get_root_id();
1813        let rec = doc.parse_record(root_id).unwrap();
1814        let value: Result<Option<i32>, String> = rec.parse_field("value").unwrap();
1815        assert_eq!(value, Err("error".to_string()));
1816    }
1817
1818    #[test]
1819    fn test_deeply_nested_option_option() {
1820        // $variant: some.some - Option<Option<i32>>
1821        let doc = create_record_with_variant(
1822            "value",
1823            NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(42))),
1824            "some.some",
1825        );
1826        let root_id = doc.get_root_id();
1827        let rec = doc.parse_record(root_id).unwrap();
1828        let value: Option<Option<i32>> = rec.parse_field("value").unwrap();
1829        assert_eq!(value, Some(Some(42)));
1830    }
1831
1832    #[test]
1833    fn test_deeply_nested_option_none() {
1834        // $variant: some.none - Option<Option<i32>> inner None
1835        let doc = create_record_with_variant(
1836            "value",
1837            NodeValue::Primitive(PrimitiveValue::Null),
1838            "some.none",
1839        );
1840        let root_id = doc.get_root_id();
1841        let rec = doc.parse_record(root_id).unwrap();
1842        let value: Option<Option<i32>> = rec.parse_field("value").unwrap();
1843        assert_eq!(value, Some(None));
1844    }
1845
1846    #[test]
1847    fn test_outer_none() {
1848        // $variant: none - Option<Option<i32>> outer None
1849        let doc =
1850            create_record_with_variant("value", NodeValue::Primitive(PrimitiveValue::Null), "none");
1851        let root_id = doc.get_root_id();
1852        let rec = doc.parse_record(root_id).unwrap();
1853        let value: Option<Option<i32>> = rec.parse_field("value").unwrap();
1854        assert_eq!(value, None);
1855    }
1856}