extern crate alloc;
use alloc::boxed::Box;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use crate::document::{EureDocument, NodeId};
use crate::identifier::Identifier;
use crate::parse::{DocumentParser, FromEure};
use super::variant_path::VariantPath;
use super::{
AccessedSet, AccessedSnapshot, BestParseVariantMatch, ParseContext, ParseError, ParseErrorKind,
UnionParseError,
};
pub const VARIANT: Identifier = Identifier::new_unchecked("variant");
pub fn extract_explicit_variant_path(
doc: &EureDocument,
node_id: NodeId,
) -> Result<Option<VariantPath>, ParseError> {
let node = doc.node(node_id);
let Some(&variant_node_id) = node.extensions.get(&VARIANT) else {
return Ok(None);
};
let variant_node = doc.node(variant_node_id);
let s: &str = doc.parse(variant_node_id).map_err(|_| ParseError {
node_id: variant_node_id,
kind: ParseErrorKind::InvalidVariantType(variant_node.content.value_kind()),
})?;
VariantPath::parse(s).map(Some).map_err(|_| ParseError {
node_id: variant_node_id,
kind: ParseErrorKind::InvalidVariantPath(s.to_string()),
})
}
pub fn has_explicit_variant_tag(doc: &EureDocument, node_id: NodeId) -> Result<bool, ParseError> {
Ok(extract_explicit_variant_path(doc, node_id)?.is_some())
}
pub struct UnionParser<'doc, 'ctx, T, E = ParseError> {
ctx: &'ctx ParseContext<'doc>,
variant: Option<(String, ParseContext<'doc>, Option<VariantPath>)>,
variant_result: Option<Result<T, E>>,
priority_result: Option<T>,
other_results: Vec<(String, T, AccessedSnapshot)>,
failures: Vec<(String, E)>,
accessed: AccessedSet,
}
impl<'doc, 'ctx, T, E> UnionParser<'doc, 'ctx, T, E>
where
E: UnionParseError,
{
pub(crate) fn new(ctx: &'ctx ParseContext<'doc>) -> Result<Self, ParseError> {
let variant = Self::resolve_variant(ctx)?;
let accessed = ctx.accessed().clone();
accessed.push_snapshot();
Ok(Self {
ctx,
variant,
variant_result: None,
priority_result: None,
other_results: Vec::new(),
failures: Vec::new(),
accessed,
})
}
fn resolve_variant(
ctx: &ParseContext<'doc>,
) -> Result<Option<(String, ParseContext<'doc>, Option<VariantPath>)>, ParseError> {
let explicit_variant = match ctx.variant_path() {
Some(vp) if !vp.is_empty() => Some(vp.clone()),
Some(_) => None, None => {
let variant = Self::extract_explicit_variant(ctx)?;
if variant.is_some() {
ctx.accessed().add_ext(VARIANT.clone());
}
variant
}
};
match explicit_variant {
Some(ev) => {
let name = ev
.first()
.map(|i| i.as_ref().to_string())
.unwrap_or_default();
let rest = ev.rest().unwrap_or_else(VariantPath::empty);
Ok(Some((name, ctx.clone(), Some(rest))))
}
None => Ok(None),
}
}
fn extract_explicit_variant(
ctx: &ParseContext<'doc>,
) -> Result<Option<VariantPath>, ParseError> {
extract_explicit_variant_path(ctx.doc(), ctx.node_id())
}
pub fn variant<P: DocumentParser<'doc, Output = T, Error = E>>(
mut self,
name: &str,
f: P,
) -> Self {
self.try_variant(name, f, true);
self
}
pub fn parse_variant<V: FromEure<'doc, Error = E>>(
mut self,
name: &str,
mut then: impl FnMut(V) -> Result<T, E>,
) -> Self {
self.try_variant(
name,
move |ctx: &ParseContext<'doc>| {
let v = V::parse(ctx)?;
then(v)
},
true,
);
self
}
pub fn variant_unambiguous<P: DocumentParser<'doc, Output = T, Error = E>>(
mut self,
name: &str,
f: P,
) -> Self {
self.try_variant(name, f, false);
self
}
pub fn parse_variant_unambiguous<V: FromEure<'doc, Error = E>>(
mut self,
name: &str,
mut then: impl FnMut(V) -> Result<T, E>,
) -> Self {
self.try_variant(
name,
move |ctx: &ParseContext<'doc>| {
let v = V::parse(ctx)?;
then(v)
},
false,
);
self
}
fn try_variant<P: DocumentParser<'doc, Output = T, Error = E>>(
&mut self,
name: &str,
mut f: P,
is_priority: bool,
) {
if let Some((ref v_name, ref v_ctx, ref rest)) = self.variant {
if v_name == name && self.variant_result.is_none() {
let child_ctx = v_ctx.with_variant_rest(rest.clone());
let result = f.parse(&child_ctx);
self.variant_result = Some(result);
}
return;
}
if self.priority_result.is_some() {
return;
}
let child_ctx = self.ctx.with_variant_rest(None);
match f.parse(&child_ctx) {
Ok(value) => {
if is_priority {
self.priority_result = Some(value);
} else {
let captured = self.accessed.capture_current_state();
self.accessed.restore_to_current_snapshot();
self.other_results.push((name.to_string(), value, captured));
}
}
Err(e) => {
self.accessed.restore_to_current_snapshot();
self.failures.push((name.to_string(), e));
}
}
}
pub fn parse(self) -> Result<T, E> {
let node_id = self.ctx.node_id();
if let Some((v_name, _, _)) = self.variant {
let result = self.variant_result.unwrap_or_else(|| {
Err(ParseError {
node_id,
kind: ParseErrorKind::UnknownVariant(v_name),
}
.into())
});
match &result {
Ok(_) => self.accessed.pop_without_restore(),
Err(_) => self.accessed.pop_and_restore(),
}
return result;
}
if let Some(value) = self.priority_result {
self.accessed.pop_without_restore();
return Ok(value);
}
match self.other_results.len() {
0 => {
self.accessed.pop_and_restore();
Err(self.no_match_error(node_id))
}
1 => {
let (_, value, captured_state) = self.other_results.into_iter().next().unwrap();
self.accessed.restore_to_state(captured_state);
self.accessed.pop_without_restore();
Ok(value)
}
_ => {
self.accessed.pop_and_restore();
Err(ParseError {
node_id,
kind: ParseErrorKind::AmbiguousUnion(
self.other_results
.into_iter()
.map(|(name, _, _)| name)
.collect(),
),
}
.into())
}
}
}
fn no_match_error(self, node_id: crate::document::NodeId) -> E {
E::from_no_matching_variant(
node_id,
None,
select_best_parse_variant_match(&self.failures),
&self.failures,
)
}
}
fn select_best_parse_variant_match<E>(failures: &[(String, E)]) -> Option<BestParseVariantMatch>
where
E: UnionParseError,
{
failures
.iter()
.filter_map(|(variant_name, error)| {
error
.as_parse_error()
.map(|parse_error| (variant_name, parse_error))
})
.max_by_key(|(_, parse_error)| parse_error_match_metrics(parse_error))
.map(|(variant_name, parse_error)| BestParseVariantMatch {
variant_name: variant_name.clone(),
error: Box::new(parse_error.clone()),
})
}
fn parse_error_match_metrics(error: &ParseError) -> (bool, usize, u8) {
parse_error_kind_metrics(&error.kind)
}
fn parse_error_kind_metrics(kind: &ParseErrorKind) -> (bool, usize, u8) {
match kind {
ParseErrorKind::Nested { source, .. } => {
let (structural, depth, priority) = parse_error_kind_metrics(source);
(structural, depth + 1, priority)
}
ParseErrorKind::NoMatchingVariant {
best_match: Some(best),
..
} => {
let (structural, depth, priority) = parse_error_match_metrics(&best.error);
(structural, depth + 1, priority)
}
_ => (
is_structural_parse_mismatch(kind),
1,
parse_error_priority(kind),
),
}
}
fn is_structural_parse_mismatch(kind: &ParseErrorKind) -> bool {
matches!(
kind,
ParseErrorKind::MissingField(_)
| ParseErrorKind::MissingExtension(_)
| ParseErrorKind::UnknownField(_)
| ParseErrorKind::UnknownExtension(_)
| ParseErrorKind::LiteralMismatch { .. }
| ParseErrorKind::InvalidPattern { .. }
)
}
fn parse_error_priority(kind: &ParseErrorKind) -> u8 {
match kind {
ParseErrorKind::UnknownField(_) | ParseErrorKind::UnknownExtension(_) => 4,
ParseErrorKind::MissingField(_) | ParseErrorKind::MissingExtension(_) => 3,
ParseErrorKind::UnknownVariant(_) | ParseErrorKind::UnexpectedVariantPath(_) => 2,
ParseErrorKind::LiteralMismatch { .. } | ParseErrorKind::InvalidPattern { .. } => 2,
ParseErrorKind::TypeMismatch { .. }
| ParseErrorKind::UnexpectedTupleLength { .. }
| ParseErrorKind::UnexpectedArrayLength { .. }
| ParseErrorKind::NotPrimitive { .. } => 1,
_ => 0,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::eure;
use crate::parse::AlwaysParser;
use crate::parse::DocumentParserExt as _;
#[derive(Debug, PartialEq, Clone)]
enum TestEnum {
Foo,
Bar,
}
#[test]
fn test_union_single_match() {
let doc = eure!({ = "foo" });
let root_id = doc.get_root_id();
let ctx = doc.parse_context(root_id);
let result: TestEnum = ctx
.parse_union()
.unwrap()
.variant("foo", |ctx: &ParseContext<'_>| {
let s: &str = ctx.parse()?;
if s == "foo" {
Ok(TestEnum::Foo)
} else {
Err(ParseError {
node_id: ctx.node_id(),
kind: ParseErrorKind::UnknownVariant(s.to_string()),
})
}
})
.variant("bar", |ctx: &ParseContext<'_>| {
let s: &str = ctx.parse()?;
if s == "bar" {
Ok(TestEnum::Bar)
} else {
Err(ParseError {
node_id: ctx.node_id(),
kind: ParseErrorKind::UnknownVariant(s.to_string()),
})
}
})
.parse()
.unwrap();
assert_eq!(result, TestEnum::Foo);
}
#[test]
fn test_union_priority_short_circuit() {
let doc = eure!({ = "value" });
let root_id = doc.get_root_id();
let ctx = doc.parse_context(root_id);
let result: String = ctx
.parse_union()
.unwrap()
.variant("first", String::parse)
.variant("second", String::parse)
.parse()
.unwrap();
assert_eq!(result, "value");
}
#[test]
fn test_union_no_match() {
let doc = eure!({ = "baz" });
let root_id = doc.get_root_id();
let ctx = doc.parse_context(root_id);
let result: Result<TestEnum, ParseError> = ctx
.parse_union()
.unwrap()
.variant("foo", |ctx: &ParseContext<'_>| {
let s: &str = ctx.parse()?;
if s == "foo" {
Ok(TestEnum::Foo)
} else {
Err(ParseError {
node_id: ctx.node_id(),
kind: ParseErrorKind::UnknownVariant(s.to_string()),
})
}
})
.parse();
assert!(result.is_err());
}
#[test]
fn test_variant_extension_match_success() {
let doc = eure!({ %variant = "baz", = "anything" });
let root_id = doc.get_root_id();
let ctx = doc.parse_context(root_id);
let result: TestEnum = ctx
.parse_union()
.unwrap()
.variant(
"foo",
AlwaysParser::<TestEnum, ParseError>::new(TestEnum::Foo),
)
.variant_unambiguous("baz", AlwaysParser::new(TestEnum::Bar))
.parse()
.unwrap();
assert_eq!(result, TestEnum::Bar);
}
#[test]
fn test_variant_extension_unknown() {
let doc = eure!({ %variant = "unknown", = "anything" });
let root_id = doc.get_root_id();
let ctx = doc.parse_context(root_id);
let err: ParseError = ctx
.parse_union()
.unwrap()
.variant("foo", AlwaysParser::new(TestEnum::Foo))
.variant_unambiguous("baz", AlwaysParser::new(TestEnum::Bar))
.parse()
.unwrap_err();
assert_eq!(err.node_id, root_id);
assert_eq!(
err.kind,
ParseErrorKind::UnknownVariant("unknown".to_string())
);
}
#[test]
fn test_variant_extension_match_parse_failure() {
let doc = eure!({ %variant = "baz", = "anything" });
let root_id = doc.get_root_id();
let ctx = doc.parse_context(root_id);
let err = ctx
.parse_union()
.unwrap()
.variant("foo", AlwaysParser::new(TestEnum::Foo))
.variant_unambiguous("baz", |ctx: &ParseContext<'_>| {
Err(ParseError {
node_id: ctx.node_id(),
kind: ParseErrorKind::MissingField("test".to_string()),
})
})
.parse()
.unwrap_err();
assert_eq!(err.node_id, root_id);
assert_eq!(err.kind, ParseErrorKind::MissingField("test".to_string()));
}
#[test]
fn test_union_rolls_back_accessed_fields_between_untagged_variants() {
let doc = eure!({ age = 42 });
let root_id = doc.get_root_id();
let ctx = doc.parse_context(root_id);
let err = ctx
.parse_union()
.unwrap()
.variant("full", |ctx: &ParseContext<'_>| {
let rec = ctx.parse_record()?;
let _: Option<i64> = rec.parse_field_optional("age")?;
let name: Option<String> = rec.parse_field_optional("name")?;
rec.deny_unknown_fields()?;
if name.is_none() {
return Err(ParseError {
node_id: ctx.node_id(),
kind: ParseErrorKind::MissingField("name".to_string()),
});
}
Ok(TestEnum::Foo)
})
.variant("minimal", |ctx: &ParseContext<'_>| {
let rec = ctx.parse_record()?;
let unknown_fields: Vec<_> = rec
.unknown_fields()
.filter_map(|result| match result {
Ok((field_name, _)) => Some(field_name.to_string()),
Err(_) => None,
})
.collect();
if unknown_fields.iter().any(|field| field == "age") {
return Err(ParseError {
node_id: ctx.node_id(),
kind: ParseErrorKind::UnknownField("age".to_string()),
});
}
let name: Option<String> = rec.parse_field_optional("name")?;
rec.deny_unknown_fields()?;
if name.is_none() {
return Err(ParseError {
node_id: ctx.node_id(),
kind: ParseErrorKind::MissingField("name".to_string()),
});
}
Ok(TestEnum::Bar)
})
.parse()
.unwrap_err();
let ParseErrorKind::NoMatchingVariant {
best_match: Some(best_match),
..
} = err.kind
else {
panic!("expected no matching variant with best match, got {err:?}");
};
assert_eq!(best_match.variant_name, "minimal");
assert_eq!(
best_match.error.kind,
ParseErrorKind::UnknownField("age".to_string())
);
}
#[derive(Debug, PartialEq, Clone)]
enum Outer {
A(Inner),
B(i32),
}
#[derive(Debug, PartialEq, Clone)]
enum Inner {
X,
Y,
}
fn parse_inner(ctx: &ParseContext<'_>) -> Result<Inner, ParseError> {
ctx.parse_union()
.unwrap()
.variant("x", AlwaysParser::new(Inner::X))
.variant("y", AlwaysParser::new(Inner::Y))
.parse()
}
#[test]
fn test_variant_nested_single_segment() {
let doc = eure!({ %variant = "a", = "value" });
let root_id = doc.get_root_id();
let ctx = doc.parse_context(root_id);
let result: Outer = ctx
.parse_union()
.unwrap()
.variant("a", parse_inner.map(Outer::A))
.variant("b", AlwaysParser::new(Outer::B(42)))
.parse()
.unwrap();
assert_eq!(result, Outer::A(Inner::X));
}
#[test]
fn test_variant_nested_multi_segment() {
let doc = eure!({ %variant = "a.y", = "value" });
let root_id = doc.get_root_id();
let ctx = doc.parse_context(root_id);
let result: Outer = ctx
.parse_union()
.unwrap()
.variant("a", parse_inner.map(Outer::A))
.variant("b", AlwaysParser::new(Outer::B(42)))
.parse()
.unwrap();
assert_eq!(result, Outer::A(Inner::Y));
}
#[test]
fn test_variant_nested_invalid_inner() {
let doc = eure!({ %variant = "a.z", = "value" });
let root_id = doc.get_root_id();
let ctx = doc.parse_context(root_id);
let err = ctx
.parse_union()
.unwrap()
.variant("a", parse_inner.map(Outer::A))
.variant("b", AlwaysParser::new(Outer::B(42)))
.parse()
.unwrap_err();
assert_eq!(err.kind, ParseErrorKind::UnknownVariant("z".to_string()));
}
#[test]
fn test_variant_non_nested_with_nested_path() {
let doc = eure!({ %variant = "b.x", = "value" });
let root_id = doc.get_root_id();
let ctx = doc.parse_context(root_id);
let err = ctx
.parse_union()
.unwrap()
.variant("a", parse_inner.map(Outer::A))
.variant("b", |ctx: &ParseContext<'_>| {
ctx.parse_primitive()?;
Ok(Outer::B(42))
})
.parse()
.unwrap_err();
assert!(matches!(err.kind, ParseErrorKind::UnexpectedVariantPath(_)));
}
use crate::value::ValueKind;
#[test]
fn test_invalid_variant_type_errors() {
use crate::document::node::NodeValue;
use crate::value::PrimitiveValue;
use num_bigint::BigInt;
let mut doc = eure!({ = "foo" });
let root_id = doc.get_root_id();
let variant_node_id = doc
.add_extension("variant".parse().unwrap(), root_id)
.unwrap()
.node_id;
doc.node_mut(variant_node_id).content =
NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(123)));
let ctx = doc.parse_context(root_id);
let Err(err) = ctx.parse_union::<TestEnum, ParseError>() else {
panic!("Expected error");
};
assert_eq!(
err,
ParseError {
node_id: variant_node_id,
kind: ParseErrorKind::InvalidVariantType(ValueKind::Integer),
}
);
}
#[test]
fn test_invalid_variant_path_syntax_errors() {
let doc = eure!({ %variant = "foo..bar", = "foo" });
let root_id = doc.get_root_id();
let variant_node_id = *doc.node(root_id).extensions.get(&VARIANT).unwrap();
let ctx = doc.parse_context(root_id);
let Err(err) = ctx.parse_union::<TestEnum, ParseError>() else {
panic!("Expected error");
};
assert_eq!(
err,
ParseError {
node_id: variant_node_id,
kind: ParseErrorKind::InvalidVariantPath("foo..bar".to_string()),
}
);
}
#[test]
fn test_variant_path_empty_uses_untagged() {
let doc = eure!({ = "value" });
let root_id = doc.get_root_id();
let ctx = doc.parse_context(root_id);
let child_ctx = ctx.with_variant_rest(Some(VariantPath::empty()));
let result: String = child_ctx
.parse_union()
.unwrap()
.variant("first", String::parse)
.variant("second", String::parse)
.parse()
.unwrap();
assert_eq!(result, "value");
}
#[derive(Debug, PartialEq, Clone)]
enum OuterUnion {
Normal(InnerUnion),
List(Vec<InnerUnion>),
}
#[derive(Debug, PartialEq, Clone)]
enum InnerUnion {
Text(String),
Number(i64),
}
fn parse_inner_union(ctx: &ParseContext<'_>) -> Result<InnerUnion, ParseError> {
ctx.parse_union()?
.variant("text", |ctx: &ParseContext<'_>| {
let s: String = ctx.parse()?;
Ok(InnerUnion::Text(s))
})
.variant("number", |ctx: &ParseContext<'_>| {
let n: i64 = ctx.parse()?;
Ok(InnerUnion::Number(n))
})
.parse()
}
fn parse_outer_union(ctx: &ParseContext<'_>) -> Result<OuterUnion, ParseError> {
use crate::document::node::NodeArray;
ctx.parse_union()?
.variant("normal", |ctx: &ParseContext<'_>| {
let inner = parse_inner_union(ctx)?;
Ok(OuterUnion::Normal(inner))
})
.variant("list", |ctx: &ParseContext<'_>| {
let arr: &NodeArray = ctx.parse()?;
let items: Result<Vec<InnerUnion>, _> = arr
.iter()
.map(|&node_id| parse_inner_union(&ctx.at(node_id)))
.collect();
Ok(OuterUnion::List(items?))
})
.parse()
}
#[test]
fn test_nested_union_basic_text() {
let doc = eure!({ = "hello" });
let root_id = doc.get_root_id();
let ctx = doc.parse_context(root_id);
let result = parse_outer_union(&ctx).unwrap();
assert_eq!(
result,
OuterUnion::Normal(InnerUnion::Text("hello".to_string()))
);
}
#[test]
fn test_nested_union_basic_number() {
let doc = eure!({ = 42 });
let root_id = doc.get_root_id();
let ctx = doc.parse_context(root_id);
let result = parse_outer_union(&ctx).unwrap();
assert_eq!(result, OuterUnion::Normal(InnerUnion::Number(42)));
}
#[test]
fn test_nested_union_variant_path_propagation() {
let doc = eure!({ %variant = "normal.text", = "test value" });
let root_id = doc.get_root_id();
let ctx = doc.parse_context(root_id);
let result = parse_outer_union(&ctx).unwrap();
assert_eq!(
result,
OuterUnion::Normal(InnerUnion::Text("test value".to_string()))
);
}
#[test]
fn test_nested_union_variant_path_number() {
let doc = eure!({ %variant = "normal.number", = 99 });
let root_id = doc.get_root_id();
let ctx = doc.parse_context(root_id);
let result = parse_outer_union(&ctx).unwrap();
assert_eq!(result, OuterUnion::Normal(InnerUnion::Number(99)));
}
#[test]
fn test_nested_union_inner_fails_outer_recovers() {
let doc = eure!({ = ["a", "b"] });
let root_id = doc.get_root_id();
let ctx = doc.parse_context(root_id);
let result = parse_outer_union(&ctx).unwrap();
assert_eq!(
result,
OuterUnion::List(alloc::vec![
InnerUnion::Text("a".to_string()),
InnerUnion::Text("b".to_string()),
])
);
}
#[derive(Debug, PartialEq, Clone)]
enum Level1 {
A(Level2Union),
B(String),
}
#[derive(Debug, PartialEq, Clone)]
enum Level2Union {
X(Level3),
Y(i64),
}
#[derive(Debug, PartialEq, Clone)]
enum Level3 {
Leaf(String),
}
fn parse_level3(ctx: &ParseContext<'_>) -> Result<Level3, ParseError> {
ctx.parse_union()?
.variant("leaf", |ctx: &ParseContext<'_>| {
let s: String = ctx.parse()?;
Ok(Level3::Leaf(s))
})
.parse()
}
fn parse_level2(ctx: &ParseContext<'_>) -> Result<Level2Union, ParseError> {
ctx.parse_union()?
.variant("x", |ctx: &ParseContext<'_>| {
let inner = parse_level3(ctx)?;
Ok(Level2Union::X(inner))
})
.variant("y", |ctx: &ParseContext<'_>| {
let n: i64 = ctx.parse()?;
Ok(Level2Union::Y(n))
})
.parse()
}
fn parse_level1(ctx: &ParseContext<'_>) -> Result<Level1, ParseError> {
ctx.parse_union()?
.variant("a", |ctx: &ParseContext<'_>| {
let inner = parse_level2(ctx)?;
Ok(Level1::A(inner))
})
.variant("b", |ctx: &ParseContext<'_>| {
let s: String = ctx.parse()?;
Ok(Level1::B(s))
})
.parse()
}
#[test]
fn test_nested_union_three_levels_untagged() {
let doc = eure!({ = "deep value" });
let root_id = doc.get_root_id();
let ctx = doc.parse_context(root_id);
let result = parse_level1(&ctx).unwrap();
assert_eq!(
result,
Level1::A(Level2Union::X(Level3::Leaf("deep value".to_string())))
);
}
#[test]
fn test_nested_union_three_levels_variant_path() {
let doc = eure!({ %variant = "a.x.leaf", = "explicit deep" });
let root_id = doc.get_root_id();
let ctx = doc.parse_context(root_id);
let result = parse_level1(&ctx).unwrap();
assert_eq!(
result,
Level1::A(Level2Union::X(Level3::Leaf("explicit deep".to_string())))
);
}
#[test]
fn test_nested_union_three_levels_variant_path_partial() {
let doc = eure!({ %variant = "a.y", = 123 });
let root_id = doc.get_root_id();
let ctx = doc.parse_context(root_id);
let result = parse_level1(&ctx).unwrap();
assert_eq!(result, Level1::A(Level2Union::Y(123)));
}
#[test]
fn test_nested_union_invalid_inner_variant_path() {
let doc = eure!({ %variant = "a.x.invalid", = "value" });
let root_id = doc.get_root_id();
let ctx = doc.parse_context(root_id);
let err = parse_level1(&ctx).unwrap_err();
assert_eq!(
err.kind,
ParseErrorKind::UnknownVariant("invalid".to_string())
);
}
#[test]
fn test_flatten_nested_union_accessed_fields_basic() {
use crate::parse::AccessedSet;
use crate::parse::FlattenContext;
use crate::parse::ParserScope;
let doc = eure!({
field_a = "value_a"
field_b = "value_b"
});
let root_id = doc.get_root_id();
let flatten_ctx = FlattenContext::new(AccessedSet::new(), ParserScope::Record);
let ctx = ParseContext::with_flatten_ctx(&doc, root_id, flatten_ctx.clone());
let record = ctx.parse_record().unwrap();
let _field_a: String = record.parse_field("field_a").unwrap();
let (accessed, _) = flatten_ctx.capture_current_state();
assert!(accessed.contains("field_a"));
assert!(!accessed.contains("field_b"));
}
}