Serializer namespaces and attributes (#13)

This commit is contained in:
choinskib 2022-08-31 17:03:01 +02:00 committed by GitHub
parent fcf20aa507
commit 2a9901bc84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 300 additions and 131 deletions

View File

@ -3,7 +3,7 @@ extern crate proc_macro;
mod de; mod de;
mod se; mod se;
use std::collections::{BTreeSet, HashMap}; use std::collections::HashMap;
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use quote::quote; use quote::quote;
@ -33,19 +33,22 @@ pub(crate) fn get_namespaces(
if name == "namespace" { if name == "namespace" {
let mut iter = list.nested.iter(); let mut iter = list.nested.iter();
if let Some(NestedMeta::Lit(Lit::Str(v))) = iter.next() { let mut next = iter.next();
if let Some(NestedMeta::Lit(Lit::Str(v))) = next {
default_namespace = v.value(); default_namespace = v.value();
next = iter.next();
} }
for item in iter { while let Some(value) = next {
if let NestedMeta::Meta(Meta::NameValue(key)) = item { if let NestedMeta::Meta(Meta::NameValue(key)) = value {
if let Lit::Str(value) = &key.lit { if let Lit::Str(value) = &key.lit {
other_namespaces other_namespaces
.insert(key.path.get_ident().unwrap().to_string(), value.value()); .insert(key.path.get_ident().unwrap().to_string(), value.value());
next = iter.next();
continue; continue;
} }
} }
panic!("Wrong data"); panic!("Wrong data")
} }
} }
@ -102,17 +105,19 @@ pub fn to_xml(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let ast = parse_macro_input!(input as syn::DeriveInput); let ast = parse_macro_input!(input as syn::DeriveInput);
let ident = &ast.ident; let ident = &ast.ident;
let root_name = ident.to_string(); let root_name = ident.to_string();
let mut missing_prefixes = BTreeSet::new();
let mut serializer = Serializer::new(&ast.attrs); let mut serializer = Serializer::new(&ast.attrs);
let mut output = TokenStream::new();
serializer.add_header(&mut output);
let mut header = TokenStream::new();
serializer.add_header(&mut header);
let mut body = TokenStream::new();
let mut attributes = TokenStream::new();
match &ast.data { match &ast.data {
syn::Data::Struct(ref data) => { syn::Data::Struct(ref data) => {
match data.fields { match data.fields {
syn::Fields::Named(ref fields) => { syn::Fields::Named(ref fields) => {
fields.named.iter().for_each(|field| { fields.named.iter().for_each(|field| {
serializer.process_named_field(field, &mut output, &mut missing_prefixes); serializer.process_named_field(field, &mut body, &mut attributes);
}); });
} }
syn::Fields::Unnamed(_) => todo!(), syn::Fields::Unnamed(_) => todo!(),
@ -122,39 +127,34 @@ pub fn to_xml(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
_ => todo!(), _ => todo!(),
}; };
serializer.add_footer(&root_name, &mut output); let mut footer = TokenStream::new();
serializer.add_footer(&root_name, &mut footer);
let current_prefixes = serializer.keys_set(); let current_namespaces = serializer.namespaces_token();
proc_macro::TokenStream::from(quote!( proc_macro::TokenStream::from(quote!(
impl ToXml for #ident { impl ToXml for #ident {
fn serialize<W>(&self, serializer: &mut instant_xml::Serializer<W>, _field_data: Option<&instant_xml::FieldContext>) -> Result<(), instant_xml::Error> fn serialize<W>(&self, serializer: &mut instant_xml::Serializer<W>) -> Result<(), instant_xml::Error>
where where
W: std::fmt::Write, W: std::fmt::Write,
{ {
println!("ident: {}", #root_name);
let _ = serializer.consume_field_context();
let mut field_context = instant_xml::FieldContext { let mut field_context = instant_xml::FieldContext {
name: #root_name, name: #root_name,
attribute: None, attribute: None,
}; };
// Check if prefix exist #attributes
#(
if serializer.parent_prefixes.get(#missing_prefixes).is_none() {
return Err(instant_xml::Error::WrongNamespace);
}
)*;
// Adding current prefixes #header
let mut to_remove: Vec<&str> = Vec::new(); #current_namespaces
#(if serializer.parent_prefixes.insert(#current_prefixes) { #body
to_remove.push(#current_prefixes); #footer
};)*;
#output // Removing current namespaces
// Removing current prefixes
for it in to_remove { for it in to_remove {
serializer.parent_prefixes.remove(it); serializer.parent_namespaces.remove(it);
} }
Ok(()) Ok(())

View File

@ -1,4 +1,4 @@
use std::collections::{BTreeSet, HashMap}; use std::collections::HashMap;
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use quote::quote; use quote::quote;
@ -20,40 +20,43 @@ impl<'a> Serializer {
} }
} }
pub fn keys_set(&self) -> BTreeSet<&str> {
self.other_namespaces
.iter()
.map(|(k, _)| k.as_str())
.collect()
}
pub fn add_header(&mut self, output: &'a mut TokenStream) { pub fn add_header(&mut self, output: &'a mut TokenStream) {
output.extend(quote!( output.extend(quote!(
serializer.output.write_char('<')?; serializer.output.write_char('<')?;
serializer.output.write_str(field_context.name)?; serializer.output.write_str(field_context.name)?;
)); ));
if !self.default_namespace.is_empty() {
let default_namespace = &self.default_namespace; let default_namespace = &self.default_namespace;
output.extend(quote!( output.extend(quote!(
// Check if parent default namespace equals
if serializer.parent_default_namespace() != #default_namespace {
serializer.output.write_str(" xmlns=\"")?; serializer.output.write_str(" xmlns=\"")?;
serializer.output.write_str(#default_namespace)?; serializer.output.write_str(#default_namespace)?;
serializer.output.write_char('\"')?; serializer.output.write_char('\"')?;
));
} }
serializer.update_parent_default_namespace(#default_namespace);
));
let mut sorted_values: Vec<_> = self.other_namespaces.iter().collect(); let mut sorted_values: Vec<_> = self.other_namespaces.iter().collect();
sorted_values.sort(); sorted_values.sort();
for (key, val) in sorted_values { for (key, val) in sorted_values {
output.extend(quote!( output.extend(quote!(
if serializer.parent_namespaces.get(#val).is_none() {
serializer.output.write_str(" xmlns:")?; serializer.output.write_str(" xmlns:")?;
serializer.output.write_str(#key)?; serializer.output.write_str(#key)?;
serializer.output.write_str("=\"")?; serializer.output.write_str("=\"")?;
serializer.output.write_str(#val)?; serializer.output.write_str(#val)?;
serializer.output.write_char('\"')?; serializer.output.write_char('\"')?;
}
)); ));
} }
// Attributes
output.extend(quote!(
serializer.consume_current_attributes()?;
));
output.extend(quote!( output.extend(quote!(
serializer.output.write_char('>')?; serializer.output.write_char('>')?;
)); ));
@ -64,45 +67,97 @@ impl<'a> Serializer {
serializer.output.write_str("</")?; serializer.output.write_str("</")?;
serializer.output.write_str(#root_name)?; serializer.output.write_str(#root_name)?;
serializer.output.write_char('>')?; serializer.output.write_char('>')?;
serializer.retrive_parent_default_namespace();
)); ));
} }
pub fn process_named_field( pub fn process_named_field(
&mut self, &mut self,
field: &syn::Field, field: &syn::Field,
output: &'a mut TokenStream, body: &mut TokenStream,
missing_prefixes: &'a mut BTreeSet<String>, attributes: &mut TokenStream,
) { ) {
let name = field.ident.as_ref().unwrap().to_string(); let name = field.ident.as_ref().unwrap().to_string();
let field_value = field.ident.as_ref().unwrap(); let field_value = field.ident.as_ref().unwrap();
output.extend(quote!( let declaration = quote!(
let mut field = instant_xml::FieldContext { let mut field = instant_xml::FieldContext {
name: #name, name: #name,
attribute: None, attribute: None,
}; };
)); );
match retrieve_field_attribute(field) { let stream_ref = match retrieve_field_attribute(field) {
Some(FieldAttribute::Namespace(namespace_key)) => { Some(FieldAttribute::Namespace(namespace)) => {
output.extend(quote!( body.extend(quote!(
field.attribute = Some(instant_xml::FieldAttribute::Namespace(#namespace_key)); #declaration
// // Check if such namespace already exist, if so change it to use its prefix
// match serializer.parent_namespaces.get(#namespace) {
// Some(key) => field.attribute = Some(instant_xml::FieldAttribute::Prefix(key)),
// None => field.attribute = Some(instant_xml::FieldAttribute::Namespace(#namespace)),
// };
field.attribute = Some(instant_xml::FieldAttribute::Namespace(#namespace));
)); ));
body
} }
Some(FieldAttribute::PrefixIdentifier(prefix_key)) => { Some(FieldAttribute::PrefixIdentifier(prefix_key)) => {
output.extend(quote!( match self.other_namespaces.get(&prefix_key) {
field.attribute = Some(instant_xml::FieldAttribute::Prefix(#prefix_key)); Some(val) => {
)); body.extend(quote!(
#declaration
if self.other_namespaces.get(&prefix_key).is_none() { // Check if such namespace already exist, if so change its prefix to parent prefix
missing_prefixes.insert(prefix_key); let prefix_key = match serializer.parent_namespaces.get(#val) {
Some(key) => key,
None => #prefix_key,
}; };
));
} }
_ => {} None => panic!("Prefix not defined: {}", prefix_key),
}; };
output.extend(quote!( body.extend(quote!(
self.#field_value.serialize(serializer, Some(&field))?; field.attribute = Some(instant_xml::FieldAttribute::Prefix(prefix_key));
)); ));
body
}
Some(FieldAttribute::Attribute) => {
attributes.extend(quote!(
#declaration
serializer.add_attribute_key(&#name);
field.attribute = Some(instant_xml::FieldAttribute::Attribute);
));
attributes
}
_ => {
body.extend(quote!(
#declaration
));
body
}
};
stream_ref.extend(quote!(
serializer.set_field_context(field)?;
self.#field_value.serialize(serializer)?;
));
}
pub fn namespaces_token(&self) -> TokenStream {
let mut namespaces = quote!(
let mut to_remove: Vec<&str> = Vec::new();
);
for (k, v) in self.other_namespaces.iter() {
namespaces.extend(quote!(
// Only adding to HashMap if namespace do not exist, if it exist it will use the parent defined prefix
if let std::collections::hash_map::Entry::Vacant(v) = serializer.parent_namespaces.entry(#v) {
v.insert(#k);
// Will remove added namespaces when going "up"
to_remove.push(#v);
};
))
}
namespaces
} }
} }

View File

@ -1,4 +1,4 @@
use std::collections::{BTreeSet, HashMap}; use std::collections::HashMap;
use std::fmt; use std::fmt;
use thiserror::Error; use thiserror::Error;
@ -29,15 +29,11 @@ pub trait ToXml {
fn to_xml(&self) -> Result<String, Error> { fn to_xml(&self) -> Result<String, Error> {
let mut output = String::new(); let mut output = String::new();
let mut serializer = Serializer::new(&mut output); let mut serializer = Serializer::new(&mut output);
self.serialize(&mut serializer, None)?; self.serialize(&mut serializer)?;
Ok(output) Ok(output)
} }
fn serialize<W>( fn serialize<W>(&self, serializer: &mut Serializer<W>) -> Result<(), Error>
&self,
serializer: &mut Serializer<W>,
field_context: Option<&FieldContext>,
) -> Result<(), Error>
where where
W: fmt::Write; W: fmt::Write;
} }
@ -45,19 +41,22 @@ pub trait ToXml {
macro_rules! to_xml_for_number { macro_rules! to_xml_for_number {
($typ:ty) => { ($typ:ty) => {
impl ToXml for $typ { impl ToXml for $typ {
fn serialize<W>( fn serialize<W>(&self, serializer: &mut Serializer<W>) -> Result<(), Error>
&self,
serializer: &mut Serializer<W>,
field_context: Option<&FieldContext>,
) -> Result<(), Error>
where where
W: fmt::Write, W: fmt::Write,
{ {
match field_context { match serializer.consume_field_context() {
Some(field_context) => { Some(field_context) => {
serializer.add_open_tag(field_context)?; match field_context.attribute {
Some(FieldAttribute::Attribute) => {
serializer.add_attribute_value(&self.to_string());
}
_ => {
serializer.add_open_tag(&field_context)?;
write!(serializer.output, "{}", &self)?; write!(serializer.output, "{}", &self)?;
serializer.add_close_tag(field_context)?; serializer.add_close_tag(field_context)?;
}
}
Ok(()) Ok(())
} }
None => Err(Error::UnexpectedValue), None => Err(Error::UnexpectedValue),
@ -77,11 +76,7 @@ to_xml_for_number!(u32);
to_xml_for_number!(u64); to_xml_for_number!(u64);
impl ToXml for bool { impl ToXml for bool {
fn serialize<W>( fn serialize<W>(&self, serializer: &mut Serializer<W>) -> Result<(), Error>
&self,
serializer: &mut Serializer<W>,
field_context: Option<&FieldContext>,
) -> Result<(), Error>
where where
W: fmt::Write, W: fmt::Write,
{ {
@ -90,11 +85,18 @@ impl ToXml for bool {
false => "false", false => "false",
}; };
match field_context { match serializer.consume_field_context() {
Some(field_context) => { Some(field_context) => {
serializer.add_open_tag(field_context)?; match field_context.attribute {
Some(FieldAttribute::Attribute) => {
serializer.add_attribute_value(value);
}
_ => {
serializer.add_open_tag(&field_context)?;
serializer.output.write_str(value)?; serializer.output.write_str(value)?;
serializer.add_close_tag(field_context)?; serializer.add_close_tag(field_context)?;
}
}
Ok(()) Ok(())
} }
None => Err(Error::UnexpectedValue), None => Err(Error::UnexpectedValue),
@ -103,19 +105,22 @@ impl ToXml for bool {
} }
impl ToXml for String { impl ToXml for String {
fn serialize<W>( fn serialize<W>(&self, serializer: &mut Serializer<W>) -> Result<(), Error>
&self,
serializer: &mut Serializer<W>,
field_context: Option<&FieldContext>,
) -> Result<(), Error>
where where
W: fmt::Write, W: fmt::Write,
{ {
match field_context { match serializer.consume_field_context() {
Some(field_context) => { Some(field_context) => {
serializer.add_open_tag(field_context)?; match field_context.attribute {
Some(FieldAttribute::Attribute) => {
serializer.add_attribute_value(self);
}
_ => {
serializer.add_open_tag(&field_context)?;
serializer.output.write_str(self)?; serializer.output.write_str(self)?;
serializer.add_close_tag(field_context)?; serializer.add_close_tag(field_context)?;
}
}
Ok(()) Ok(())
} }
None => Err(Error::UnexpectedValue), None => Err(Error::UnexpectedValue),
@ -127,20 +132,80 @@ pub struct Serializer<'xml, W>
where where
W: fmt::Write, W: fmt::Write,
{ {
// For parent namespaces the key is the namespace and the value is the prefix. We are adding to map
// only if the namespaces do not exist, if it does exist then we are using an already defined parent prefix.
#[doc(hidden)] #[doc(hidden)]
pub parent_prefixes: BTreeSet<&'xml str>, pub parent_namespaces: HashMap<&'xml str, &'xml str>,
#[doc(hidden)] #[doc(hidden)]
pub output: &'xml mut W, pub output: &'xml mut W,
parent_default_namespace: &'xml str,
parent_default_namespace_to_revert: &'xml str,
current_attributes: String,
next_field_context: Option<FieldContext<'xml>>,
} }
impl<'xml, W: std::fmt::Write> Serializer<'xml, W> { impl<'xml, W: std::fmt::Write> Serializer<'xml, W> {
pub fn new(output: &'xml mut W) -> Self { pub fn new(output: &'xml mut W) -> Self {
Self { Self {
parent_prefixes: BTreeSet::new(), parent_namespaces: HashMap::new(),
output, output,
parent_default_namespace: "",
parent_default_namespace_to_revert: "",
next_field_context: None,
current_attributes: String::new(),
} }
} }
pub fn consume_current_attributes(&mut self) -> Result<(), Error> {
self.output.write_str(&self.current_attributes)?;
self.current_attributes.clear();
Ok(())
}
pub fn add_attribute_key(&mut self, attr_key: &str) {
self.current_attributes.push(' ');
self.current_attributes.push_str(attr_key);
self.current_attributes.push('=');
}
pub fn add_attribute_value(&mut self, attr_value: &str) {
self.current_attributes.push('"');
self.current_attributes.push_str(attr_value);
self.current_attributes.push('"');
}
pub fn set_field_context(&mut self, field_context: FieldContext<'xml>) -> Result<(), Error> {
if self.next_field_context.is_some() {
return Err(Error::UnexpectedState);
};
self.next_field_context = Some(field_context);
Ok(())
}
pub fn consume_field_context(&mut self) -> Option<FieldContext<'xml>> {
self.next_field_context.take()
}
pub fn set_parent_default_namespace(&mut self, namespace: &'xml str) -> Result<(), Error> {
self.parent_default_namespace = namespace;
Ok(())
}
pub fn parent_default_namespace(&self) -> &'xml str {
self.parent_default_namespace
}
pub fn update_parent_default_namespace(&mut self, namespace: &'xml str) {
self.parent_default_namespace_to_revert = self.parent_default_namespace;
self.parent_default_namespace = namespace;
}
pub fn retrive_parent_default_namespace(&mut self) {
self.parent_default_namespace = self.parent_default_namespace_to_revert;
}
fn add_open_tag(&mut self, field_context: &FieldContext) -> Result<(), Error> { fn add_open_tag(&mut self, field_context: &FieldContext) -> Result<(), Error> {
match field_context.attribute { match field_context.attribute {
Some(FieldAttribute::Prefix(prefix)) => { Some(FieldAttribute::Prefix(prefix)) => {
@ -150,7 +215,9 @@ impl<'xml, W: std::fmt::Write> Serializer<'xml, W> {
self.output.write_str(field_context.name)?; self.output.write_str(field_context.name)?;
self.output.write_char('>')?; self.output.write_char('>')?;
} }
Some(FieldAttribute::Namespace(namespace)) => { Some(FieldAttribute::Namespace(namespace))
if self.parent_default_namespace != namespace =>
{
self.output.write_char('<')?; self.output.write_char('<')?;
self.output.write_str(field_context.name)?; self.output.write_str(field_context.name)?;
self.output.write_str(" xmlns=\"")?; self.output.write_str(" xmlns=\"")?;
@ -166,7 +233,7 @@ impl<'xml, W: std::fmt::Write> Serializer<'xml, W> {
Ok(()) Ok(())
} }
fn add_close_tag(&mut self, field_context: &FieldContext) -> Result<(), Error> { fn add_close_tag(&mut self, field_context: FieldContext) -> Result<(), Error> {
match field_context.attribute { match field_context.attribute {
Some(FieldAttribute::Prefix(prefix)) => { Some(FieldAttribute::Prefix(prefix)) => {
self.output.write_str("</")?; self.output.write_str("</")?;
@ -188,6 +255,7 @@ impl<'xml, W: std::fmt::Write> Serializer<'xml, W> {
pub enum FieldAttribute<'xml> { pub enum FieldAttribute<'xml> {
Prefix(&'xml str), Prefix(&'xml str),
Namespace(&'xml str), Namespace(&'xml str),
Attribute,
} }
pub struct FieldContext<'xml> { pub struct FieldContext<'xml> {

View File

@ -1,16 +1,5 @@
use instant_xml::{Error, FromXml, ToXml}; use instant_xml::{Error, FromXml, ToXml};
//TODO: Add compile time errors check?
#[derive(Debug, Eq, PartialEq, ToXml)]
struct Nested {
#[xml(namespace(bar))]
flag: bool,
}
#[derive(Debug, Eq, PartialEq, ToXml)]
struct NestedWrongPrefix {
#[xml(namespace(dar))]
flag: bool,
}
#[derive(Debug, Eq, PartialEq, ToXml)] #[derive(Debug, Eq, PartialEq, ToXml)]
struct Unit; struct Unit;
@ -22,7 +11,7 @@ fn unit() {
} }
#[derive(Debug, Eq, PartialEq, ToXml)] #[derive(Debug, Eq, PartialEq, ToXml)]
#[xml(namespace("URI", bar = "BAZ", foo = "BAR"))] #[xml(namespace(bar = "BAZ", foo = "BAR"))]
struct StructWithNamedFields { struct StructWithNamedFields {
flag: bool, flag: bool,
#[xml(namespace(bar))] #[xml(namespace(bar))]
@ -31,6 +20,11 @@ struct StructWithNamedFields {
number: i32, number: i32,
} }
// Tests:
// - Empty default namespace
// - Prefix namespace
// - Direct namespace
#[test] #[test]
fn struct_with_named_fields() { fn struct_with_named_fields() {
assert_eq!( assert_eq!(
@ -41,52 +35,104 @@ fn struct_with_named_fields() {
} }
.to_xml() .to_xml()
.unwrap(), .unwrap(),
"<StructWithNamedFields xmlns=\"URI\" xmlns:bar=\"BAZ\" xmlns:foo=\"BAR\"><flag>true</flag><bar:string>test</bar:string><number xmlns=\"typo\">1</number></StructWithNamedFields>" "<StructWithNamedFields xmlns:bar=\"BAZ\" xmlns:foo=\"BAR\"><flag>true</flag><bar:string>test</bar:string><number xmlns=\"typo\">1</number></StructWithNamedFields>"
); );
} }
#[derive(Debug, Eq, PartialEq, ToXml)]
#[xml(namespace("URI", dar = "BAZ", internal = "INTERNAL"))]
struct Nested {
#[xml(namespace(dar))]
flag_parent_prefix: bool,
#[xml(namespace(internal))]
flag_internal_prefix: bool,
}
#[derive(Debug, Eq, PartialEq, ToXml)] #[derive(Debug, Eq, PartialEq, ToXml)]
#[xml(namespace("URI", bar = "BAZ", foo = "BAR"))] #[xml(namespace("URI", bar = "BAZ", foo = "BAR"))]
struct StructWithCustomField { struct StructWithCustomField {
#[xml(attribute)]
int_attribute: i32,
#[xml(namespace("BAZ"))]
flag_direct_namespace_same_the_same_as_prefix: bool,
#[xml(namespace(bar))]
flag_prefix: bool,
#[xml(namespace("DIFFERENT"))]
flag_direct_namespace: bool,
test: Nested, test: Nested,
} }
// Tests:
// - The same direct namespace as the one from prefix
// - Attribute handling
// - Omitting redeclared child default namespace
// - Omitting redeclared child namespace with different prefix
// - Unique direct namespace
// - Child unique prefix
// - Child repeated prefix
// - Child default namespace the same as parent
#[test] #[test]
fn struct_with_custom_field() { fn struct_with_custom_field() {
assert_eq!( assert_eq!(
StructWithCustomField { StructWithCustomField {
int_attribute: 42,
flag_direct_namespace_same_the_same_as_prefix: true,
flag_prefix: false,
flag_direct_namespace: true,
test: Nested { test: Nested {
flag: true, flag_parent_prefix: true,
flag_internal_prefix: false,
}, },
} }
.to_xml() .to_xml()
.unwrap(), .unwrap(),
"<StructWithCustomField xmlns=\"URI\" xmlns:bar=\"BAZ\" xmlns:foo=\"BAR\"><Nested><bar:flag>true</bar:flag></Nested></StructWithCustomField>" "<StructWithCustomField xmlns=\"URI\" xmlns:bar=\"BAZ\" xmlns:foo=\"BAR\" int_attribute=\"42\"><flag_direct_namespace_same_the_same_as_prefix xmlns=\"BAZ\">true</flag_direct_namespace_same_the_same_as_prefix><bar:flag_prefix>false</bar:flag_prefix><flag_direct_namespace xmlns=\"DIFFERENT\">true</flag_direct_namespace><Nested xmlns:internal=\"INTERNAL\"><bar:flag_parent_prefix>true</bar:flag_parent_prefix><internal:flag_internal_prefix>false</internal:flag_internal_prefix></Nested></StructWithCustomField>"
); );
} }
#[derive(Debug, Eq, PartialEq, ToXml, FromXml)] #[derive(Debug, Eq, PartialEq, ToXml)]
#[xml(namespace("URI", bar = "BAZ"))] #[xml(namespace(dar = "BAZ", internal = "INTERNAL"))]
struct NestedDe { struct NestedDifferentNamespace {
#[xml(namespace(bar))] #[xml(namespace(dar))]
flag: bool, flag_parent_prefix: bool,
#[xml(namespace(internal))]
flag_internal_prefix: bool,
} }
#[derive(Debug, Eq, PartialEq, ToXml)] #[derive(Debug, Eq, PartialEq, ToXml)]
#[xml(namespace("URI", bar = "BAZ", foo = "BAR"))] #[xml(namespace("URI", bar = "BAZ", foo = "BAR"))]
struct StructWithCustomFieldWrongPrefix { struct StructChildNamespaces {
test: NestedWrongPrefix, different_child_namespace: NestedDifferentNamespace,
same_child_namespace: Nested,
} }
// Tests:
// - Different child namespace
// - The same child namespace
#[test] #[test]
#[should_panic] fn struct_child_namespaces() {
fn struct_with_custom_field_wrong_prefix() { assert_eq!(
StructWithCustomFieldWrongPrefix { StructChildNamespaces {
test: NestedWrongPrefix { flag: true }, different_child_namespace: NestedDifferentNamespace {
flag_parent_prefix: true,
flag_internal_prefix: false,
},
same_child_namespace: Nested {
flag_parent_prefix: true,
flag_internal_prefix: false,
},
} }
.to_xml() .to_xml()
.unwrap(); .unwrap(),
"<StructChildNamespaces xmlns=\"URI\" xmlns:bar=\"BAZ\" xmlns:foo=\"BAR\"><NestedDifferentNamespace xmlns=\"\" xmlns:internal=\"INTERNAL\"><bar:flag_parent_prefix>true</bar:flag_parent_prefix><internal:flag_internal_prefix>false</internal:flag_internal_prefix></NestedDifferentNamespace><Nested xmlns:internal=\"INTERNAL\"><bar:flag_parent_prefix>true</bar:flag_parent_prefix><internal:flag_internal_prefix>false</internal:flag_internal_prefix></Nested></StructChildNamespaces>"
);
}
#[derive(Debug, Eq, PartialEq, FromXml)]
#[xml(namespace("URI", bar = "BAZ"))]
struct NestedDe {
#[xml(namespace(bar))]
flag: bool,
} }
#[derive(Debug, Eq, PartialEq, FromXml)] #[derive(Debug, Eq, PartialEq, FromXml)]
@ -138,7 +184,7 @@ fn struct_with_custom_field_from_xml() {
); );
} }
#[derive(Debug, Eq, PartialEq, ToXml, FromXml)] #[derive(Debug, Eq, PartialEq, FromXml)]
struct NestedWrongNamespace { struct NestedWrongNamespace {
flag: bool, flag: bool,
} }