-
Notifications
You must be signed in to change notification settings - Fork 29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement choice #36
base: main
Are you sure you want to change the base?
Implement choice #36
Changes from all commits
b4bb239
c4cfd83
6fbffc3
450c63f
819b837
8878f4d
b503c4e
1aa88b5
b5bfd40
3cfb3c2
5495f12
1f5ce0d
4e1000c
78cdeae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
extern crate yaserde_derive; | ||
|
||
use xml_schema_derive::XmlSchema; | ||
use yaserde::de::from_str; | ||
use yaserde::ser::to_string; | ||
|
||
#[test] | ||
fn choice() { | ||
#[derive(Debug, XmlSchema)] | ||
#[xml_schema(source = "xml_schema/tests/choice.xsd")] | ||
struct ChoiceTypeSchema; | ||
|
||
use choice_type_schema::xml_schema_types::*; | ||
|
||
let xml_1 = r#"<?xml version="1.0" encoding="UTF-8"?> | ||
<person> | ||
<firstname>John</firstname> | ||
</person> | ||
"#; | ||
|
||
let sample_1: Person = from_str(xml_1).unwrap(); | ||
|
||
let model = Person { | ||
firstname: Some(Firstname { | ||
base: "John".to_string(), | ||
scope: None, | ||
}), | ||
lastname: None, | ||
}; | ||
|
||
assert_eq!(sample_1, model); | ||
|
||
let data = to_string(&model).unwrap(); | ||
assert_eq!( | ||
data, | ||
r#"<?xml version="1.0" encoding="utf-8"?><Person><firstname>John</firstname></Person>"# | ||
); | ||
} | ||
|
||
#[test] | ||
fn choice_sequence() { | ||
#[derive(Debug, XmlSchema)] | ||
#[xml_schema(source = "xml_schema/tests/choice_sequence.xsd")] | ||
struct ChoiceTypeSchema; | ||
|
||
use choice_type_schema::xml_schema_types::*; | ||
|
||
let xml_1 = r#"<?xml version="1.0" encoding="UTF-8"?> | ||
<person> | ||
<name>Doe</name> | ||
<firstname>John</firstname> | ||
</person> | ||
"#; | ||
|
||
let sample_1: Person = from_str(xml_1).unwrap(); | ||
|
||
let model = Person { | ||
name: "Doe".to_string(), | ||
firstname: Some(Firstname { | ||
base: "John".to_string(), | ||
scope: None, | ||
}), | ||
lastname: None, | ||
}; | ||
|
||
assert_eq!(sample_1, model); | ||
|
||
let data = to_string(&model).unwrap(); | ||
assert_eq!( | ||
data, | ||
r#"<?xml version="1.0" encoding="utf-8"?><Person><name>Doe</name><firstname>John</firstname></Person>"# | ||
); | ||
} | ||
|
||
#[test] | ||
fn choice_multiple() { | ||
#[derive(Debug, XmlSchema)] | ||
#[xml_schema(source = "xml_schema/tests/choice_multiple.xsd")] | ||
struct ChoiceTypeSchema; | ||
|
||
let xml_1 = r#"<?xml version="1.0" encoding="UTF-8"?> | ||
<person> | ||
<firstname>John</firstname> | ||
</person> | ||
"#; | ||
|
||
let sample_1: Person = from_str(xml_1).unwrap(); | ||
|
||
let model = Person { | ||
firstname_list: vec!["John".to_string()], | ||
lastname_list: vec![], | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. in that case I expect to have: Person {
parents: vec![Parent::Firstname("John".to_string())],
}
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yup exactly, I just patched this in one commit just to get it to compile. I am currently working on making this a vector. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh sorry I just implemented:
which i notice now is a bit different from what you imagined. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have some attempts which try to implement this as enum, you can see the last commit here: 740c37b Unforuntately the test fails with:
So I probably got the yaserde config wrong :( I will pause this work for now. |
||
|
||
assert_eq!(sample_1, model); | ||
|
||
let data = to_string(&model).unwrap(); | ||
assert_eq!( | ||
data, | ||
r#"<?xml version="1.0" encoding="utf-8"?><Person><firstname>John</firstname></Person>"# | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> | ||
<xs:complexType name="firstname"> | ||
<xs:simpleContent> | ||
<xs:extension base="xs:string"> | ||
<xs:attribute name="scope" type="xs:anyURI" use="optional" default="http://example.com#elements"/> | ||
</xs:extension> | ||
</xs:simpleContent> | ||
</xs:complexType> | ||
|
||
<xs:complexType name="lastname"> | ||
<xs:simpleContent> | ||
<xs:extension base="xs:string"> | ||
<xs:attribute name="scope" type="xs:anyURI" use="optional" default="http://example.com#elements"/> | ||
</xs:extension> | ||
</xs:simpleContent> | ||
</xs:complexType> | ||
|
||
|
||
<xs:complexType name="person"> | ||
<xs:choice minOccurs="0"> | ||
<xs:element name="firstname" type="firstname"/> | ||
<xs:element name="lastname" type="lastname"/> | ||
</xs:choice> | ||
</xs:complexType> | ||
</xs:schema> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> | ||
<xs:element name="person"> | ||
<xs:complexType name="parents"> | ||
<xs:choice minOccurs="0" maxOccurs="unbounded"> | ||
<xs:element name="firstname" type="xs:string"/> | ||
<xs:element name="lastname" type="xs:string"/> | ||
</xs:choice> | ||
</xs:complexType> | ||
</xs:element> | ||
</xs:schema> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> | ||
<xs:complexType name="firstname"> | ||
<xs:simpleContent> | ||
<xs:extension base="xs:string"> | ||
<xs:attribute name="scope" type="xs:anyURI" use="optional" default="http://example.com#elements"/> | ||
</xs:extension> | ||
</xs:simpleContent> | ||
</xs:complexType> | ||
|
||
<xs:complexType name="lastname"> | ||
<xs:simpleContent> | ||
<xs:extension base="xs:string"> | ||
<xs:attribute name="scope" type="xs:anyURI" use="optional" default="http://example.com#elements"/> | ||
</xs:extension> | ||
</xs:simpleContent> | ||
</xs:complexType> | ||
|
||
<xs:element name="person"> | ||
<xs:complexType name="parent"> | ||
<xs:sequence> | ||
<xs:element name="name" type="xs:string" minOccurs="1"/> | ||
<xs:choice> | ||
<xs:element name="firstname" type="firstname"/> | ||
<xs:element name="lastname" type="lastname"/> | ||
</xs:choice> | ||
</xs:sequence> | ||
</xs:complexType> | ||
</xs:element> | ||
</xs:schema> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
//! The children of a choice are mapped to Option fields. | ||
//! Generating an enum would have been the better way but the choice element | ||
//! may not have a name, so it's impossible to name the generated Rust enum. | ||
//! The enum would have been nice to avoid runtime checks that only a single choice element is used. | ||
|
||
use crate::xsd::{ | ||
annotation::Annotation, attribute::Attribute, element::Element, max_occurences::MaxOccurences, | ||
Implementation, XsdContext, | ||
}; | ||
use log::info; | ||
use proc_macro2::TokenStream; | ||
|
||
#[derive(Clone, Default, Debug, PartialEq, YaDeserialize)] | ||
#[yaserde( | ||
rename = "choice" | ||
prefix = "xs", | ||
namespace = "xs: http://www.w3.org/2001/XMLSchema" | ||
)] | ||
pub struct Choice { | ||
#[yaserde(attribute)] | ||
pub id: Option<String>, | ||
#[yaserde(rename = "attribute")] | ||
pub attributes: Vec<Attribute>, | ||
#[yaserde(rename = "minOccurs", attribute)] | ||
pub min_occurences: Option<u64>, | ||
#[yaserde(rename = "maxOccurs", attribute)] | ||
pub max_occurences: Option<MaxOccurences>, | ||
#[yaserde(rename = "annotation")] | ||
pub annotation: Option<Annotation>, | ||
#[yaserde(rename = "element")] | ||
pub element: Vec<Element>, | ||
} | ||
|
||
impl Implementation for Choice { | ||
fn implement( | ||
&self, | ||
namespace_definition: &TokenStream, | ||
prefix: &Option<String>, | ||
context: &XsdContext, | ||
) -> TokenStream { | ||
let elements: TokenStream = self | ||
.element | ||
.iter() | ||
.map(|element| element.implement(&namespace_definition, prefix, context)) | ||
.collect(); | ||
|
||
quote! { | ||
#elements | ||
} | ||
} | ||
} | ||
|
||
impl Choice { | ||
pub fn get_sub_types_implementation( | ||
&self, | ||
context: &XsdContext, | ||
namespace_definition: &TokenStream, | ||
prefix: &Option<String>, | ||
) -> TokenStream { | ||
info!("Generate choice sub types implementation"); | ||
self | ||
.element | ||
.iter() | ||
.map(|element| element.get_subtypes_implementation(namespace_definition, prefix, context)) | ||
.collect() | ||
} | ||
|
||
pub fn get_field_implementation( | ||
&self, | ||
context: &XsdContext, | ||
prefix: &Option<String>, | ||
) -> TokenStream { | ||
info!("Generate choice elements"); | ||
|
||
let multiple = matches!(self.min_occurences, Some(min_occurences) if min_occurences > 1) | ||
|| matches!(self.max_occurences, Some(MaxOccurences::Unbounded)) | ||
|| matches!(self.max_occurences, Some(MaxOccurences::Number{value}) if value > 1); | ||
|
||
// Element fields are by default declared as Option type due to the nature of the choice element. | ||
// Since a vector can also be empty, use Vec<_>, rather than Option<Vec<_>>. | ||
let optional = !multiple; | ||
|
||
self | ||
.element | ||
.iter() | ||
.map(|element| element.get_field_implementation(context, prefix, multiple, optional)) | ||
.collect() | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In that case I expect more to retrieve an enum here.
The test is not easy to understand as a person has basically a first name and a last name, so it's not a choice, more a combination.
I think the example here is more relevant: https://www.w3schools.com/xml/el_choice.asp
Where. Person may have role
employee
ormember
.So for me the expected code will be: