@@ -148,3 +148,24 @@ fn empty_qname_check_has_precedence_over_attr_check() {
other => panic!("unexpected result: {:?}", other),
}
}
+
+static SOME_NAME: &::xso::exports::rxml::strings::NcNameStr = {
+ #[allow(unsafe_code)]
+ unsafe {
+ ::xso::exports::rxml::strings::NcNameStr::from_str_unchecked("bar")
+ }
+};
+
+#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
+#[xml(namespace = NS1, name = SOME_NAME)]
+struct NamePath;
+
+#[test]
+fn name_path_roundtrip() {
+ #[allow(unused_imports)]
+ use std::{
+ option::Option::{None, Some},
+ result::Result::{Err, Ok},
+ };
+ roundtrip_full::<NamePath>("<bar xmlns='urn:example:ns1'/>");
+}
@@ -77,8 +77,11 @@ fn from_xml_impl(input: Item) -> Result<TokenStream> {
let from_events_builder_ty_name = quote::format_ident!("{}FromEvents", ident);
let state_ty_name = quote::format_ident!("{}FromEventsState", ident);
- let unknown_attr_err = format!("Unknown attribute in {} element.", xml_name.as_str());
- let unknown_child_err = format!("Unknown child in {} element.", xml_name.as_str());
+ let unknown_attr_err = format!(
+ "Unknown attribute in {} element.",
+ xml_name.repr_to_string()
+ );
+ let unknown_child_err = format!("Unknown child in {} element.", xml_name.repr_to_string());
let docstr = format!("Build a [`{}`] from XML events", ident);
#[cfg_attr(not(feature = "minidom"), allow(unused_mut))]
@@ -215,7 +218,7 @@ fn into_xml_impl(input: Item) -> Result<TokenStream> {
::xso::exports::rxml::parser::EventMetrics::zero(),
(
::xso::exports::rxml::Namespace::from_str(#xml_namespace),
- ::xso::exports::rxml::NcName::from(#xml_name),
+ #xml_name.to_owned(),
),
::xso::exports::rxml::AttrMap::new(),
)))
@@ -9,6 +9,8 @@
//! This module is concerned with parsing attributes from the Rust "meta"
//! annotations on structs, enums, enum variants and fields.
+use std::borrow::Cow;
+
use proc_macro2::{Span, TokenStream};
use quote::{quote, quote_spanned};
use syn::{spanned::Spanned, *};
@@ -23,47 +25,73 @@ pub(crate) type NamespaceRef = Path;
/// Value for the `#[xml(name = .. )]` attribute.
#[derive(Debug)]
-pub(crate) struct NameRef {
- value: NcName,
- span: Span,
+pub(crate) enum NameRef {
+ /// The XML name is specified as a string literal.
+ Literal {
+ /// The validated XML name.
+ value: NcName,
+
+ /// The span of the original [`syn::LitStr`].
+ span: Span,
+ },
+
+ /// The XML name is specified as a path.
+ Path(Path),
}
impl NameRef {
- /// Access the XML name as str.
+ /// Access a representation of the XML name as str.
+ ///
+ /// If this name reference is a [`Self::Path`], this will return the name
+ /// of the rightmost identifier in the path.
///
- /// *Note*: This function may vanish in the future if we ever support
- /// non-literal XML names.
- pub(crate) fn as_str(&self) -> &str {
- self.value.as_str()
+ /// If this name reference is a [`Self::Literal`], this will return the
+ /// contents of the literal.
+ pub(crate) fn repr_to_string(&self) -> Cow<'_, str> {
+ match self {
+ Self::Literal { ref value, .. } => Cow::Borrowed(value.as_str()),
+ Self::Path(ref path) => path.segments.last().unwrap().ident.to_string().into(),
+ }
}
}
impl syn::parse::Parse for NameRef {
fn parse(input: syn::parse::ParseStream<'_>) -> Result<Self> {
- let s: LitStr = input.parse()?;
- let span = s.span();
- match NcName::try_from(s.value()) {
- Ok(value) => Ok(Self { value, span }),
- Err(e) => Err(Error::new(
- span,
- format!("not a valid XML element name: {}", e),
- )),
+ if input.peek(syn::LitStr) {
+ let s: LitStr = input.parse()?;
+ let span = s.span();
+ match NcName::try_from(s.value()) {
+ Ok(value) => Ok(Self::Literal { value, span }),
+ Err(e) => Err(Error::new(
+ span,
+ format!("not a valid XML element name: {}", e),
+ )),
+ }
+ } else {
+ let p: Path = input.parse()?;
+ Ok(Self::Path(p))
}
}
}
impl quote::ToTokens for NameRef {
fn to_tokens(&self, tokens: &mut TokenStream) {
- let value = self.value.as_str();
- let value = quote_spanned! { self.span=> #value };
- // SAFETY: self.0 is a known-good NcName, so converting it to an
- // NcNameStr is known to be safe.
- // NOTE: we cannot use `quote_spanned! { self.span=> }` for the unsafe
- // block as that would then in fact trip a `#[deny(unsafe_code)]` lint
- // at the use site of the macro.
- tokens.extend(quote! {
- unsafe { ::xso::exports::rxml::NcNameStr::from_str_unchecked(#value) }
- })
+ match self {
+ Self::Literal { ref value, span } => {
+ let span = *span;
+ let value = value.as_str();
+ let value = quote_spanned! { span=> #value };
+ // SAFETY: self.0 is a known-good NcName, so converting it to an
+ // NcNameStr is known to be safe.
+ // NOTE: we cannot use `quote_spanned! { self.span=> }` for the unsafe
+ // block as that would then in fact trip a `#[deny(unsafe_code)]` lint
+ // at the use site of the macro.
+ tokens.extend(quote! {
+ unsafe { ::xso::exports::rxml::NcNameStr::from_str_unchecked(#value) }
+ })
+ }
+ Self::Path(ref path) => path.to_tokens(tokens),
+ }
}
}
@@ -32,7 +32,7 @@ All key-value pairs interpreted by these derive macros must be wrapped in a
| Key | Value type | Description |
| --- | --- | --- |
| `namespace` | *path* | The path to a `&'static str` which holds the XML namespace to match. |
-| `name` | *string literal* | The XML element name to match. |
+| `name` | *string literal* or *path* | The XML element name to match. If it is a *path*, it must point at a `&'static NcNameStr`. |
Note that the `name` value must be a valid XML element name, without colons.
The namespace prefix, if any, is assigned automatically at serialisation time