Track xml:lang throughout parsing

This commit is contained in:
Jonas Schäfer 2025-04-18 17:20:06 +02:00
commit d9a21dd8c9
21 changed files with 446 additions and 88 deletions

View file

@ -318,7 +318,7 @@ impl Compound {
substate_data,
&builder,
).with_mut(substate_data).with_impl(quote! {
match #feed(&mut #substate_data, ev)? {
match #feed(&mut #substate_data, ev, ctx)? {
::core::option::Option::Some(#substate_result) => {
#collect
::core::result::Result::Ok(::core::ops::ControlFlow::Break(Self::#default_state_ident {
@ -467,7 +467,7 @@ impl Compound {
substate_data,
&discard_builder_ty,
).with_mut(substate_data).with_impl(quote! {
match #discard_feed(&mut #substate_data, ev)? {
match #discard_feed(&mut #substate_data, ev, ctx)? {
::core::option::Option::Some(#substate_result) => {
::core::result::Result::Ok(::core::ops::ControlFlow::Break(Self::#default_state_ident {
#builder_data_ident,

View file

@ -547,7 +547,7 @@ impl ItemDef for EnumDef {
Ok(FromXmlParts {
defs,
from_events_body: quote! {
#builder_ty_ident::new(#name_ident, #attrs_ident)
#builder_ty_ident::new(#name_ident, #attrs_ident, ctx)
},
builder_ty_ident: builder_ty_ident.clone(),
})

View file

@ -68,7 +68,7 @@ impl Field for ChildField {
let from_events = from_events_fn(element_ty.clone());
let matcher = quote! { #from_events(name, attrs) };
let matcher = quote! { #from_events(name, attrs, ctx) };
let builder = from_xml_builder_ty(element_ty.clone());
(
@ -307,7 +307,7 @@ impl ExtractDef {
)?;
let from_xml_builder_ty = ty_from_ident(from_xml_builder_ty_ident.clone()).into();
let matcher = quote! { #state_ty_ident::new(name, attrs).map(|x| #from_xml_builder_ty_ident(::core::option::Option::Some(x))) };
let matcher = quote! { #state_ty_ident::new(name, attrs, ctx).map(|x| #from_xml_builder_ty_ident(::core::option::Option::Some(x))) };
let inner_ty = self.parts.to_single_or_tuple_ty();

View file

@ -80,7 +80,7 @@ impl Field for ElementField {
if #field_access.is_some() {
::core::result::Result::Err(::xso::error::FromEventsError::Mismatch { name, attrs })
} else {
#from_events(name, attrs)
#from_events(name, attrs, ctx)
}
}),
builder,

View file

@ -0,0 +1,72 @@
// Copyright (c) 2025 Jonas Schäfer <jonas@zombofant.net>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//! This module concerns the processing of inherited `xml:lang` values.
//!
//! In particular, it provides the `#[xml(lang)]` implementation.
use proc_macro2::Span;
use quote::quote;
use syn::*;
use crate::error_message::ParentRef;
use crate::scope::{AsItemsScope, FromEventsScope};
use crate::types::{as_optional_xml_text_fn, option_ty, string_ty};
use super::{Field, FieldBuilderPart, FieldIteratorPart, FieldTempInit};
/// The field maps to a potentially inherited `xml:lang` value.
pub(super) struct LangField;
impl Field for LangField {
fn make_builder_part(
&self,
_scope: &FromEventsScope,
_container_name: &ParentRef,
_member: &Member,
_ty: &Type,
) -> Result<FieldBuilderPart> {
let string_ty = string_ty(Span::call_site());
let ty = option_ty(string_ty.clone());
Ok(FieldBuilderPart::Init {
value: FieldTempInit {
ty,
init: quote! {
ctx.language().map(#string_ty::from).into()
},
},
})
}
fn make_iterator_part(
&self,
_scope: &AsItemsScope,
_container_name: &ParentRef,
bound_name: &Ident,
_member: &Member,
ty: &Type,
) -> Result<FieldIteratorPart> {
let as_optional_xml_text = as_optional_xml_text_fn(ty.clone());
Ok(FieldIteratorPart::Header {
generator: quote! {
#as_optional_xml_text(#bound_name)?.map(|#bound_name|
::xso::Item::Attribute(
::xso::exports::rxml::Namespace::XML,
// SAFETY: `lang` is a known-good NcName
unsafe {
::xso::exports::alloc::borrow::Cow::Borrowed(
::xso::exports::rxml::NcNameStr::from_str_unchecked("lang"),
)
},
#bound_name,
)
)
},
})
}
}

View file

@ -21,6 +21,7 @@ mod child;
#[cfg(feature = "minidom")]
mod element;
mod flag;
mod lang;
mod text;
use self::attribute::AttributeField;
@ -28,6 +29,7 @@ use self::child::{ChildField, ExtractDef};
#[cfg(feature = "minidom")]
use self::element::ElementField;
use self::flag::FlagField;
use self::lang::LangField;
use self::text::TextField;
/// Code slices necessary for declaring and initializing a temporary variable
@ -444,6 +446,8 @@ fn new_field(
xml_name,
}))
}
XmlFieldMeta::Language { span: _ } => Ok(Box::new(LangField)),
}
}

View file

@ -83,6 +83,7 @@ fn from_xml_impl(input: Item) -> Result<TokenStream> {
fn from_events(
name: ::xso::exports::rxml::QName,
attrs: ::xso::exports::rxml::AttrMap,
ctx: &::xso::Context<'_>,
) -> ::core::result::Result<Self::Builder, ::xso::error::FromEventsError> {
#from_events_body
}

View file

@ -874,6 +874,14 @@ pub(crate) enum XmlFieldMeta {
/// The namespace/name keys.
qname: QNameRef,
},
/// `#[xml(lang)]`
Language {
/// The span of the `#[xml(lang)]` meta from which this was parsed.
///
/// This is useful for error messages.
span: Span,
},
}
impl XmlFieldMeta {
@ -1182,6 +1190,13 @@ impl XmlFieldMeta {
})
}
/// Parse a `#[xml(lang)]` meta.
fn lang_from_meta(meta: ParseNestedMeta<'_>) -> Result<Self> {
Ok(Self::Language {
span: meta.path.span(),
})
}
/// Parse [`Self`] from a nestd meta, switching on the identifier
/// of that nested meta.
fn parse_from_meta(meta: ParseNestedMeta<'_>) -> Result<Self> {
@ -1197,6 +1212,8 @@ impl XmlFieldMeta {
Self::element_from_meta(meta)
} else if meta.path.is_ident("flag") {
Self::flag_from_meta(meta)
} else if meta.path.is_ident("lang") {
Self::lang_from_meta(meta)
} else {
Err(Error::new_spanned(meta.path, "unsupported field meta"))
}
@ -1280,6 +1297,7 @@ impl XmlFieldMeta {
Self::Extract { ref span, .. } => *span,
Self::Element { ref span, .. } => *span,
Self::Flag { ref span, .. } => *span,
Self::Language { ref span, .. } => *span,
}
}

View file

@ -509,7 +509,7 @@ impl FromEventsStateMachine {
}
impl #state_ty_ident {
fn advance(mut self, ev: ::xso::exports::rxml::Event) -> ::core::result::Result<::core::ops::ControlFlow<Self, #output_ty>, ::xso::error::Error> {
fn advance(mut self, ev: ::xso::exports::rxml::Event, ctx: &::xso::Context<'_>) -> ::core::result::Result<::core::ops::ControlFlow<Self, #output_ty>, ::xso::error::Error> {
match self {
#advance_match_arms
}.and_then(|__ok| {
@ -527,8 +527,9 @@ impl FromEventsStateMachine {
fn new(
name: ::xso::exports::rxml::QName,
attrs: ::xso::exports::rxml::AttrMap,
ctx: &::xso::Context<'_>,
) -> ::core::result::Result<Self, ::xso::error::FromEventsError> {
#state_ty_ident::new(name, attrs).map(|ok| Self(::core::option::Option::Some(ok)))
#state_ty_ident::new(name, attrs, ctx).map(|ok| Self(::core::option::Option::Some(ok)))
}
}
@ -539,9 +540,9 @@ impl FromEventsStateMachine {
impl ::xso::FromEventsBuilder for #builder_ty_ident {
type Output = #output_ty;
fn feed(&mut self, ev: ::xso::exports::rxml::Event) -> ::core::result::Result<::core::option::Option<Self::Output>, ::xso::error::Error> {
fn feed(&mut self, ev: ::xso::exports::rxml::Event, ctx: &::xso::Context<'_>) -> ::core::result::Result<::core::option::Option<Self::Output>, ::xso::error::Error> {
let inner = self.0.take().expect("feed called after completion");
match inner.advance(ev)? {
match inner.advance(ev, ctx)? {
::core::ops::ControlFlow::Continue(mut value) => {
#validate_call
::core::result::Result::Ok(::core::option::Option::Some(value))
@ -558,6 +559,7 @@ impl FromEventsStateMachine {
fn new(
name: ::xso::exports::rxml::QName,
mut attrs: ::xso::exports::rxml::AttrMap,
ctx: &::xso::Context<'_>,
) -> ::core::result::Result<Self, ::xso::error::FromEventsError> {
#init_body
{ let _ = &mut attrs; }

View file

@ -200,7 +200,7 @@ impl StructInner {
&from_xml_builder_ty,
)
.with_impl(quote! {
match #feed_fn(&mut #builder_data_ident, ev)? {
match #feed_fn(&mut #builder_data_ident, ev, ctx)? {
::core::option::Option::Some(result) => {
::core::result::Result::Ok(::core::ops::ControlFlow::Continue(#output_cons))
}
@ -213,7 +213,7 @@ impl StructInner {
})
],
init: quote! {
#from_events_fn(name, attrs).map(|#builder_data_ident| Self::#state_name { #builder_data_ident })
#from_events_fn(name, attrs, ctx).map(|#builder_data_ident| Self::#state_name { #builder_data_ident })
},
})
}
@ -392,7 +392,7 @@ impl ItemDef for StructDef {
Ok(FromXmlParts {
defs,
from_events_body: quote! {
#builder_ty_ident::new(#name_ident, #attrs_ident)
#builder_ty_ident::new(#name_ident, #attrs_ident, ctx)
},
builder_ty_ident: builder_ty_ident.clone(),
})