Skip to content

Commit 5c8182f

Browse files
committed
initial choice type support
1 parent 2969839 commit 5c8182f

File tree

8 files changed

+159
-6
lines changed

8 files changed

+159
-6
lines changed

xml_schema/tests/choice.rs

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#[macro_use]
2+
extern crate yaserde_derive;
3+
4+
use log::debug;
5+
use std::io::prelude::*;
6+
use xml_schema_derive::XmlSchema;
7+
use yaserde::de::from_str;
8+
use yaserde::ser::to_string;
9+
use yaserde::{YaDeserialize, YaSerialize};
10+
11+
#[test]
12+
fn choice() {
13+
#[derive(Debug, XmlSchema)]
14+
#[xml_schema(source = "xml_schema/tests/choice.xsd")]
15+
struct ChoiceTypeSchema;
16+
17+
let xml_1 = r#"
18+
<?xml version="1.0" encoding="UTF-8"?>
19+
<Parent>
20+
<XFirstname>John</XFirstname>
21+
</Parent>
22+
"#;
23+
24+
let sample_1: Parent = from_str(xml_1).unwrap();
25+
26+
let model = Parent {
27+
x_firstname: Some(Firstname {
28+
content: "John".to_string(),
29+
scope: None,
30+
}),
31+
x_lastname: None,
32+
};
33+
34+
assert_eq!(sample_1, model);
35+
36+
let data = to_string(&model).unwrap();
37+
assert_eq!(
38+
data,
39+
r#"<?xml version="1.0" encoding="utf-8"?><Parent><XFirstname>John</XFirstname></Parent>"#
40+
);
41+
}

xml_schema/tests/choice.xsd

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
2+
<xs:complexType name="Firstname">
3+
<xs:simpleContent>
4+
<xs:extension base="xs:string">
5+
<xs:attribute name="scope" type="xs:anyURI" use="optional" default="http://example.com#elements"/>
6+
</xs:extension>
7+
</xs:simpleContent>
8+
</xs:complexType>
9+
10+
<xs:complexType name="Lastname">
11+
<xs:simpleContent>
12+
<xs:extension base="xs:string">
13+
<xs:attribute name="scope" type="xs:anyURI" use="optional" default="http://example.com#elements"/>
14+
</xs:extension>
15+
</xs:simpleContent>
16+
</xs:complexType>
17+
18+
19+
<xs:complexType name="Parent">
20+
<xs:choice maxOccurs="unbounded" minOccurs="0">
21+
<xs:element name="XFirstname" type="Firstname"/>
22+
<xs:element name="XLastname" type="Lastname"/>
23+
</xs:choice>
24+
</xs:complexType>
25+
</xs:schema>

xml_schema_derive/src/xsd/choice.rs

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
//! The children of a choice are mapped to Option fields.
2+
//! Generating an enum would have been the better way but the choice element
3+
//! may not have a name, so it's impossible to name the generated Rust enum.
4+
//! The enum would have been nice to avoid runtime checks that only a single choice element is used.
5+
6+
use crate::xsd::{
7+
annotation::Annotation, attribute::Attribute, element::Element, max_occurences::MaxOccurences,
8+
Implementation, XsdContext,
9+
};
10+
use log::{debug, info};
11+
use proc_macro2::TokenStream;
12+
use std::io::prelude::*;
13+
use yaserde::YaDeserialize;
14+
15+
#[derive(Clone, Default, Debug, PartialEq, YaDeserialize)]
16+
#[yaserde(
17+
rename = "choice"
18+
prefix = "xs",
19+
namespace = "xs: http://www.w3.org/2001/XMLSchema"
20+
)]
21+
pub struct Choice {
22+
#[yaserde(attribute)]
23+
pub id: Option<String>,
24+
#[yaserde(rename = "attribute")]
25+
pub attributes: Vec<Attribute>,
26+
#[yaserde(rename = "minOccurs", attribute)]
27+
pub min_occurences: Option<u64>,
28+
#[yaserde(rename = "maxOccurs", attribute)]
29+
pub max_occurences: Option<MaxOccurences>,
30+
#[yaserde(rename = "annotation")]
31+
pub annotation: Option<Annotation>,
32+
#[yaserde(rename = "element")]
33+
pub element: Vec<Element>,
34+
}
35+
36+
impl Implementation for Choice {
37+
fn implement(
38+
&self,
39+
namespace_definition: &TokenStream,
40+
prefix: &Option<String>,
41+
context: &XsdContext,
42+
) -> TokenStream {
43+
let elements: TokenStream = self
44+
.element
45+
.iter()
46+
.map(|element| element.implement(&namespace_definition, prefix, context))
47+
.collect();
48+
49+
quote! {
50+
#elements
51+
}
52+
}
53+
}
54+
55+
impl Choice {
56+
pub fn get_field_implementation(
57+
&self,
58+
context: &XsdContext,
59+
prefix: &Option<String>,
60+
) -> TokenStream {
61+
info!("Generate choice elements");
62+
self
63+
.element
64+
.iter()
65+
.map(|element| element.get_field_implementation(context, prefix, false, true))
66+
.collect()
67+
}
68+
}

xml_schema_derive/src/xsd/complex_type.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::xsd::{
2-
annotation::Annotation, attribute::Attribute, complex_content::ComplexContent,
2+
annotation::Annotation, attribute::Attribute, choice::Choice, complex_content::ComplexContent,
33
sequence::Sequence, simple_content::SimpleContent, Implementation, XsdContext,
44
};
55
use heck::CamelCase;
@@ -27,6 +27,8 @@ pub struct ComplexType {
2727
pub complex_content: Option<ComplexContent>,
2828
#[yaserde(rename = "annotation")]
2929
pub annotation: Option<Annotation>,
30+
#[yaserde(rename = "choice")]
31+
pub choice: Option<Choice>,
3032
}
3133

3234
impl Implementation for ComplexType {
@@ -84,6 +86,18 @@ impl Implementation for ComplexType {
8486
.map(|annotation| annotation.implement(&namespace_definition, prefix, context))
8587
.unwrap_or_else(TokenStream::new);
8688

89+
let choice = self
90+
.choice
91+
.as_ref()
92+
.map(|choice| choice.implement(&namespace_definition, prefix, context))
93+
.unwrap_or_else(TokenStream::new);
94+
95+
let choice_field = self
96+
.choice
97+
.as_ref()
98+
.map(|choice| choice.get_field_implementation(context, prefix))
99+
.unwrap_or_else(TokenStream::new);
100+
87101
quote! {
88102
#docs
89103

@@ -93,10 +107,13 @@ impl Implementation for ComplexType {
93107
#sequence
94108
#simple_content
95109
#complex_content
110+
#choice_field
96111
#attributes
97112
}
98113

99114
#sub_types_implementation
115+
116+
#choice
100117
}
101118
}
102119
}

xml_schema_derive/src/xsd/element.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ impl Element {
106106
context: &XsdContext,
107107
prefix: &Option<String>,
108108
multiple: bool,
109+
optional: bool,
109110
) -> TokenStream {
110111
if self.name == "" {
111112
return quote!();
@@ -143,7 +144,7 @@ impl Element {
143144
rust_type
144145
};
145146

146-
let rust_type = if self.min_occurences == Some(0) {
147+
let rust_type = if optional || self.min_occurences == Some(0) {
147148
quote!(Option<#rust_type>)
148149
} else {
149150
rust_type

xml_schema_derive/src/xsd/extension.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::xsd::{
2-
attribute::Attribute, rust_types_mapping::RustTypesMapping, sequence::Sequence, Implementation,
3-
XsdContext,
2+
attribute::Attribute, choice::Choice, rust_types_mapping::RustTypesMapping, sequence::Sequence,
3+
Implementation, XsdContext,
44
};
55
use log::debug;
66
use proc_macro2::TokenStream;

xml_schema_derive/src/xsd/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
mod annotation;
22
mod attribute;
33
mod attribute_group;
4+
mod choice;
45
mod complex_content;
56
mod complex_type;
67
mod element;

xml_schema_derive/src/xsd/sequence.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ impl Implementation for Sequence {
2222
self
2323
.elements
2424
.iter()
25-
.map(|element| element.get_field_implementation(context, prefix, false))
25+
.map(|element| element.get_field_implementation(context, prefix, false, false))
2626
.collect()
2727
}
2828
}
@@ -50,7 +50,7 @@ impl Sequence {
5050
self
5151
.elements
5252
.iter()
53-
.map(|element| element.get_field_implementation(context, prefix, true))
53+
.map(|element| element.get_field_implementation(context, prefix, true, false))
5454
.collect()
5555
}
5656
}

0 commit comments

Comments
 (0)