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

@ -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)),
}
}