1extern crate alloc;
6
7use alloc::string::{String, ToString};
8use alloc::vec::Vec;
9
10use crate::data_model::VariantRepr;
11use crate::document::node::NodeValue;
12use crate::document::{EureDocument, NodeId};
13use crate::identifier::Identifier;
14use crate::parse::{DocumentParser, FromEure};
15use crate::value::ObjectKey;
16
17use super::variant_path::VariantPath;
18use super::{
19 AccessedSnapshot, FlattenContext, ParseContext, ParseError, ParseErrorKind, ParserScope,
20 UnionTagMode,
21};
22
23pub const VARIANT: Identifier = Identifier::new_unchecked("variant");
25
26pub fn extract_repr_variant(
37 doc: &EureDocument,
38 node_id: NodeId,
39 repr: &VariantRepr,
40) -> Result<Option<(String, NodeId)>, ParseError> {
41 match repr {
42 VariantRepr::Untagged => Ok(None),
43 VariantRepr::External => Ok(try_extract_external(doc, node_id)),
44 VariantRepr::Internal { tag } => try_extract_internal(doc, node_id, tag),
45 VariantRepr::Adjacent { tag, content } => try_extract_adjacent(doc, node_id, tag, content),
46 }
47}
48
49fn try_extract_external(doc: &EureDocument, node_id: NodeId) -> Option<(String, NodeId)> {
51 let node = doc.node(node_id);
52 let NodeValue::Map(map) = &node.content else {
53 return None;
54 };
55
56 if map.len() != 1 {
57 return None;
58 }
59
60 let (key, &content_node_id) = map.iter().next()?;
61 let ObjectKey::String(variant_name) = key else {
62 return None;
63 };
64 Some((variant_name.clone(), content_node_id))
65}
66
67fn try_extract_internal(
71 doc: &EureDocument,
72 node_id: NodeId,
73 tag: &str,
74) -> Result<Option<(String, NodeId)>, ParseError> {
75 let node = doc.node(node_id);
76 let NodeValue::Map(map) = &node.content else {
77 return Ok(None);
78 };
79
80 let tag_key = ObjectKey::String(tag.to_string());
81 let Some(tag_node_id) = map.get(&tag_key) else {
82 return Ok(None);
83 };
84
85 let variant_name: &str = doc.parse(*tag_node_id)?;
86 Ok(Some((variant_name.to_string(), node_id)))
87}
88
89fn try_extract_adjacent(
91 doc: &EureDocument,
92 node_id: NodeId,
93 tag: &str,
94 content: &str,
95) -> Result<Option<(String, NodeId)>, ParseError> {
96 let node = doc.node(node_id);
97 let NodeValue::Map(map) = &node.content else {
98 return Ok(None);
99 };
100
101 let tag_key = ObjectKey::String(tag.to_string());
102 let Some(tag_node_id) = map.get(&tag_key) else {
103 return Ok(None);
104 };
105
106 let variant_name: &str = doc.parse(*tag_node_id)?;
107
108 let content_key = ObjectKey::String(content.to_string());
109 let Some(content_node_id) = map.get(&content_key) else {
110 return Ok(None);
111 };
112
113 Ok(Some((variant_name.to_string(), *content_node_id)))
114}
115
116pub struct UnionParser<'doc, 'ctx, T, E = ParseError> {
156 ctx: &'ctx ParseContext<'doc>,
157 variant: Option<(String, ParseContext<'doc>, Option<VariantPath>)>,
162 variant_result: Option<Result<T, E>>,
164 priority_result: Option<T>,
166 other_results: Vec<(String, T, AccessedSnapshot)>,
169 failures: Vec<(String, E)>,
171 flatten_ctx: Option<FlattenContext>,
173}
174
175impl<'doc, 'ctx, T, E> UnionParser<'doc, 'ctx, T, E>
176where
177 E: From<ParseError>,
178{
179 pub(crate) fn new(
185 ctx: &'ctx ParseContext<'doc>,
186 repr: VariantRepr,
187 ) -> Result<Self, ParseError> {
188 let variant = Self::resolve_variant(ctx, &repr)?;
189
190 let flatten_ctx = ctx.flatten_ctx().cloned();
192 if let Some(ref fc) = flatten_ctx {
193 fc.push_snapshot();
194 }
195
196 Ok(Self {
197 ctx,
198 variant,
199 variant_result: None,
200 priority_result: None,
201 other_results: Vec::new(),
202 failures: Vec::new(),
203 flatten_ctx,
204 })
205 }
206
207 fn resolve_variant(
217 ctx: &ParseContext<'doc>,
218 repr: &VariantRepr,
219 ) -> Result<Option<(String, ParseContext<'doc>, Option<VariantPath>)>, ParseError> {
220 match ctx.union_tag_mode() {
221 UnionTagMode::Eure => Self::resolve_variant_eure_mode(ctx),
222 UnionTagMode::Repr => Self::resolve_variant_repr_mode(ctx, repr),
223 }
224 }
225
226 fn resolve_variant_eure_mode(
233 ctx: &ParseContext<'doc>,
234 ) -> Result<Option<(String, ParseContext<'doc>, Option<VariantPath>)>, ParseError> {
235 let explicit_variant = match ctx.variant_path() {
237 Some(vp) if !vp.is_empty() => Some(vp.clone()),
238 Some(_) => None, None => {
240 let variant = Self::extract_explicit_variant(ctx)?;
241 if variant.is_some() {
242 ctx.accessed().add_ext(VARIANT.clone());
244 }
245 variant
246 }
247 };
248
249 match explicit_variant {
250 Some(ev) => {
252 let name = ev
253 .first()
254 .map(|i| i.as_ref().to_string())
255 .unwrap_or_default();
256 let rest = ev.rest().unwrap_or_else(VariantPath::empty);
257 Ok(Some((name, ctx.clone(), Some(rest))))
258 }
259 None => Ok(None),
261 }
262 }
263
264 fn resolve_variant_repr_mode(
271 ctx: &ParseContext<'doc>,
272 repr: &VariantRepr,
273 ) -> Result<Option<(String, ParseContext<'doc>, Option<VariantPath>)>, ParseError> {
274 let repr_variant = extract_repr_variant(ctx.doc(), ctx.node_id(), repr)?;
276
277 match repr_variant {
278 Some((name, content_node_id)) => {
280 let content_ctx = Self::make_content_context(ctx, repr, content_node_id);
281 Ok(Some((name, content_ctx, Some(VariantPath::empty()))))
282 }
283 None => {
286 Ok(None)
289 }
290 }
291 }
292
293 fn make_content_context(
295 ctx: &ParseContext<'doc>,
296 repr: &VariantRepr,
297 content_node_id: NodeId,
298 ) -> ParseContext<'doc> {
299 match repr {
300 VariantRepr::Internal { tag } => {
303 let flatten_ctx = match ctx.flatten_ctx() {
305 Some(fc) => {
306 fc.add_field(tag);
307 fc.clone()
308 }
309 None => {
310 let fc = super::FlattenContext::new(
311 super::AccessedSet::new(),
312 ParserScope::Record,
313 );
314 fc.add_field(tag);
315 fc
316 }
317 };
318 ParseContext::with_flatten_ctx(
319 ctx.doc(),
320 content_node_id,
321 flatten_ctx,
322 ctx.union_tag_mode(),
323 )
324 }
325 _ => ctx.at(content_node_id),
327 }
328 }
329
330 fn extract_explicit_variant(
332 ctx: &ParseContext<'doc>,
333 ) -> Result<Option<VariantPath>, ParseError> {
334 let node = ctx.node();
335 let Some(&variant_node_id) = node.extensions.get(&VARIANT) else {
336 return Ok(None);
337 };
338
339 let variant_node = ctx.doc().node(variant_node_id);
340 let s: &str = ctx.doc().parse(variant_node_id).map_err(|_| ParseError {
341 node_id: variant_node_id,
342 kind: ParseErrorKind::InvalidVariantType(variant_node.content.value_kind()),
343 })?;
344
345 VariantPath::parse(s).map(Some).map_err(|_| ParseError {
346 node_id: variant_node_id,
347 kind: ParseErrorKind::InvalidVariantPath(s.to_string()),
348 })
349 }
350
351 pub fn variant<P: DocumentParser<'doc, Output = T, Error = E>>(
356 mut self,
357 name: &str,
358 f: P,
359 ) -> Self {
360 self.try_variant(name, f, true);
361 self
362 }
363
364 pub fn parse_variant<V: FromEure<'doc, Error = E>>(
366 mut self,
367 name: &str,
368 mut then: impl FnMut(V) -> Result<T, E>,
369 ) -> Self {
370 self.try_variant(
371 name,
372 move |ctx: &ParseContext<'doc>| {
373 let v = V::parse(ctx)?;
374 then(v)
375 },
376 true,
377 );
378 self
379 }
380
381 pub fn variant_unambiguous<P: DocumentParser<'doc, Output = T, Error = E>>(
387 mut self,
388 name: &str,
389 f: P,
390 ) -> Self {
391 self.try_variant(name, f, false);
392 self
393 }
394
395 pub fn parse_variant_unambiguous<V: FromEure<'doc, Error = E>>(
397 mut self,
398 name: &str,
399 mut then: impl FnMut(V) -> Result<T, E>,
400 ) -> Self {
401 self.try_variant(
402 name,
403 move |ctx: &ParseContext<'doc>| {
404 let v = V::parse(ctx)?;
405 then(v)
406 },
407 false,
408 );
409 self
410 }
411
412 fn try_variant<P: DocumentParser<'doc, Output = T, Error = E>>(
414 &mut self,
415 name: &str,
416 mut f: P,
417 is_priority: bool,
418 ) {
419 if let Some((ref v_name, ref v_ctx, ref rest)) = self.variant {
421 if v_name == name && self.variant_result.is_none() {
422 let child_ctx = v_ctx.with_variant_rest(rest.clone());
423 let result = f.parse(&child_ctx);
424 self.variant_result = Some(result);
427 }
428 return;
429 }
430
431 if self.priority_result.is_some() {
435 return;
436 }
437
438 let child_ctx = self.ctx.with_variant_rest(None);
439 match f.parse(&child_ctx) {
440 Ok(value) => {
441 if is_priority {
442 self.priority_result = Some(value);
445 } else {
446 if let Some(ref fc) = self.flatten_ctx {
449 let captured = fc.capture_current_state();
450 fc.restore_to_current_snapshot();
451 self.other_results.push((name.to_string(), value, captured));
452 } else {
453 self.other_results.push((
455 name.to_string(),
456 value,
457 (Default::default(), Default::default()),
458 ));
459 }
460 }
461 }
462 Err(e) => {
463 if let Some(ref fc) = self.flatten_ctx {
465 fc.restore_to_current_snapshot();
466 }
467 self.failures.push((name.to_string(), e));
468 }
469 }
470 }
471
472 pub fn parse(self) -> Result<T, E> {
474 let node_id = self.ctx.node_id();
475
476 if let Some((v_name, _, _)) = self.variant {
480 let result = self.variant_result.unwrap_or_else(|| {
481 Err(ParseError {
482 node_id,
483 kind: ParseErrorKind::UnknownVariant(v_name),
484 }
485 .into())
486 });
487 if let Some(ref fc) = self.flatten_ctx {
489 match &result {
490 Ok(_) => fc.pop_without_restore(),
491 Err(_) => fc.pop_and_restore(),
492 }
493 }
494 return result;
495 }
496
497 if let Some(value) = self.priority_result {
499 if let Some(ref fc) = self.flatten_ctx {
500 fc.pop_without_restore();
501 }
502 return Ok(value);
503 }
504
505 match self.other_results.len() {
507 0 => {
508 if let Some(ref fc) = self.flatten_ctx {
510 fc.pop_and_restore();
511 }
512 Err(self.no_match_error(node_id))
513 }
514 1 => {
515 let (_, value, captured_state) = self.other_results.into_iter().next().unwrap();
517 if let Some(ref fc) = self.flatten_ctx {
518 fc.restore_to_state(captured_state);
519 fc.pop_without_restore();
520 }
521 Ok(value)
522 }
523 _ => {
524 if let Some(ref fc) = self.flatten_ctx {
526 fc.pop_and_restore();
527 }
528 Err(ParseError {
529 node_id,
530 kind: ParseErrorKind::AmbiguousUnion(
531 self.other_results
532 .into_iter()
533 .map(|(name, _, _)| name)
534 .collect(),
535 ),
536 }
537 .into())
538 }
539 }
540 }
541
542 fn no_match_error(self, node_id: crate::document::NodeId) -> E {
544 self.failures
545 .into_iter()
546 .next()
547 .map(|(_, e)| e)
548 .unwrap_or_else(|| {
549 ParseError {
550 node_id,
551 kind: ParseErrorKind::NoMatchingVariant { variant: None },
552 }
553 .into()
554 })
555 }
556}
557
558#[cfg(test)]
559mod tests {
560 use super::*;
561 use crate::eure;
562 use crate::parse::AlwaysParser;
563 use crate::parse::DocumentParserExt as _;
564
565 #[derive(Debug, PartialEq, Clone)]
566 enum TestEnum {
567 Foo,
568 Bar,
569 }
570
571 #[test]
572 fn test_union_single_match() {
573 let doc = eure!({ = "foo" });
574 let root_id = doc.get_root_id();
575 let ctx = doc.parse_context(root_id);
576
577 let result: TestEnum = ctx
578 .parse_union(VariantRepr::default())
579 .unwrap()
580 .variant("foo", |ctx: &ParseContext<'_>| {
581 let s: &str = ctx.parse()?;
582 if s == "foo" {
583 Ok(TestEnum::Foo)
584 } else {
585 Err(ParseError {
586 node_id: ctx.node_id(),
587 kind: ParseErrorKind::UnknownVariant(s.to_string()),
588 })
589 }
590 })
591 .variant("bar", |ctx: &ParseContext<'_>| {
592 let s: &str = ctx.parse()?;
593 if s == "bar" {
594 Ok(TestEnum::Bar)
595 } else {
596 Err(ParseError {
597 node_id: ctx.node_id(),
598 kind: ParseErrorKind::UnknownVariant(s.to_string()),
599 })
600 }
601 })
602 .parse()
603 .unwrap();
604
605 assert_eq!(result, TestEnum::Foo);
606 }
607
608 #[test]
609 fn test_union_priority_short_circuit() {
610 let doc = eure!({ = "value" });
611 let root_id = doc.get_root_id();
612 let ctx = doc.parse_context(root_id);
613
614 let result: String = ctx
616 .parse_union(VariantRepr::default())
617 .unwrap()
618 .variant("first", String::parse)
619 .variant("second", String::parse)
620 .parse()
621 .unwrap();
622
623 assert_eq!(result, "value");
624 }
625
626 #[test]
627 fn test_union_no_match() {
628 let doc = eure!({ = "baz" });
629 let root_id = doc.get_root_id();
630 let ctx = doc.parse_context(root_id);
631
632 let result: Result<TestEnum, ParseError> = ctx
633 .parse_union(VariantRepr::default())
634 .unwrap()
635 .variant("foo", |ctx: &ParseContext<'_>| {
636 let s: &str = ctx.parse()?;
637 if s == "foo" {
638 Ok(TestEnum::Foo)
639 } else {
640 Err(ParseError {
641 node_id: ctx.node_id(),
642 kind: ParseErrorKind::UnknownVariant(s.to_string()),
643 })
644 }
645 })
646 .parse();
647
648 assert!(result.is_err());
649 }
650
651 #[test]
654 fn test_variant_extension_match_success() {
655 let doc = eure!({ %variant = "baz", = "anything" });
658 let root_id = doc.get_root_id();
659 let ctx = doc.parse_context(root_id);
660
661 let result: TestEnum = ctx
662 .parse_union(VariantRepr::default())
663 .unwrap()
664 .variant(
665 "foo",
666 AlwaysParser::<TestEnum, ParseError>::new(TestEnum::Foo),
667 )
668 .variant_unambiguous("baz", AlwaysParser::new(TestEnum::Bar))
669 .parse()
670 .unwrap();
671
672 assert_eq!(result, TestEnum::Bar);
673 }
674
675 #[test]
676 fn test_variant_extension_unknown() {
677 let doc = eure!({ %variant = "unknown", = "anything" });
680 let root_id = doc.get_root_id();
681 let ctx = doc.parse_context(root_id);
682
683 let err: ParseError = ctx
684 .parse_union(VariantRepr::default())
685 .unwrap()
686 .variant("foo", AlwaysParser::new(TestEnum::Foo))
687 .variant_unambiguous("baz", AlwaysParser::new(TestEnum::Bar))
688 .parse()
689 .unwrap_err();
690
691 assert_eq!(err.node_id, root_id);
692 assert_eq!(
693 err.kind,
694 ParseErrorKind::UnknownVariant("unknown".to_string())
695 );
696 }
697
698 #[test]
699 fn test_variant_extension_match_parse_failure() {
700 let doc = eure!({ %variant = "baz", = "anything" });
702 let root_id = doc.get_root_id();
703 let ctx = doc.parse_context(root_id);
704
705 let err = ctx
706 .parse_union(VariantRepr::default())
707 .unwrap()
708 .variant("foo", AlwaysParser::new(TestEnum::Foo))
709 .variant_unambiguous("baz", |ctx: &ParseContext<'_>| {
710 Err(ParseError {
711 node_id: ctx.node_id(),
712 kind: ParseErrorKind::MissingField("test".to_string()),
713 })
714 })
715 .parse()
716 .unwrap_err();
717
718 assert_eq!(err.node_id, root_id);
720 assert_eq!(err.kind, ParseErrorKind::MissingField("test".to_string()));
721 }
722
723 #[derive(Debug, PartialEq, Clone)]
726 enum Outer {
727 A(Inner),
728 B(i32),
729 }
730
731 #[derive(Debug, PartialEq, Clone)]
732 enum Inner {
733 X,
734 Y,
735 }
736
737 fn parse_inner(ctx: &ParseContext<'_>) -> Result<Inner, ParseError> {
738 ctx.parse_union(VariantRepr::default())
739 .unwrap()
740 .variant("x", AlwaysParser::new(Inner::X))
741 .variant("y", AlwaysParser::new(Inner::Y))
742 .parse()
743 }
744
745 #[test]
746 fn test_variant_nested_single_segment() {
747 let doc = eure!({ %variant = "a", = "value" });
749 let root_id = doc.get_root_id();
750 let ctx = doc.parse_context(root_id);
751
752 let result: Outer = ctx
753 .parse_union(VariantRepr::default())
754 .unwrap()
755 .variant("a", parse_inner.map(Outer::A))
756 .variant("b", AlwaysParser::new(Outer::B(42)))
757 .parse()
758 .unwrap();
759
760 assert_eq!(result, Outer::A(Inner::X));
761 }
762
763 #[test]
764 fn test_variant_nested_multi_segment() {
765 let doc = eure!({ %variant = "a.y", = "value" });
767 let root_id = doc.get_root_id();
768 let ctx = doc.parse_context(root_id);
769
770 let result: Outer = ctx
771 .parse_union(VariantRepr::default())
772 .unwrap()
773 .variant("a", parse_inner.map(Outer::A))
774 .variant("b", AlwaysParser::new(Outer::B(42)))
775 .parse()
776 .unwrap();
777
778 assert_eq!(result, Outer::A(Inner::Y));
779 }
780
781 #[test]
782 fn test_variant_nested_invalid_inner() {
783 let doc = eure!({ %variant = "a.z", = "value" });
785 let root_id = doc.get_root_id();
786 let ctx = doc.parse_context(root_id);
787
788 let err = ctx
789 .parse_union(VariantRepr::default())
790 .unwrap()
791 .variant("a", parse_inner.map(Outer::A))
792 .variant("b", AlwaysParser::new(Outer::B(42)))
793 .parse()
794 .unwrap_err();
795
796 assert_eq!(err.kind, ParseErrorKind::UnknownVariant("z".to_string()));
797 }
798
799 #[test]
800 fn test_variant_non_nested_with_nested_path() {
801 let doc = eure!({ %variant = "b.x", = "value" });
805 let root_id = doc.get_root_id();
806 let ctx = doc.parse_context(root_id);
807
808 let err = ctx
813 .parse_union(VariantRepr::default())
814 .unwrap()
815 .variant("a", parse_inner.map(Outer::A))
816 .variant("b", |ctx: &ParseContext<'_>| {
817 ctx.parse_primitive()?;
819 Ok(Outer::B(42))
820 })
821 .parse()
822 .unwrap_err();
823
824 assert!(matches!(err.kind, ParseErrorKind::UnexpectedVariantPath(_)));
826 }
827
828 use crate::value::ValueKind;
831
832 #[test]
833 fn test_invalid_variant_type_errors() {
834 use crate::document::node::NodeValue;
837 use crate::value::PrimitiveValue;
838 use num_bigint::BigInt;
839
840 let mut doc = eure!({ = "foo" });
842 let root_id = doc.get_root_id();
843 let variant_node_id = doc
844 .add_extension("variant".parse().unwrap(), root_id)
845 .unwrap()
846 .node_id;
847 doc.node_mut(variant_node_id).content =
848 NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(123)));
849
850 let ctx = doc.parse_context(root_id);
851
852 let Err(err) = ctx.parse_union::<TestEnum, ParseError>(VariantRepr::default()) else {
853 panic!("Expected error");
854 };
855 assert_eq!(
856 err,
857 ParseError {
858 node_id: variant_node_id,
859 kind: ParseErrorKind::InvalidVariantType(ValueKind::Integer),
860 }
861 );
862 }
863
864 #[test]
865 fn test_invalid_variant_path_syntax_errors() {
866 let doc = eure!({ %variant = "foo..bar", = "foo" });
868 let root_id = doc.get_root_id();
869 let variant_node_id = *doc.node(root_id).extensions.get(&VARIANT).unwrap();
870 let ctx = doc.parse_context(root_id);
871
872 let Err(err) = ctx.parse_union::<TestEnum, ParseError>(VariantRepr::default()) else {
873 panic!("Expected error");
874 };
875 assert_eq!(
876 err,
877 ParseError {
878 node_id: variant_node_id,
879 kind: ParseErrorKind::InvalidVariantPath("foo..bar".to_string()),
880 }
881 );
882 }
883
884 #[derive(Debug, PartialEq)]
887 enum ReprTestEnum {
888 A { value: i64 },
889 B { name: String },
890 }
891
892 fn parse_repr_test_enum(
893 ctx: &ParseContext<'_>,
894 repr: VariantRepr,
895 ) -> Result<ReprTestEnum, ParseError> {
896 ctx.parse_union(repr)?
897 .variant("a", |ctx: &ParseContext<'_>| {
898 let rec = ctx.parse_record()?;
899 let value: i64 = rec.parse_field("value")?;
900 rec.deny_unknown_fields()?;
901 Ok(ReprTestEnum::A { value })
902 })
903 .variant("b", |ctx: &ParseContext<'_>| {
904 let rec = ctx.parse_record()?;
905 let name: String = rec.parse_field("name")?;
906 rec.deny_unknown_fields()?;
907 Ok(ReprTestEnum::B { name })
908 })
909 .parse()
910 }
911
912 #[test]
913 fn test_internal_repr_success() {
914 let doc = eure!({ type = "a", value = 42 });
917 let root_id = doc.get_root_id();
918 let ctx = ParseContext::with_union_tag_mode(&doc, root_id, UnionTagMode::Repr);
919
920 let result = parse_repr_test_enum(
921 &ctx,
922 VariantRepr::Internal {
923 tag: "type".to_string(),
924 },
925 );
926 assert_eq!(result.unwrap(), ReprTestEnum::A { value: 42 });
927 }
928
929 #[test]
930 fn test_external_repr_success() {
931 let doc = eure!({ a { value = 42 } });
934 let root_id = doc.get_root_id();
935 let ctx = ParseContext::with_union_tag_mode(&doc, root_id, UnionTagMode::Repr);
936
937 let result = parse_repr_test_enum(&ctx, VariantRepr::External);
938 assert_eq!(result.unwrap(), ReprTestEnum::A { value: 42 });
939 }
940
941 #[test]
942 fn test_adjacent_repr_success() {
943 let doc = eure!({ type = "a", content { value = 42 } });
946 let root_id = doc.get_root_id();
947 let ctx = ParseContext::with_union_tag_mode(&doc, root_id, UnionTagMode::Repr);
948
949 let result = parse_repr_test_enum(
950 &ctx,
951 VariantRepr::Adjacent {
952 tag: "type".to_string(),
953 content: "content".to_string(),
954 },
955 );
956 assert_eq!(result.unwrap(), ReprTestEnum::A { value: 42 });
957 }
958
959 #[test]
960 fn test_repr_mode_ignores_variant_extension() {
961 let doc = eure!({ %variant = "b", type = "a", value = 42 });
964 let root_id = doc.get_root_id();
965 let ctx = ParseContext::with_union_tag_mode(&doc, root_id, UnionTagMode::Repr);
966
967 let result = parse_repr_test_enum(
968 &ctx,
969 VariantRepr::Internal {
970 tag: "type".to_string(),
971 },
972 );
973 assert_eq!(result.unwrap(), ReprTestEnum::A { value: 42 });
974 }
975
976 #[test]
977 fn test_eure_mode_ignores_repr() {
978 let doc = eure!({ type = "a", value = 42 });
980 let root_id = doc.get_root_id();
981
982 let ctx = doc.parse_context(root_id);
984
985 let result = ctx
988 .parse_union::<_, ParseError>(VariantRepr::Internal {
989 tag: "type".to_string(),
990 })
991 .unwrap()
992 .variant("a", |ctx: &ParseContext<'_>| {
993 let rec = ctx.parse_record()?;
994 let value: i64 = rec.parse_field("value")?;
995 rec.allow_unknown_fields()?;
996 Ok(ReprTestEnum::A { value })
997 })
998 .variant("b", |ctx: &ParseContext<'_>| {
999 let rec = ctx.parse_record()?;
1000 let name: String = rec.parse_field("name")?;
1001 rec.deny_unknown_fields()?;
1002 Ok(ReprTestEnum::B { name })
1003 })
1004 .parse();
1005
1006 assert_eq!(result.unwrap(), ReprTestEnum::A { value: 42 });
1007 }
1008
1009 #[test]
1010 fn test_internal_repr_unknown_variant_name() {
1011 let doc = eure!({ type = "unknown", value = 42 });
1014 let root_id = doc.get_root_id();
1015 let ctx = ParseContext::with_union_tag_mode(&doc, root_id, UnionTagMode::Repr);
1016
1017 let result = parse_repr_test_enum(
1018 &ctx,
1019 VariantRepr::Internal {
1020 tag: "type".to_string(),
1021 },
1022 );
1023
1024 let err = result.unwrap_err();
1026 assert_eq!(
1027 err.kind,
1028 ParseErrorKind::UnknownVariant("unknown".to_string())
1029 );
1030 }
1031
1032 #[test]
1033 fn test_repr_not_extracted_falls_back_to_untagged() {
1034 let doc = eure!({ value = 100, extra = "ignored" });
1037 let root_id = doc.get_root_id();
1038 let ctx = doc.parse_context(root_id);
1039
1040 let result = ctx
1042 .parse_union::<_, ParseError>(VariantRepr::External)
1043 .unwrap()
1044 .variant("a", |ctx: &ParseContext<'_>| {
1045 let rec = ctx.parse_record()?;
1046 let value: i64 = rec.parse_field("value")?;
1047 Ok(ReprTestEnum::A { value })
1049 })
1050 .variant("b", |ctx: &ParseContext<'_>| {
1051 let rec = ctx.parse_record()?;
1052 let name: String = rec.parse_field("name")?;
1053 rec.deny_unknown_fields()?;
1054 Ok(ReprTestEnum::B { name })
1055 })
1056 .parse();
1057
1058 assert_eq!(result.unwrap(), ReprTestEnum::A { value: 100 });
1060 }
1061
1062 #[test]
1063 fn test_external_repr_single_key_extracts_variant() {
1064 let doc = eure!({ value = 100 });
1067 let root_id = doc.get_root_id();
1068 let ctx = ParseContext::with_union_tag_mode(&doc, root_id, UnionTagMode::Repr);
1069
1070 let err: ParseError = ctx
1073 .parse_union(VariantRepr::External)
1074 .unwrap()
1075 .variant("a", |ctx: &ParseContext<'_>| {
1076 let rec = ctx.parse_record()?;
1077 let value: i64 = rec.parse_field("value")?;
1078 rec.deny_unknown_fields()?;
1079 Ok(ReprTestEnum::A { value })
1080 })
1081 .variant("b", |ctx: &ParseContext<'_>| {
1082 let rec = ctx.parse_record()?;
1083 let name: String = rec.parse_field("name")?;
1084 rec.deny_unknown_fields()?;
1085 Ok(ReprTestEnum::B { name })
1086 })
1087 .parse()
1088 .unwrap_err();
1089
1090 assert_eq!(
1091 err.kind,
1092 ParseErrorKind::UnknownVariant("value".to_string())
1093 );
1094 }
1095
1096 #[test]
1099 fn test_internal_repr_tag_is_integer_errors() {
1100 use crate::document::EureDocument;
1105 use crate::document::node::NodeValue;
1106 use crate::value::{ObjectKey, PrimitiveValue};
1107 use num_bigint::BigInt;
1108
1109 let mut doc = EureDocument::new();
1110 let root_id = doc.get_root_id();
1111 doc.node_mut(root_id).content = NodeValue::Map(Default::default());
1112
1113 let type_node_id = doc
1114 .add_map_child(ObjectKey::String("type".to_string()), root_id)
1115 .unwrap()
1116 .node_id;
1117 doc.node_mut(type_node_id).content =
1118 NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(123)));
1119
1120 let value_node_id = doc
1121 .add_map_child(ObjectKey::String("value".to_string()), root_id)
1122 .unwrap()
1123 .node_id;
1124 doc.node_mut(value_node_id).content =
1125 NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(42)));
1126
1127 let ctx = ParseContext::with_union_tag_mode(&doc, root_id, UnionTagMode::Repr);
1128
1129 let Err(err) = ctx.parse_union::<ReprTestEnum, ParseError>(VariantRepr::Internal {
1131 tag: "type".to_string(),
1132 }) else {
1133 panic!("Expected error");
1134 };
1135
1136 assert_eq!(err.node_id, type_node_id);
1138 }
1139
1140 #[test]
1141 fn test_adjacent_repr_missing_content_falls_back_to_untagged() {
1142 let doc = eure!({ type = "a", value = 42 });
1145 let root_id = doc.get_root_id();
1146 let ctx = doc.parse_context(root_id);
1147
1148 let result = ctx
1150 .parse_union::<_, ParseError>(VariantRepr::Adjacent {
1151 tag: "type".to_string(),
1152 content: "content".to_string(),
1153 })
1154 .unwrap()
1155 .variant("a", |ctx: &ParseContext<'_>| {
1156 let rec = ctx.parse_record()?;
1157 let value: i64 = rec.parse_field("value")?;
1158 Ok(ReprTestEnum::A { value })
1160 })
1161 .variant("b", |ctx: &ParseContext<'_>| {
1162 let rec = ctx.parse_record()?;
1163 let name: String = rec.parse_field("name")?;
1164 rec.deny_unknown_fields()?;
1165 Ok(ReprTestEnum::B { name })
1166 })
1167 .parse();
1168
1169 assert_eq!(result.unwrap(), ReprTestEnum::A { value: 42 });
1171 }
1172
1173 #[test]
1174 fn test_external_repr_non_string_key_falls_back_to_untagged() {
1175 use crate::document::EureDocument;
1178 use crate::document::node::NodeValue;
1179 use crate::value::{ObjectKey, PrimitiveValue};
1180 use num_bigint::BigInt;
1181
1182 let mut doc = EureDocument::new();
1183 let root_id = doc.get_root_id();
1184 doc.node_mut(root_id).content = NodeValue::Map(Default::default());
1185
1186 let variant_node_id = doc
1188 .add_map_child(ObjectKey::Number(BigInt::from(123)), root_id)
1189 .unwrap()
1190 .node_id;
1191 doc.node_mut(variant_node_id).content = NodeValue::Map(Default::default());
1192
1193 let value_node_id = doc
1195 .add_map_child(ObjectKey::String("value".to_string()), variant_node_id)
1196 .unwrap()
1197 .node_id;
1198 doc.node_mut(value_node_id).content =
1199 NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(42)));
1200
1201 let ctx = doc.parse_context(root_id);
1202
1203 let err: ParseError = ctx
1207 .parse_union(VariantRepr::External)
1208 .unwrap()
1209 .variant("a", |ctx: &ParseContext<'_>| {
1210 let rec = ctx.parse_record()?;
1211 let value: i64 = rec.parse_field("value")?;
1212 rec.deny_unknown_fields()?;
1213 Ok(ReprTestEnum::A { value })
1214 })
1215 .variant("b", |ctx: &ParseContext<'_>| {
1216 let rec = ctx.parse_record()?;
1217 let name: String = rec.parse_field("name")?;
1218 rec.deny_unknown_fields()?;
1219 Ok(ReprTestEnum::B { name })
1220 })
1221 .parse()
1222 .unwrap_err();
1223
1224 assert_eq!(err.kind, ParseErrorKind::MissingField("value".to_string()));
1226 }
1227
1228 #[test]
1229 fn test_eure_mode_uses_variant_extension_over_repr() {
1230 let doc = eure!({ %variant = "b", type = "a", value = 42 });
1233 let root_id = doc.get_root_id();
1234 let ctx = doc.parse_context(root_id);
1235
1236 let err: ParseError = ctx
1239 .parse_union(VariantRepr::Internal {
1240 tag: "type".to_string(),
1241 })
1242 .unwrap()
1243 .variant("a", |ctx: &ParseContext<'_>| {
1244 let rec = ctx.parse_record()?;
1245 let value: i64 = rec.parse_field("value")?;
1246 rec.allow_unknown_fields()?;
1247 Ok(ReprTestEnum::A { value })
1248 })
1249 .variant("b", |ctx: &ParseContext<'_>| {
1250 let rec = ctx.parse_record()?;
1251 let name: String = rec.parse_field("name")?;
1252 rec.deny_unknown_fields()?;
1253 Ok(ReprTestEnum::B { name })
1254 })
1255 .parse()
1256 .unwrap_err();
1257
1258 assert_eq!(err.kind, ParseErrorKind::MissingField("name".to_string()));
1260 }
1261
1262 #[test]
1263 fn test_variant_path_empty_uses_untagged() {
1264 let doc = eure!({ = "value" });
1267 let root_id = doc.get_root_id();
1268 let ctx = doc.parse_context(root_id);
1269
1270 let child_ctx = ctx.with_variant_rest(Some(VariantPath::empty()));
1272
1273 let result: String = child_ctx
1275 .parse_union(VariantRepr::default())
1276 .unwrap()
1277 .variant("first", String::parse)
1278 .variant("second", String::parse)
1279 .parse()
1280 .unwrap();
1281
1282 assert_eq!(result, "value");
1284 }
1285
1286 #[derive(Debug, PartialEq, Clone)]
1292 enum OuterUnion {
1293 Normal(InnerUnion),
1294 List(Vec<InnerUnion>),
1295 }
1296
1297 #[derive(Debug, PartialEq, Clone)]
1299 enum InnerUnion {
1300 Text(String),
1301 Number(i64),
1302 }
1303
1304 fn parse_inner_union(ctx: &ParseContext<'_>) -> Result<InnerUnion, ParseError> {
1305 ctx.parse_union(VariantRepr::default())?
1306 .variant("text", |ctx: &ParseContext<'_>| {
1307 let s: String = ctx.parse()?;
1308 Ok(InnerUnion::Text(s))
1309 })
1310 .variant("number", |ctx: &ParseContext<'_>| {
1311 let n: i64 = ctx.parse()?;
1312 Ok(InnerUnion::Number(n))
1313 })
1314 .parse()
1315 }
1316
1317 fn parse_outer_union(ctx: &ParseContext<'_>) -> Result<OuterUnion, ParseError> {
1318 use crate::document::node::NodeArray;
1319
1320 ctx.parse_union(VariantRepr::default())?
1321 .variant("normal", |ctx: &ParseContext<'_>| {
1322 let inner = parse_inner_union(ctx)?;
1323 Ok(OuterUnion::Normal(inner))
1324 })
1325 .variant("list", |ctx: &ParseContext<'_>| {
1326 let arr: &NodeArray = ctx.parse()?;
1328 let items: Result<Vec<InnerUnion>, _> = arr
1329 .iter()
1330 .map(|&node_id| parse_inner_union(&ctx.at(node_id)))
1331 .collect();
1332 Ok(OuterUnion::List(items?))
1333 })
1334 .parse()
1335 }
1336
1337 #[test]
1338 fn test_nested_union_basic_text() {
1339 let doc = eure!({ = "hello" });
1341 let root_id = doc.get_root_id();
1342 let ctx = doc.parse_context(root_id);
1343
1344 let result = parse_outer_union(&ctx).unwrap();
1345 assert_eq!(
1346 result,
1347 OuterUnion::Normal(InnerUnion::Text("hello".to_string()))
1348 );
1349 }
1350
1351 #[test]
1352 fn test_nested_union_basic_number() {
1353 let doc = eure!({ = 42 });
1354 let root_id = doc.get_root_id();
1355 let ctx = doc.parse_context(root_id);
1356 let result = parse_outer_union(&ctx).unwrap();
1357 assert_eq!(result, OuterUnion::Normal(InnerUnion::Number(42)));
1358 }
1359
1360 #[test]
1361 fn test_nested_union_variant_path_propagation() {
1362 let doc = eure!({ %variant = "normal.text", = "test value" });
1364 let root_id = doc.get_root_id();
1365 let ctx = doc.parse_context(root_id);
1366
1367 let result = parse_outer_union(&ctx).unwrap();
1368 assert_eq!(
1369 result,
1370 OuterUnion::Normal(InnerUnion::Text("test value".to_string()))
1371 );
1372 }
1373
1374 #[test]
1375 fn test_nested_union_variant_path_number() {
1376 let doc = eure!({ %variant = "normal.number", = 99 });
1378 let root_id = doc.get_root_id();
1379 let ctx = doc.parse_context(root_id);
1380 let result = parse_outer_union(&ctx).unwrap();
1381 assert_eq!(result, OuterUnion::Normal(InnerUnion::Number(99)));
1382 }
1383
1384 #[test]
1385 fn test_nested_union_inner_fails_outer_recovers() {
1386 let doc = eure!({ = ["a", "b"] });
1390 let root_id = doc.get_root_id();
1391 let ctx = doc.parse_context(root_id);
1392
1393 let result = parse_outer_union(&ctx).unwrap();
1394 assert_eq!(
1395 result,
1396 OuterUnion::List(alloc::vec![
1397 InnerUnion::Text("a".to_string()),
1398 InnerUnion::Text("b".to_string()),
1399 ])
1400 );
1401 }
1402
1403 #[derive(Debug, PartialEq, Clone)]
1408 enum Level1 {
1409 A(Level2Union),
1410 B(String),
1411 }
1412
1413 #[derive(Debug, PartialEq, Clone)]
1414 enum Level2Union {
1415 X(Level3),
1416 Y(i64),
1417 }
1418
1419 #[derive(Debug, PartialEq, Clone)]
1420 enum Level3 {
1421 Leaf(String),
1422 }
1423
1424 fn parse_level3(ctx: &ParseContext<'_>) -> Result<Level3, ParseError> {
1425 ctx.parse_union(VariantRepr::default())?
1426 .variant("leaf", |ctx: &ParseContext<'_>| {
1427 let s: String = ctx.parse()?;
1428 Ok(Level3::Leaf(s))
1429 })
1430 .parse()
1431 }
1432
1433 fn parse_level2(ctx: &ParseContext<'_>) -> Result<Level2Union, ParseError> {
1434 ctx.parse_union(VariantRepr::default())?
1435 .variant("x", |ctx: &ParseContext<'_>| {
1436 let inner = parse_level3(ctx)?;
1437 Ok(Level2Union::X(inner))
1438 })
1439 .variant("y", |ctx: &ParseContext<'_>| {
1440 let n: i64 = ctx.parse()?;
1441 Ok(Level2Union::Y(n))
1442 })
1443 .parse()
1444 }
1445
1446 fn parse_level1(ctx: &ParseContext<'_>) -> Result<Level1, ParseError> {
1447 ctx.parse_union(VariantRepr::default())?
1448 .variant("a", |ctx: &ParseContext<'_>| {
1449 let inner = parse_level2(ctx)?;
1450 Ok(Level1::A(inner))
1451 })
1452 .variant("b", |ctx: &ParseContext<'_>| {
1453 let s: String = ctx.parse()?;
1454 Ok(Level1::B(s))
1455 })
1456 .parse()
1457 }
1458
1459 #[test]
1460 fn test_nested_union_three_levels_untagged() {
1461 let doc = eure!({ = "deep value" });
1464 let root_id = doc.get_root_id();
1465 let ctx = doc.parse_context(root_id);
1466
1467 let result = parse_level1(&ctx).unwrap();
1468 assert_eq!(
1469 result,
1470 Level1::A(Level2Union::X(Level3::Leaf("deep value".to_string())))
1471 );
1472 }
1473
1474 #[test]
1475 fn test_nested_union_three_levels_variant_path() {
1476 let doc = eure!({ %variant = "a.x.leaf", = "explicit deep" });
1478 let root_id = doc.get_root_id();
1479 let ctx = doc.parse_context(root_id);
1480
1481 let result = parse_level1(&ctx).unwrap();
1482 assert_eq!(
1483 result,
1484 Level1::A(Level2Union::X(Level3::Leaf("explicit deep".to_string())))
1485 );
1486 }
1487
1488 #[test]
1489 fn test_nested_union_three_levels_variant_path_partial() {
1490 let doc = eure!({ %variant = "a.y", = 123 });
1492 let root_id = doc.get_root_id();
1493 let ctx = doc.parse_context(root_id);
1494 let result = parse_level1(&ctx).unwrap();
1495 assert_eq!(result, Level1::A(Level2Union::Y(123)));
1496 }
1497
1498 #[test]
1499 fn test_nested_union_invalid_inner_variant_path() {
1500 let doc = eure!({ %variant = "a.x.invalid", = "value" });
1502 let root_id = doc.get_root_id();
1503 let ctx = doc.parse_context(root_id);
1504
1505 let err = parse_level1(&ctx).unwrap_err();
1506 assert_eq!(
1507 err.kind,
1508 ParseErrorKind::UnknownVariant("invalid".to_string())
1509 );
1510 }
1511
1512 #[test]
1517 fn test_flatten_nested_union_accessed_fields_basic() {
1518 use crate::parse::AccessedSet;
1519 use crate::parse::FlattenContext;
1520 use crate::parse::ParserScope;
1521
1522 let doc = eure!({
1524 field_a = "value_a"
1525 field_b = "value_b"
1526 });
1527 let root_id = doc.get_root_id();
1528
1529 let flatten_ctx = FlattenContext::new(AccessedSet::new(), ParserScope::Record);
1531 let ctx =
1532 ParseContext::with_flatten_ctx(&doc, root_id, flatten_ctx.clone(), UnionTagMode::Eure);
1533
1534 let record = ctx.parse_record().unwrap();
1536 let _field_a: String = record.parse_field("field_a").unwrap();
1537
1538 let (accessed, _) = flatten_ctx.capture_current_state();
1540 assert!(accessed.contains("field_a"));
1541 assert!(!accessed.contains("field_b"));
1542 }
1543}