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(
343 variant_node
344 .content
345 .value_kind()
346 .unwrap_or(crate::value::ValueKind::Null),
347 ),
348 })?;
349
350 VariantPath::parse(s).map(Some).map_err(|_| ParseError {
351 node_id: variant_node_id,
352 kind: ParseErrorKind::InvalidVariantPath(s.to_string()),
353 })
354 }
355
356 pub fn variant<P: DocumentParser<'doc, Output = T, Error = E>>(
361 mut self,
362 name: &str,
363 f: P,
364 ) -> Self {
365 self.try_variant(name, f, true);
366 self
367 }
368
369 pub fn parse_variant<V: FromEure<'doc, Error = E>>(
371 mut self,
372 name: &str,
373 mut then: impl FnMut(V) -> Result<T, E>,
374 ) -> Self {
375 self.try_variant(
376 name,
377 move |ctx: &ParseContext<'doc>| {
378 let v = V::parse(ctx)?;
379 then(v)
380 },
381 true,
382 );
383 self
384 }
385
386 pub fn variant_unambiguous<P: DocumentParser<'doc, Output = T, Error = E>>(
392 mut self,
393 name: &str,
394 f: P,
395 ) -> Self {
396 self.try_variant(name, f, false);
397 self
398 }
399
400 pub fn parse_variant_unambiguous<V: FromEure<'doc, Error = E>>(
402 mut self,
403 name: &str,
404 mut then: impl FnMut(V) -> Result<T, E>,
405 ) -> Self {
406 self.try_variant(
407 name,
408 move |ctx: &ParseContext<'doc>| {
409 let v = V::parse(ctx)?;
410 then(v)
411 },
412 false,
413 );
414 self
415 }
416
417 fn try_variant<P: DocumentParser<'doc, Output = T, Error = E>>(
419 &mut self,
420 name: &str,
421 mut f: P,
422 is_priority: bool,
423 ) {
424 if let Some((ref v_name, ref v_ctx, ref rest)) = self.variant {
426 if v_name == name && self.variant_result.is_none() {
427 let child_ctx = v_ctx.with_variant_rest(rest.clone());
428 let result = f.parse(&child_ctx);
429 self.variant_result = Some(result);
432 }
433 return;
434 }
435
436 if self.priority_result.is_some() {
440 return;
441 }
442
443 let child_ctx = self.ctx.with_variant_rest(None);
444 match f.parse(&child_ctx) {
445 Ok(value) => {
446 if is_priority {
447 self.priority_result = Some(value);
450 } else {
451 if let Some(ref fc) = self.flatten_ctx {
454 let captured = fc.capture_current_state();
455 fc.restore_to_current_snapshot();
456 self.other_results.push((name.to_string(), value, captured));
457 } else {
458 self.other_results.push((
460 name.to_string(),
461 value,
462 (Default::default(), Default::default()),
463 ));
464 }
465 }
466 }
467 Err(e) => {
468 if let Some(ref fc) = self.flatten_ctx {
470 fc.restore_to_current_snapshot();
471 }
472 self.failures.push((name.to_string(), e));
473 }
474 }
475 }
476
477 pub fn parse(self) -> Result<T, E> {
479 let node_id = self.ctx.node_id();
480
481 if let Some((v_name, _, _)) = self.variant {
485 let result = self.variant_result.unwrap_or_else(|| {
486 Err(ParseError {
487 node_id,
488 kind: ParseErrorKind::UnknownVariant(v_name),
489 }
490 .into())
491 });
492 if let Some(ref fc) = self.flatten_ctx {
494 match &result {
495 Ok(_) => fc.pop_without_restore(),
496 Err(_) => fc.pop_and_restore(),
497 }
498 }
499 return result;
500 }
501
502 if let Some(value) = self.priority_result {
504 if let Some(ref fc) = self.flatten_ctx {
505 fc.pop_without_restore();
506 }
507 return Ok(value);
508 }
509
510 match self.other_results.len() {
512 0 => {
513 if let Some(ref fc) = self.flatten_ctx {
515 fc.pop_and_restore();
516 }
517 Err(self.no_match_error(node_id))
518 }
519 1 => {
520 let (_, value, captured_state) = self.other_results.into_iter().next().unwrap();
522 if let Some(ref fc) = self.flatten_ctx {
523 fc.restore_to_state(captured_state);
524 fc.pop_without_restore();
525 }
526 Ok(value)
527 }
528 _ => {
529 if let Some(ref fc) = self.flatten_ctx {
531 fc.pop_and_restore();
532 }
533 Err(ParseError {
534 node_id,
535 kind: ParseErrorKind::AmbiguousUnion(
536 self.other_results
537 .into_iter()
538 .map(|(name, _, _)| name)
539 .collect(),
540 ),
541 }
542 .into())
543 }
544 }
545 }
546
547 fn no_match_error(self, node_id: crate::document::NodeId) -> E {
549 self.failures
550 .into_iter()
551 .next()
552 .map(|(_, e)| e)
553 .unwrap_or_else(|| {
554 ParseError {
555 node_id,
556 kind: ParseErrorKind::NoMatchingVariant { variant: None },
557 }
558 .into()
559 })
560 }
561}
562
563#[cfg(test)]
564mod tests {
565 use super::*;
566 use crate::eure;
567 use crate::parse::AlwaysParser;
568 use crate::parse::DocumentParserExt as _;
569
570 #[derive(Debug, PartialEq, Clone)]
571 enum TestEnum {
572 Foo,
573 Bar,
574 }
575
576 #[test]
577 fn test_union_single_match() {
578 let doc = eure!({ = "foo" });
579 let root_id = doc.get_root_id();
580 let ctx = doc.parse_context(root_id);
581
582 let result: TestEnum = ctx
583 .parse_union(VariantRepr::default())
584 .unwrap()
585 .variant("foo", |ctx: &ParseContext<'_>| {
586 let s: &str = ctx.parse()?;
587 if s == "foo" {
588 Ok(TestEnum::Foo)
589 } else {
590 Err(ParseError {
591 node_id: ctx.node_id(),
592 kind: ParseErrorKind::UnknownVariant(s.to_string()),
593 })
594 }
595 })
596 .variant("bar", |ctx: &ParseContext<'_>| {
597 let s: &str = ctx.parse()?;
598 if s == "bar" {
599 Ok(TestEnum::Bar)
600 } else {
601 Err(ParseError {
602 node_id: ctx.node_id(),
603 kind: ParseErrorKind::UnknownVariant(s.to_string()),
604 })
605 }
606 })
607 .parse()
608 .unwrap();
609
610 assert_eq!(result, TestEnum::Foo);
611 }
612
613 #[test]
614 fn test_union_priority_short_circuit() {
615 let doc = eure!({ = "value" });
616 let root_id = doc.get_root_id();
617 let ctx = doc.parse_context(root_id);
618
619 let result: String = ctx
621 .parse_union(VariantRepr::default())
622 .unwrap()
623 .variant("first", String::parse)
624 .variant("second", String::parse)
625 .parse()
626 .unwrap();
627
628 assert_eq!(result, "value");
629 }
630
631 #[test]
632 fn test_union_no_match() {
633 let doc = eure!({ = "baz" });
634 let root_id = doc.get_root_id();
635 let ctx = doc.parse_context(root_id);
636
637 let result: Result<TestEnum, ParseError> = ctx
638 .parse_union(VariantRepr::default())
639 .unwrap()
640 .variant("foo", |ctx: &ParseContext<'_>| {
641 let s: &str = ctx.parse()?;
642 if s == "foo" {
643 Ok(TestEnum::Foo)
644 } else {
645 Err(ParseError {
646 node_id: ctx.node_id(),
647 kind: ParseErrorKind::UnknownVariant(s.to_string()),
648 })
649 }
650 })
651 .parse();
652
653 assert!(result.is_err());
654 }
655
656 #[test]
659 fn test_variant_extension_match_success() {
660 let doc = eure!({ %variant = "baz", = "anything" });
663 let root_id = doc.get_root_id();
664 let ctx = doc.parse_context(root_id);
665
666 let result: TestEnum = ctx
667 .parse_union(VariantRepr::default())
668 .unwrap()
669 .variant(
670 "foo",
671 AlwaysParser::<TestEnum, ParseError>::new(TestEnum::Foo),
672 )
673 .variant_unambiguous("baz", AlwaysParser::new(TestEnum::Bar))
674 .parse()
675 .unwrap();
676
677 assert_eq!(result, TestEnum::Bar);
678 }
679
680 #[test]
681 fn test_variant_extension_unknown() {
682 let doc = eure!({ %variant = "unknown", = "anything" });
685 let root_id = doc.get_root_id();
686 let ctx = doc.parse_context(root_id);
687
688 let err: ParseError = ctx
689 .parse_union(VariantRepr::default())
690 .unwrap()
691 .variant("foo", AlwaysParser::new(TestEnum::Foo))
692 .variant_unambiguous("baz", AlwaysParser::new(TestEnum::Bar))
693 .parse()
694 .unwrap_err();
695
696 assert_eq!(err.node_id, root_id);
697 assert_eq!(
698 err.kind,
699 ParseErrorKind::UnknownVariant("unknown".to_string())
700 );
701 }
702
703 #[test]
704 fn test_variant_extension_match_parse_failure() {
705 let doc = eure!({ %variant = "baz", = "anything" });
707 let root_id = doc.get_root_id();
708 let ctx = doc.parse_context(root_id);
709
710 let err = ctx
711 .parse_union(VariantRepr::default())
712 .unwrap()
713 .variant("foo", AlwaysParser::new(TestEnum::Foo))
714 .variant_unambiguous("baz", |ctx: &ParseContext<'_>| {
715 Err(ParseError {
716 node_id: ctx.node_id(),
717 kind: ParseErrorKind::MissingField("test".to_string()),
718 })
719 })
720 .parse()
721 .unwrap_err();
722
723 assert_eq!(err.node_id, root_id);
725 assert_eq!(err.kind, ParseErrorKind::MissingField("test".to_string()));
726 }
727
728 #[derive(Debug, PartialEq, Clone)]
731 enum Outer {
732 A(Inner),
733 B(i32),
734 }
735
736 #[derive(Debug, PartialEq, Clone)]
737 enum Inner {
738 X,
739 Y,
740 }
741
742 fn parse_inner(ctx: &ParseContext<'_>) -> Result<Inner, ParseError> {
743 ctx.parse_union(VariantRepr::default())
744 .unwrap()
745 .variant("x", AlwaysParser::new(Inner::X))
746 .variant("y", AlwaysParser::new(Inner::Y))
747 .parse()
748 }
749
750 #[test]
751 fn test_variant_nested_single_segment() {
752 let doc = eure!({ %variant = "a", = "value" });
754 let root_id = doc.get_root_id();
755 let ctx = doc.parse_context(root_id);
756
757 let result: Outer = ctx
758 .parse_union(VariantRepr::default())
759 .unwrap()
760 .variant("a", parse_inner.map(Outer::A))
761 .variant("b", AlwaysParser::new(Outer::B(42)))
762 .parse()
763 .unwrap();
764
765 assert_eq!(result, Outer::A(Inner::X));
766 }
767
768 #[test]
769 fn test_variant_nested_multi_segment() {
770 let doc = eure!({ %variant = "a.y", = "value" });
772 let root_id = doc.get_root_id();
773 let ctx = doc.parse_context(root_id);
774
775 let result: Outer = ctx
776 .parse_union(VariantRepr::default())
777 .unwrap()
778 .variant("a", parse_inner.map(Outer::A))
779 .variant("b", AlwaysParser::new(Outer::B(42)))
780 .parse()
781 .unwrap();
782
783 assert_eq!(result, Outer::A(Inner::Y));
784 }
785
786 #[test]
787 fn test_variant_nested_invalid_inner() {
788 let doc = eure!({ %variant = "a.z", = "value" });
790 let root_id = doc.get_root_id();
791 let ctx = doc.parse_context(root_id);
792
793 let err = ctx
794 .parse_union(VariantRepr::default())
795 .unwrap()
796 .variant("a", parse_inner.map(Outer::A))
797 .variant("b", AlwaysParser::new(Outer::B(42)))
798 .parse()
799 .unwrap_err();
800
801 assert_eq!(err.kind, ParseErrorKind::UnknownVariant("z".to_string()));
802 }
803
804 #[test]
805 fn test_variant_non_nested_with_nested_path() {
806 let doc = eure!({ %variant = "b.x", = "value" });
810 let root_id = doc.get_root_id();
811 let ctx = doc.parse_context(root_id);
812
813 let err = ctx
818 .parse_union(VariantRepr::default())
819 .unwrap()
820 .variant("a", parse_inner.map(Outer::A))
821 .variant("b", |ctx: &ParseContext<'_>| {
822 ctx.parse_primitive()?;
824 Ok(Outer::B(42))
825 })
826 .parse()
827 .unwrap_err();
828
829 assert!(matches!(err.kind, ParseErrorKind::UnexpectedVariantPath(_)));
831 }
832
833 use crate::value::ValueKind;
836
837 #[test]
838 fn test_invalid_variant_type_errors() {
839 use crate::document::node::NodeValue;
842 use crate::value::PrimitiveValue;
843 use num_bigint::BigInt;
844
845 let mut doc = eure!({ = "foo" });
847 let root_id = doc.get_root_id();
848 let variant_node_id = doc
849 .add_extension("variant".parse().unwrap(), root_id)
850 .unwrap()
851 .node_id;
852 doc.node_mut(variant_node_id).content =
853 NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(123)));
854
855 let ctx = doc.parse_context(root_id);
856
857 let Err(err) = ctx.parse_union::<TestEnum, ParseError>(VariantRepr::default()) else {
858 panic!("Expected error");
859 };
860 assert_eq!(
861 err,
862 ParseError {
863 node_id: variant_node_id,
864 kind: ParseErrorKind::InvalidVariantType(ValueKind::Integer),
865 }
866 );
867 }
868
869 #[test]
870 fn test_invalid_variant_path_syntax_errors() {
871 let doc = eure!({ %variant = "foo..bar", = "foo" });
873 let root_id = doc.get_root_id();
874 let variant_node_id = *doc.node(root_id).extensions.get(&VARIANT).unwrap();
875 let ctx = doc.parse_context(root_id);
876
877 let Err(err) = ctx.parse_union::<TestEnum, ParseError>(VariantRepr::default()) else {
878 panic!("Expected error");
879 };
880 assert_eq!(
881 err,
882 ParseError {
883 node_id: variant_node_id,
884 kind: ParseErrorKind::InvalidVariantPath("foo..bar".to_string()),
885 }
886 );
887 }
888
889 #[derive(Debug, PartialEq)]
892 enum ReprTestEnum {
893 A { value: i64 },
894 B { name: String },
895 }
896
897 fn parse_repr_test_enum(
898 ctx: &ParseContext<'_>,
899 repr: VariantRepr,
900 ) -> Result<ReprTestEnum, ParseError> {
901 ctx.parse_union(repr)?
902 .variant("a", |ctx: &ParseContext<'_>| {
903 let rec = ctx.parse_record()?;
904 let value: i64 = rec.parse_field("value")?;
905 rec.deny_unknown_fields()?;
906 Ok(ReprTestEnum::A { value })
907 })
908 .variant("b", |ctx: &ParseContext<'_>| {
909 let rec = ctx.parse_record()?;
910 let name: String = rec.parse_field("name")?;
911 rec.deny_unknown_fields()?;
912 Ok(ReprTestEnum::B { name })
913 })
914 .parse()
915 }
916
917 #[test]
918 fn test_internal_repr_success() {
919 let doc = eure!({ type = "a", value = 42 });
922 let root_id = doc.get_root_id();
923 let ctx = ParseContext::with_union_tag_mode(&doc, root_id, UnionTagMode::Repr);
924
925 let result = parse_repr_test_enum(
926 &ctx,
927 VariantRepr::Internal {
928 tag: "type".to_string(),
929 },
930 );
931 assert_eq!(result.unwrap(), ReprTestEnum::A { value: 42 });
932 }
933
934 #[test]
935 fn test_external_repr_success() {
936 let doc = eure!({ a { value = 42 } });
939 let root_id = doc.get_root_id();
940 let ctx = ParseContext::with_union_tag_mode(&doc, root_id, UnionTagMode::Repr);
941
942 let result = parse_repr_test_enum(&ctx, VariantRepr::External);
943 assert_eq!(result.unwrap(), ReprTestEnum::A { value: 42 });
944 }
945
946 #[test]
947 fn test_adjacent_repr_success() {
948 let doc = eure!({ type = "a", content { value = 42 } });
951 let root_id = doc.get_root_id();
952 let ctx = ParseContext::with_union_tag_mode(&doc, root_id, UnionTagMode::Repr);
953
954 let result = parse_repr_test_enum(
955 &ctx,
956 VariantRepr::Adjacent {
957 tag: "type".to_string(),
958 content: "content".to_string(),
959 },
960 );
961 assert_eq!(result.unwrap(), ReprTestEnum::A { value: 42 });
962 }
963
964 #[test]
965 fn test_repr_mode_ignores_variant_extension() {
966 let doc = eure!({ %variant = "b", type = "a", value = 42 });
969 let root_id = doc.get_root_id();
970 let ctx = ParseContext::with_union_tag_mode(&doc, root_id, UnionTagMode::Repr);
971
972 let result = parse_repr_test_enum(
973 &ctx,
974 VariantRepr::Internal {
975 tag: "type".to_string(),
976 },
977 );
978 assert_eq!(result.unwrap(), ReprTestEnum::A { value: 42 });
979 }
980
981 #[test]
982 fn test_eure_mode_ignores_repr() {
983 let doc = eure!({ type = "a", value = 42 });
985 let root_id = doc.get_root_id();
986
987 let ctx = doc.parse_context(root_id);
989
990 let result = ctx
993 .parse_union::<_, ParseError>(VariantRepr::Internal {
994 tag: "type".to_string(),
995 })
996 .unwrap()
997 .variant("a", |ctx: &ParseContext<'_>| {
998 let rec = ctx.parse_record()?;
999 let value: i64 = rec.parse_field("value")?;
1000 rec.allow_unknown_fields()?;
1001 Ok(ReprTestEnum::A { value })
1002 })
1003 .variant("b", |ctx: &ParseContext<'_>| {
1004 let rec = ctx.parse_record()?;
1005 let name: String = rec.parse_field("name")?;
1006 rec.deny_unknown_fields()?;
1007 Ok(ReprTestEnum::B { name })
1008 })
1009 .parse();
1010
1011 assert_eq!(result.unwrap(), ReprTestEnum::A { value: 42 });
1012 }
1013
1014 #[test]
1015 fn test_internal_repr_unknown_variant_name() {
1016 let doc = eure!({ type = "unknown", value = 42 });
1019 let root_id = doc.get_root_id();
1020 let ctx = ParseContext::with_union_tag_mode(&doc, root_id, UnionTagMode::Repr);
1021
1022 let result = parse_repr_test_enum(
1023 &ctx,
1024 VariantRepr::Internal {
1025 tag: "type".to_string(),
1026 },
1027 );
1028
1029 let err = result.unwrap_err();
1031 assert_eq!(
1032 err.kind,
1033 ParseErrorKind::UnknownVariant("unknown".to_string())
1034 );
1035 }
1036
1037 #[test]
1038 fn test_repr_not_extracted_falls_back_to_untagged() {
1039 let doc = eure!({ value = 100, extra = "ignored" });
1042 let root_id = doc.get_root_id();
1043 let ctx = doc.parse_context(root_id);
1044
1045 let result = ctx
1047 .parse_union::<_, ParseError>(VariantRepr::External)
1048 .unwrap()
1049 .variant("a", |ctx: &ParseContext<'_>| {
1050 let rec = ctx.parse_record()?;
1051 let value: i64 = rec.parse_field("value")?;
1052 Ok(ReprTestEnum::A { value })
1054 })
1055 .variant("b", |ctx: &ParseContext<'_>| {
1056 let rec = ctx.parse_record()?;
1057 let name: String = rec.parse_field("name")?;
1058 rec.deny_unknown_fields()?;
1059 Ok(ReprTestEnum::B { name })
1060 })
1061 .parse();
1062
1063 assert_eq!(result.unwrap(), ReprTestEnum::A { value: 100 });
1065 }
1066
1067 #[test]
1068 fn test_external_repr_single_key_extracts_variant() {
1069 let doc = eure!({ value = 100 });
1072 let root_id = doc.get_root_id();
1073 let ctx = ParseContext::with_union_tag_mode(&doc, root_id, UnionTagMode::Repr);
1074
1075 let err: ParseError = ctx
1078 .parse_union(VariantRepr::External)
1079 .unwrap()
1080 .variant("a", |ctx: &ParseContext<'_>| {
1081 let rec = ctx.parse_record()?;
1082 let value: i64 = rec.parse_field("value")?;
1083 rec.deny_unknown_fields()?;
1084 Ok(ReprTestEnum::A { value })
1085 })
1086 .variant("b", |ctx: &ParseContext<'_>| {
1087 let rec = ctx.parse_record()?;
1088 let name: String = rec.parse_field("name")?;
1089 rec.deny_unknown_fields()?;
1090 Ok(ReprTestEnum::B { name })
1091 })
1092 .parse()
1093 .unwrap_err();
1094
1095 assert_eq!(
1096 err.kind,
1097 ParseErrorKind::UnknownVariant("value".to_string())
1098 );
1099 }
1100
1101 #[test]
1104 fn test_internal_repr_tag_is_integer_errors() {
1105 use crate::document::EureDocument;
1110 use crate::document::node::NodeValue;
1111 use crate::value::{ObjectKey, PrimitiveValue};
1112 use num_bigint::BigInt;
1113
1114 let mut doc = EureDocument::new();
1115 let root_id = doc.get_root_id();
1116 doc.node_mut(root_id).content = NodeValue::Map(Default::default());
1117
1118 let type_node_id = doc
1119 .add_map_child(ObjectKey::String("type".to_string()), root_id)
1120 .unwrap()
1121 .node_id;
1122 doc.node_mut(type_node_id).content =
1123 NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(123)));
1124
1125 let value_node_id = doc
1126 .add_map_child(ObjectKey::String("value".to_string()), root_id)
1127 .unwrap()
1128 .node_id;
1129 doc.node_mut(value_node_id).content =
1130 NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(42)));
1131
1132 let ctx = ParseContext::with_union_tag_mode(&doc, root_id, UnionTagMode::Repr);
1133
1134 let Err(err) = ctx.parse_union::<ReprTestEnum, ParseError>(VariantRepr::Internal {
1136 tag: "type".to_string(),
1137 }) else {
1138 panic!("Expected error");
1139 };
1140
1141 assert_eq!(err.node_id, type_node_id);
1143 }
1144
1145 #[test]
1146 fn test_adjacent_repr_missing_content_falls_back_to_untagged() {
1147 let doc = eure!({ type = "a", value = 42 });
1150 let root_id = doc.get_root_id();
1151 let ctx = doc.parse_context(root_id);
1152
1153 let result = ctx
1155 .parse_union::<_, ParseError>(VariantRepr::Adjacent {
1156 tag: "type".to_string(),
1157 content: "content".to_string(),
1158 })
1159 .unwrap()
1160 .variant("a", |ctx: &ParseContext<'_>| {
1161 let rec = ctx.parse_record()?;
1162 let value: i64 = rec.parse_field("value")?;
1163 Ok(ReprTestEnum::A { value })
1165 })
1166 .variant("b", |ctx: &ParseContext<'_>| {
1167 let rec = ctx.parse_record()?;
1168 let name: String = rec.parse_field("name")?;
1169 rec.deny_unknown_fields()?;
1170 Ok(ReprTestEnum::B { name })
1171 })
1172 .parse();
1173
1174 assert_eq!(result.unwrap(), ReprTestEnum::A { value: 42 });
1176 }
1177
1178 #[test]
1179 fn test_external_repr_non_string_key_falls_back_to_untagged() {
1180 use crate::document::EureDocument;
1183 use crate::document::node::NodeValue;
1184 use crate::value::{ObjectKey, PrimitiveValue};
1185 use num_bigint::BigInt;
1186
1187 let mut doc = EureDocument::new();
1188 let root_id = doc.get_root_id();
1189 doc.node_mut(root_id).content = NodeValue::Map(Default::default());
1190
1191 let variant_node_id = doc
1193 .add_map_child(ObjectKey::Number(BigInt::from(123)), root_id)
1194 .unwrap()
1195 .node_id;
1196 doc.node_mut(variant_node_id).content = NodeValue::Map(Default::default());
1197
1198 let value_node_id = doc
1200 .add_map_child(ObjectKey::String("value".to_string()), variant_node_id)
1201 .unwrap()
1202 .node_id;
1203 doc.node_mut(value_node_id).content =
1204 NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(42)));
1205
1206 let ctx = doc.parse_context(root_id);
1207
1208 let err: ParseError = ctx
1212 .parse_union(VariantRepr::External)
1213 .unwrap()
1214 .variant("a", |ctx: &ParseContext<'_>| {
1215 let rec = ctx.parse_record()?;
1216 let value: i64 = rec.parse_field("value")?;
1217 rec.deny_unknown_fields()?;
1218 Ok(ReprTestEnum::A { value })
1219 })
1220 .variant("b", |ctx: &ParseContext<'_>| {
1221 let rec = ctx.parse_record()?;
1222 let name: String = rec.parse_field("name")?;
1223 rec.deny_unknown_fields()?;
1224 Ok(ReprTestEnum::B { name })
1225 })
1226 .parse()
1227 .unwrap_err();
1228
1229 assert_eq!(err.kind, ParseErrorKind::MissingField("value".to_string()));
1231 }
1232
1233 #[test]
1234 fn test_eure_mode_uses_variant_extension_over_repr() {
1235 let doc = eure!({ %variant = "b", type = "a", value = 42 });
1238 let root_id = doc.get_root_id();
1239 let ctx = doc.parse_context(root_id);
1240
1241 let err: ParseError = ctx
1244 .parse_union(VariantRepr::Internal {
1245 tag: "type".to_string(),
1246 })
1247 .unwrap()
1248 .variant("a", |ctx: &ParseContext<'_>| {
1249 let rec = ctx.parse_record()?;
1250 let value: i64 = rec.parse_field("value")?;
1251 rec.allow_unknown_fields()?;
1252 Ok(ReprTestEnum::A { value })
1253 })
1254 .variant("b", |ctx: &ParseContext<'_>| {
1255 let rec = ctx.parse_record()?;
1256 let name: String = rec.parse_field("name")?;
1257 rec.deny_unknown_fields()?;
1258 Ok(ReprTestEnum::B { name })
1259 })
1260 .parse()
1261 .unwrap_err();
1262
1263 assert_eq!(err.kind, ParseErrorKind::MissingField("name".to_string()));
1265 }
1266
1267 #[test]
1268 fn test_variant_path_empty_uses_untagged() {
1269 let doc = eure!({ = "value" });
1272 let root_id = doc.get_root_id();
1273 let ctx = doc.parse_context(root_id);
1274
1275 let child_ctx = ctx.with_variant_rest(Some(VariantPath::empty()));
1277
1278 let result: String = child_ctx
1280 .parse_union(VariantRepr::default())
1281 .unwrap()
1282 .variant("first", String::parse)
1283 .variant("second", String::parse)
1284 .parse()
1285 .unwrap();
1286
1287 assert_eq!(result, "value");
1289 }
1290
1291 #[derive(Debug, PartialEq, Clone)]
1297 enum OuterUnion {
1298 Normal(InnerUnion),
1299 List(Vec<InnerUnion>),
1300 }
1301
1302 #[derive(Debug, PartialEq, Clone)]
1304 enum InnerUnion {
1305 Text(String),
1306 Number(i64),
1307 }
1308
1309 fn parse_inner_union(ctx: &ParseContext<'_>) -> Result<InnerUnion, ParseError> {
1310 ctx.parse_union(VariantRepr::default())?
1311 .variant("text", |ctx: &ParseContext<'_>| {
1312 let s: String = ctx.parse()?;
1313 Ok(InnerUnion::Text(s))
1314 })
1315 .variant("number", |ctx: &ParseContext<'_>| {
1316 let n: i64 = ctx.parse()?;
1317 Ok(InnerUnion::Number(n))
1318 })
1319 .parse()
1320 }
1321
1322 fn parse_outer_union(ctx: &ParseContext<'_>) -> Result<OuterUnion, ParseError> {
1323 use crate::document::node::NodeArray;
1324
1325 ctx.parse_union(VariantRepr::default())?
1326 .variant("normal", |ctx: &ParseContext<'_>| {
1327 let inner = parse_inner_union(ctx)?;
1328 Ok(OuterUnion::Normal(inner))
1329 })
1330 .variant("list", |ctx: &ParseContext<'_>| {
1331 let arr: &NodeArray = ctx.parse()?;
1333 let items: Result<Vec<InnerUnion>, _> = arr
1334 .iter()
1335 .map(|&node_id| parse_inner_union(&ctx.at(node_id)))
1336 .collect();
1337 Ok(OuterUnion::List(items?))
1338 })
1339 .parse()
1340 }
1341
1342 #[test]
1343 fn test_nested_union_basic_text() {
1344 let doc = eure!({ = "hello" });
1346 let root_id = doc.get_root_id();
1347 let ctx = doc.parse_context(root_id);
1348
1349 let result = parse_outer_union(&ctx).unwrap();
1350 assert_eq!(
1351 result,
1352 OuterUnion::Normal(InnerUnion::Text("hello".to_string()))
1353 );
1354 }
1355
1356 #[test]
1357 fn test_nested_union_basic_number() {
1358 let doc = eure!({ = 42 });
1359 let root_id = doc.get_root_id();
1360 let ctx = doc.parse_context(root_id);
1361 let result = parse_outer_union(&ctx).unwrap();
1362 assert_eq!(result, OuterUnion::Normal(InnerUnion::Number(42)));
1363 }
1364
1365 #[test]
1366 fn test_nested_union_variant_path_propagation() {
1367 let doc = eure!({ %variant = "normal.text", = "test value" });
1369 let root_id = doc.get_root_id();
1370 let ctx = doc.parse_context(root_id);
1371
1372 let result = parse_outer_union(&ctx).unwrap();
1373 assert_eq!(
1374 result,
1375 OuterUnion::Normal(InnerUnion::Text("test value".to_string()))
1376 );
1377 }
1378
1379 #[test]
1380 fn test_nested_union_variant_path_number() {
1381 let doc = eure!({ %variant = "normal.number", = 99 });
1383 let root_id = doc.get_root_id();
1384 let ctx = doc.parse_context(root_id);
1385 let result = parse_outer_union(&ctx).unwrap();
1386 assert_eq!(result, OuterUnion::Normal(InnerUnion::Number(99)));
1387 }
1388
1389 #[test]
1390 fn test_nested_union_inner_fails_outer_recovers() {
1391 let doc = eure!({ = ["a", "b"] });
1395 let root_id = doc.get_root_id();
1396 let ctx = doc.parse_context(root_id);
1397
1398 let result = parse_outer_union(&ctx).unwrap();
1399 assert_eq!(
1400 result,
1401 OuterUnion::List(alloc::vec![
1402 InnerUnion::Text("a".to_string()),
1403 InnerUnion::Text("b".to_string()),
1404 ])
1405 );
1406 }
1407
1408 #[derive(Debug, PartialEq, Clone)]
1413 enum Level1 {
1414 A(Level2Union),
1415 B(String),
1416 }
1417
1418 #[derive(Debug, PartialEq, Clone)]
1419 enum Level2Union {
1420 X(Level3),
1421 Y(i64),
1422 }
1423
1424 #[derive(Debug, PartialEq, Clone)]
1425 enum Level3 {
1426 Leaf(String),
1427 }
1428
1429 fn parse_level3(ctx: &ParseContext<'_>) -> Result<Level3, ParseError> {
1430 ctx.parse_union(VariantRepr::default())?
1431 .variant("leaf", |ctx: &ParseContext<'_>| {
1432 let s: String = ctx.parse()?;
1433 Ok(Level3::Leaf(s))
1434 })
1435 .parse()
1436 }
1437
1438 fn parse_level2(ctx: &ParseContext<'_>) -> Result<Level2Union, ParseError> {
1439 ctx.parse_union(VariantRepr::default())?
1440 .variant("x", |ctx: &ParseContext<'_>| {
1441 let inner = parse_level3(ctx)?;
1442 Ok(Level2Union::X(inner))
1443 })
1444 .variant("y", |ctx: &ParseContext<'_>| {
1445 let n: i64 = ctx.parse()?;
1446 Ok(Level2Union::Y(n))
1447 })
1448 .parse()
1449 }
1450
1451 fn parse_level1(ctx: &ParseContext<'_>) -> Result<Level1, ParseError> {
1452 ctx.parse_union(VariantRepr::default())?
1453 .variant("a", |ctx: &ParseContext<'_>| {
1454 let inner = parse_level2(ctx)?;
1455 Ok(Level1::A(inner))
1456 })
1457 .variant("b", |ctx: &ParseContext<'_>| {
1458 let s: String = ctx.parse()?;
1459 Ok(Level1::B(s))
1460 })
1461 .parse()
1462 }
1463
1464 #[test]
1465 fn test_nested_union_three_levels_untagged() {
1466 let doc = eure!({ = "deep value" });
1469 let root_id = doc.get_root_id();
1470 let ctx = doc.parse_context(root_id);
1471
1472 let result = parse_level1(&ctx).unwrap();
1473 assert_eq!(
1474 result,
1475 Level1::A(Level2Union::X(Level3::Leaf("deep value".to_string())))
1476 );
1477 }
1478
1479 #[test]
1480 fn test_nested_union_three_levels_variant_path() {
1481 let doc = eure!({ %variant = "a.x.leaf", = "explicit deep" });
1483 let root_id = doc.get_root_id();
1484 let ctx = doc.parse_context(root_id);
1485
1486 let result = parse_level1(&ctx).unwrap();
1487 assert_eq!(
1488 result,
1489 Level1::A(Level2Union::X(Level3::Leaf("explicit deep".to_string())))
1490 );
1491 }
1492
1493 #[test]
1494 fn test_nested_union_three_levels_variant_path_partial() {
1495 let doc = eure!({ %variant = "a.y", = 123 });
1497 let root_id = doc.get_root_id();
1498 let ctx = doc.parse_context(root_id);
1499 let result = parse_level1(&ctx).unwrap();
1500 assert_eq!(result, Level1::A(Level2Union::Y(123)));
1501 }
1502
1503 #[test]
1504 fn test_nested_union_invalid_inner_variant_path() {
1505 let doc = eure!({ %variant = "a.x.invalid", = "value" });
1507 let root_id = doc.get_root_id();
1508 let ctx = doc.parse_context(root_id);
1509
1510 let err = parse_level1(&ctx).unwrap_err();
1511 assert_eq!(
1512 err.kind,
1513 ParseErrorKind::UnknownVariant("invalid".to_string())
1514 );
1515 }
1516
1517 #[test]
1522 fn test_flatten_nested_union_accessed_fields_basic() {
1523 use crate::parse::AccessedSet;
1524 use crate::parse::FlattenContext;
1525 use crate::parse::ParserScope;
1526
1527 let doc = eure!({
1529 field_a = "value_a"
1530 field_b = "value_b"
1531 });
1532 let root_id = doc.get_root_id();
1533
1534 let flatten_ctx = FlattenContext::new(AccessedSet::new(), ParserScope::Record);
1536 let ctx =
1537 ParseContext::with_flatten_ctx(&doc, root_id, flatten_ctx.clone(), UnionTagMode::Eure);
1538
1539 let record = ctx.parse_record().unwrap();
1541 let _field_a: String = record.parse_field("field_a").unwrap();
1542
1543 let (accessed, _) = flatten_ctx.capture_current_state();
1545 assert!(accessed.contains("field_a"));
1546 assert!(!accessed.contains("field_b"));
1547 }
1548}