1extern crate alloc;
51
52use alloc::format;
53use alloc::string::{String, ToString};
54use alloc::vec::Vec;
55
56use crate::errors::XmlError;
57use crate::parser::{XmlElement, parse_xml_tree};
58use crate::xtypes_def::{
59 EnumLiteral, EnumType, PrimitiveType, StructMember, StructType, TypeDef, TypeLibrary, TypeRef,
60};
61
62fn map_builtin(local: &str) -> Option<PrimitiveType> {
66 let trimmed = match local.split_once(':') {
67 Some((_prefix, suffix)) => suffix,
68 None => local,
69 };
70 Some(match trimmed {
71 "boolean" => PrimitiveType::Boolean,
72 "byte" => PrimitiveType::Octet,
73 "unsignedByte" => PrimitiveType::Octet,
74 "short" => PrimitiveType::Short,
75 "unsignedShort" => PrimitiveType::UShort,
76 "int" | "integer" => PrimitiveType::Long,
77 "unsignedInt" => PrimitiveType::ULong,
78 "long" => PrimitiveType::LongLong,
79 "unsignedLong" => PrimitiveType::ULongLong,
80 "float" => PrimitiveType::Float,
81 "double" => PrimitiveType::Double,
82 "string" | "normalizedString" | "token" => PrimitiveType::String,
83 _ => return None,
84 })
85}
86
87fn resolve_type_ref(raw: &str) -> TypeRef {
92 if let Some(prim) = map_builtin(raw) {
93 TypeRef::Primitive(prim)
94 } else {
95 let local = match raw.split_once(':') {
97 Some((_p, s)) => s,
98 None => raw,
99 };
100 TypeRef::Named(local.to_string())
101 }
102}
103
104pub fn parse_xsd_schema(xml: &str) -> Result<TypeLibrary, XmlError> {
110 let doc = parse_xml_tree(xml)?;
111 if doc.root.name != "schema" {
112 return Err(XmlError::InvalidXml(format!(
113 "expected <xsd:schema> root, got <{}>",
114 doc.root.name
115 )));
116 }
117
118 let mut lib = TypeLibrary::default();
119 for child in &doc.root.children {
120 match child.name.as_str() {
121 "complexType" => lib.types.push(TypeDef::Struct(parse_complex_type(child)?)),
122 "simpleType" => {
123 if let Some(en) = parse_simple_type_as_enum(child)? {
124 lib.types.push(TypeDef::Enum(en));
125 }
126 }
129 "element" => {
130 if let Some(ct) = child.child("complexType") {
134 let mut s = parse_complex_type(ct)?;
135 if s.name.is_empty() {
136 s.name = child.attribute("name").unwrap_or_default().to_string();
137 }
138 if !s.name.is_empty() {
139 lib.types.push(TypeDef::Struct(s));
140 }
141 }
142 }
143 "include" | "import" | "annotation" | "notation" | "group" | "attributeGroup" => {
144 }
147 _ => {
148 }
151 }
152 }
153 Ok(lib)
154}
155
156fn parse_complex_type(node: &XmlElement) -> Result<StructType, XmlError> {
158 let name = node.attribute("name").unwrap_or_default().to_string();
159
160 let mut members = Vec::new();
161 if let Some(seq) = node.child("sequence").or_else(|| node.child("all")) {
164 for el in seq.children_named("element") {
165 members.push(parse_element_as_member(el)?);
166 }
167 }
168 let mut base_type: Option<String> = None;
171 if let Some(cc) = node.child("complexContent") {
172 if let Some(ext) = cc.child("extension") {
173 if let Some(b) = ext.attribute("base") {
174 let local = b.split_once(':').map(|(_p, s)| s).unwrap_or(b);
175 base_type = Some(local.to_string());
176 }
177 if let Some(seq) = ext.child("sequence").or_else(|| ext.child("all")) {
178 for el in seq.children_named("element") {
179 members.push(parse_element_as_member(el)?);
180 }
181 }
182 }
183 }
184
185 Ok(StructType {
186 name,
187 extensibility: None,
188 base_type,
189 members,
190 })
191}
192
193fn parse_element_as_member(node: &XmlElement) -> Result<StructMember, XmlError> {
196 let name = node.attribute("name").unwrap_or_default().to_string();
197 let raw_type = node.attribute("type").unwrap_or("xsd:string");
198 let type_ref = resolve_type_ref(raw_type);
199
200 let min_occurs = node
201 .attribute("minOccurs")
202 .and_then(|v| v.parse::<u32>().ok())
203 .unwrap_or(1);
204 let max_occurs_raw = node.attribute("maxOccurs").unwrap_or("1");
205 let max_occurs: Option<u32> = if max_occurs_raw == "unbounded" {
206 Some(u32::MAX)
207 } else {
208 max_occurs_raw.parse::<u32>().ok()
209 };
210
211 let optional = min_occurs == 0 && max_occurs == Some(1);
212 let sequence_max_length = match max_occurs {
213 Some(u32::MAX) => Some(0), Some(n) if n > 1 => Some(n), _ => None,
216 };
217
218 Ok(StructMember {
219 name,
220 type_ref,
221 optional,
222 sequence_max_length,
223 ..Default::default()
224 })
225}
226
227fn parse_simple_type_as_enum(node: &XmlElement) -> Result<Option<EnumType>, XmlError> {
233 let name = node.attribute("name").unwrap_or_default().to_string();
234 let restriction = match node.child("restriction") {
235 Some(r) => r,
236 None => return Ok(None),
237 };
238
239 let mut enumerators: Vec<EnumLiteral> = Vec::new();
240 for en in restriction.children_named("enumeration") {
241 if let Some(value) = en.attribute("value") {
242 enumerators.push(EnumLiteral {
243 name: value.to_string(),
244 value: None,
245 });
246 }
247 }
248 if enumerators.is_empty() {
249 return Ok(None);
250 }
251 Ok(Some(EnumType {
252 name,
253 bit_bound: None,
254 enumerators,
255 }))
256}
257
258#[cfg(test)]
259#[allow(clippy::expect_used, clippy::panic, clippy::unwrap_used)]
260mod tests {
261 use super::*;
262 use alloc::string::String;
263
264 fn lib_of(xml: &str) -> TypeLibrary {
265 parse_xsd_schema(xml).expect("parse")
266 }
267
268 #[test]
269 fn empty_schema_yields_empty_library() {
270 let xml = r#"<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"/>"#;
271 let lib = lib_of(xml);
272 assert!(lib.types.is_empty());
273 }
274
275 #[test]
276 fn complex_type_maps_to_struct_with_primitive_members() {
277 let xml = r#"
279 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
280 <xsd:complexType name="Point">
281 <xsd:sequence>
282 <xsd:element name="x" type="xsd:int"/>
283 <xsd:element name="y" type="xsd:int"/>
284 </xsd:sequence>
285 </xsd:complexType>
286 </xsd:schema>
287 "#;
288 let lib = lib_of(xml);
289 assert_eq!(lib.types.len(), 1);
290 let TypeDef::Struct(s) = &lib.types[0] else {
291 panic!("expected struct, got {:?}", lib.types[0]);
292 };
293 assert_eq!(s.name, "Point");
294 assert_eq!(s.members.len(), 2);
295 assert_eq!(s.members[0].name, "x");
296 assert_eq!(
297 s.members[0].type_ref,
298 TypeRef::Primitive(PrimitiveType::Long)
299 );
300 assert_eq!(s.members[1].name, "y");
301 }
302
303 #[test]
304 fn xsd_long_maps_to_dds_longlong() {
305 let xml = r#"
307 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
308 <xsd:complexType name="Big">
309 <xsd:sequence>
310 <xsd:element name="v" type="xsd:long"/>
311 </xsd:sequence>
312 </xsd:complexType>
313 </xsd:schema>
314 "#;
315 let lib = lib_of(xml);
316 let TypeDef::Struct(s) = &lib.types[0] else {
317 panic!()
318 };
319 assert_eq!(
320 s.members[0].type_ref,
321 TypeRef::Primitive(PrimitiveType::LongLong)
322 );
323 }
324
325 #[test]
326 fn unsigned_xsd_types_map_to_unsigned_dds_primitives() {
327 let xml = r#"
328 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
329 <xsd:complexType name="U">
330 <xsd:sequence>
331 <xsd:element name="a" type="xsd:unsignedByte"/>
332 <xsd:element name="b" type="xsd:unsignedShort"/>
333 <xsd:element name="c" type="xsd:unsignedInt"/>
334 <xsd:element name="d" type="xsd:unsignedLong"/>
335 </xsd:sequence>
336 </xsd:complexType>
337 </xsd:schema>
338 "#;
339 let lib = lib_of(xml);
340 let TypeDef::Struct(s) = &lib.types[0] else {
341 panic!()
342 };
343 assert_eq!(
344 s.members[0].type_ref,
345 TypeRef::Primitive(PrimitiveType::Octet)
346 );
347 assert_eq!(
348 s.members[1].type_ref,
349 TypeRef::Primitive(PrimitiveType::UShort)
350 );
351 assert_eq!(
352 s.members[2].type_ref,
353 TypeRef::Primitive(PrimitiveType::ULong)
354 );
355 assert_eq!(
356 s.members[3].type_ref,
357 TypeRef::Primitive(PrimitiveType::ULongLong)
358 );
359 }
360
361 #[test]
362 fn min_occurs_zero_yields_optional_member() {
363 let xml = r#"
364 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
365 <xsd:complexType name="Opt">
366 <xsd:sequence>
367 <xsd:element name="maybe" type="xsd:int" minOccurs="0"/>
368 </xsd:sequence>
369 </xsd:complexType>
370 </xsd:schema>
371 "#;
372 let lib = lib_of(xml);
373 let TypeDef::Struct(s) = &lib.types[0] else {
374 panic!()
375 };
376 assert!(s.members[0].optional);
377 }
378
379 #[test]
380 fn max_occurs_unbounded_yields_sequence() {
381 let xml = r#"
382 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
383 <xsd:complexType name="WithSeq">
384 <xsd:sequence>
385 <xsd:element name="items" type="xsd:int" maxOccurs="unbounded"/>
386 </xsd:sequence>
387 </xsd:complexType>
388 </xsd:schema>
389 "#;
390 let lib = lib_of(xml);
391 let TypeDef::Struct(s) = &lib.types[0] else {
392 panic!()
393 };
394 assert!(s.members[0].sequence_max_length.is_some());
395 }
396
397 #[test]
398 fn max_occurs_bounded_yields_bounded_sequence() {
399 let xml = r#"
400 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
401 <xsd:complexType name="WithBoundedSeq">
402 <xsd:sequence>
403 <xsd:element name="items" type="xsd:int" maxOccurs="5"/>
404 </xsd:sequence>
405 </xsd:complexType>
406 </xsd:schema>
407 "#;
408 let lib = lib_of(xml);
409 let TypeDef::Struct(s) = &lib.types[0] else {
410 panic!()
411 };
412 assert_eq!(s.members[0].sequence_max_length, Some(5));
413 }
414
415 #[test]
416 fn simple_type_with_enumeration_maps_to_dds_enum() {
417 let xml = r#"
419 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
420 <xsd:simpleType name="Color">
421 <xsd:restriction base="xsd:string">
422 <xsd:enumeration value="RED"/>
423 <xsd:enumeration value="GREEN"/>
424 <xsd:enumeration value="BLUE"/>
425 </xsd:restriction>
426 </xsd:simpleType>
427 </xsd:schema>
428 "#;
429 let lib = lib_of(xml);
430 let TypeDef::Enum(e) = &lib.types[0] else {
431 panic!("expected enum, got {:?}", lib.types[0]);
432 };
433 assert_eq!(e.name, "Color");
434 assert_eq!(e.enumerators.len(), 3);
435 assert_eq!(e.enumerators[0].name, "RED");
436 assert_eq!(e.enumerators[1].name, "GREEN");
437 assert_eq!(e.enumerators[2].name, "BLUE");
438 }
439
440 #[test]
441 fn user_type_reference_yields_named_typeref() {
442 let xml = r#"
443 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
444 <xsd:complexType name="Origin">
445 <xsd:sequence>
446 <xsd:element name="lat" type="xsd:double"/>
447 </xsd:sequence>
448 </xsd:complexType>
449 <xsd:complexType name="Trip">
450 <xsd:sequence>
451 <xsd:element name="from" type="Origin"/>
452 </xsd:sequence>
453 </xsd:complexType>
454 </xsd:schema>
455 "#;
456 let lib = lib_of(xml);
457 assert_eq!(lib.types.len(), 2);
458 let TypeDef::Struct(trip) = &lib.types[1] else {
459 panic!()
460 };
461 assert_eq!(
462 trip.members[0].type_ref,
463 TypeRef::Named(String::from("Origin"))
464 );
465 }
466
467 #[test]
468 fn complex_content_extension_yields_inheritance() {
469 let xml = r#"
472 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
473 <xsd:complexType name="Parent">
474 <xsd:sequence>
475 <xsd:element name="a" type="xsd:int"/>
476 </xsd:sequence>
477 </xsd:complexType>
478 <xsd:complexType name="Child">
479 <xsd:complexContent>
480 <xsd:extension base="Parent">
481 <xsd:sequence>
482 <xsd:element name="b" type="xsd:int"/>
483 </xsd:sequence>
484 </xsd:extension>
485 </xsd:complexContent>
486 </xsd:complexType>
487 </xsd:schema>
488 "#;
489 let lib = lib_of(xml);
490 let TypeDef::Struct(child) = &lib.types[1] else {
491 panic!()
492 };
493 assert_eq!(child.name, "Child");
494 assert_eq!(child.base_type.as_deref(), Some("Parent"));
495 assert_eq!(child.members.len(), 1);
496 assert_eq!(child.members[0].name, "b");
497 }
498
499 #[test]
500 fn non_schema_root_is_rejected() {
501 let xml = r#"<dds><types/></dds>"#;
502 let r = parse_xsd_schema(xml);
503 assert!(r.is_err(), "non-<xsd:schema> root muss rejected werden");
504 }
505
506 #[test]
507 fn xsd_to_typeobject_via_bridge_produces_minimal_struct() {
508 use crate::typeobject_bridge::bridge_library;
513
514 let xml = r#"
515 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
516 <xsd:complexType name="Sensor">
517 <xsd:sequence>
518 <xsd:element name="id" type="xsd:int"/>
519 <xsd:element name="name" type="xsd:string"/>
520 </xsd:sequence>
521 </xsd:complexType>
522 </xsd:schema>
523 "#;
524 let lib = parse_xsd_schema(xml).expect("xsd-parse");
525 let map = bridge_library(&lib).expect("bridge");
526 assert!(
527 map.contains_key("Sensor"),
528 "TypeObject fuer Sensor fehlt: {:?}",
529 map.keys().collect::<Vec<_>>()
530 );
531 }
532}