diff --git a/bauble/src/parse/parser.rs b/bauble/src/parse/parser.rs index 60a4023..77bc902 100644 --- a/bauble/src/parse/parser.rs +++ b/bauble/src/parse/parser.rs @@ -484,11 +484,18 @@ pub fn parser<'a>() -> impl Parser<'a, ParserSource<'a>, ParseValues, Extra<'a>> |_| Vec::new(), ))); - let structure = ident + let field_ident = ident + .then(just("::").then(ident)) + .map_with(|(ident_a, (separator, ident_b)), extra| { + format!("{}{}{}", ident_a.value, separator, ident_b.value).spanned(extra.span()) + }) + .or(ident); + let field = field_ident .padded_by(comments) .padded() .then_ignore(just(':').padded().padded_by(comments)) - .then(object.clone()) + .then(object.clone()); + let structure = field .separated_by(separator) .allow_trailing() .collect::>() diff --git a/bauble/tests/integration.rs b/bauble/tests/integration.rs index 31588f7..1eea570 100644 --- a/bauble/tests/integration.rs +++ b/bauble/tests/integration.rs @@ -528,3 +528,102 @@ fn decimal_digits_identifiers() { &[a], ); } + +#[derive(PartialEq, Debug)] +struct TestNamespaceFieldIdent { + x: i32, + mynamespace_y: u32, +} + +impl<'alloc_lifetime> bauble::Bauble<'alloc_lifetime, bauble::DefaultAllocator> + for TestNamespaceFieldIdent +{ + fn construct_type(registry: &mut bauble::types::TypeRegistry) -> bauble::types::Type { + let path = + bauble::path::TypePath::new("integration::TestNamespaceFieldIdent".to_owned()).unwrap(); + let meta = bauble::types::TypeMeta { + path, + ..Default::default() + }; + + let x_field = ( + "x", + bauble::types::FieldType::from( + registry.get_or_register_type::(), + ), + ); + let mynamespace_y_field = ( + "mynamespace::y", + bauble::types::FieldType::from( + registry.get_or_register_type::(), + ), + ); + + bauble::types::Type { + meta, + kind: bauble::types::TypeKind::Struct(bauble::types::Fields::Named( + bauble::types::NamedFields::empty().with_required([x_field, mynamespace_y_field]), + )), + } + } + fn from_bauble( + bauble::Val { + attributes: + bauble::Spanned { + value: mut _attributes, + span: _attributes_span, + }, + value: bauble::Spanned { span, value }, + ty, + }: bauble::Val, + allocator: &bauble::DefaultAllocator, + ) -> Result< + >::Out, + bauble::ToRustError, + > { + let bauble::Value::Struct(bauble::FieldsKind::Named(mut fields)) = value else { + Err(Self::error( + span, + bauble::ToRustErrorKind::WrongType { found: ty }, + ))? + }; + + let mut take_field = |name: &str| { + fields.swap_remove(name).ok_or_else(|| { + Self::error( + span, + bauble::ToRustErrorKind::MissingField { + field: name.to_owned(), + }, + ) + }) + }; + + let x = bauble::Bauble::from_bauble(take_field("x")?, allocator) + .and_then(|res| unsafe { bauble::BaubleAllocator::validate(allocator, res) })?; + let mynamespace_y = + bauble::Bauble::from_bauble(take_field("mynamespace::y")?, allocator) + .and_then(|res| unsafe { bauble::BaubleAllocator::validate(allocator, res) })?; + let this = Self { x, mynamespace_y }; + Ok(unsafe { bauble::BaubleAllocator::wrap(allocator, this) }) + } +} + +#[test] +fn two_part_field() { + let a = &test_file!( + "a", + "test = integration::TestNamespaceFieldIdent{ x: -5, mynamespace::y: 5 }", + TestNamespaceFieldIdent { + x: -5, + mynamespace_y: 5 + }, + ); + + test_load( + &|ctx| { + ctx.register_type::(); + }, + &[a], + ); +}