mirror of
https://gitlab.com/xmpp-rs/xmpp-rs.git
synced 2024-07-12 22:21:53 +00:00
xso-proc: validate XML names against rxml_validation::NcName
That way we avoid a fallible conversion at runtime.
This commit is contained in:
parent
bc785fde28
commit
1611c5fba9
4 changed files with 65 additions and 17 deletions
|
@ -18,6 +18,7 @@ proc-macro = true
|
||||||
quote = "^1"
|
quote = "^1"
|
||||||
syn = { version = "^2", features = ["full", "extra-traits"] }
|
syn = { version = "^2", features = ["full", "extra-traits"] }
|
||||||
proc-macro2 = "^1"
|
proc-macro2 = "^1"
|
||||||
|
rxml_validation = { version = "0.11.0", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
panicking-into-impl = ["minidom"]
|
panicking-into-impl = ["minidom"]
|
||||||
|
|
|
@ -77,8 +77,8 @@ 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.value());
|
let unknown_attr_err = format!("Unknown attribute in {} element.", xml_name.as_str());
|
||||||
let unknown_child_err = format!("Unknown child in {} element.", xml_name.value());
|
let unknown_child_err = format!("Unknown child in {} element.", xml_name.as_str());
|
||||||
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,15 +215,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),
|
||||||
match ::xso::exports::rxml::NcName::try_from(#xml_name) {
|
::xso::exports::rxml::NcName::from(#xml_name),
|
||||||
::core::result::Result::Ok(v) => v,
|
|
||||||
::core::result::Result::Err(e) => {
|
|
||||||
self.0 = ::core::option::Option::None;
|
|
||||||
return ::core::option::Option::Some(::core::result::Result::Err(e.into()));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
),
|
),
|
||||||
::xso::exports::rxml::AttrMap::new(),
|
::xso::exports::rxml::AttrMap::new(),
|
||||||
)))
|
)))
|
||||||
|
|
|
@ -9,20 +9,63 @@
|
||||||
//! 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 proc_macro2::Span;
|
use proc_macro2::{Span, TokenStream};
|
||||||
|
use quote::{quote, quote_spanned};
|
||||||
use syn::{spanned::Spanned, *};
|
use syn::{spanned::Spanned, *};
|
||||||
|
|
||||||
|
use rxml_validation::NcName;
|
||||||
|
|
||||||
/// Type alias for a `#[xml(namespace = ..)]` attribute.
|
/// Type alias for a `#[xml(namespace = ..)]` attribute.
|
||||||
///
|
///
|
||||||
/// This may, in the future, be replaced by an enum supporting multiple
|
/// This may, in the future, be replaced by an enum supporting multiple
|
||||||
/// ways to specify a namespace.
|
/// ways to specify a namespace.
|
||||||
pub(crate) type NamespaceRef = Path;
|
pub(crate) type NamespaceRef = Path;
|
||||||
|
|
||||||
/// Type alias for a `#[xml(name = ..)]` attribute.
|
/// Value for the `#[xml(name = .. )]` attribute.
|
||||||
///
|
#[derive(Debug)]
|
||||||
/// This may, in the future, be replaced by an enum supporting both `Path` and
|
pub(crate) struct NameRef {
|
||||||
/// `LitStr`.
|
value: NcName,
|
||||||
pub(crate) type NameRef = LitStr;
|
span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NameRef {
|
||||||
|
/// Access the XML name as str.
|
||||||
|
///
|
||||||
|
/// *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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Contents of an `#[xml(..)]` attribute on a struct, enum variant, or enum.
|
/// Contents of an `#[xml(..)]` attribute on a struct, enum variant, or enum.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
|
@ -34,6 +34,18 @@ All key-value pairs interpreted by these derive macros must be wrapped in a
|
||||||
| `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* | The XML element name to match. |
|
||||||
|
|
||||||
|
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
|
||||||
|
and cannot be overridden. The following will thus not compile:
|
||||||
|
|
||||||
|
```compile_fail
|
||||||
|
# use xso::FromXml;
|
||||||
|
# static MY_NAMESPACE: &'static str = "urn:example";
|
||||||
|
#[derive(FromXml, Debug, PartialEq)]
|
||||||
|
#[xml(namespace = MY_NAMESPACE, name = "fnord:foo")] // colon not allowed
|
||||||
|
struct Foo;
|
||||||
|
```
|
||||||
|
|
||||||
## Limitations
|
## Limitations
|
||||||
|
|
||||||
Supports only empty structs currently. For example, the following will not
|
Supports only empty structs currently. For example, the following will not
|
||||||
|
|
Loading…
Reference in a new issue