diff --git a/xml_schema_derive/src/xsd/extension.rs b/xml_schema_derive/src/xsd/extension.rs index 27d2b47..536787f 100644 --- a/xml_schema_derive/src/xsd/extension.rs +++ b/xml_schema_derive/src/xsd/extension.rs @@ -1,6 +1,6 @@ use crate::xsd::{ - attribute::Attribute, rust_types_mapping::RustTypesMapping, sequence::Sequence, Implementation, - XsdContext, + attribute::Attribute, group::Group, rust_types_mapping::RustTypesMapping, sequence::Sequence, + Implementation, XsdContext, }; use proc_macro2::TokenStream; @@ -17,6 +17,8 @@ pub struct Extension { pub attributes: Vec, #[yaserde(rename = "sequence")] pub sequences: Vec, + #[yaserde(rename = "group")] + pub group: Option, } impl Implementation for Extension { @@ -34,7 +36,7 @@ impl Implementation for Extension { .map(|attribute| attribute.implement(namespace_definition, prefix, context)) .collect(); - let inner_attribute = if format!("{rust_type}") == "String" { +let inner_attribute = if format!("{rust_type}") == "String" { quote!(#[yaserde(text)]) } else { TokenStream::new() @@ -42,7 +44,7 @@ impl Implementation for Extension { quote!( #inner_attribute - pub content: #rust_type, + pub base: #rust_type, #attributes ) } @@ -52,10 +54,28 @@ impl Extension { pub fn get_field_implementation( &self, context: &XsdContext, - _prefix: &Option, + prefix: &Option, ) -> TokenStream { let rust_type = RustTypesMapping::get(context, &self.base); - quote!(pub content : #rust_type) + + let group_content = self + .group + .as_ref() + .map(|group| { + let group_type = group.get_type_implementation(context, prefix); + + quote!( + , + #[serde(flatten)] + pub extension : #group_type + ) + }) + .unwrap_or_default(); + + quote!( + pub base : #rust_type + #group_content + ) } } @@ -70,6 +90,7 @@ mod tests { base: "xs:string".to_string(), attributes: vec![], sequences: vec![], + group: None, }; let context = @@ -112,6 +133,7 @@ mod tests { }, ], sequences: vec![], + group: None, }; let context = diff --git a/xml_schema_derive/src/xsd/group.rs b/xml_schema_derive/src/xsd/group.rs new file mode 100644 index 0000000..e0eec34 --- /dev/null +++ b/xml_schema_derive/src/xsd/group.rs @@ -0,0 +1,108 @@ +use crate::xsd::{ + rust_types_mapping::RustTypesMapping, sequence::Sequence, Implementation, XsdContext, +}; +use heck::ToUpperCamelCase; +use proc_macro2::{Span, TokenStream}; +use syn::Ident; + +#[derive(Clone, Default, Debug, PartialEq, YaDeserialize)] +#[yaserde(prefix = "xs", namespace = "xs: http://www.w3.org/2001/XMLSchema")] +pub struct Group { + #[yaserde(attribute)] + pub name: Option, + #[yaserde(attribute, rename = "ref")] + pub reference: Option, + #[yaserde()] + pub sequence: Option, +} + +impl Implementation for Group { + fn implement( + &self, + namespace_definition: &TokenStream, + prefix: &Option, + context: &XsdContext, + ) -> TokenStream { + if self.name.is_none() { + return quote!(); + } + let raw_name = self.name.clone().unwrap(); + + let struct_name = Ident::new(&raw_name.to_upper_camel_case(), Span::call_site()); + + let fields = self + .sequence + .as_ref() + .map(|sequence| sequence.get_field_implementation(context, prefix)) + .unwrap_or_default(); + + quote!( + #[derive(Clone, Debug, Default, PartialEq, serde::Deserialize, serde::Serialize)] + #namespace_definition + pub struct #struct_name { + #fields + } + ) + } +} + +impl Group { + pub fn get_type_implementation( + &self, + context: &XsdContext, + _prefix: &Option, + ) -> TokenStream { + if let Some(reference) = &self.reference { + RustTypesMapping::get(context, reference) + } else { + panic!("Missing reference for group"); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use yaserde::de::from_str; + + #[test] + fn check_group_implementation() { + let xml = r#" + + + + + + + "#; + + let group: Group = from_str(xml).unwrap(); + + let context = + XsdContext::new(r#""#) + .unwrap(); + + let implementation = format!("{}", group.implement(&TokenStream::new(), &None, &context)); + + assert_eq!(implementation, "# [derive (Clone , Debug , Default , PartialEq , serde :: Deserialize , serde :: Serialize)] \ +pub struct Groupthing { \ +# [yaserde (rename = \"CX_X\")] pub cx_x : xml_schema_types :: Asdfg , \ +# [yaserde (rename = \"CY_X\")] pub cy_x : xml_schema_types :: Asdfg , }"); + } + + #[test] + fn check_group_ref() { + let xml = r#""#; + + let group: Group = from_str(xml).unwrap(); + + let context = + XsdContext::new(r#""#) + .unwrap(); + + let type_implementation = format!("{}", group.get_type_implementation(&context, &None)); + + assert_eq!(type_implementation, "Groupthing"); + } +} diff --git a/xml_schema_derive/src/xsd/mod.rs b/xml_schema_derive/src/xsd/mod.rs index a3eb8e1..43811b4 100644 --- a/xml_schema_derive/src/xsd/mod.rs +++ b/xml_schema_derive/src/xsd/mod.rs @@ -5,6 +5,7 @@ mod complex_content; mod complex_type; mod element; mod extension; +mod group; mod import; mod list; mod max_occurences; diff --git a/xml_schema_derive/src/xsd/schema.rs b/xml_schema_derive/src/xsd/schema.rs index 8966828..e14ec5f 100644 --- a/xml_schema_derive/src/xsd/schema.rs +++ b/xml_schema_derive/src/xsd/schema.rs @@ -1,5 +1,5 @@ -use crate::xsd::{ - attribute, attribute_group, complex_type, element, import, qualification, simple_type, + use crate::xsd::{ + attribute, attribute_group, complex_type, element, group, import, qualification, simple_type, Implementation, XsdContext, }; use proc_macro2::TokenStream; @@ -29,6 +29,8 @@ pub struct Schema { pub attributes: Vec, #[yaserde(rename = "attributeGroup")] pub attribute_group: Vec, + #[yaserde(rename = "group")] + pub group: Vec, } impl Implementation for Schema { @@ -71,10 +73,23 @@ impl Implementation for Schema { .collect() }; + log::info!("Generate groups"); + let groups: TokenStream = { + let mut context = context.clone(); + context.set_is_in_sub_module(true); + + self + .group + .iter() + .map(|group| group.implement(&namespace_definition, target_prefix, &context)) + .collect() + }; + quote!( pub mod xml_schema_types { #simple_types #complex_types + #groups } #elements