mirror of
https://gitlab.com/xmpp-rs/xmpp-rs.git
synced 2024-07-12 22:21:53 +00:00
xso-proc: allow paths as XML names
Not sure if this is something useful to have, but it feels consistent with `namespace`.
This commit is contained in:
parent
1611c5fba9
commit
4d1166b66d
4 changed files with 82 additions and 30 deletions
|
@ -148,3 +148,24 @@ fn empty_qname_check_has_precedence_over_attr_check() {
|
||||||
other => panic!("unexpected result: {:?}", other),
|
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 from_events_builder_ty_name = quote::format_ident!("{}FromEvents", ident);
|
||||||
let state_ty_name = quote::format_ident!("{}FromEventsState", 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_attr_err = format!(
|
||||||
let unknown_child_err = format!("Unknown child in {} element.", xml_name.as_str());
|
"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);
|
let docstr = format!("Build a [`{}`] from XML events", ident);
|
||||||
|
|
||||||
#[cfg_attr(not(feature = "minidom"), allow(unused_mut))]
|
#[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::parser::EventMetrics::zero(),
|
||||||
(
|
(
|
||||||
::xso::exports::rxml::Namespace::from_str(#xml_namespace),
|
::xso::exports::rxml::Namespace::from_str(#xml_namespace),
|
||||||
::xso::exports::rxml::NcName::from(#xml_name),
|
#xml_name.to_owned(),
|
||||||
),
|
),
|
||||||
::xso::exports::rxml::AttrMap::new(),
|
::xso::exports::rxml::AttrMap::new(),
|
||||||
)))
|
)))
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
//! This module is concerned with parsing attributes from the Rust "meta"
|
//! This module is concerned with parsing attributes from the Rust "meta"
|
||||||
//! annotations on structs, enums, enum variants and fields.
|
//! annotations on structs, enums, enum variants and fields.
|
||||||
|
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use proc_macro2::{Span, TokenStream};
|
use proc_macro2::{Span, TokenStream};
|
||||||
use quote::{quote, quote_spanned};
|
use quote::{quote, quote_spanned};
|
||||||
use syn::{spanned::Spanned, *};
|
use syn::{spanned::Spanned, *};
|
||||||
|
@ -23,47 +25,73 @@ pub(crate) type NamespaceRef = Path;
|
||||||
|
|
||||||
/// Value for the `#[xml(name = .. )]` attribute.
|
/// Value for the `#[xml(name = .. )]` attribute.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct NameRef {
|
pub(crate) enum NameRef {
|
||||||
value: NcName,
|
/// The XML name is specified as a string literal.
|
||||||
span: Span,
|
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 {
|
impl NameRef {
|
||||||
/// Access the XML name as str.
|
/// Access a representation of the XML name as str.
|
||||||
///
|
///
|
||||||
/// *Note*: This function may vanish in the future if we ever support
|
/// If this name reference is a [`Self::Path`], this will return the name
|
||||||
/// non-literal XML names.
|
/// of the rightmost identifier in the path.
|
||||||
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 {
|
impl syn::parse::Parse for NameRef {
|
||||||
fn parse(input: syn::parse::ParseStream<'_>) -> Result<Self> {
|
fn parse(input: syn::parse::ParseStream<'_>) -> Result<Self> {
|
||||||
let s: LitStr = input.parse()?;
|
if input.peek(syn::LitStr) {
|
||||||
let span = s.span();
|
let s: LitStr = input.parse()?;
|
||||||
match NcName::try_from(s.value()) {
|
let span = s.span();
|
||||||
Ok(value) => Ok(Self { value, span }),
|
match NcName::try_from(s.value()) {
|
||||||
Err(e) => Err(Error::new(
|
Ok(value) => Ok(Self::Literal { value, span }),
|
||||||
span,
|
Err(e) => Err(Error::new(
|
||||||
format!("not a valid XML element name: {}", e),
|
span,
|
||||||
)),
|
format!("not a valid XML element name: {}", e),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let p: Path = input.parse()?;
|
||||||
|
Ok(Self::Path(p))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl quote::ToTokens for NameRef {
|
impl quote::ToTokens for NameRef {
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
let value = self.value.as_str();
|
match self {
|
||||||
let value = quote_spanned! { self.span=> #value };
|
Self::Literal { ref value, span } => {
|
||||||
// SAFETY: self.0 is a known-good NcName, so converting it to an
|
let span = *span;
|
||||||
// NcNameStr is known to be safe.
|
let value = value.as_str();
|
||||||
// NOTE: we cannot use `quote_spanned! { self.span=> }` for the unsafe
|
let value = quote_spanned! { span=> #value };
|
||||||
// block as that would then in fact trip a `#[deny(unsafe_code)]` lint
|
// SAFETY: self.0 is a known-good NcName, so converting it to an
|
||||||
// at the use site of the macro.
|
// NcNameStr is known to be safe.
|
||||||
tokens.extend(quote! {
|
// NOTE: we cannot use `quote_spanned! { self.span=> }` for the unsafe
|
||||||
unsafe { ::xso::exports::rxml::NcNameStr::from_str_unchecked(#value) }
|
// 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 |
|
| Key | Value type | Description |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `namespace` | *path* | The path to a `&'static str` which holds the XML namespace to match. |
|
| `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.
|
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
|
The namespace prefix, if any, is assigned automatically at serialisation time
|
||||||
|
|
Loading…
Reference in a new issue