opcua_xml/schema/
xml_schema.rs

1//! A limited implementation of code generation based on an xml schema. Adapted for
2//! OPC-UA code generation.
3
4use roxmltree::{Document, Node};
5
6use crate::{
7    ext::{
8        children_of_type, children_with_name, first_child_of_type, first_child_of_type_req,
9        first_child_with_name, first_child_with_name_opt, value_from_attr, value_from_attr_opt,
10    },
11    FromValue, XmlError, XmlLoad,
12};
13
14/// Load an XSD schema from a document.
15pub fn load_xsd_schema(document: &str) -> Result<XmlSchema, XmlError> {
16    let document = Document::parse(document).map_err(|e| XmlError {
17        span: 0..1,
18        error: crate::error::XmlErrorInner::Xml(e),
19    })?;
20    let root = document.root();
21    first_child_with_name(&root, "schema")
22}
23
24#[derive(Debug, Clone)]
25/// Value of a Facet node.
26pub struct FacetValue {
27    /// Facet value.
28    pub value: String,
29    /// Whether the facet is fixed.
30    pub fixed: bool,
31}
32
33impl<'input> XmlLoad<'input> for FacetValue {
34    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
35        Ok(Self {
36            value: value_from_attr(node, "value")?,
37            fixed: value_from_attr_opt(node, "fixed")?.unwrap_or(false),
38        })
39    }
40}
41
42#[derive(Debug, Clone)]
43/// A restriction facet.
44pub enum Facet {
45    /// Exclusive minimum of the type value.
46    MinExclusive(FacetValue),
47    /// Inclusive minimum of the type value.
48    MinInclusive(FacetValue),
49    /// Exclusive maximum of the type value.
50    MaxExclusive(FacetValue),
51    /// Inclusive maximum of the type value.
52    MaxInclusive(FacetValue),
53    /// Total number of digits in values of this type.
54    TotalDigits(FacetValue),
55    /// Total number of digits after the comma in values of this type.
56    FractionDigits(FacetValue),
57    /// Length of values of this type.
58    Length(FacetValue),
59    /// Minimum length of values of this type.
60    MinLength(FacetValue),
61    /// Maximum length of values of this type.
62    MaxLength(FacetValue),
63    /// Possible value. The presence of one of these means the type is an enum.
64    Enumeration(FacetValue),
65    /// Handling of white space in the value.
66    WhiteSpace(FacetValue),
67    /// Pattern values of this type must match.
68    Pattern(FacetValue),
69}
70
71impl<'input> XmlLoad<'input> for Option<Facet> {
72    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
73        Ok(Some(match node.tag_name().name() {
74            "minExclusive" => Facet::MinExclusive(FacetValue::load(node)?),
75            "minInclusive" => Facet::MinInclusive(FacetValue::load(node)?),
76            "maxExclusive" => Facet::MaxExclusive(FacetValue::load(node)?),
77            "maxInclusive" => Facet::MaxInclusive(FacetValue::load(node)?),
78            "totalDigits" => Facet::TotalDigits(FacetValue::load(node)?),
79            "fractionDigits" => Facet::FractionDigits(FacetValue::load(node)?),
80            "length" => Facet::Length(FacetValue::load(node)?),
81            "minLength" => Facet::MinLength(FacetValue::load(node)?),
82            "maxLength" => Facet::MaxLength(FacetValue::load(node)?),
83            "enumeration" => Facet::Enumeration(FacetValue::load(node)?),
84            "whiteSpace" => Facet::WhiteSpace(FacetValue::load(node)?),
85            "pattern" => Facet::Pattern(FacetValue::load(node)?),
86            _ => return Ok(None),
87        }))
88    }
89}
90
91#[derive(Debug, Clone)]
92/// A restriction is a property of a type that limits valid values.
93pub struct Restriction {
94    /// Inherit from some other type.
95    pub base: Option<String>,
96    /// List of facets.
97    pub facets: Vec<Facet>,
98    /// Inner structure type, may not be present for primitive types.
99    pub content: Option<SimpleType>,
100    /// Type definition particle.
101    pub particle: Option<TypeDefParticle>,
102    /// Extra attributes.
103    pub attributes: Vec<Attribute>,
104}
105
106impl<'input> XmlLoad<'input> for Restriction {
107    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
108        Ok(Self {
109            base: value_from_attr_opt(node, "base")?,
110            facets: children_of_type(node)?,
111            particle: first_child_of_type(node)?,
112            attributes: children_with_name(node, "")?,
113            content: first_child_with_name_opt(node, "simpleType")?,
114        })
115    }
116}
117
118#[derive(Debug, Clone)]
119/// List of values.
120pub struct List {
121    /// Inner type.
122    pub content: Option<SimpleType>,
123    /// Item type.
124    pub item_type: Option<String>,
125}
126
127impl<'input> XmlLoad<'input> for List {
128    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
129        Ok(Self {
130            content: first_child_with_name_opt(node, "simpleType")?,
131            item_type: value_from_attr_opt(node, "itemType")?,
132        })
133    }
134}
135
136#[derive(Debug, Clone)]
137/// Discriminated union of different variants.
138pub struct Union {
139    /// Possible variants.
140    pub variants: Vec<SimpleType>,
141    /// Member types.
142    pub member_types: Option<Vec<String>>,
143}
144
145impl<'input> XmlLoad<'input> for Union {
146    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
147        Ok(Self {
148            variants: children_with_name(node, "simpleType")?,
149            member_types: value_from_attr_opt(node, "memberTypes")?,
150        })
151    }
152}
153
154#[derive(Debug, Clone)]
155/// Simple derivation.
156pub enum SimpleDerivation {
157    /// Restriction type.
158    Restriction(Box<Restriction>),
159    /// List type.
160    List(Box<List>),
161    /// Union type.
162    Union(Box<Union>),
163}
164
165impl<'input> XmlLoad<'input> for Option<SimpleDerivation> {
166    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
167        Ok(Some(match node.tag_name().name() {
168            "restriction" => SimpleDerivation::Restriction(Box::new(XmlLoad::load(node)?)),
169            "list" => SimpleDerivation::List(Box::new(XmlLoad::load(node)?)),
170            "union" => SimpleDerivation::Union(Box::new(XmlLoad::load(node)?)),
171            _ => return Ok(None),
172        }))
173    }
174}
175
176#[derive(Debug, Clone)]
177/// A simple type.
178pub struct SimpleType {
179    /// Type name.
180    pub name: Option<String>,
181    /// Type content.
182    pub content: Option<SimpleDerivation>,
183}
184
185impl<'input> XmlLoad<'input> for SimpleType {
186    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
187        Ok(Self {
188            name: value_from_attr_opt(node, "name")?,
189            content: first_child_of_type(node)?,
190        })
191    }
192}
193
194#[derive(Debug, Clone)]
195/// The Any type.
196pub struct Any {
197    /// Minimum number of times this field occurs.
198    pub min_occurs: Option<u32>,
199    /// Maximum number of times this field occurs.
200    pub max_uccors: Option<MaxOccurs>,
201}
202
203impl<'input> XmlLoad<'input> for Any {
204    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
205        Ok(Self {
206            min_occurs: value_from_attr_opt(node, "minOccurs")?,
207            max_uccors: value_from_attr_opt(node, "maxOccurs")?,
208        })
209    }
210}
211
212/// Particle, some element of the schema.
213#[derive(Debug, Clone)]
214pub enum Particle {
215    /// General element.
216    Element(Element),
217    /// An list of particles that must all be true.
218    All(Group),
219    /// A choice between a list of different particles.
220    Choice(Group),
221    /// A sequence of particles in a fixed order.
222    Sequence(Group),
223    /// Anything
224    Any(Any),
225}
226
227impl<'input> XmlLoad<'input> for Option<Particle> {
228    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
229        Ok(Some(match node.tag_name().name() {
230            "element" => Particle::Element(XmlLoad::load(node)?),
231            "all" => Particle::All(XmlLoad::load(node)?),
232            "choice" => Particle::Choice(XmlLoad::load(node)?),
233            "sequence" => Particle::Sequence(XmlLoad::load(node)?),
234            "any" => Particle::Any(XmlLoad::load(node)?),
235            _ => return Ok(None),
236        }))
237    }
238}
239
240#[derive(Debug, Clone)]
241/// A variant of particle that can occur inside another object.
242pub enum NestedParticle {
243    /// Element type
244    Element(Element),
245    /// Choice between different types.
246    Choice(Group),
247    /// Sequence of particles in a fixed order.
248    Sequence(Group),
249    /// Anything
250    Any(Any),
251}
252
253impl<'input> XmlLoad<'input> for Option<NestedParticle> {
254    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
255        Ok(Some(match node.tag_name().name() {
256            "element" => NestedParticle::Element(XmlLoad::load(node)?),
257            "choice" => NestedParticle::Choice(XmlLoad::load(node)?),
258            "sequence" => NestedParticle::Sequence(XmlLoad::load(node)?),
259            "any" => NestedParticle::Any(XmlLoad::load(node)?),
260            _ => return Ok(None),
261        }))
262    }
263}
264
265#[derive(Debug, Clone)]
266/// A group of multiple particles.
267pub struct Group {
268    /// Particles in the group.
269    pub content: Vec<NestedParticle>,
270    /// Minimum occurences of the group.
271    pub min_occurs: Option<u32>,
272    /// Maximum occurences of the group.
273    pub max_uccors: Option<MaxOccurs>,
274}
275
276impl<'input> XmlLoad<'input> for Group {
277    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
278        Ok(Self {
279            content: children_of_type(node)?,
280            min_occurs: value_from_attr_opt(node, "minOccurs")?,
281            max_uccors: value_from_attr_opt(node, "maxOccurs")?,
282        })
283    }
284}
285
286#[derive(Debug, Clone)]
287/// Type definition particle.
288pub enum TypeDefParticle {
289    /// All elements of the group must hold.
290    All(Group),
291    /// One of the elements of the group.
292    Choice(Group),
293    /// A fixed sequence of group elements.
294    Sequence(Group),
295}
296
297impl<'input> XmlLoad<'input> for Option<TypeDefParticle> {
298    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
299        Ok(Some(match node.tag_name().name() {
300            "all" => TypeDefParticle::All(XmlLoad::load(node)?),
301            "choice" => TypeDefParticle::Choice(XmlLoad::load(node)?),
302            "sequence" => TypeDefParticle::Sequence(XmlLoad::load(node)?),
303            _ => return Ok(None),
304        }))
305    }
306}
307
308#[derive(Debug, Clone)]
309/// A type that extends another type.
310pub struct Extension {
311    /// Extension content.
312    pub content: Option<TypeDefParticle>,
313    /// Attributes on the extension.
314    pub attributes: Vec<Attribute>,
315    /// Base type.
316    pub base: String,
317}
318
319impl<'input> XmlLoad<'input> for Extension {
320    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
321        Ok(Self {
322            content: first_child_of_type(node)?,
323            attributes: children_with_name(node, "attributes")?,
324            base: value_from_attr(node, "base")?,
325        })
326    }
327}
328
329#[derive(Debug, Clone)]
330/// Content of a simple type.
331pub enum SimpleContent {
332    /// Restriction of some other type.
333    Restriction(Restriction),
334    /// Extension of some other type.
335    Extension(Extension),
336}
337
338impl<'input> XmlLoad<'input> for Option<SimpleContent> {
339    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
340        Ok(Some(match node.tag_name().name() {
341            "restriction" => SimpleContent::Restriction(XmlLoad::load(node)?),
342            "extension" => SimpleContent::Extension(XmlLoad::load(node)?),
343            _ => return Ok(None),
344        }))
345    }
346}
347
348#[derive(Debug, Clone)]
349/// Complex restriction variant.
350pub struct ComplexRestriction {
351    /// Inner base restriction.
352    pub base: Restriction,
353    /// Extension particle.
354    pub particle: Option<TypeDefParticle>,
355}
356
357impl<'input> XmlLoad<'input> for ComplexRestriction {
358    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
359        Ok(Self {
360            base: Restriction::load(node)?,
361            particle: first_child_of_type(node)?,
362        })
363    }
364}
365
366#[derive(Debug, Clone)]
367/// Content of a complex type.
368pub enum ComplexContent {
369    /// Complex restriction of some other type.
370    Restriction(ComplexRestriction),
371    /// Extension of some other type.
372    Extension(Extension),
373}
374
375impl<'input> XmlLoad<'input> for Option<ComplexContent> {
376    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
377        Ok(Some(match node.tag_name().name() {
378            "restriction" => ComplexContent::Restriction(XmlLoad::load(node)?),
379            "extension" => ComplexContent::Extension(XmlLoad::load(node)?),
380            _ => return Ok(None),
381        }))
382    }
383}
384
385#[derive(Debug, Clone)]
386/// Possible contents of a complex type.
387pub enum ComplexTypeContents {
388    /// Simple content.
389    Simple(SimpleContent),
390    /// Complex content.
391    Complex(ComplexContent),
392}
393
394impl<'input> XmlLoad<'input> for Option<ComplexTypeContents> {
395    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
396        Ok(Some(match node.tag_name().name() {
397            "simpleContent" => ComplexTypeContents::Simple(first_child_of_type_req(
398                node,
399                "restriction or extension",
400            )?),
401            "complexContent" => ComplexTypeContents::Complex(first_child_of_type_req(
402                node,
403                "restriction or extension",
404            )?),
405            _ => {
406                return Ok(None);
407            }
408        }))
409    }
410}
411
412#[derive(Debug, Clone)]
413/// A complex structure.
414pub struct ComplexType {
415    /// Complex type contents.
416    pub content: Option<ComplexTypeContents>,
417    /// Inner particle.
418    pub particle: Option<TypeDefParticle>,
419    /// List of attributes.
420    pub attributes: Vec<Attribute>,
421    /// Type name.
422    pub name: Option<String>,
423}
424
425impl<'input> XmlLoad<'input> for ComplexType {
426    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
427        Ok(Self {
428            content: first_child_of_type(node)?,
429            particle: first_child_of_type(node)?,
430            attributes: children_with_name(node, "attribute")?,
431            name: value_from_attr_opt(node, "name")?,
432        })
433    }
434}
435
436#[derive(Debug, Clone)]
437/// Contents of an element.
438pub enum ElementContents {
439    /// A simple type.
440    SimpleType(Box<SimpleType>),
441    /// A complex type.
442    ComplexType(Box<ComplexType>),
443}
444
445impl<'input> XmlLoad<'input> for Option<ElementContents> {
446    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
447        Ok(Some(match node.tag_name().name() {
448            "simpleType" => ElementContents::SimpleType(Box::new(XmlLoad::load(node)?)),
449            "complexType" => ElementContents::ComplexType(Box::new(XmlLoad::load(node)?)),
450            _ => return Ok(None),
451        }))
452    }
453}
454
455#[derive(Debug, Clone)]
456/// Maximum number of occurences of something.
457pub enum MaxOccurs {
458    /// A specific number.
459    Count(u32),
460    /// Unbounded.
461    Unbounded,
462}
463
464impl FromValue for MaxOccurs {
465    fn from_value(node: &Node<'_, '_>, attr: &str, v: &str) -> Result<Self, XmlError> {
466        if v == "unbounded" {
467            return Ok(Self::Unbounded);
468        }
469
470        Ok(Self::Count(u32::from_value(node, attr, v)?))
471    }
472}
473
474#[derive(Debug, Clone)]
475/// Legal uses of an attribute.
476pub enum AttributeUse {
477    /// Attribute cannot be used.
478    Prohibited,
479    /// Attribute is optional.
480    Optional,
481    /// Attribute is required.
482    Required,
483}
484
485impl FromValue for AttributeUse {
486    fn from_value(node: &Node<'_, '_>, attr: &str, v: &str) -> Result<Self, XmlError> {
487        match v {
488            "prohibited" => Ok(Self::Prohibited),
489            "optional" => Ok(Self::Optional),
490            "required" => Ok(Self::Required),
491            r => Err(XmlError::other(
492                node,
493                &format!("Unexpected value for {attr}: {r}"),
494            )),
495        }
496    }
497}
498
499#[derive(Debug, Clone)]
500/// Definition of an attribute on a type.
501pub struct Attribute {
502    /// Attribute content.
503    pub content: Option<SimpleType>,
504    /// Attribute name.
505    pub name: Option<String>,
506    /// Attribute reference.
507    pub r#ref: Option<String>,
508    /// Attribute type.
509    pub r#type: Option<String>,
510    /// Usage patterns.
511    pub r#use: AttributeUse,
512    /// Attribute default value.
513    pub default: Option<String>,
514}
515
516impl<'input> XmlLoad<'input> for Attribute {
517    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
518        Ok(Self {
519            content: first_child_with_name_opt(node, "simpleType")?,
520            name: value_from_attr_opt(node, "name")?,
521            r#ref: value_from_attr_opt(node, "ref")?,
522            r#type: value_from_attr_opt(node, "type")?,
523            r#use: value_from_attr_opt(node, "use")?.unwrap_or(AttributeUse::Optional),
524            default: value_from_attr_opt(node, "default")?,
525        })
526    }
527}
528
529#[derive(Debug, Clone)]
530/// Attribute declaration wrapper.
531pub struct AttrDecls {
532    /// Attributes in declaration.
533    pub attributes: Vec<Attribute>,
534}
535
536impl<'input> XmlLoad<'input> for AttrDecls {
537    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
538        Ok(Self {
539            attributes: children_with_name(node, "attribute")?,
540        })
541    }
542}
543
544#[derive(Debug, Clone)]
545/// Element, representing some part of a type.
546pub struct Element {
547    /// Element type.
548    pub r#type: Option<String>,
549    /// Element default value.
550    pub default: Option<String>,
551    /// Whether the element is nullable.
552    pub nillable: bool,
553    /// Contents.
554    pub contents: Option<ElementContents>,
555    /// Element name.
556    pub name: Option<String>,
557    /// Element reference.
558    pub r#ref: Option<String>,
559    /// Minimum number of occurences.
560    pub min_occurs: Option<u32>,
561    /// Maximum number of occurences.
562    pub max_occurs: Option<MaxOccurs>,
563}
564
565impl<'input> XmlLoad<'input> for Element {
566    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
567        Ok(Self {
568            r#type: value_from_attr_opt(node, "type")?,
569            default: value_from_attr_opt(node, "default")?,
570            nillable: value_from_attr_opt(node, "nillable")?.unwrap_or(false),
571            name: value_from_attr_opt(node, "name")?,
572            r#ref: value_from_attr_opt(node, "ref")?,
573            min_occurs: value_from_attr_opt(node, "minOccurs")?,
574            max_occurs: value_from_attr_opt(node, "maxOccurs")?,
575            contents: first_child_of_type(node)?,
576        })
577    }
578}
579
580#[derive(Debug)]
581/// Item in an XSD file.
582pub enum XsdFileItem {
583    /// A simple type.
584    SimpleType(SimpleType),
585    /// A complex type.
586    ComplexType(ComplexType),
587    /// A top level element.
588    Element(Element),
589}
590
591impl<'input> XmlLoad<'input> for Option<XsdFileItem> {
592    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
593        Ok(Some(match node.tag_name().name() {
594            "simpleType" => XsdFileItem::SimpleType(XmlLoad::load(node)?),
595            "complexType" => XsdFileItem::ComplexType(XmlLoad::load(node)?),
596            "element" => XsdFileItem::Element(XmlLoad::load(node)?),
597            _ => return Ok(None),
598        }))
599    }
600}
601
602#[derive(Debug)]
603/// Type representing a full XmlSchema file.
604pub struct XmlSchema {
605    /// Top level items.
606    pub items: Vec<XsdFileItem>,
607    /// Target namespace.
608    pub target_namespace: Option<String>,
609    /// Schema version.
610    pub version: Option<String>,
611}
612
613impl<'input> XmlLoad<'input> for XmlSchema {
614    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
615        Ok(Self {
616            items: children_of_type(node)?,
617            target_namespace: value_from_attr_opt(node, "targetNamespace")?,
618            version: value_from_attr_opt(node, "version")?,
619        })
620    }
621}
622
623#[derive(Debug)]
624/// Top level element in an XML schema when it is a type.
625pub enum XsdFileType {
626    /// Simple type.
627    Simple(Box<SimpleType>),
628    /// Complex type.
629    Complex(Box<ComplexType>),
630}