mirror of
https://gitlab.com/xmpp-rs/xmpp-rs.git
synced 2024-07-12 22:21:53 +00:00
xso-proc: add support for non-String typed attributes
This commit is contained in:
parent
1f679c3af7
commit
c0fc7f49cf
4 changed files with 104 additions and 6 deletions
|
@ -313,3 +313,20 @@ fn prefixed_attribute_roundtrip() {
|
||||||
};
|
};
|
||||||
roundtrip_full::<PrefixedAttribute>("<attr xmlns='urn:example:ns1' xml:lang='foo'/>");
|
roundtrip_full::<PrefixedAttribute>("<attr xmlns='urn:example:ns1' xml:lang='foo'/>");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
|
||||||
|
#[xml(namespace = NS1, name = "attr")]
|
||||||
|
struct RequiredNonStringAttribute {
|
||||||
|
#[xml(attribute)]
|
||||||
|
foo: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn required_non_string_attribute_roundtrip() {
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use std::{
|
||||||
|
option::Option::{None, Some},
|
||||||
|
result::Result::{Err, Ok},
|
||||||
|
};
|
||||||
|
roundtrip_full::<RequiredNonStringAttribute>("<attr xmlns='urn:example:ns1' foo='-16'/>");
|
||||||
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ use rxml_validation::NcName;
|
||||||
use crate::error_message::{self, ParentRef};
|
use crate::error_message::{self, ParentRef};
|
||||||
use crate::meta::{NameRef, NamespaceRef, XmlFieldMeta};
|
use crate::meta::{NameRef, NamespaceRef, XmlFieldMeta};
|
||||||
use crate::scope::{FromEventsScope, IntoEventsScope};
|
use crate::scope::{FromEventsScope, IntoEventsScope};
|
||||||
|
use crate::types::{from_xml_text_fn, into_optional_xml_text_fn};
|
||||||
|
|
||||||
/// Code slices necessary for declaring and initializing a temporary variable
|
/// Code slices necessary for declaring and initializing a temporary variable
|
||||||
/// for parsing purposes.
|
/// for parsing purposes.
|
||||||
|
@ -182,6 +183,7 @@ impl FieldDef {
|
||||||
ref xml_namespace,
|
ref xml_namespace,
|
||||||
} => {
|
} => {
|
||||||
let FromEventsScope { ref attrs, .. } = scope;
|
let FromEventsScope { ref attrs, .. } = scope;
|
||||||
|
let ty = self.ty.clone();
|
||||||
|
|
||||||
let missing_msg = error_message::on_missing_attribute(container_name, &self.member);
|
let missing_msg = error_message::on_missing_attribute(container_name, &self.member);
|
||||||
|
|
||||||
|
@ -192,15 +194,17 @@ impl FieldDef {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let from_xml_text = from_xml_text_fn(ty.clone());
|
||||||
|
|
||||||
return Ok(FieldBuilderPart::Init {
|
return Ok(FieldBuilderPart::Init {
|
||||||
value: FieldTempInit {
|
value: FieldTempInit {
|
||||||
ty: self.ty.clone(),
|
|
||||||
init: quote! {
|
init: quote! {
|
||||||
match #attrs.remove(#xml_namespace, #xml_name) {
|
match #attrs.remove(#xml_namespace, #xml_name).map(#from_xml_text).transpose()? {
|
||||||
::core::option::Option::Some(v) => v,
|
::core::option::Option::Some(v) => v,
|
||||||
::core::option::Option::None => return ::core::result::Result::Err(::xso::error::Error::Other(#missing_msg).into()),
|
::core::option::Option::None => return ::core::result::Result::Err(::xso::error::Error::Other(#missing_msg).into()),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
ty: self.ty.clone(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -230,13 +234,15 @@ impl FieldDef {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let into_optional_xml_text = into_optional_xml_text_fn(self.ty.clone());
|
||||||
|
|
||||||
return Ok(FieldIteratorPart::Header {
|
return Ok(FieldIteratorPart::Header {
|
||||||
setter: quote! {
|
setter: quote! {
|
||||||
#attrs.insert(
|
#into_optional_xml_text(#bound_name)?.and_then(|#bound_name| #attrs.insert(
|
||||||
#xml_namespace,
|
#xml_namespace,
|
||||||
#xml_name.to_owned(),
|
#xml_name.to_owned(),
|
||||||
#bound_name,
|
#bound_name,
|
||||||
);
|
));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
//! Module with specific [`syn::Type`] constructors.
|
//! Module with specific [`syn::Type`] constructors.
|
||||||
|
|
||||||
use proc_macro2::Span;
|
use proc_macro2::Span;
|
||||||
use syn::*;
|
use syn::{spanned::Spanned, *};
|
||||||
|
|
||||||
/// Construct a [`syn::Type`] referring to `::xso::exports::rxml::QName`.
|
/// Construct a [`syn::Type`] referring to `::xso::exports::rxml::QName`.
|
||||||
pub(crate) fn qname_ty(span: Span) -> Type {
|
pub(crate) fn qname_ty(span: Span) -> Type {
|
||||||
|
@ -40,3 +40,77 @@ pub(crate) fn qname_ty(span: Span) -> Type {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Construct a [`syn::Expr`] referring to
|
||||||
|
/// `<#ty as ::xso::FromXmlText>::from_xml_text`.
|
||||||
|
pub(crate) fn from_xml_text_fn(ty: Type) -> Expr {
|
||||||
|
let span = ty.span();
|
||||||
|
Expr::Path(ExprPath {
|
||||||
|
attrs: Vec::new(),
|
||||||
|
qself: Some(QSelf {
|
||||||
|
lt_token: syn::token::Lt { spans: [span] },
|
||||||
|
ty: Box::new(ty),
|
||||||
|
position: 2,
|
||||||
|
as_token: Some(syn::token::As { span }),
|
||||||
|
gt_token: syn::token::Gt { spans: [span] },
|
||||||
|
}),
|
||||||
|
path: Path {
|
||||||
|
leading_colon: Some(syn::token::PathSep {
|
||||||
|
spans: [span, span],
|
||||||
|
}),
|
||||||
|
segments: [
|
||||||
|
PathSegment {
|
||||||
|
ident: Ident::new("xso", span),
|
||||||
|
arguments: PathArguments::None,
|
||||||
|
},
|
||||||
|
PathSegment {
|
||||||
|
ident: Ident::new("FromXmlText", span),
|
||||||
|
arguments: PathArguments::None,
|
||||||
|
},
|
||||||
|
PathSegment {
|
||||||
|
ident: Ident::new("from_xml_text", span),
|
||||||
|
arguments: PathArguments::None,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a [`syn::Expr`] referring to
|
||||||
|
/// `<#ty as ::xso::IntoOptionalXmlText>::into_optional_xml_text`.
|
||||||
|
pub(crate) fn into_optional_xml_text_fn(ty: Type) -> Expr {
|
||||||
|
let span = ty.span();
|
||||||
|
Expr::Path(ExprPath {
|
||||||
|
attrs: Vec::new(),
|
||||||
|
qself: Some(QSelf {
|
||||||
|
lt_token: syn::token::Lt { spans: [span] },
|
||||||
|
ty: Box::new(ty),
|
||||||
|
position: 2,
|
||||||
|
as_token: Some(syn::token::As { span }),
|
||||||
|
gt_token: syn::token::Gt { spans: [span] },
|
||||||
|
}),
|
||||||
|
path: Path {
|
||||||
|
leading_colon: Some(syn::token::PathSep {
|
||||||
|
spans: [span, span],
|
||||||
|
}),
|
||||||
|
segments: [
|
||||||
|
PathSegment {
|
||||||
|
ident: Ident::new("xso", span),
|
||||||
|
arguments: PathArguments::None,
|
||||||
|
},
|
||||||
|
PathSegment {
|
||||||
|
ident: Ident::new("IntoOptionalXmlText", span),
|
||||||
|
arguments: PathArguments::None,
|
||||||
|
},
|
||||||
|
PathSegment {
|
||||||
|
ident: Ident::new("into_optional_xml_text", span),
|
||||||
|
arguments: PathArguments::None,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -63,7 +63,8 @@ The following mapping types are defined:
|
||||||
#### `attribute` meta
|
#### `attribute` meta
|
||||||
|
|
||||||
The `attribute` meta causes the field to be mapped to an XML attribute of the
|
The `attribute` meta causes the field to be mapped to an XML attribute of the
|
||||||
same name. The field must be of type [`String`].
|
same name. For `FromXml`, the field's type must implement [`FromXmlText`] and
|
||||||
|
for `IntoXml`, the field's type must implement [`IntoOptionalXmlText`].
|
||||||
|
|
||||||
The following keys can be used inside the `#[xml(attribute(..))]` meta:
|
The following keys can be used inside the `#[xml(attribute(..))]` meta:
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue