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, ParseDocument};
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 => Self::extract_explicit_variant(ctx)?,
240 };
241
242 match explicit_variant {
243 Some(ev) => {
245 let name = ev
246 .first()
247 .map(|i| i.as_ref().to_string())
248 .unwrap_or_default();
249 let rest = ev.rest().unwrap_or_else(VariantPath::empty);
250 Ok(Some((name, ctx.clone(), Some(rest))))
251 }
252 None => Ok(None),
254 }
255 }
256
257 fn resolve_variant_repr_mode(
264 ctx: &ParseContext<'doc>,
265 repr: &VariantRepr,
266 ) -> Result<Option<(String, ParseContext<'doc>, Option<VariantPath>)>, ParseError> {
267 let repr_variant = extract_repr_variant(ctx.doc(), ctx.node_id(), repr)?;
269
270 match repr_variant {
271 Some((name, content_node_id)) => {
273 let content_ctx = Self::make_content_context(ctx, repr, content_node_id);
274 Ok(Some((name, content_ctx, Some(VariantPath::empty()))))
275 }
276 None => {
279 Ok(None)
282 }
283 }
284 }
285
286 fn make_content_context(
288 ctx: &ParseContext<'doc>,
289 repr: &VariantRepr,
290 content_node_id: NodeId,
291 ) -> ParseContext<'doc> {
292 match repr {
293 VariantRepr::Internal { tag } => {
296 let flatten_ctx = match ctx.flatten_ctx() {
298 Some(fc) => {
299 fc.add_field(tag);
300 fc.clone()
301 }
302 None => {
303 let fc = super::FlattenContext::new(
304 super::AccessedSet::new(),
305 ParserScope::Record,
306 );
307 fc.add_field(tag);
308 fc
309 }
310 };
311 ParseContext::with_flatten_ctx(
312 ctx.doc(),
313 content_node_id,
314 flatten_ctx,
315 ctx.union_tag_mode(),
316 )
317 }
318 _ => ctx.at(content_node_id),
320 }
321 }
322
323 fn extract_explicit_variant(
325 ctx: &ParseContext<'doc>,
326 ) -> Result<Option<VariantPath>, ParseError> {
327 let node = ctx.node();
328 let Some(&variant_node_id) = node.extensions.get(&VARIANT) else {
329 return Ok(None);
330 };
331
332 let variant_node = ctx.doc().node(variant_node_id);
333 let s: &str = ctx.doc().parse(variant_node_id).map_err(|_| ParseError {
334 node_id: variant_node_id,
335 kind: ParseErrorKind::InvalidVariantType(
336 variant_node
337 .content
338 .value_kind()
339 .unwrap_or(crate::value::ValueKind::Null),
340 ),
341 })?;
342
343 VariantPath::parse(s).map(Some).map_err(|_| ParseError {
344 node_id: variant_node_id,
345 kind: ParseErrorKind::InvalidVariantPath(s.to_string()),
346 })
347 }
348
349 pub fn variant<P: DocumentParser<'doc, Output = T, Error = E>>(
354 mut self,
355 name: &str,
356 f: P,
357 ) -> Self {
358 self.try_variant(name, f, true);
359 self
360 }
361
362 pub fn parse_variant<V: ParseDocument<'doc, Error = E>>(
364 mut self,
365 name: &str,
366 mut then: impl FnMut(V) -> Result<T, E>,
367 ) -> Self {
368 self.try_variant(
369 name,
370 move |ctx: &ParseContext<'doc>| {
371 let v = V::parse(ctx)?;
372 then(v)
373 },
374 true,
375 );
376 self
377 }
378
379 pub fn variant_unambiguous<P: DocumentParser<'doc, Output = T, Error = E>>(
385 mut self,
386 name: &str,
387 f: P,
388 ) -> Self {
389 self.try_variant(name, f, false);
390 self
391 }
392
393 pub fn parse_variant_unambiguous<V: ParseDocument<'doc, Error = E>>(
395 mut self,
396 name: &str,
397 mut then: impl FnMut(V) -> Result<T, E>,
398 ) -> Self {
399 self.try_variant(
400 name,
401 move |ctx: &ParseContext<'doc>| {
402 let v = V::parse(ctx)?;
403 then(v)
404 },
405 false,
406 );
407 self
408 }
409
410 fn try_variant<P: DocumentParser<'doc, Output = T, Error = E>>(
412 &mut self,
413 name: &str,
414 mut f: P,
415 is_priority: bool,
416 ) {
417 if let Some((ref v_name, ref v_ctx, ref rest)) = self.variant {
419 if v_name == name && self.variant_result.is_none() {
420 let child_ctx = v_ctx.with_variant_rest(rest.clone());
421 let result = f.parse(&child_ctx);
422 self.variant_result = Some(result);
425 }
426 return;
427 }
428
429 if self.priority_result.is_some() {
433 return;
434 }
435
436 let child_ctx = self.ctx.with_variant_rest(None);
437 match f.parse(&child_ctx) {
438 Ok(value) => {
439 if is_priority {
440 self.priority_result = Some(value);
443 } else {
444 if let Some(ref fc) = self.flatten_ctx {
447 let captured = fc.capture_current_state();
448 fc.restore_to_current_snapshot();
449 self.other_results.push((name.to_string(), value, captured));
450 } else {
451 self.other_results.push((
453 name.to_string(),
454 value,
455 (Default::default(), Default::default()),
456 ));
457 }
458 }
459 }
460 Err(e) => {
461 if let Some(ref fc) = self.flatten_ctx {
463 fc.restore_to_current_snapshot();
464 }
465 self.failures.push((name.to_string(), e));
466 }
467 }
468 }
469
470 pub fn parse(self) -> Result<T, E> {
472 let node_id = self.ctx.node_id();
473
474 if let Some((v_name, _, _)) = self.variant {
478 let result = self.variant_result.unwrap_or_else(|| {
479 Err(ParseError {
480 node_id,
481 kind: ParseErrorKind::UnknownVariant(v_name),
482 }
483 .into())
484 });
485 if let Some(ref fc) = self.flatten_ctx {
487 match &result {
488 Ok(_) => fc.pop_without_restore(),
489 Err(_) => fc.pop_and_restore(),
490 }
491 }
492 return result;
493 }
494
495 if let Some(value) = self.priority_result {
497 if let Some(ref fc) = self.flatten_ctx {
498 fc.pop_without_restore();
499 }
500 return Ok(value);
501 }
502
503 match self.other_results.len() {
505 0 => {
506 if let Some(ref fc) = self.flatten_ctx {
508 fc.pop_and_restore();
509 }
510 Err(self.no_match_error(node_id))
511 }
512 1 => {
513 let (_, value, captured_state) = self.other_results.into_iter().next().unwrap();
515 if let Some(ref fc) = self.flatten_ctx {
516 fc.restore_to_state(captured_state);
517 fc.pop_without_restore();
518 }
519 Ok(value)
520 }
521 _ => {
522 if let Some(ref fc) = self.flatten_ctx {
524 fc.pop_and_restore();
525 }
526 Err(ParseError {
527 node_id,
528 kind: ParseErrorKind::AmbiguousUnion(
529 self.other_results
530 .into_iter()
531 .map(|(name, _, _)| name)
532 .collect(),
533 ),
534 }
535 .into())
536 }
537 }
538 }
539
540 fn no_match_error(self, node_id: crate::document::NodeId) -> E {
542 self.failures
543 .into_iter()
544 .next()
545 .map(|(_, e)| e)
546 .unwrap_or_else(|| {
547 ParseError {
548 node_id,
549 kind: ParseErrorKind::NoMatchingVariant { variant: None },
550 }
551 .into()
552 })
553 }
554}
555
556#[cfg(test)]
557mod tests {
558 use super::*;
559 use crate::document::EureDocument;
560 use crate::document::node::NodeValue;
561 use crate::parse::AlwaysParser;
562 use crate::parse::DocumentParserExt as _;
563 use crate::text::Text;
564 use crate::value::PrimitiveValue;
565
566 fn identifier(s: &str) -> Identifier {
567 s.parse().unwrap()
568 }
569
570 #[derive(Debug, PartialEq, Clone)]
571 enum TestEnum {
572 Foo,
573 Bar,
574 }
575
576 fn create_text_doc(text: &str) -> EureDocument {
577 let mut doc = EureDocument::new();
578 let root_id = doc.get_root_id();
579 doc.node_mut(root_id).content =
580 NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext(text.to_string())));
581 doc
582 }
583
584 fn create_doc_with_variant(content: &str, variant: &str) -> EureDocument {
586 let mut doc = EureDocument::new();
587 let root_id = doc.get_root_id();
588
589 doc.node_mut(root_id).content =
591 NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext(content.to_string())));
592
593 let variant_node_id = doc
595 .add_extension(identifier("variant"), root_id)
596 .unwrap()
597 .node_id;
598 doc.node_mut(variant_node_id).content =
599 NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext(variant.to_string())));
600
601 doc
602 }
603
604 #[test]
605 fn test_union_single_match() {
606 let doc = create_text_doc("foo");
607 let root_id = doc.get_root_id();
608 let ctx = doc.parse_context(root_id);
609
610 let result: TestEnum = ctx
611 .parse_union(VariantRepr::default())
612 .unwrap()
613 .variant("foo", |ctx: &ParseContext<'_>| {
614 let s: &str = ctx.parse()?;
615 if s == "foo" {
616 Ok(TestEnum::Foo)
617 } else {
618 Err(ParseError {
619 node_id: ctx.node_id(),
620 kind: ParseErrorKind::UnknownVariant(s.to_string()),
621 })
622 }
623 })
624 .variant("bar", |ctx: &ParseContext<'_>| {
625 let s: &str = ctx.parse()?;
626 if s == "bar" {
627 Ok(TestEnum::Bar)
628 } else {
629 Err(ParseError {
630 node_id: ctx.node_id(),
631 kind: ParseErrorKind::UnknownVariant(s.to_string()),
632 })
633 }
634 })
635 .parse()
636 .unwrap();
637
638 assert_eq!(result, TestEnum::Foo);
639 }
640
641 #[test]
642 fn test_union_priority_short_circuit() {
643 let doc = create_text_doc("value");
644 let root_id = doc.get_root_id();
645 let ctx = doc.parse_context(root_id);
646
647 let result: String = ctx
649 .parse_union(VariantRepr::default())
650 .unwrap()
651 .variant("first", String::parse)
652 .variant("second", String::parse)
653 .parse()
654 .unwrap();
655
656 assert_eq!(result, "value");
657 }
658
659 #[test]
660 fn test_union_no_match() {
661 let doc = create_text_doc("baz");
662 let root_id = doc.get_root_id();
663 let ctx = doc.parse_context(root_id);
664
665 let result: Result<TestEnum, ParseError> = ctx
666 .parse_union(VariantRepr::default())
667 .unwrap()
668 .variant("foo", |ctx: &ParseContext<'_>| {
669 let s: &str = ctx.parse()?;
670 if s == "foo" {
671 Ok(TestEnum::Foo)
672 } else {
673 Err(ParseError {
674 node_id: ctx.node_id(),
675 kind: ParseErrorKind::UnknownVariant(s.to_string()),
676 })
677 }
678 })
679 .parse();
680
681 assert!(result.is_err());
682 }
683
684 #[test]
687 fn test_variant_extension_match_success() {
688 let doc = create_doc_with_variant("anything", "baz");
691 let root_id = doc.get_root_id();
692 let ctx = doc.parse_context(root_id);
693
694 let result: TestEnum = ctx
695 .parse_union(VariantRepr::default())
696 .unwrap()
697 .variant(
698 "foo",
699 AlwaysParser::<TestEnum, ParseError>::new(TestEnum::Foo),
700 )
701 .variant_unambiguous("baz", AlwaysParser::new(TestEnum::Bar))
702 .parse()
703 .unwrap();
704
705 assert_eq!(result, TestEnum::Bar);
706 }
707
708 #[test]
709 fn test_variant_extension_unknown() {
710 let doc = create_doc_with_variant("anything", "unknown");
713 let root_id = doc.get_root_id();
714 let ctx = doc.parse_context(root_id);
715
716 let err: ParseError = ctx
717 .parse_union(VariantRepr::default())
718 .unwrap()
719 .variant("foo", AlwaysParser::new(TestEnum::Foo))
720 .variant_unambiguous("baz", AlwaysParser::new(TestEnum::Bar))
721 .parse()
722 .unwrap_err();
723
724 assert_eq!(err.node_id, root_id);
725 assert_eq!(
726 err.kind,
727 ParseErrorKind::UnknownVariant("unknown".to_string())
728 );
729 }
730
731 #[test]
732 fn test_variant_extension_match_parse_failure() {
733 let doc = create_doc_with_variant("anything", "baz");
735 let root_id = doc.get_root_id();
736 let ctx = doc.parse_context(root_id);
737
738 let err = ctx
739 .parse_union(VariantRepr::default())
740 .unwrap()
741 .variant("foo", AlwaysParser::new(TestEnum::Foo))
742 .variant_unambiguous("baz", |ctx: &ParseContext<'_>| {
743 Err(ParseError {
744 node_id: ctx.node_id(),
745 kind: ParseErrorKind::MissingField("test".to_string()),
746 })
747 })
748 .parse()
749 .unwrap_err();
750
751 assert_eq!(err.node_id, root_id);
753 assert_eq!(err.kind, ParseErrorKind::MissingField("test".to_string()));
754 }
755
756 #[derive(Debug, PartialEq, Clone)]
759 enum Outer {
760 A(Inner),
761 B(i32),
762 }
763
764 #[derive(Debug, PartialEq, Clone)]
765 enum Inner {
766 X,
767 Y,
768 }
769
770 fn parse_inner(ctx: &ParseContext<'_>) -> Result<Inner, ParseError> {
771 ctx.parse_union(VariantRepr::default())
772 .unwrap()
773 .variant("x", AlwaysParser::new(Inner::X))
774 .variant("y", AlwaysParser::new(Inner::Y))
775 .parse()
776 }
777
778 #[test]
779 fn test_variant_nested_single_segment() {
780 let doc = create_doc_with_variant("value", "a");
782 let root_id = doc.get_root_id();
783 let ctx = doc.parse_context(root_id);
784
785 let result: Outer = ctx
786 .parse_union(VariantRepr::default())
787 .unwrap()
788 .variant("a", parse_inner.map(Outer::A))
789 .variant("b", AlwaysParser::new(Outer::B(42)))
790 .parse()
791 .unwrap();
792
793 assert_eq!(result, Outer::A(Inner::X));
794 }
795
796 #[test]
797 fn test_variant_nested_multi_segment() {
798 let doc = create_doc_with_variant("value", "a.y");
800 let root_id = doc.get_root_id();
801 let ctx = doc.parse_context(root_id);
802
803 let result: Outer = ctx
804 .parse_union(VariantRepr::default())
805 .unwrap()
806 .variant("a", parse_inner.map(Outer::A))
807 .variant("b", AlwaysParser::new(Outer::B(42)))
808 .parse()
809 .unwrap();
810
811 assert_eq!(result, Outer::A(Inner::Y));
812 }
813
814 #[test]
815 fn test_variant_nested_invalid_inner() {
816 let doc = create_doc_with_variant("value", "a.z");
818 let root_id = doc.get_root_id();
819 let ctx = doc.parse_context(root_id);
820
821 let err = ctx
822 .parse_union(VariantRepr::default())
823 .unwrap()
824 .variant("a", parse_inner.map(Outer::A))
825 .variant("b", AlwaysParser::new(Outer::B(42)))
826 .parse()
827 .unwrap_err();
828
829 assert_eq!(err.kind, ParseErrorKind::UnknownVariant("z".to_string()));
830 }
831
832 #[test]
833 fn test_variant_non_nested_with_nested_path() {
834 let doc = create_doc_with_variant("value", "b.x");
838 let root_id = doc.get_root_id();
839 let ctx = doc.parse_context(root_id);
840
841 let err = ctx
846 .parse_union(VariantRepr::default())
847 .unwrap()
848 .variant("a", parse_inner.map(Outer::A))
849 .variant("b", |ctx: &ParseContext<'_>| {
850 ctx.parse_primitive()?;
852 Ok(Outer::B(42))
853 })
854 .parse()
855 .unwrap_err();
856
857 assert!(matches!(err.kind, ParseErrorKind::UnexpectedVariantPath(_)));
859 }
860
861 use crate::value::ValueKind;
864
865 fn create_doc_with_integer_variant(
868 content: &str,
869 variant_value: i64,
870 ) -> (EureDocument, crate::document::NodeId) {
871 use num_bigint::BigInt;
872
873 let mut doc = EureDocument::new();
874 let root_id = doc.get_root_id();
875
876 doc.node_mut(root_id).content =
878 NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext(content.to_string())));
879
880 let variant_node_id = doc
882 .add_extension(identifier("variant"), root_id)
883 .unwrap()
884 .node_id;
885 doc.node_mut(variant_node_id).content =
886 NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(variant_value)));
887
888 (doc, variant_node_id)
889 }
890
891 fn create_doc_with_variant_ext(
894 content: &str,
895 variant: &str,
896 ) -> (EureDocument, crate::document::NodeId) {
897 let mut doc = EureDocument::new();
898 let root_id = doc.get_root_id();
899
900 doc.node_mut(root_id).content =
902 NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext(content.to_string())));
903
904 let variant_node_id = doc
906 .add_extension(identifier("variant"), root_id)
907 .unwrap()
908 .node_id;
909 doc.node_mut(variant_node_id).content =
910 NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext(variant.to_string())));
911
912 (doc, variant_node_id)
913 }
914
915 #[test]
916 fn test_invalid_variant_type_errors() {
917 let (doc, variant_node_id) = create_doc_with_integer_variant("foo", 123);
919 let root_id = doc.get_root_id();
920 let ctx = doc.parse_context(root_id);
921
922 let Err(err) = ctx.parse_union::<TestEnum, ParseError>(VariantRepr::default()) else {
923 panic!("Expected error");
924 };
925 assert_eq!(
926 err,
927 ParseError {
928 node_id: variant_node_id,
929 kind: ParseErrorKind::InvalidVariantType(ValueKind::Integer),
930 }
931 );
932 }
933
934 #[test]
935 fn test_invalid_variant_path_syntax_errors() {
936 let (doc, variant_node_id) = create_doc_with_variant_ext("foo", "foo..bar");
938 let root_id = doc.get_root_id();
939 let ctx = doc.parse_context(root_id);
940
941 let Err(err) = ctx.parse_union::<TestEnum, ParseError>(VariantRepr::default()) else {
942 panic!("Expected error");
943 };
944 assert_eq!(
945 err,
946 ParseError {
947 node_id: variant_node_id,
948 kind: ParseErrorKind::InvalidVariantPath("foo..bar".to_string()),
949 }
950 );
951 }
952
953 use crate::eure;
956 use crate::value::ObjectKey;
957
958 #[derive(Debug, PartialEq)]
959 enum ReprTestEnum {
960 A { value: i64 },
961 B { name: String },
962 }
963
964 fn parse_repr_test_enum(
965 ctx: &ParseContext<'_>,
966 repr: VariantRepr,
967 ) -> Result<ReprTestEnum, ParseError> {
968 ctx.parse_union(repr)?
969 .variant("a", |ctx: &ParseContext<'_>| {
970 let rec = ctx.parse_record()?;
971 let value: i64 = rec.parse_field("value")?;
972 rec.deny_unknown_fields()?;
973 Ok(ReprTestEnum::A { value })
974 })
975 .variant("b", |ctx: &ParseContext<'_>| {
976 let rec = ctx.parse_record()?;
977 let name: String = rec.parse_field("name")?;
978 rec.deny_unknown_fields()?;
979 Ok(ReprTestEnum::B { name })
980 })
981 .parse()
982 }
983
984 fn create_internal_repr_doc(type_val: &str, value: i64) -> EureDocument {
986 use num_bigint::BigInt;
987
988 let mut doc = EureDocument::new();
989 let root_id = doc.get_root_id();
990 doc.node_mut(root_id).content = NodeValue::Map(Default::default());
991
992 let type_node_id = doc
994 .add_map_child(ObjectKey::String("type".to_string()), root_id)
995 .unwrap()
996 .node_id;
997 doc.node_mut(type_node_id).content =
998 NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext(type_val.to_string())));
999
1000 let value_node_id = doc
1002 .add_map_child(ObjectKey::String("value".to_string()), root_id)
1003 .unwrap()
1004 .node_id;
1005 doc.node_mut(value_node_id).content =
1006 NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(value)));
1007
1008 doc
1009 }
1010
1011 fn create_external_repr_doc(variant_name: &str, value: i64) -> EureDocument {
1013 use num_bigint::BigInt;
1014
1015 let mut doc = EureDocument::new();
1016 let root_id = doc.get_root_id();
1017 doc.node_mut(root_id).content = NodeValue::Map(Default::default());
1018
1019 let variant_node_id = doc
1021 .add_map_child(ObjectKey::String(variant_name.to_string()), root_id)
1022 .unwrap()
1023 .node_id;
1024 doc.node_mut(variant_node_id).content = NodeValue::Map(Default::default());
1025
1026 let value_node_id = doc
1028 .add_map_child(ObjectKey::String("value".to_string()), variant_node_id)
1029 .unwrap()
1030 .node_id;
1031 doc.node_mut(value_node_id).content =
1032 NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(value)));
1033
1034 doc
1035 }
1036
1037 fn create_adjacent_repr_doc(type_val: &str, value: i64) -> EureDocument {
1039 use num_bigint::BigInt;
1040
1041 let mut doc = EureDocument::new();
1042 let root_id = doc.get_root_id();
1043 doc.node_mut(root_id).content = NodeValue::Map(Default::default());
1044
1045 let type_node_id = doc
1047 .add_map_child(ObjectKey::String("type".to_string()), root_id)
1048 .unwrap()
1049 .node_id;
1050 doc.node_mut(type_node_id).content =
1051 NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext(type_val.to_string())));
1052
1053 let content_node_id = doc
1055 .add_map_child(ObjectKey::String("content".to_string()), root_id)
1056 .unwrap()
1057 .node_id;
1058 doc.node_mut(content_node_id).content = NodeValue::Map(Default::default());
1059
1060 let value_node_id = doc
1062 .add_map_child(ObjectKey::String("value".to_string()), content_node_id)
1063 .unwrap()
1064 .node_id;
1065 doc.node_mut(value_node_id).content =
1066 NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(value)));
1067
1068 doc
1069 }
1070
1071 #[test]
1072 fn test_internal_repr_success() {
1073 let doc = create_internal_repr_doc("a", 42);
1076 let root_id = doc.get_root_id();
1077 let ctx = ParseContext::with_union_tag_mode(&doc, root_id, UnionTagMode::Repr);
1078
1079 let result = parse_repr_test_enum(
1080 &ctx,
1081 VariantRepr::Internal {
1082 tag: "type".to_string(),
1083 },
1084 );
1085 assert_eq!(result.unwrap(), ReprTestEnum::A { value: 42 });
1086 }
1087
1088 #[test]
1089 fn test_external_repr_success() {
1090 let doc = create_external_repr_doc("a", 42);
1093 let root_id = doc.get_root_id();
1094 let ctx = ParseContext::with_union_tag_mode(&doc, root_id, UnionTagMode::Repr);
1095
1096 let result = parse_repr_test_enum(&ctx, VariantRepr::External);
1097 assert_eq!(result.unwrap(), ReprTestEnum::A { value: 42 });
1098 }
1099
1100 #[test]
1101 fn test_adjacent_repr_success() {
1102 let doc = create_adjacent_repr_doc("a", 42);
1105 let root_id = doc.get_root_id();
1106 let ctx = ParseContext::with_union_tag_mode(&doc, root_id, UnionTagMode::Repr);
1107
1108 let result = parse_repr_test_enum(
1109 &ctx,
1110 VariantRepr::Adjacent {
1111 tag: "type".to_string(),
1112 content: "content".to_string(),
1113 },
1114 );
1115 assert_eq!(result.unwrap(), ReprTestEnum::A { value: 42 });
1116 }
1117
1118 #[test]
1119 fn test_repr_mode_ignores_variant_extension() {
1120 let mut doc = create_internal_repr_doc("a", 42);
1122 let root_id = doc.get_root_id();
1123
1124 let variant_node_id = doc
1126 .add_extension(identifier("variant"), root_id)
1127 .unwrap()
1128 .node_id;
1129 doc.node_mut(variant_node_id).content =
1130 NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext("b".to_string())));
1131
1132 let ctx = ParseContext::with_union_tag_mode(&doc, root_id, UnionTagMode::Repr);
1134
1135 let result = parse_repr_test_enum(
1136 &ctx,
1137 VariantRepr::Internal {
1138 tag: "type".to_string(),
1139 },
1140 );
1141 assert_eq!(result.unwrap(), ReprTestEnum::A { value: 42 });
1142 }
1143
1144 #[test]
1145 fn test_eure_mode_ignores_repr() {
1146 let doc = create_internal_repr_doc("a", 42);
1148 let root_id = doc.get_root_id();
1149
1150 let ctx = doc.parse_context(root_id);
1152
1153 let result = ctx
1156 .parse_union::<_, ParseError>(VariantRepr::Internal {
1157 tag: "type".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 rec.allow_unknown_fields()?;
1164 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 });
1175 }
1176
1177 #[test]
1178 fn test_internal_repr_unknown_variant_name() {
1179 let doc = create_internal_repr_doc("unknown", 42);
1182 let root_id = doc.get_root_id();
1183 let ctx = ParseContext::with_union_tag_mode(&doc, root_id, UnionTagMode::Repr);
1184
1185 let result = parse_repr_test_enum(
1186 &ctx,
1187 VariantRepr::Internal {
1188 tag: "type".to_string(),
1189 },
1190 );
1191
1192 let err = result.unwrap_err();
1194 assert_eq!(
1195 err.kind,
1196 ParseErrorKind::UnknownVariant("unknown".to_string())
1197 );
1198 }
1199
1200 #[test]
1201 fn test_repr_not_extracted_falls_back_to_untagged() {
1202 let doc = eure!({ value = 100, extra = "ignored" });
1205 let root_id = doc.get_root_id();
1206 let ctx = doc.parse_context(root_id);
1207
1208 let result = ctx
1210 .parse_union::<_, ParseError>(VariantRepr::External)
1211 .unwrap()
1212 .variant("a", |ctx: &ParseContext<'_>| {
1213 let rec = ctx.parse_record()?;
1214 let value: i64 = rec.parse_field("value")?;
1215 Ok(ReprTestEnum::A { value })
1217 })
1218 .variant("b", |ctx: &ParseContext<'_>| {
1219 let rec = ctx.parse_record()?;
1220 let name: String = rec.parse_field("name")?;
1221 rec.deny_unknown_fields()?;
1222 Ok(ReprTestEnum::B { name })
1223 })
1224 .parse();
1225
1226 assert_eq!(result.unwrap(), ReprTestEnum::A { value: 100 });
1228 }
1229
1230 #[test]
1231 fn test_external_repr_single_key_extracts_variant() {
1232 let doc = eure!({ value = 100 });
1235 let root_id = doc.get_root_id();
1236 let ctx = ParseContext::with_union_tag_mode(&doc, root_id, UnionTagMode::Repr);
1237
1238 let err: ParseError = ctx
1241 .parse_union(VariantRepr::External)
1242 .unwrap()
1243 .variant("a", |ctx: &ParseContext<'_>| {
1244 let rec = ctx.parse_record()?;
1245 let value: i64 = rec.parse_field("value")?;
1246 rec.deny_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!(
1259 err.kind,
1260 ParseErrorKind::UnknownVariant("value".to_string())
1261 );
1262 }
1263
1264 #[test]
1267 fn test_internal_repr_tag_is_integer_errors() {
1268 use num_bigint::BigInt;
1271
1272 let mut doc = EureDocument::new();
1273 let root_id = doc.get_root_id();
1274 doc.node_mut(root_id).content = NodeValue::Map(Default::default());
1275
1276 let type_node_id = doc
1278 .add_map_child(ObjectKey::String("type".to_string()), root_id)
1279 .unwrap()
1280 .node_id;
1281 doc.node_mut(type_node_id).content =
1282 NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(123)));
1283
1284 let value_node_id = doc
1286 .add_map_child(ObjectKey::String("value".to_string()), root_id)
1287 .unwrap()
1288 .node_id;
1289 doc.node_mut(value_node_id).content =
1290 NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(42)));
1291
1292 let ctx = ParseContext::with_union_tag_mode(&doc, root_id, UnionTagMode::Repr);
1293
1294 let Err(err) = ctx.parse_union::<ReprTestEnum, ParseError>(VariantRepr::Internal {
1296 tag: "type".to_string(),
1297 }) else {
1298 panic!("Expected error");
1299 };
1300
1301 assert_eq!(err.node_id, type_node_id);
1303 }
1304
1305 #[test]
1306 fn test_adjacent_repr_missing_content_falls_back_to_untagged() {
1307 let doc = create_internal_repr_doc("a", 42); let root_id = doc.get_root_id();
1311 let ctx = doc.parse_context(root_id);
1312
1313 let result = ctx
1315 .parse_union::<_, ParseError>(VariantRepr::Adjacent {
1316 tag: "type".to_string(),
1317 content: "content".to_string(),
1318 })
1319 .unwrap()
1320 .variant("a", |ctx: &ParseContext<'_>| {
1321 let rec = ctx.parse_record()?;
1322 let value: i64 = rec.parse_field("value")?;
1323 Ok(ReprTestEnum::A { value })
1325 })
1326 .variant("b", |ctx: &ParseContext<'_>| {
1327 let rec = ctx.parse_record()?;
1328 let name: String = rec.parse_field("name")?;
1329 rec.deny_unknown_fields()?;
1330 Ok(ReprTestEnum::B { name })
1331 })
1332 .parse();
1333
1334 assert_eq!(result.unwrap(), ReprTestEnum::A { value: 42 });
1336 }
1337
1338 #[test]
1339 fn test_external_repr_non_string_key_falls_back_to_untagged() {
1340 use num_bigint::BigInt;
1342
1343 let mut doc = EureDocument::new();
1344 let root_id = doc.get_root_id();
1345 doc.node_mut(root_id).content = NodeValue::Map(Default::default());
1346
1347 let variant_node_id = doc
1349 .add_map_child(ObjectKey::Number(BigInt::from(123)), root_id)
1350 .unwrap()
1351 .node_id;
1352 doc.node_mut(variant_node_id).content = NodeValue::Map(Default::default());
1353
1354 let value_node_id = doc
1356 .add_map_child(ObjectKey::String("value".to_string()), variant_node_id)
1357 .unwrap()
1358 .node_id;
1359 doc.node_mut(value_node_id).content =
1360 NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(42)));
1361
1362 let ctx = doc.parse_context(root_id);
1363
1364 let err: ParseError = ctx
1368 .parse_union(VariantRepr::External)
1369 .unwrap()
1370 .variant("a", |ctx: &ParseContext<'_>| {
1371 let rec = ctx.parse_record()?;
1372 let value: i64 = rec.parse_field("value")?;
1373 rec.deny_unknown_fields()?;
1374 Ok(ReprTestEnum::A { value })
1375 })
1376 .variant("b", |ctx: &ParseContext<'_>| {
1377 let rec = ctx.parse_record()?;
1378 let name: String = rec.parse_field("name")?;
1379 rec.deny_unknown_fields()?;
1380 Ok(ReprTestEnum::B { name })
1381 })
1382 .parse()
1383 .unwrap_err();
1384
1385 assert_eq!(err.kind, ParseErrorKind::MissingField("value".to_string()));
1387 }
1388
1389 #[test]
1390 fn test_eure_mode_uses_variant_extension_over_repr() {
1391 let mut doc = create_internal_repr_doc("a", 42);
1394 let root_id = doc.get_root_id();
1395
1396 let variant_node_id = doc
1398 .add_extension(identifier("variant"), root_id)
1399 .unwrap()
1400 .node_id;
1401 doc.node_mut(variant_node_id).content =
1402 NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext("b".to_string())));
1403
1404 let ctx = doc.parse_context(root_id);
1405
1406 let err: ParseError = ctx
1409 .parse_union(VariantRepr::Internal {
1410 tag: "type".to_string(),
1411 })
1412 .unwrap()
1413 .variant("a", |ctx: &ParseContext<'_>| {
1414 let rec = ctx.parse_record()?;
1415 let value: i64 = rec.parse_field("value")?;
1416 rec.allow_unknown_fields()?;
1417 Ok(ReprTestEnum::A { value })
1418 })
1419 .variant("b", |ctx: &ParseContext<'_>| {
1420 let rec = ctx.parse_record()?;
1421 let name: String = rec.parse_field("name")?;
1422 rec.deny_unknown_fields()?;
1423 Ok(ReprTestEnum::B { name })
1424 })
1425 .parse()
1426 .unwrap_err();
1427
1428 assert_eq!(err.kind, ParseErrorKind::MissingField("name".to_string()));
1430 }
1431
1432 #[test]
1433 fn test_variant_path_empty_uses_untagged() {
1434 let doc = create_text_doc("value");
1437 let root_id = doc.get_root_id();
1438 let ctx = doc.parse_context(root_id);
1439
1440 let child_ctx = ctx.with_variant_rest(Some(VariantPath::empty()));
1442
1443 let result: String = child_ctx
1445 .parse_union(VariantRepr::default())
1446 .unwrap()
1447 .variant("first", String::parse)
1448 .variant("second", String::parse)
1449 .parse()
1450 .unwrap();
1451
1452 assert_eq!(result, "value");
1454 }
1455}