Mandate that attribute namespaces are valid prefixes
This commit is contained in:
parent
570cdc81a5
commit
b30859929b
|
@ -4,6 +4,7 @@ mod de;
|
|||
mod ser;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt;
|
||||
|
||||
use proc_macro2::{Delimiter, Group, Ident, Literal, Punct, Span, TokenStream, TokenTree};
|
||||
use quote::ToTokens;
|
||||
|
@ -456,6 +457,18 @@ impl ToTokens for Namespace {
|
|||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Namespace {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Path(arg0) => f
|
||||
.debug_tuple("Path")
|
||||
.field(&arg0.into_token_stream().to_string())
|
||||
.finish(),
|
||||
Self::Literal(arg0) => f.debug_tuple("Literal").field(arg0).finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn discard_lifetimes(ty: &mut syn::Type) {
|
||||
match ty {
|
||||
syn::Type::Path(ty) => discard_path_lifetimes(ty),
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
use crate::Namespace;
|
||||
|
||||
use super::{discard_lifetimes, ContainerMeta, FieldMeta};
|
||||
|
||||
|
@ -85,11 +88,46 @@ fn process_named_field(
|
|||
) {
|
||||
let name = field.ident.as_ref().unwrap().to_string();
|
||||
let field_value = field.ident.as_ref().unwrap();
|
||||
|
||||
let field_meta = FieldMeta::from_field(field);
|
||||
|
||||
let default_ns = &meta.ns.uri;
|
||||
if field_meta.attribute {
|
||||
let (ns, error) = match &field_meta.ns.uri {
|
||||
Some(Namespace::Path(path)) => match path.get_ident() {
|
||||
Some(prefix) => match &meta.ns.prefixes.get(&prefix.to_string()) {
|
||||
Some(ns) => (quote!(#ns), quote!()),
|
||||
None => (
|
||||
quote!(""),
|
||||
syn::Error::new(
|
||||
field_meta.ns.uri.span(),
|
||||
&format!("unknown prefix `{prefix}` (prefix must be defined on the field's type)"),
|
||||
)
|
||||
.into_compile_error(),
|
||||
),
|
||||
},
|
||||
None => (
|
||||
quote!(""),
|
||||
syn::Error::new(
|
||||
field_meta.ns.uri.span(),
|
||||
"attribute namespace must be a prefix identifier",
|
||||
)
|
||||
.into_compile_error(),
|
||||
),
|
||||
},
|
||||
Some(Namespace::Literal(_)) => (
|
||||
quote!(""),
|
||||
syn::Error::new(
|
||||
field_meta.ns.uri.span(),
|
||||
"attribute namespace must be a prefix identifier",
|
||||
)
|
||||
.into_compile_error(),
|
||||
),
|
||||
None => (quote!(#default_ns), quote!()),
|
||||
};
|
||||
|
||||
attributes.extend(quote!(
|
||||
serializer.write_attr(#name, &self.#field_value)?;
|
||||
#error
|
||||
serializer.write_attr(#name, #ns, &self.#field_value)?;
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -63,12 +63,24 @@ impl<'xml, W: fmt::Write + ?Sized> Serializer<'xml, W> {
|
|||
Ok(prefix)
|
||||
}
|
||||
|
||||
pub fn write_attr<V: ToXml + ?Sized>(&mut self, name: &str, value: &V) -> Result<(), Error> {
|
||||
pub fn write_attr<V: ToXml + ?Sized>(
|
||||
&mut self,
|
||||
name: &str,
|
||||
ns: &str,
|
||||
value: &V,
|
||||
) -> Result<(), Error> {
|
||||
if self.state != State::Attribute {
|
||||
return Err(Error::UnexpectedState);
|
||||
}
|
||||
|
||||
self.output.write_fmt(format_args!(" {}=\"", name))?;
|
||||
match ns == self.default_ns {
|
||||
true => self.output.write_fmt(format_args!(" {name}=\""))?,
|
||||
false => {
|
||||
let prefix = self.prefixes.get(ns).ok_or(dbg!(Error::UnexpectedState))?;
|
||||
self.output.write_fmt(format_args!(" {prefix}:{name}=\""))?;
|
||||
}
|
||||
}
|
||||
|
||||
self.state = State::Scalar;
|
||||
value.serialize(self)?;
|
||||
self.state = State::Attribute;
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
use similar_asserts::assert_eq;
|
||||
|
||||
use instant_xml::{to_string, ToXml};
|
||||
|
||||
#[derive(ToXml)]
|
||||
#[xml(ns(bar = "BAR"))]
|
||||
struct NoPrefixAttrNs {
|
||||
#[xml(attribute, ns(bar))]
|
||||
flag: bool,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_prefix_attr_ns() {
|
||||
assert_eq!(
|
||||
to_string(&NoPrefixAttrNs { flag: true }).unwrap(),
|
||||
"<NoPrefixAttrNs xmlns:bar=\"BAR\" bar:flag=\"true\"></NoPrefixAttrNs>"
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue