Serializer namespaces and attributes (#13)
This commit is contained in:
parent
fcf20aa507
commit
2a9901bc84
|
@ -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(())
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue