Skip to main content

xbrl_rs/taxonomy/schema/
parser.rs

1use crate::{
2    Balance, NamespacePrefix, NamespaceUri, PeriodType, RoleUri, XbrlError,
3    xml::{self, ArcroleUri, QName},
4};
5use quick_xml::{
6    Reader,
7    events::{BytesStart, Event, attributes::Attributes},
8};
9use std::{
10    collections::HashMap,
11    fmt,
12    fs::File,
13    io::{BufRead, BufReader},
14    path::{Path, PathBuf},
15    str::FromStr,
16};
17
18/// Represents the `elementFormDefault` and `attributeFormDefault` values from
19/// an XBRL schema's root `xs:schema` element.
20#[derive(Debug, PartialEq, Eq, Default)]
21pub enum FormDefault {
22    /// Elements or attributes must be qualified with the target namespace to be
23    /// valid.
24    Qualified,
25    /// Elements or attributes can be unqualified (no namespace) to be valid.
26    /// This is the default in XBRL taxonomies.
27    #[default]
28    Unqualified,
29}
30
31/// The kind of derivation in a `simpleContent`.
32///
33/// For example, `xbrli:monetaryItemType` derives from `decimalItemType`.
34#[derive(Debug, Clone, PartialEq, Eq)]
35pub enum DerivationKind {
36    /// Derivation by extending the base type (from `xs:extension`).
37    ///
38    /// For example an attribute is added to a string type:
39    ///
40    /// ```xml
41    /// <xs:extension base="xs:string">
42    ///   <xs:attribute name="lang" type="xs:string"/>
43    /// </xs:extension>
44    /// ```
45    Extension,
46    /// Derivation by restricting the base type (from `xs:restriction`).
47    ///
48    /// For example string is limit to max 10 characters:
49    ///
50    /// ```xml
51    /// <xs:restriction base="xs:string">
52    ///   <xs:maxLength value="10"/>
53    /// </xs:restriction>
54    /// ```
55    Restriction,
56}
57
58/// The allowed cycle direction for an arcrole (`cyclesAllowed` attribute).
59#[derive(Debug, Clone, PartialEq, Eq)]
60pub enum CyclesAllowed {
61    /// Any cycles are allowed.
62    Any,
63    /// Only undirected cycles are allowed.
64    Undirected,
65    /// No cycles are allowed.
66    None,
67}
68
69impl FromStr for CyclesAllowed {
70    type Err = XbrlError;
71
72    fn from_str(str: &str) -> Result<Self, XbrlError> {
73        match str {
74            "any" => Ok(Self::Any),
75            "undirected" => Ok(Self::Undirected),
76            "none" => Ok(Self::None),
77            _ => Err(XbrlError::ParseError {
78                expected: "CyclesAllowed",
79                value: str.to_owned(),
80            }),
81        }
82    }
83}
84
85impl fmt::Display for CyclesAllowed {
86    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87        match self {
88            Self::Any => f.write_str("any"),
89            Self::Undirected => f.write_str("undirected"),
90            Self::None => f.write_str("none"),
91        }
92    }
93}
94
95/// Represents an `xs:import` in the schema. Used when a schema needs to import
96/// types from another namespace. The `namespace` field is required, but the
97/// `schema_location` is optional in XBRL taxonomies.
98#[derive(Debug, PartialEq, Eq)]
99pub struct SchemaImport {
100    /// Namespace being imported.
101    pub namespace: String,
102    /// Location of the imported schema file (from schemaLocation).
103    pub schema_location: Option<String>,
104}
105
106/// Represents an `xs:include` in the schema. Used when a schema needs to include
107/// types from another schema in the same namespace.
108#[derive(Debug, PartialEq, Eq)]
109pub struct SchemaInclude {
110    /// Location of the included schema file.
111    pub schema_location: String,
112}
113
114/// Represents a `link:linkbaseRef` in the schema's `xs:annotation/xs:appinfo`.
115#[derive(Debug, PartialEq, Eq)]
116pub struct LinkbaseRef {
117    /// Href to the linkbase file.
118    ///
119    /// The xlink:href value (relative path to the linkbase file).
120    pub href: String,
121    /// Role type of the linkbase.
122    ///
123    /// The xlink:role (e.g., <http://www.xbrl.org/2003/role/labelLinkbaseRef>).
124    pub role: Option<String>,
125    /// The xlink:arcrole (typically <http://www.w3.org/1999/xlink/properties/linkbase>).
126    pub arcrole: Option<String>,
127    /// Type of the linkbase (extended/simple).
128    pub link_type: Option<String>,
129}
130
131/// A `link:roleType` definition from a taxonomy schema.
132#[derive(Debug, PartialEq, Eq)]
133pub struct RawRoleType {
134    /// The id attribute (e.g., "role_balanceSheet").
135    pub id: String,
136    /// The roleURI attribute.
137    pub role_uri: RoleUri,
138    /// The human-readable definition (child `link:definition` text).
139    pub definition: Option<String>,
140    /// Which link types this role is used on (child `link:usedOn` texts).
141    pub used_on: Vec<QName>,
142}
143
144/// A `link:arcroleType` definition from a taxonomy schema.
145#[derive(Debug, PartialEq, Eq)]
146pub struct RawArcroleType {
147    /// The id attribute.
148    pub id: String,
149    /// The arcroleURI attribute.
150    pub arcrole_uri: ArcroleUri,
151    /// The human-readable definition.
152    pub definition: Option<String>,
153    /// Which link types this arcrole is used on.
154    pub used_on: Vec<QName>,
155    /// The cycles-allowed attribute.
156    pub cycles_allowed: Option<CyclesAllowed>,
157}
158
159/// Represents an attribute use in a `simpleContent` extension or restriction.
160#[derive(Debug, Clone, PartialEq, Eq)]
161pub struct AttributeUse {
162    /// The QName of the referenced attribute.
163    pub ref_name: String,
164    /// Whether this attribute is required (use="required").
165    pub required: bool,
166}
167
168/// Represents an `xs:anyAttribute` in a complex type's content model.
169#[derive(Debug, Clone, PartialEq, Eq)]
170pub struct AnyAttribute {
171    /// The `namespace` attribute of the `xs:anyAttribute`, which determines
172    /// which attributes are allowed.
173    pub namespace: AnyAttributeNamespace,
174}
175
176/// Represents the `namespace` attribute of an `xs:anyAttribute`.
177#[derive(Debug, Clone, PartialEq, Eq)]
178pub enum AnyAttributeNamespace {
179    /// All attributes from any namespace are allowed.
180    Any,
181    /// Only attributes from namespaces other than the target namespace are allowed.
182    Other,
183    /// Only attributes from the target namespace are allowed.
184    TargetNamespace,
185    /// Only attributes from specific namespaces are allowed (from a
186    /// whitespace-separated list in the `namespace` attribute).
187    List(Vec<String>),
188}
189
190/// Represents the occurence constraints for a
191/// particle in a complex type's content model.
192#[derive(Debug, Clone, PartialEq, Eq)]
193pub struct Occurrence {
194    /// The minimum number of occurrences (from `minOccurs`).
195    pub min: u32,
196    /// The maximum number of occurrences (from `maxOccurs`). None means
197    /// unbounded.
198    pub max: Option<u32>,
199}
200
201/// Represents the derivation method (extension or restriction) for a
202/// `complexContent` in a complex type.
203#[derive(Debug, Clone, PartialEq, Eq)]
204pub enum Derivation {
205    /// Derivation by extension (from `xs:extension`).
206    Extension(QName),
207    /// Derivation by restriction (from `xs:restriction`).
208    Restriction(QName),
209}
210
211/// Represents an element declaration in the schema, which can be either a
212/// global element or an inline element declaration inside a compositor.
213#[derive(Debug, Clone, PartialEq, Eq)]
214pub struct ElementDecl {
215    /// The name of the element.
216    pub name: String,
217    /// The type of the element, if specified.
218    pub type_name: Option<QName>,
219    /// The inline complex type of the element, if specified.
220    pub inline_type: Option<Box<ComplexType>>,
221}
222
223/// Represents an element particle in a complex type's content model.
224#[derive(Debug, Clone, PartialEq, Eq)]
225pub enum ElementParticle {
226    /// A reference to a globally defined element (from `xs:element[@ref]`).
227    Ref(QName),
228    /// An inline element declaration (from `xs:element` inside a compositor).
229    /// This is used in complex content of tuple elements in older XBRL
230    /// taxonomies that don't use `complexContent` for tuples.
231    Decl(ElementDecl),
232}
233
234impl ElementParticle {
235    /// Returns the local name of the element: the `ref` local name for
236    /// references, or the declared `name` for inline declarations.
237    pub fn local_name(&self) -> &str {
238        match self {
239            ElementParticle::Ref(qname) => &qname.local_name,
240            ElementParticle::Decl(declaration) => &declaration.name,
241        }
242    }
243}
244
245/// Represents a group definition in a complex type's content model.
246#[derive(Debug, Clone, PartialEq, Eq)]
247pub struct GroupDef {
248    /// The name of the group (from `xs:group[@name]`). This is optional because
249    /// XBRL allows anonymous groups defined via `xs:group` inside compositors.
250    pub name: Option<QName>,
251    /// The particle that this group wraps (sequence, choice, or another group).
252    pub particle: Box<Particle>,
253}
254
255/// Represents a group particle in a complex type's content model.
256#[derive(Debug, Clone, PartialEq, Eq)]
257pub enum GroupParticle {
258    /// A reference to a globally defined group (from `xs:group[@ref]`).
259    Ref(QName),
260    /// An inline group definition (from `xs:group` inside a compositor).
261    Def(GroupDef),
262}
263
264/// Represents a particle in a complex type's content model.
265///
266/// A particle is a building block of a complex type's content model, which can
267/// be an element, a sequence, a choice, or a group.
268#[derive(Debug, Clone, PartialEq, Eq)]
269pub enum Particle {
270    /// An element, which can be either a reference to a globally defined
271    /// element or an inline element declaration.
272    Element {
273        element: ElementParticle,
274        occurs: Occurrence,
275    },
276    /// A sequence compositor, which contains a list of child particles.
277    Sequence {
278        children: Vec<Particle>,
279        occurs: Occurrence,
280    },
281    /// A choice compositor, which contains a list of child particles.
282    Choice {
283        children: Vec<Particle>,
284        occurs: Occurrence,
285    },
286    /// A group reference or definition, which can be either a reference to a
287    /// globally defined group or an inline group definition.
288    Group {
289        group: GroupParticle,
290        occurs: Occurrence,
291    },
292}
293
294impl Particle {
295    /// Returns all [`ElementParticle`]s reachable from this particle, flattened
296    /// across any nesting depth of sequences and choices.
297    pub fn elements(&self) -> Vec<&ElementParticle> {
298        let mut elements = Vec::new();
299        self.collect_elements(&mut elements);
300        elements
301    }
302
303    fn collect_elements<'a>(&'a self, elements: &mut Vec<&'a ElementParticle>) {
304        match self {
305            Particle::Element { element, .. } => elements.push(element),
306            Particle::Sequence { children, .. } | Particle::Choice { children, .. } => {
307                for child in children {
308                    child.collect_elements(elements);
309                }
310            }
311            Particle::Group { .. } => {}
312        }
313    }
314
315    /// Returns `true` if this particle contains an element whose local name
316    /// (for refs) or declared name (for inline declarations) matches `local_name`.
317    pub fn allows_local_name(&self, local_name: &str) -> bool {
318        self.elements()
319            .iter()
320            .any(|element_particle| match element_particle {
321                ElementParticle::Ref(qname) => qname.local_name == local_name,
322                ElementParticle::Decl(declaration) => declaration.name == local_name,
323            })
324    }
325}
326
327/// Represents a `simpleContent` in a complex type, which is used for defining
328/// tuple types in XBRL.
329#[derive(Debug, Clone, PartialEq, Eq)]
330pub struct SimpleContent {
331    /// The base type of the simple content (from `xs:extension` or
332    /// `xs:restriction`).
333    pub base: QName,
334    /// The kind of derivation (extension or restriction) for this simple
335    /// content.
336    pub derivation: DerivationKind,
337}
338/// Represents a `complexContent` in a complex type, which is used for defining
339/// tuple types with child elements in XBRL.
340#[derive(Debug, Clone, PartialEq, Eq)]
341pub struct ComplexContent {
342    /// The kind of derivation (extension or restriction) for this complex
343    /// content.
344    pub derivation: Option<Derivation>,
345    /// The content model particle, if any. `None` means the particle is
346    /// inherited from the base type (extension/restriction with no inline
347    /// particle).
348    pub particle: Option<Particle>,
349}
350
351/// Represents a simple type definition (`xs:simpleType`) in the schema.
352#[derive(Debug, PartialEq, Eq)]
353pub struct SimpleType {
354    /// The name of the simple type.
355    pub name: Option<String>,
356    /// The base type of the simple type in a restriction (`xs:restriction
357    /// base="..."`).
358    pub base: Option<QName>,
359    /// The enumerations of the simple type. Only relevant for simple types that
360    /// are restrictions of an enumeration.
361    ///
362    /// Usually it's sufficient to support xs:enumeration facets.
363    pub enumerations: Vec<String>,
364}
365
366/// Represents a complex type definition (`xs:complexType`) in the schema, which
367/// can be used for both item and tuple elements in XBRL.
368#[derive(Debug, Clone, PartialEq, Eq)]
369pub enum ComplexTypeContent {
370    /// A `simpleContent` is used for tuple types that only have attributes and
371    /// no child elements.
372    SimpleContent(SimpleContent),
373    /// A `complexContent` is used for tuple types that have child elements, and
374    /// can also have attributes.
375    ///
376    /// In XML Schema, this may be defined either via `<complexContent>` or
377    /// implicitly by directly containing a particle (`sequence`, `choice`,
378    /// `all`). Both forms are treated uniformly.
379    ComplexContent(ComplexContent),
380    /// An empty complex type has no explicit content. This is used for tuple
381    /// types that have no attributes and no child elements.
382    Empty,
383}
384
385/// Represents a complex type definition (`xs:complexType`) in the schema.
386#[derive(Debug, Clone, PartialEq, Eq)]
387pub struct ComplexType {
388    /// The name of the complex type.
389    pub name: Option<String>,
390    /// Whether this complex type is mixed (from `mixed="true"` in the
391    /// `xs:complexType`).
392    pub mixed: bool,
393    /// Attributes defined on this type via `xs:attribute` elements.
394    pub attributes: Vec<AttributeUse>,
395    /// The `xs:anyAttribute` of this complex content, if present.
396    pub any_attribute: Option<AnyAttribute>,
397    /// The content of the complex type.
398    pub content: Option<ComplexTypeContent>,
399}
400
401impl ComplexType {
402    /// Extracts the top-level [`Particle`] from this type.
403    pub fn into_content_model(self) -> Option<Particle> {
404        match self.content? {
405            ComplexTypeContent::ComplexContent(complex_content) => complex_content.particle,
406            ComplexTypeContent::SimpleContent(_) | ComplexTypeContent::Empty => None,
407        }
408    }
409
410    /// Returns the base type [`QName`] if this type derives from another via
411    /// `simpleContent` or `complexContent` extension/restriction.
412    pub fn base_type(&self) -> Option<&QName> {
413        match self.content.as_ref()? {
414            ComplexTypeContent::SimpleContent(SimpleContent { base, .. }) => Some(base),
415            ComplexTypeContent::ComplexContent(ComplexContent { derivation, .. }) => {
416                match derivation.as_ref()? {
417                    Derivation::Extension(q) | Derivation::Restriction(q) => Some(q),
418                }
419            }
420            ComplexTypeContent::Empty => None,
421        }
422    }
423}
424
425/// A parsed XML element from the schema (xs:element).
426#[derive(Debug, PartialEq, Eq)]
427pub struct Element {
428    /// The element's local name (e.g., "bs.ass.fixAss").
429    pub name: String,
430    /// The element's id attribute (e.g. "de-gaap-ci_bs.ass.fixAss"). Optional
431    /// in XBRL.
432    pub id: Option<String>,
433    /// The type QName (e.g., "xbrli:monetaryItemType").
434    pub type_name: Option<QName>,
435    /// Substitution group (e.g., "xbrli:item", "xbrli:tuple").
436    pub substitution_group: Option<QName>,
437    /// Whether this element is nillable.
438    pub is_nillable: bool,
439    /// Whether this element is abstract.
440    pub is_abstract: bool,
441    /// The XBRL period type ("instant" or "duration").
442    pub period_type: Option<PeriodType>,
443    /// The XBRL balance ("debit" or "credit").
444    pub balance: Option<Balance>,
445    /// The complex type of a tuple element.
446    pub complex_type: Option<ComplexType>,
447}
448
449/// Represents a raw parsed XBRL schema. Contains only the syntax-level data; no
450/// resolved `Concept`s yet.
451#[derive(Debug, PartialEq, Eq)]
452pub struct RawSchema {
453    /// The targetNamespace of the schema.
454    pub target_namespace: Option<String>,
455    /// Namespace declarations (prefix -> URI).
456    pub namespaces: HashMap<NamespacePrefix, NamespaceUri>,
457    /// The `elementFormDefault` of the schema.
458    pub element_form_default: FormDefault,
459    /// The `attributeFormDefault` of the schema.
460    pub attribute_form_default: FormDefault,
461    /// Parsed `xs:import` references.
462    pub imports: Vec<SchemaImport>,
463    /// Parsed `xs:include` references.
464    pub includes: Vec<SchemaInclude>,
465    /// Parsed `link:linkbaseRef` entries.
466    pub linkbase_refs: Vec<LinkbaseRef>,
467    /// Parsed `link:roleType` definitions.
468    pub role_types: Vec<RawRoleType>,
469    /// Parsed `link:arcroleType` definitions.
470    pub arcrole_types: Vec<RawArcroleType>,
471    /// Parsed elements (`xs:element`) in this schema.
472    pub elements: Vec<Element>,
473    /// Parsed simple type definitions (`xs:simpleType`) in this schema.
474    pub simple_types: Vec<SimpleType>,
475    /// Parsed complex type definitions (`xs:complexType`) in this schema.
476    pub complex_types: Vec<ComplexType>,
477}
478
479/// The parser for XBRL schema documents.
480pub struct SchemaParser<R> {
481    /// Path of the currently parsed schema if read from a file. Used for error
482    /// reporting.
483    path: Option<PathBuf>,
484    /// The XML reader for the schema document.
485    reader: Reader<R>,
486}
487
488impl SchemaParser<BufReader<File>> {
489    pub fn from_file(path: &Path) -> Result<Self, XbrlError> {
490        let file = File::open(path).map_err(|err| XbrlError::FileOpen {
491            path: path.to_path_buf(),
492            source: err,
493        })?;
494        let mut reader = Reader::from_reader(BufReader::new(file));
495
496        reader.config_mut().trim_text_start = true;
497        reader.config_mut().trim_text_end = true;
498
499        Ok(Self {
500            path: Some(path.to_path_buf()),
501            reader,
502        })
503    }
504}
505
506impl<R: BufRead> SchemaParser<R> {
507    /// Creates a new `SchemaParser` with the given XML reader and file path.
508    pub fn new(reader: Reader<R>) -> Self {
509        Self { path: None, reader }
510    }
511
512    pub fn from_reader(reader: R) -> Self {
513        let mut reader = Reader::from_reader(reader);
514
515        reader.config_mut().trim_text_start = true;
516        reader.config_mut().trim_text_end = true;
517
518        Self { path: None, reader }
519    }
520
521    /// Parses an XBRL schema document from the reader. Path is used for error
522    /// reporting.
523    pub fn parse(&mut self) -> Result<RawSchema, XbrlError> {
524        let mut schema = RawSchema {
525            target_namespace: None,
526            namespaces: HashMap::new(),
527            element_form_default: FormDefault::Unqualified,
528            attribute_form_default: FormDefault::Unqualified,
529            imports: vec![],
530            includes: vec![],
531            linkbase_refs: vec![],
532            role_types: vec![],
533            arcrole_types: vec![],
534            elements: vec![],
535            simple_types: vec![],
536            complex_types: vec![],
537        };
538
539        let mut has_schema_root = false;
540        let mut buf = Vec::new();
541
542        loop {
543            match self.reader.read_event_into(&mut buf) {
544                Ok(Event::Start(ref event)) => {
545                    let event_name = event.name();
546                    let local_name = event_name.local_name();
547                    let attributes = event.attributes();
548
549                    match local_name.as_ref() {
550                        b"schema" => {
551                            has_schema_root = true;
552                            self.parse_schema_root(&mut schema, attributes)?;
553                        }
554                        b"import" => self.parse_import(&mut schema, attributes)?,
555                        b"include" => self.parse_include(&mut schema, attributes)?,
556                        b"linkbaseRef" => {
557                            let linkbase_ref = self.parse_linkbase_ref(attributes)?;
558                            schema.linkbase_refs.push(linkbase_ref);
559                        }
560                        b"roleType" => {
561                            let role_type = self.parse_role_type(attributes)?;
562                            schema.role_types.push(role_type);
563                        }
564                        b"arcroleType" => {
565                            let arcrole_type = self.parse_arcrole_type(attributes)?;
566                            schema.arcrole_types.push(arcrole_type);
567                        }
568                        b"element" => {
569                            let element = self.parse_element(event, true)?;
570                            schema.elements.push(element);
571                        }
572                        b"simpleType" => {
573                            let simple_type = self.parse_simple_type(attributes)?;
574                            schema.simple_types.push(simple_type);
575                        }
576                        b"complexType" => {
577                            let complex_type = self.parse_complex_type(event)?;
578                            schema.complex_types.push(complex_type);
579                        }
580                        b"annotation" => self.parse_annotation(&mut schema)?,
581                        b"group" => {
582                            // xs:group definitions at the schema level are
583                            // skipped; group refs inside compositors are parsed.
584                            self.skip_until(b"group")?;
585                        }
586                        b"redefine" => {
587                            return Err(XbrlError::InvalidSchemaDocument {
588                                path: self.path.clone(),
589                                reason: "xsd:redefine is not allowed in taxonomy schemas"
590                                    .to_string(),
591                            });
592                        }
593                        other => {
594                            return Err(XbrlError::InvalidSchemaDocument {
595                                path: self.path.clone(),
596                                reason: format!(
597                                    "{} is not allowed in taxonomy schemas",
598                                    String::from_utf8_lossy(other)
599                                ),
600                            });
601                        }
602                    }
603                }
604                Ok(Event::Empty(ref event)) => {
605                    let event_name = event.name();
606                    let local_name = event_name.local_name();
607                    let attributes = event.attributes();
608
609                    match local_name.as_ref() {
610                        b"import" => self.parse_import(&mut schema, attributes)?,
611                        b"include" => self.parse_include(&mut schema, attributes)?,
612                        b"linkbaseRef" => {
613                            let linkbase_ref = self.parse_linkbase_ref(attributes)?;
614                            schema.linkbase_refs.push(linkbase_ref);
615                        }
616                        b"element" => {
617                            let element = self.parse_element(event, false)?;
618                            schema.elements.push(element);
619                        }
620                        b"complexType" => {
621                            let complex_type = self.parse_complex_type(event)?;
622                            schema.complex_types.push(complex_type);
623                        }
624                        b"annotation" => self.parse_annotation(&mut schema)?,
625                        other => {
626                            return Err(XbrlError::InvalidSchemaDocument {
627                                path: self.path.clone(),
628                                reason: format!(
629                                    "{} is not allowed in taxonomy schemas",
630                                    String::from_utf8_lossy(other)
631                                ),
632                            });
633                        }
634                    }
635                }
636                Ok(Event::End(_)) => {}
637                Ok(Event::Text(_)) => {
638                    // TODO: parse `xs:annotation` and `xs:documentation`
639                }
640                Ok(Event::Eof) => break,
641                Err(err) => {
642                    return Err(XbrlError::XmlParse {
643                        path: self.path.clone(),
644                        position: self.reader.buffer_position(),
645                        element: Some("schema".to_string()),
646                        source: err,
647                    });
648                }
649                _ => {}
650            }
651        }
652
653        if !has_schema_root {
654            return Err(XbrlError::InvalidSchemaDocument {
655                path: self.path.clone(),
656                reason: "missing <schema> root element".to_string(),
657            });
658        }
659
660        Ok(schema)
661    }
662
663    /// Parses the root `xs:schema` element.
664    fn parse_schema_root(
665        &mut self,
666        schema: &mut RawSchema,
667        attributes: Attributes,
668    ) -> Result<(), XbrlError> {
669        for attribute in attributes {
670            let attribute = attribute.map_err(|err| XbrlError::XmlParse {
671                path: self.path.clone(),
672                position: self.reader.buffer_position(),
673                element: Some("schema".to_string()),
674                source: err.into(),
675            })?;
676            let local_name = attribute.key.local_name();
677            let value = attribute.decode_and_unescape_value(self.reader.decoder())?;
678
679            match attribute.key.prefix() {
680                Some(prefix) if prefix.as_ref() == b"xmlns" => {
681                    schema.namespaces.insert(
682                        NamespacePrefix::from(str::from_utf8(local_name.as_ref())?),
683                        NamespaceUri::from(value.to_string()),
684                    );
685                }
686                _ => {}
687            }
688
689            match local_name.as_ref() {
690                b"targetNamespace" => {
691                    schema.target_namespace = Some(value.to_string());
692                }
693                b"elementFormDefault" => {
694                    schema.element_form_default = match value.as_ref() {
695                        "qualified" => FormDefault::Qualified,
696                        "unqualified" => FormDefault::Unqualified,
697                        _ => {
698                            return Err(XbrlError::ParseError {
699                                expected: "elementFormDefault value",
700                                value: value.to_string(),
701                            });
702                        }
703                    };
704                }
705                b"attributeFormDefault" => {
706                    schema.attribute_form_default = match value.as_ref() {
707                        "qualified" => FormDefault::Qualified,
708                        "unqualified" => FormDefault::Unqualified,
709                        _ => {
710                            return Err(XbrlError::ParseError {
711                                expected: "attributeFormDefault value",
712                                value: value.to_string(),
713                            });
714                        }
715                    };
716                }
717                _ => {}
718            }
719        }
720
721        Ok(())
722    }
723
724    /// Parses an `xs:import` element.
725    fn parse_import(
726        &mut self,
727        schema: &mut RawSchema,
728        attributes: Attributes,
729    ) -> Result<(), XbrlError> {
730        let mut namespace = None;
731        let mut schema_location = None;
732
733        for attribute in attributes {
734            let attribute = attribute.map_err(|err| XbrlError::XmlParse {
735                path: self.path.clone(),
736                position: self.reader.buffer_position(),
737                element: Some("import".to_string()),
738                source: err.into(),
739            })?;
740            let local_name = attribute.key.local_name();
741            let value = attribute.decode_and_unescape_value(self.reader.decoder())?;
742
743            match local_name.as_ref() {
744                b"namespace" => namespace = Some(value.to_string()),
745                b"schemaLocation" => schema_location = Some(value.to_string()),
746                _ => {}
747            }
748        }
749
750        schema.imports.push(SchemaImport {
751            namespace: namespace.ok_or_else(|| XbrlError::InvalidSchemaDocument {
752                path: self.path.clone(),
753                reason: "missing namespace in xsd:import".to_string(),
754            })?,
755            schema_location,
756        });
757
758        Ok(())
759    }
760
761    /// Parses an `xs:include` element.
762    fn parse_include(
763        &mut self,
764        schema: &mut RawSchema,
765        attributes: Attributes,
766    ) -> Result<(), XbrlError> {
767        let mut schema_location = None;
768
769        for attribute in attributes {
770            let attribute = attribute.map_err(|err| XbrlError::XmlParse {
771                path: self.path.clone(),
772                position: self.reader.buffer_position(),
773                element: Some("include".to_string()),
774                source: err.into(),
775            })?;
776            let local_name = attribute.key.local_name();
777            let value = attribute.decode_and_unescape_value(self.reader.decoder())?;
778
779            if local_name.as_ref() == b"schemaLocation" {
780                schema_location = Some(value.to_string());
781            }
782        }
783
784        schema.includes.push(SchemaInclude {
785            schema_location: schema_location.ok_or_else(|| XbrlError::InvalidSchemaDocument {
786                path: self.path.clone(),
787                reason: "missing schemaLocation in xsd:include".to_string(),
788            })?,
789        });
790
791        Ok(())
792    }
793
794    /// Parse a `link:linkbaseRef` element.
795    fn parse_linkbase_ref(&mut self, attributes: Attributes) -> Result<LinkbaseRef, XbrlError> {
796        let mut href = String::new();
797        let mut role = None;
798        let mut arcrole = None;
799        let mut link_type = None;
800
801        for attribute in attributes {
802            let attribute = attribute.map_err(|err| XbrlError::XmlParse {
803                path: self.path.clone(),
804                position: self.reader.buffer_position(),
805                element: Some("include".to_string()),
806                source: err.into(),
807            })?;
808            let local_name = attribute.key.local_name();
809            let value = attribute.decode_and_unescape_value(self.reader.decoder())?;
810
811            match local_name.as_ref() {
812                b"href" => {
813                    href = value.to_string();
814                }
815                b"role" => {
816                    role = Some(value.to_string());
817                }
818                b"arcrole" => {
819                    arcrole = Some(value.to_string());
820                }
821                b"type" => {
822                    link_type = Some(value.to_string());
823                }
824                _ => {}
825            }
826        }
827
828        Ok(LinkbaseRef {
829            href,
830            role,
831            arcrole,
832            link_type,
833        })
834    }
835
836    /// Parses a `link:roleType` element.
837    fn parse_role_type(&mut self, attributes: Attributes) -> Result<RawRoleType, XbrlError> {
838        let mut id = String::new();
839        let mut role_uri = String::new();
840
841        for attribute in attributes {
842            let attribute = attribute.map_err(|err| XbrlError::XmlParse {
843                path: self.path.clone(),
844                position: self.reader.buffer_position(),
845                element: Some("roleType".to_string()),
846                source: err.into(),
847            })?;
848            let local_name = attribute.key.local_name();
849            let value = attribute.decode_and_unescape_value(self.reader.decoder())?;
850            match local_name.as_ref() {
851                b"id" => id = value.into_owned(),
852                b"roleURI" => role_uri = value.into_owned(),
853                _ => {}
854            }
855        }
856
857        let mut definition = None;
858        let mut used_on = Vec::new();
859        let mut buf = Vec::new();
860
861        loop {
862            match self.reader.read_event_into(&mut buf)? {
863                Event::Start(ref event) => match event.local_name().as_ref() {
864                    b"definition" => {
865                        if let Event::Text(text) = self.reader.read_event_into(&mut buf)? {
866                            definition = Some(
867                                text.xml_content()
868                                    .map_err(quick_xml::Error::from)?
869                                    .into_owned(),
870                            );
871                        }
872                    }
873                    b"usedOn" => {
874                        if let Event::Text(text) = self.reader.read_event_into(&mut buf)? {
875                            used_on.push(xml::parse_qname(
876                                &text.xml_content().map_err(quick_xml::Error::from)?,
877                            ));
878                        }
879                    }
880                    _ => self.skip_element()?,
881                },
882                Event::End(ref event) if event.local_name().as_ref() == b"roleType" => break,
883                _ => {}
884            }
885            buf.clear();
886        }
887
888        Ok(RawRoleType {
889            id,
890            role_uri: RoleUri::from(role_uri),
891            definition,
892            used_on,
893        })
894    }
895
896    /// Parses a `link:arcroleType` element.
897    fn parse_arcrole_type(&mut self, attributes: Attributes) -> Result<RawArcroleType, XbrlError> {
898        let mut id = String::new();
899        let mut arcrole_uri = String::new();
900        let mut cycles_allowed = None;
901
902        for attribute in attributes {
903            let attribute = attribute.map_err(|err| XbrlError::XmlParse {
904                path: self.path.clone(),
905                position: self.reader.buffer_position(),
906                element: Some("arcroleType".to_string()),
907                source: err.into(),
908            })?;
909            let local_name = attribute.key.local_name();
910            let value = attribute.decode_and_unescape_value(self.reader.decoder())?;
911            match local_name.as_ref() {
912                b"id" => id = value.into_owned(),
913                b"arcroleURI" => arcrole_uri = value.into_owned(),
914                b"cyclesAllowed" => cycles_allowed = Some(value.parse::<CyclesAllowed>()?),
915                _ => {}
916            }
917        }
918
919        let mut definition = None;
920        let mut used_on = Vec::new();
921        let mut buf = Vec::new();
922
923        loop {
924            match self.reader.read_event_into(&mut buf)? {
925                Event::Start(ref event) => match event.local_name().as_ref() {
926                    b"definition" => {
927                        if let Event::Text(text) = self.reader.read_event_into(&mut buf)? {
928                            definition = Some(
929                                text.xml_content()
930                                    .map_err(quick_xml::Error::from)?
931                                    .into_owned(),
932                            );
933                        }
934                    }
935                    b"usedOn" => {
936                        if let Event::Text(text) = self.reader.read_event_into(&mut buf)? {
937                            used_on.push(xml::parse_qname(
938                                &text.xml_content().map_err(quick_xml::Error::from)?,
939                            ));
940                        }
941                    }
942                    _ => self.skip_element()?,
943                },
944                Event::End(ref event) if event.local_name().as_ref() == b"arcroleType" => break,
945                _ => {}
946            }
947            buf.clear();
948        }
949
950        Ok(RawArcroleType {
951            id,
952            arcrole_uri: ArcroleUri::from(arcrole_uri),
953            definition,
954            used_on,
955            cycles_allowed,
956        })
957    }
958
959    /// Parses an `xs:annotation` element, including its child `xs:appinfo`.
960    fn parse_annotation(&mut self, schema: &mut RawSchema) -> Result<(), XbrlError> {
961        let mut buf = Vec::new();
962
963        loop {
964            match self.reader.read_event_into(&mut buf)? {
965                Event::Start(event) => match event.local_name().as_ref() {
966                    b"appinfo" => self.parse_appinfo(schema)?,
967                    _ => self.skip_element()?,
968                },
969                Event::End(event) if event.local_name().as_ref() == b"annotation" => break,
970                _ => {}
971            }
972        }
973
974        Ok(())
975    }
976
977    /// Parses an `xs:appinfo` element, including its child elements like
978    /// `link:roleType`, `link:arcroleType`, and `link:linkbaseRef`.
979    fn parse_appinfo(&mut self, schema: &mut RawSchema) -> Result<(), XbrlError> {
980        let mut buf = Vec::new();
981
982        loop {
983            match self.reader.read_event_into(&mut buf)? {
984                Event::Start(event) | Event::Empty(event) => match event.local_name().as_ref() {
985                    b"linkbaseRef" => {
986                        let linkbase_ref = self.parse_linkbase_ref(event.attributes())?;
987                        schema.linkbase_refs.push(linkbase_ref);
988                    }
989                    b"roleType" => {
990                        let role_type = self.parse_role_type(event.attributes())?;
991                        schema.role_types.push(role_type);
992                    }
993                    b"arcroleType" => {
994                        let arcrole_type = self.parse_arcrole_type(event.attributes())?;
995                        schema.arcrole_types.push(arcrole_type);
996                    }
997                    _ => {}
998                },
999                Event::End(event) if event.local_name().as_ref() == b"appinfo" => break,
1000                _ => {}
1001            }
1002        }
1003
1004        Ok(())
1005    }
1006
1007    /// Skips the current element, including all nested child elements.
1008    /// The reader must be positioned at the `Start` or `Empty` event of the element.
1009    fn skip_element(&mut self) -> Result<(), XbrlError> {
1010        let mut buf = Vec::new();
1011        let mut depth = 0;
1012
1013        loop {
1014            match self.reader.read_event_into(&mut buf)? {
1015                Event::Start(_) => {
1016                    // entering a nested element
1017                    depth += 1;
1018                }
1019                Event::End(_) => {
1020                    if depth == 0 {
1021                        // matched the original element's end
1022                        break;
1023                    } else {
1024                        depth -= 1;
1025                    }
1026                }
1027                Event::Empty(_) => {
1028                    // empty element counts as start+end, so no depth change needed
1029                }
1030                Event::Eof => {
1031                    return Err(XbrlError::ParseError {
1032                        expected: "end tag while skipping element",
1033                        value: "".to_string(),
1034                    });
1035                }
1036                _ => {}
1037            }
1038
1039            buf.clear();
1040        }
1041
1042        Ok(())
1043    }
1044
1045    /// Skips all events until (and including) the closing tag with `end_tag`
1046    /// local name.
1047    fn skip_until(&mut self, end_tag: &[u8]) -> Result<(), XbrlError> {
1048        let mut buf = Vec::new();
1049        let mut depth = 1usize;
1050
1051        loop {
1052            match self.reader.read_event_into(&mut buf)? {
1053                Event::Start(_) => depth += 1,
1054                Event::End(ref event) => {
1055                    depth -= 1;
1056
1057                    if depth == 0 {
1058                        debug_assert_eq!(event.local_name().as_ref(), end_tag);
1059                        break;
1060                    }
1061                }
1062                Event::Eof => break,
1063                _ => {}
1064            }
1065            buf.clear();
1066        }
1067
1068        Ok(())
1069    }
1070
1071    /// Parses an `xs:element` element, which can be either an item or a tuple
1072    /// depending on the `substitutionGroup` attribute. If `has_children` is
1073    /// true, also looks for an inline `xs:complexType` child.
1074    fn parse_element(
1075        &mut self,
1076        start: &BytesStart,
1077        has_children: bool,
1078    ) -> Result<Element, XbrlError> {
1079        let mut element = self.parse_item_element(start)?;
1080
1081        if has_children {
1082            self.parse_element_children(start, &mut element)?;
1083        }
1084
1085        Ok(element)
1086    }
1087
1088    /// Parses an `xs:element` element with `substitutionGroup="xbrli:item"`.
1089    fn parse_item_element(&mut self, start: &BytesStart) -> Result<Element, XbrlError> {
1090        let mut name = None;
1091        let mut id = None;
1092        let mut type_name = None;
1093        let mut substitution_group = None;
1094        let mut is_abstract = false;
1095        let mut is_nillable = false;
1096        let mut period_type = None;
1097        let mut balance = None;
1098
1099        for attribute in start.attributes() {
1100            let attribute = attribute.map_err(|err| XbrlError::XmlParse {
1101                path: self.path.clone(),
1102                position: self.reader.buffer_position(),
1103                element: Some("element".to_string()),
1104                source: err.into(),
1105            })?;
1106            let qname = attribute.key;
1107            let local_name = qname.local_name();
1108            let value = attribute.decode_and_unescape_value(self.reader.decoder())?;
1109
1110            match local_name.as_ref() {
1111                b"name" => name = Some(value.to_string()),
1112                b"id" => id = Some(value.to_string()),
1113                b"type" => type_name = Some(xml::parse_qname(&value)),
1114                b"substitutionGroup" => substitution_group = Some(xml::parse_qname(&value)),
1115                b"abstract" => is_abstract = value == "true",
1116                b"nillable" => is_nillable = value == "true",
1117                b"periodType" => {
1118                    period_type = match value.as_ref() {
1119                        "instant" => Some(PeriodType::Instant),
1120                        "duration" => Some(PeriodType::Duration),
1121                        _ => None,
1122                    }
1123                }
1124                b"balance" => {
1125                    balance = match value.as_ref() {
1126                        "debit" => Some(Balance::Debit),
1127                        "credit" => Some(Balance::Credit),
1128                        _ => None,
1129                    }
1130                }
1131                _ => {}
1132            }
1133        }
1134
1135        let element = Element {
1136            name: name.ok_or_else(|| XbrlError::InvalidSchemaDocument {
1137                path: self.path.clone(),
1138                reason: "missing name in xsd:element".to_string(),
1139            })?,
1140            id: id.clone(),
1141            type_name,
1142            substitution_group,
1143            is_nillable,
1144            is_abstract,
1145            period_type,
1146            balance,
1147            complex_type: None,
1148        };
1149
1150        Ok(element)
1151    }
1152
1153    /// Parses child elements of an `xs:element`, looking for an inline
1154    /// `xs:complexType`. Complex types are allowed in both tuple and item
1155    /// elements.
1156    fn parse_element_children(
1157        &mut self,
1158        start: &BytesStart,
1159        element: &mut Element,
1160    ) -> Result<(), XbrlError> {
1161        let mut buf = Vec::new();
1162
1163        loop {
1164            match self.reader.read_event_into(&mut buf)? {
1165                Event::Start(ref event) if event.local_name().as_ref() == b"complexType" => {
1166                    element.complex_type = Some(self.parse_complex_type(event)?);
1167                }
1168                Event::End(ref event) if event.name().as_ref() == start.name().as_ref() => {
1169                    break;
1170                }
1171                Event::Eof => break,
1172                _ => {}
1173            }
1174            buf.clear();
1175        }
1176
1177        Ok(())
1178    }
1179
1180    /// Parses an `xs:simpleType` element.
1181    fn parse_simple_type(&mut self, attributes: Attributes) -> Result<SimpleType, XbrlError> {
1182        let mut name = None;
1183
1184        for attribute in attributes {
1185            let attribute = attribute.map_err(|err| XbrlError::XmlParse {
1186                path: self.path.clone(),
1187                position: self.reader.buffer_position(),
1188                element: Some("simpleType".to_string()),
1189                source: err.into(),
1190            })?;
1191            let qname = attribute.key;
1192            let local_name = qname.local_name();
1193
1194            if local_name.as_ref() == b"name" {
1195                let value = attribute.decode_and_unescape_value(self.reader.decoder())?;
1196                name = Some(value.to_string());
1197            }
1198        }
1199
1200        let mut base = None;
1201        let mut enumerations = Vec::new();
1202        let mut buf = Vec::new();
1203
1204        loop {
1205            match self.reader.read_event_into(&mut buf)? {
1206                Event::Start(event) | Event::Empty(event) => {
1207                    let local_name = event.local_name();
1208
1209                    match local_name.as_ref() {
1210                        b"restriction" => {
1211                            for attribute in event.attributes() {
1212                                let attribute = attribute.map_err(|err| XbrlError::XmlParse {
1213                                    path: self.path.clone(),
1214                                    position: self.reader.buffer_position(),
1215                                    element: Some("restriction".to_string()),
1216                                    source: err.into(),
1217                                })?;
1218
1219                                if attribute.key.as_ref() == b"base" {
1220                                    let value = attribute
1221                                        .decode_and_unescape_value(self.reader.decoder())?;
1222                                    base = Some(xml::parse_qname(&value));
1223                                }
1224                            }
1225                        }
1226                        b"enumeration" => {
1227                            for attribute in event.attributes() {
1228                                let attribute = attribute.map_err(|err| XbrlError::XmlParse {
1229                                    path: self.path.clone(),
1230                                    position: self.reader.buffer_position(),
1231                                    element: Some("enumeration".to_string()),
1232                                    source: err.into(),
1233                                })?;
1234
1235                                if attribute.key.as_ref() == b"value" {
1236                                    let value = attribute
1237                                        .decode_and_unescape_value(self.reader.decoder())?;
1238                                    enumerations.push(value.to_string());
1239                                }
1240                            }
1241                        }
1242                        _ => {}
1243                    }
1244                }
1245                Event::End(event) => {
1246                    let local_name = event.local_name();
1247
1248                    if local_name.as_ref() == b"simpleType" {
1249                        break;
1250                    }
1251                }
1252                Event::Eof => break,
1253                _ => {}
1254            }
1255
1256            buf.clear();
1257        }
1258
1259        Ok(SimpleType {
1260            name,
1261            base,
1262            enumerations,
1263        })
1264    }
1265
1266    /// Parses an `xs:complexType` element.
1267    fn parse_complex_type(&mut self, start: &BytesStart) -> Result<ComplexType, XbrlError> {
1268        let mut buf = Vec::new();
1269        let mut complex_type = ComplexType {
1270            name: None,
1271            mixed: false,
1272            attributes: Vec::new(),
1273            any_attribute: None,
1274            content: None,
1275        };
1276
1277        for attribute in start.attributes() {
1278            let attribute = attribute.map_err(|err| XbrlError::XmlParse {
1279                path: self.path.clone(),
1280                position: self.reader.buffer_position(),
1281                element: Some("complexType".to_string()),
1282                source: err.into(),
1283            })?;
1284
1285            if attribute.key.local_name().as_ref() == b"name" {
1286                let value = attribute.decode_and_unescape_value(self.reader.decoder())?;
1287                complex_type.name = Some(value.to_string());
1288            }
1289
1290            if attribute.key.local_name().as_ref() == b"mixed" {
1291                let value = attribute.decode_and_unescape_value(self.reader.decoder())?;
1292                complex_type.mixed = value == "true";
1293            }
1294        }
1295
1296        // Element appearing directly in the complexType body (not inside
1297        // simpleContent or complexContent).
1298        let mut direct_particle: Option<Particle> = None;
1299
1300        loop {
1301            let (event, has_body) = match self.reader.read_event_into(&mut buf)? {
1302                Event::Start(event) => (event, true),
1303                Event::Empty(event) => (event, false),
1304                Event::End(ref event) if event.name() == start.name() => break,
1305                Event::Eof => break,
1306                _ => {
1307                    buf.clear();
1308                    continue;
1309                }
1310            };
1311
1312            match event.local_name().as_ref() {
1313                b"sequence" | b"choice" if has_body => {
1314                    direct_particle = Some(self.parse_particle(&event)?);
1315                }
1316                b"sequence" => {
1317                    let occurs = self.parse_occurs(&event)?;
1318                    direct_particle = Some(Particle::Sequence {
1319                        children: vec![],
1320                        occurs,
1321                    });
1322                }
1323                b"choice" => {
1324                    let occurs = self.parse_occurs(&event)?;
1325                    direct_particle = Some(Particle::Choice {
1326                        children: vec![],
1327                        occurs,
1328                    });
1329                }
1330                b"simpleContent" => {
1331                    if direct_particle.is_some() {
1332                        return Err(XbrlError::InvalidSchemaDocument {
1333                            path: self.path.clone(),
1334                            reason: "simpleContent cannot appear alongside a direct particle"
1335                                .to_string(),
1336                        });
1337                    }
1338
1339                    self.parse_simple_content(&mut complex_type)?;
1340                    // simpleContent must be the only content of a complexType,
1341                    // so we break the loop after parsing it.
1342                    break;
1343                }
1344                b"complexContent" => {
1345                    if direct_particle.is_some() {
1346                        return Err(XbrlError::InvalidSchemaDocument {
1347                            path: self.path.clone(),
1348                            reason: "complexContent cannot appear alongside a direct particle"
1349                                .to_string(),
1350                        });
1351                    }
1352
1353                    self.parse_complex_content(&mut complex_type, &event)?;
1354                    // complexContent must be the only content of a complexType, so we
1355                    break;
1356                }
1357
1358                b"attribute" => {
1359                    complex_type.attributes.push(self.parse_attribute(&event)?);
1360                }
1361                b"anyAttribute" => {
1362                    let any_attribute = Some(self.parse_any_attribute(&event)?);
1363
1364                    if complex_type.any_attribute.is_some() {
1365                        return Err(XbrlError::InvalidSchemaDocument {
1366                            path: self.path.clone(),
1367                            reason: "only one anyAttribute is allowed per complexType".to_string(),
1368                        });
1369                    }
1370
1371                    complex_type.any_attribute = any_attribute;
1372                }
1373                b"restriction" => {
1374                    return Err(XbrlError::InvalidSchemaDocument {
1375                        path: self.path.clone(),
1376                        reason:
1377                            "restriction is only allowed inside simpleContent or complexContent"
1378                                .to_string(),
1379                    });
1380                }
1381                b"extension" => {
1382                    return Err(XbrlError::InvalidSchemaDocument {
1383                        path: self.path.clone(),
1384                        reason: "extension is only allowed inside simpleContent or complexContent"
1385                            .to_string(),
1386                    });
1387                }
1388                _ => {}
1389            }
1390
1391            buf.clear();
1392        }
1393
1394        // Build content from direct-body elements if simpleContent/complexContent
1395        // didn't already set it.
1396        if complex_type.content.is_none() {
1397            complex_type.content = direct_particle.map(|particle| {
1398                ComplexTypeContent::ComplexContent(ComplexContent {
1399                    derivation: None,
1400                    particle: Some(particle),
1401                })
1402            });
1403        }
1404
1405        Ok(complex_type)
1406    }
1407
1408    /// Parses `minOccurs`/`maxOccurs` attributes from a start tag.
1409    fn parse_occurs(&self, start: &BytesStart) -> Result<Occurrence, XbrlError> {
1410        let mut min = 1u32;
1411        let mut max = Some(1u32);
1412
1413        for attribute in start.attributes() {
1414            let attribute = attribute.map_err(|err| XbrlError::XmlParse {
1415                path: self.path.clone(),
1416                position: self.reader.buffer_position(),
1417                element: None,
1418                source: err.into(),
1419            })?;
1420            let value = attribute.decode_and_unescape_value(self.reader.decoder())?;
1421            match attribute.key.local_name().as_ref() {
1422                b"minOccurs" => min = xml::parse_u32(&value)?,
1423                b"maxOccurs" => {
1424                    max = if value == "unbounded" {
1425                        None
1426                    } else {
1427                        Some(xml::parse_u32(&value)?)
1428                    };
1429                }
1430                _ => {}
1431            }
1432        }
1433
1434        Ok(Occurrence { min, max })
1435    }
1436
1437    /// Parses an `xs:sequence` or `xs:choice` element into a recursive
1438    /// [`Particle`] tree. `start` is the opening tag (used to determine the
1439    /// compositor kind and to recognise the matching closing tag).
1440    fn parse_particle(&mut self, start: &BytesStart) -> Result<Particle, XbrlError> {
1441        let occurs = self.parse_occurs(start)?;
1442        let mut children = Vec::new();
1443        let mut buf = Vec::new();
1444
1445        loop {
1446            match self.reader.read_event_into(&mut buf)? {
1447                Event::Start(ref event) => match event.local_name().as_ref() {
1448                    b"element" => {
1449                        children.push(self.parse_element_particle(event, false)?);
1450                    }
1451                    b"sequence" | b"choice" => {
1452                        children.push(self.parse_particle(event)?);
1453                    }
1454                    b"group" => {
1455                        children.push(self.parse_group_particle(event)?);
1456                    }
1457                    _ => {}
1458                },
1459                Event::Empty(ref event) => match event.local_name().as_ref() {
1460                    b"element" => {
1461                        children.push(self.parse_element_particle(event, true)?);
1462                    }
1463                    b"sequence" => {
1464                        let nested_occurs = self.parse_occurs(event)?;
1465                        children.push(Particle::Sequence {
1466                            children: vec![],
1467                            occurs: nested_occurs,
1468                        });
1469                    }
1470                    b"choice" => {
1471                        let nested_occurs = self.parse_occurs(event)?;
1472                        children.push(Particle::Choice {
1473                            children: vec![],
1474                            occurs: nested_occurs,
1475                        });
1476                    }
1477                    b"group" => {
1478                        children.push(self.parse_group_particle(event)?);
1479                    }
1480                    _ => {}
1481                },
1482                Event::End(ref event)
1483                    if event.local_name().as_ref() == start.local_name().as_ref() =>
1484                {
1485                    break;
1486                }
1487                Event::Eof => break,
1488                _ => {}
1489            }
1490
1491            buf.clear();
1492        }
1493
1494        match start.local_name().as_ref() {
1495            b"sequence" => Ok(Particle::Sequence { children, occurs }),
1496            b"choice" => Ok(Particle::Choice { children, occurs }),
1497            _ => Err(XbrlError::InvalidSchemaDocument {
1498                path: self.path.clone(),
1499                reason: format!(
1500                    "unexpected compositor tag: {}",
1501                    String::from_utf8_lossy(start.local_name().as_ref())
1502                ),
1503            }),
1504        }
1505    }
1506
1507    /// Parses an `xs:element` inside a compositor (sequence or choice).
1508    /// `is_empty` indicates whether the event was an `Empty` (self-closing)
1509    /// tag.
1510    fn parse_element_particle(
1511        &mut self,
1512        start: &BytesStart,
1513        is_empty: bool,
1514    ) -> Result<Particle, XbrlError> {
1515        let occurs = self.parse_occurs(start)?;
1516        let mut ref_name: Option<QName> = None;
1517        let mut decl_name: Option<String> = None;
1518        let mut type_name: Option<QName> = None;
1519
1520        for attribute in start.attributes() {
1521            let attribute = attribute.map_err(|err| XbrlError::XmlParse {
1522                path: self.path.clone(),
1523                position: self.reader.buffer_position(),
1524                element: Some("element".to_string()),
1525                source: err.into(),
1526            })?;
1527            let value = attribute.decode_and_unescape_value(self.reader.decoder())?;
1528            match attribute.key.local_name().as_ref() {
1529                b"ref" => ref_name = Some(xml::parse_qname(&value)),
1530                b"name" => decl_name = Some(value.to_string()),
1531                b"type" => type_name = Some(xml::parse_qname(&value)),
1532                _ => {}
1533            }
1534        }
1535
1536        if let Some(qname) = ref_name {
1537            // xs:element[@ref] carries only ref/minOccurs/maxOccurs; any child
1538            // content (e.g. xs:annotation) is irrelevant for XBRL parsing.
1539            if !is_empty {
1540                self.skip_until(b"element")?;
1541            }
1542            return Ok(Particle::Element {
1543                element: ElementParticle::Ref(qname),
1544                occurs,
1545            });
1546        }
1547
1548        // Inline element declaration.
1549        let name = decl_name.unwrap_or_default();
1550        let mut inline_type: Option<Box<ComplexType>> = None;
1551
1552        if !is_empty {
1553            // Read child events to find an optional inline xs:complexType.
1554            let mut buf = Vec::new();
1555            loop {
1556                match self.reader.read_event_into(&mut buf)? {
1557                    Event::Start(ref event) if event.local_name().as_ref() == b"complexType" => {
1558                        inline_type = Some(Box::new(self.parse_complex_type(event)?));
1559                    }
1560                    Event::End(ref event) if event.local_name().as_ref() == b"element" => {
1561                        break;
1562                    }
1563                    Event::Eof => break,
1564                    _ => {}
1565                }
1566                buf.clear();
1567            }
1568        }
1569
1570        Ok(Particle::Element {
1571            element: ElementParticle::Decl(ElementDecl {
1572                name,
1573                type_name,
1574                inline_type,
1575            }),
1576            occurs,
1577        })
1578    }
1579
1580    /// Parses an `xs:group` reference inside a compositor.
1581    fn parse_group_particle(&mut self, start: &BytesStart) -> Result<Particle, XbrlError> {
1582        let occurs = self.parse_occurs(start)?;
1583        let mut ref_name: Option<QName> = None;
1584
1585        for attribute in start.attributes() {
1586            let attribute = attribute.map_err(|err| XbrlError::XmlParse {
1587                path: self.path.clone(),
1588                position: self.reader.buffer_position(),
1589                element: Some("group".to_string()),
1590                source: err.into(),
1591            })?;
1592            let value = attribute.decode_and_unescape_value(self.reader.decoder())?;
1593            if attribute.key.local_name().as_ref() == b"ref" {
1594                ref_name = Some(xml::parse_qname(&value));
1595            }
1596        }
1597
1598        Ok(Particle::Group {
1599            group: GroupParticle::Ref(ref_name.ok_or_else(|| {
1600                XbrlError::InvalidSchemaDocument {
1601                    path: self.path.clone(),
1602                    reason: "xs:group inside a compositor requires a ref attribute".to_string(),
1603                }
1604            })?),
1605            occurs,
1606        })
1607    }
1608
1609    /// Parses an `xs:simpleContent` element.
1610    fn parse_simple_content(&mut self, complex_type: &mut ComplexType) -> Result<(), XbrlError> {
1611        let mut buf = Vec::new();
1612
1613        loop {
1614            match self.reader.read_event_into(&mut buf)? {
1615                Event::Start(ref event)
1616                    if matches!(event.local_name().as_ref(), b"extension" | b"restriction") =>
1617                {
1618                    let derivation = if event.local_name().as_ref() == b"extension" {
1619                        DerivationKind::Extension
1620                    } else {
1621                        DerivationKind::Restriction
1622                    };
1623
1624                    let mut base: Option<QName> = None;
1625                    for attribute in event.attributes() {
1626                        let attribute = attribute.map_err(|err| XbrlError::XmlParse {
1627                            path: self.path.clone(),
1628                            position: self.reader.buffer_position(),
1629                            element: Some("simpleContent extension/restriction".to_string()),
1630                            source: err.into(),
1631                        })?;
1632
1633                        if attribute.key.local_name().as_ref() == b"base" {
1634                            let value =
1635                                attribute.decode_and_unescape_value(self.reader.decoder())?;
1636                            base = Some(xml::parse_qname(&value));
1637                        }
1638                    }
1639
1640                    if let Some(base) = base {
1641                        complex_type.content =
1642                            Some(ComplexTypeContent::SimpleContent(SimpleContent {
1643                                base,
1644                                derivation,
1645                            }));
1646                    }
1647
1648                    complex_type.attributes =
1649                        self.parse_attributes_until(event.local_name().as_ref())?;
1650                }
1651
1652                Event::End(ref event) if event.local_name().as_ref() == b"simpleContent" => {
1653                    break;
1654                }
1655
1656                Event::Eof => break,
1657                _ => {}
1658            }
1659
1660            buf.clear();
1661        }
1662
1663        Ok(())
1664    }
1665
1666    /// Reads `<xs:attribute>` elements until the closing tag `end_tag`,
1667    /// returning a vector of `AttributeUse`.
1668    fn parse_attributes_until(&mut self, end_tag: &[u8]) -> Result<Vec<AttributeUse>, XbrlError> {
1669        let mut buf = Vec::new();
1670        let mut attributes = Vec::new();
1671
1672        loop {
1673            match self.reader.read_event_into(&mut buf)? {
1674                Event::Start(ref event) | Event::Empty(ref event)
1675                    if event.local_name().as_ref() == b"attribute" =>
1676                {
1677                    attributes.push(self.parse_attribute(event)?);
1678                }
1679                Event::End(ref event) if event.local_name().as_ref() == end_tag => {
1680                    break;
1681                }
1682                Event::Eof => {
1683                    return Err(XbrlError::ParseError {
1684                        expected: "end tag while parsing attributes",
1685                        value: "EOF reached".to_string(),
1686                    });
1687                }
1688                _ => {}
1689            }
1690            buf.clear();
1691        }
1692
1693        Ok(attributes)
1694    }
1695
1696    /// Parses an `xs:complexContent` element.
1697    /// `start` is the `<xs:complexContent>` tag, used to read its `mixed`
1698    /// attribute and to recognise the matching closing tag.
1699    fn parse_complex_content(
1700        &mut self,
1701        complex_type: &mut ComplexType,
1702        start: &BytesStart,
1703    ) -> Result<(), XbrlError> {
1704        for attribute in start.attributes() {
1705            let attribute = attribute.map_err(|err| XbrlError::XmlParse {
1706                path: self.path.clone(),
1707                position: self.reader.buffer_position(),
1708                element: Some("complexContent".to_string()),
1709                source: err.into(),
1710            })?;
1711            if attribute.key.local_name().as_ref() == b"mixed" {
1712                let value = attribute.decode_and_unescape_value(self.reader.decoder())?;
1713                complex_type.mixed = value == "true";
1714            }
1715        }
1716
1717        let mut buf = Vec::new();
1718
1719        loop {
1720            match self.reader.read_event_into(&mut buf)? {
1721                Event::Start(ref event)
1722                    if matches!(event.local_name().as_ref(), b"extension" | b"restriction") =>
1723                {
1724                    let derivation_kind = match event.local_name().as_ref() {
1725                        b"extension" => DerivationKind::Extension,
1726                        b"restriction" => DerivationKind::Restriction,
1727                        _ => unreachable!(),
1728                    };
1729                    let mut base: Option<QName> = None;
1730
1731                    for attribute in event.attributes() {
1732                        let attribute = attribute.map_err(|err| XbrlError::XmlParse {
1733                            path: self.path.clone(),
1734                            position: self.reader.buffer_position(),
1735                            element: Some("complexContent extension/restriction".to_string()),
1736                            source: err.into(),
1737                        })?;
1738
1739                        if attribute.key.local_name().as_ref() == b"base" {
1740                            let value =
1741                                attribute.decode_and_unescape_value(self.reader.decoder())?;
1742                            base = Some(xml::parse_qname(&value));
1743                        }
1744                    }
1745
1746                    let tag_name = event.local_name();
1747                    let tag = tag_name.as_ref();
1748                    let (particle, attributes, any_attribute) =
1749                        self.parse_complex_derivation(tag)?;
1750
1751                    complex_type.attributes.extend(attributes);
1752                    complex_type.any_attribute = any_attribute;
1753                    complex_type.content =
1754                        Some(ComplexTypeContent::ComplexContent(ComplexContent {
1755                            derivation: base.map(|base| match derivation_kind {
1756                                DerivationKind::Extension => Derivation::Extension(base),
1757                                DerivationKind::Restriction => Derivation::Restriction(base),
1758                            }),
1759                            particle,
1760                        }));
1761                }
1762
1763                Event::End(ref event) if event.local_name().as_ref() == b"complexContent" => {
1764                    break;
1765                }
1766
1767                Event::Eof => break,
1768                _ => {}
1769            }
1770
1771            buf.clear();
1772        }
1773
1774        Ok(())
1775    }
1776
1777    /// Parses the body of an `xs:extension` or `xs:restriction` inside
1778    /// `xs:complexContent`, collecting the optional particle, attributes, and
1779    /// `anyAttribute`. Returns when the matching closing tag is consumed.
1780    #[allow(clippy::type_complexity)]
1781    fn parse_complex_derivation(
1782        &mut self,
1783        end_tag: &[u8],
1784    ) -> Result<(Option<Particle>, Vec<AttributeUse>, Option<AnyAttribute>), XbrlError> {
1785        let mut buf = Vec::new();
1786        let mut particle: Option<Particle> = None;
1787        let mut attributes = Vec::new();
1788        let mut any_attribute: Option<AnyAttribute> = None;
1789
1790        loop {
1791            match self.reader.read_event_into(&mut buf)? {
1792                Event::Start(ref event) => match event.local_name().as_ref() {
1793                    b"sequence" | b"choice" => {
1794                        particle = Some(self.parse_particle(event)?);
1795                    }
1796                    b"attribute" => {
1797                        attributes.push(self.parse_attribute(event)?);
1798                    }
1799                    _ => {}
1800                },
1801                Event::Empty(ref event) => match event.local_name().as_ref() {
1802                    b"sequence" => {
1803                        let occurs = self.parse_occurs(event)?;
1804                        particle = Some(Particle::Sequence {
1805                            children: vec![],
1806                            occurs,
1807                        });
1808                    }
1809                    b"choice" => {
1810                        let occurs = self.parse_occurs(event)?;
1811                        particle = Some(Particle::Choice {
1812                            children: vec![],
1813                            occurs,
1814                        });
1815                    }
1816                    b"attribute" => {
1817                        attributes.push(self.parse_attribute(event)?);
1818                    }
1819                    b"anyAttribute" => {
1820                        any_attribute = Some(self.parse_any_attribute(event)?);
1821                    }
1822                    _ => {}
1823                },
1824                Event::End(ref event) if event.local_name().as_ref() == end_tag => {
1825                    break;
1826                }
1827                Event::Eof => break,
1828                _ => {}
1829            }
1830
1831            buf.clear();
1832        }
1833
1834        Ok((particle, attributes, any_attribute))
1835    }
1836
1837    /// Parses an `xs:anyAttribute` element.
1838    fn parse_any_attribute(&self, start: &BytesStart) -> Result<AnyAttribute, XbrlError> {
1839        let mut namespace = AnyAttributeNamespace::Any;
1840
1841        for attribute in start.attributes() {
1842            let attribute = attribute.map_err(|err| XbrlError::XmlParse {
1843                path: self.path.clone(),
1844                position: self.reader.buffer_position(),
1845                element: Some("anyAttribute".to_string()),
1846                source: err.into(),
1847            })?;
1848
1849            if attribute.key.local_name().as_ref() == b"namespace" {
1850                let value = attribute.decode_and_unescape_value(self.reader.decoder())?;
1851
1852                namespace = match value.as_ref() {
1853                    "##any" => AnyAttributeNamespace::Any,
1854                    "##other" => AnyAttributeNamespace::Other,
1855                    "##targetNamespace" => AnyAttributeNamespace::TargetNamespace,
1856                    other => AnyAttributeNamespace::List(
1857                        other.split_whitespace().map(|s| s.to_string()).collect(),
1858                    ),
1859                };
1860            }
1861        }
1862
1863        Ok(AnyAttribute { namespace })
1864    }
1865
1866    /// Parses an `xs:attribute` element inside `xs:simpleContent`.
1867    fn parse_attribute(&self, start: &BytesStart) -> Result<AttributeUse, XbrlError> {
1868        let mut ref_name = String::new();
1869        let mut required = false;
1870
1871        for attribute in start.attributes() {
1872            let attribute = attribute.map_err(|err| XbrlError::XmlParse {
1873                path: self.path.clone(),
1874                position: self.reader.buffer_position(),
1875                element: Some("extension or restriction attribute".to_string()),
1876                source: err.into(),
1877            })?;
1878
1879            match attribute.key.local_name().as_ref() {
1880                b"ref" => {
1881                    let value = attribute.decode_and_unescape_value(self.reader.decoder())?;
1882                    ref_name = value.to_string();
1883                }
1884                b"use" => {
1885                    let value = attribute.decode_and_unescape_value(self.reader.decoder())?;
1886
1887                    if value.as_ref() == "required" {
1888                        required = true;
1889                    }
1890                }
1891                _ => {}
1892            }
1893        }
1894
1895        Ok(AttributeUse { ref_name, required })
1896    }
1897}
1898
1899#[cfg(test)]
1900mod tests {
1901    use super::*;
1902    use assert_matches::assert_matches;
1903
1904    #[test]
1905    fn test_parse_schema_root_invalid() {
1906        let xml = r#"<root/>"#;
1907        let mut parser = SchemaParser::from_reader(xml.as_bytes());
1908        let result = parser.parse();
1909
1910        assert_matches!(result, Err(XbrlError::InvalidSchemaDocument { reason, .. }) if reason == "root is not allowed in taxonomy schemas");
1911    }
1912
1913    #[test]
1914    fn test_parse_schema_root_missing() {
1915        let xml = r#"<xsd:import
1916                            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
1917                            namespace="http://www.xbrl.org/2003/instance"
1918                            schemaLocation="http://www.xbrl.org/2003/xbrl-instance-2003-12-31.xsd" />"#;
1919        let mut parser = SchemaParser::from_reader(xml.as_bytes());
1920        let result = parser.parse();
1921
1922        assert_matches!(result, Err(XbrlError::InvalidSchemaDocument { reason, .. }) if reason == "missing <schema> root element");
1923    }
1924
1925    #[test]
1926    fn test_parse_schema_root() {
1927        let xml = r#"<xsd:schema
1928                                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
1929                                xmlns:xbrli="http://www.xbrl.org/2003/instance"
1930                                targetNamespace="http://example.com/taxonomy"
1931                                elementFormDefault="qualified">
1932                            </xsd:schema>"#;
1933        let mut parser = SchemaParser::from_reader(xml.as_bytes());
1934        let schema = parser.parse().unwrap();
1935
1936        assert_matches!(schema.element_form_default, FormDefault::Qualified);
1937        assert_matches!(schema.attribute_form_default, FormDefault::Unqualified);
1938        assert_eq!(
1939            schema.target_namespace,
1940            Some("http://example.com/taxonomy".to_string())
1941        );
1942        assert_eq!(
1943            schema.namespaces,
1944            HashMap::from_iter([
1945                ("xsd".into(), "http://www.w3.org/2001/XMLSchema".into()),
1946                ("xbrli".into(), "http://www.xbrl.org/2003/instance".into()),
1947            ])
1948        );
1949    }
1950
1951    #[test]
1952    fn test_parse_import() {
1953        let xml = r#"<xsd:schema
1954                                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
1955                                xmlns:xbrli="http://www.xbrl.org/2003/instance"
1956                                targetNamespace="http://example.com/taxonomy"
1957                                elementFormDefault="qualified">
1958                                <xsd:import
1959                                    namespace="http://www.xbrl.org/2003/instance"
1960                                    schemaLocation="http://www.xbrl.org/2003/xbrl-instance-2003-12-31.xsd" />
1961                            </xsd:schema>"#;
1962        let mut parser = SchemaParser::from_reader(xml.as_bytes());
1963        let schema = parser.parse().unwrap();
1964
1965        let imports = &schema.imports;
1966        assert!(imports.len() == 1);
1967        let import = &imports[0];
1968        assert_eq!(import.namespace, "http://www.xbrl.org/2003/instance");
1969        assert_eq!(
1970            import.schema_location,
1971            Some("http://www.xbrl.org/2003/xbrl-instance-2003-12-31.xsd".to_string())
1972        );
1973    }
1974
1975    #[test]
1976    fn test_parse_include() {
1977        let xml = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
1978                                targetNamespace="http://example.com"
1979                                xmlns="http://example.com">
1980                                <xs:include schemaLocation="test.xsd" />
1981                            </xs:schema>"#;
1982        let mut parser = SchemaParser::from_reader(xml.as_bytes());
1983        let schema = parser.parse().unwrap();
1984
1985        let includes = &schema.includes;
1986        assert!(includes.len() == 1);
1987        let include = &includes[0];
1988        assert_eq!(include.schema_location, "test.xsd");
1989    }
1990
1991    #[test]
1992    fn test_parse_linkbase_ref() {
1993        let xml = r#"<xs:schema
1994                            xmlns:xs="http://www.w3.org/2001/XMLSchema"
1995                            xmlns:link="http://www.xbrl.org/2003/linkbase"
1996                            xmlns:xlink="http://www.w3.org/1999/xlink">
1997                            <xs:annotation>
1998                                <xs:appinfo>
1999                                    <link:linkbaseRef
2000                                        xlink:type="simple"
2001                                        xlink:href="de-gaap-ci_pre.xml"
2002                                        xlink:role="http://www.w3.org/1999/xlink/properties/linkbase"
2003                                        xlink:arcrole="http://www.w3.org/1999/xlink/properties/linkbase" />
2004                                </xs:appinfo>
2005                            </xs:annotation>
2006                        </xs:schema>"#;
2007        let mut parser = SchemaParser::from_reader(xml.as_bytes());
2008        let schema = parser.parse().unwrap();
2009
2010        assert_eq!(schema.linkbase_refs.len(), 1);
2011        let linkbase_ref = &schema.linkbase_refs[0];
2012        assert_eq!(linkbase_ref.href, "de-gaap-ci_pre.xml");
2013        assert_eq!(
2014            linkbase_ref.role.as_deref(),
2015            Some("http://www.w3.org/1999/xlink/properties/linkbase")
2016        );
2017        assert_eq!(
2018            linkbase_ref.arcrole.as_deref(),
2019            Some("http://www.w3.org/1999/xlink/properties/linkbase")
2020        );
2021    }
2022
2023    #[test]
2024    fn test_parse_role_type() {
2025        let xml = r#"<xs:schema
2026                            xmlns:xs="http://www.w3.org/2001/XMLSchema"
2027                            xmlns:link="http://www.xbrl.org/2003/linkbase"
2028                            xmlns:xlink="http://www.w3.org/1999/xlink">
2029                            <xs:annotation>
2030                                <xs:appinfo>
2031                                    <link:roleType
2032                                        roleURI="http://www.xbrl.de/taxonomies/de-gaap-ci/role/balanceSheet"
2033                                        id="balanceSheet">
2034                                        <link:definition>Balance Sheet</link:definition>
2035                                        <link:usedOn>link:presentationLink</link:usedOn>
2036                                    </link:roleType>
2037                                </xs:appinfo>
2038                            </xs:annotation>
2039                        </xs:schema>"#;
2040        let mut parser = SchemaParser::from_reader(xml.as_bytes());
2041        let schema = parser.parse().unwrap();
2042
2043        assert_eq!(schema.role_types.len(), 1);
2044        let role_type = &schema.role_types[0];
2045        assert_eq!(
2046            role_type.role_uri.as_str(),
2047            "http://www.xbrl.de/taxonomies/de-gaap-ci/role/balanceSheet"
2048        );
2049        assert_eq!(role_type.id, "balanceSheet");
2050    }
2051
2052    #[test]
2053    fn test_parse_arcrole_type() {
2054        let xml = r#"<xs:schema
2055                            xmlns:xs="http://www.w3.org/2001/XMLSchema"
2056                            xmlns:link="http://www.xbrl.org/2003/linkbase"
2057                            xmlns:xlink="http://www.w3.org/1999/xlink">
2058                            <xs:annotation>
2059                                <xs:appinfo>
2060                                    <link:arcroleType
2061                                        arcroleURI="http://www.xbrl.de/taxonomies/de-gaap-ci/arcrole/parent-child"
2062                                        cyclesAllowed="undirected"
2063                                        id="parentChild">
2064                                        <link:definition>Parent-child relationship</link:definition>
2065                                        <link:usedOn>link:definitionArc</link:usedOn>
2066                                    </link:arcroleType>
2067                                </xs:appinfo>
2068                            </xs:annotation>
2069                        </xs:schema>"#;
2070        let mut parser = SchemaParser::from_reader(xml.as_bytes());
2071        let schema = parser.parse().unwrap();
2072
2073        assert_eq!(schema.arcrole_types.len(), 1);
2074        let arcrole_type = &schema.arcrole_types[0];
2075        assert_eq!(
2076            arcrole_type.arcrole_uri.as_str(),
2077            "http://www.xbrl.de/taxonomies/de-gaap-ci/arcrole/parent-child"
2078        );
2079        assert_eq!(arcrole_type.cycles_allowed, Some(CyclesAllowed::Undirected));
2080        assert_eq!(arcrole_type.id, "parentChild");
2081    }
2082
2083    #[test]
2084    fn test_parse_item_element() {
2085        let xml = r#"<xsd:schema
2086                                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
2087                                xmlns:xbrli="http://www.xbrl.org/2003/instance">
2088                                <xsd:element
2089                                    name="Revenue"
2090                                    id="Revenue"
2091                                    type="xbrli:monetaryItemType"
2092                                    substitutionGroup="xbrli:item"
2093                                    xbrli:periodType="duration"
2094                                    abstract="false"
2095                                    nillable="true" />
2096                            </xsd:schema>"#;
2097        let mut parser = SchemaParser::from_reader(xml.as_bytes());
2098        let schema = parser.parse().unwrap();
2099
2100        assert_eq!(schema.elements.len(), 1);
2101        let element = &schema.elements[0];
2102        assert_eq!(
2103            *element,
2104            Element {
2105                name: "Revenue".to_string(),
2106                id: Some("Revenue".to_string()),
2107                type_name: Some(QName {
2108                    prefix: Some(NamespacePrefix::from("xbrli")),
2109                    local_name: "monetaryItemType".to_string(),
2110                }),
2111                substitution_group: Some(QName {
2112                    prefix: Some(NamespacePrefix::from("xbrli")),
2113                    local_name: "item".to_string(),
2114                }),
2115                is_nillable: true,
2116                is_abstract: false,
2117                period_type: Some(PeriodType::Duration),
2118                balance: None,
2119                complex_type: None,
2120            }
2121        );
2122    }
2123
2124    // Test parsing a complexType inside a tuple element.
2125    #[test]
2126    fn test_parse_tuple_element_with_sequence() {
2127        let xml = r#"<xsd:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
2128                            targetNamespace="http://example.com"
2129                            xmlns="http://example.com"
2130                            xmlns:xbrli="http://www.xbrl.org/2003/instance">
2131                            <xs:element name="address" substitutionGroup="xbrli:tuple">
2132                                <xs:complexType>
2133                                    <xs:sequence>
2134                                        <xs:element ref="my:city" />
2135                                        <xs:element ref="my:country" minOccurs="0" />
2136                                    </xs:sequence>
2137                                </xs:complexType>
2138                            </xs:element>
2139                        </xsd:schema>"#;
2140        let mut parser = SchemaParser::from_reader(xml.as_bytes());
2141        let mut schema = parser.parse().unwrap();
2142
2143        assert_eq!(schema.elements.len(), 1);
2144        let element = schema.elements.remove(0);
2145        assert_eq!(
2146            element,
2147            Element {
2148                name: "address".to_string(),
2149                id: None,
2150                type_name: None,
2151                substitution_group: Some(QName {
2152                    prefix: Some(NamespacePrefix::from("xbrli")),
2153                    local_name: "tuple".to_string(),
2154                }),
2155                is_nillable: false,
2156                is_abstract: false,
2157                period_type: None,
2158                balance: None,
2159                complex_type: Some(ComplexType {
2160                    name: None,
2161                    mixed: false,
2162                    attributes: vec![],
2163                    any_attribute: None,
2164                    content: Some(ComplexTypeContent::ComplexContent(ComplexContent {
2165                        derivation: None,
2166                        particle: Some(Particle::Sequence {
2167                            children: vec![
2168                                Particle::Element {
2169                                    element: ElementParticle::Ref(QName {
2170                                        prefix: Some(NamespacePrefix::from("my")),
2171                                        local_name: "city".to_string(),
2172                                    }),
2173                                    occurs: Occurrence {
2174                                        min: 1,
2175                                        max: Some(1)
2176                                    },
2177                                },
2178                                Particle::Element {
2179                                    element: ElementParticle::Ref(QName {
2180                                        prefix: Some(NamespacePrefix::from("my")),
2181                                        local_name: "country".to_string(),
2182                                    }),
2183                                    occurs: Occurrence {
2184                                        min: 0,
2185                                        max: Some(1)
2186                                    },
2187                                },
2188                            ],
2189                            occurs: Occurrence {
2190                                min: 1,
2191                                max: Some(1)
2192                            },
2193                        }),
2194                    })),
2195                }),
2196            }
2197        );
2198    }
2199
2200    #[test]
2201    fn test_parse_tuple_element_min_max() {
2202        let xml = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
2203                            targetNamespace="http://example.com"
2204                            xmlns="http://example.com"
2205                            xmlns:xbrli="http://www.xbrl.org/2003/instance">
2206                            <xs:element name="address" substitutionGroup="xbrli:tuple">
2207                                <xs:complexType>
2208                                    <xs:sequence>
2209                                        <xs:element ref="my:itemA" minOccurs="2" maxOccurs="2" />
2210                                        <xs:element ref="my:itemB" minOccurs="0" maxOccurs="unbounded" />
2211                                    </xs:sequence>
2212                                </xs:complexType>
2213                            </xs:element>
2214                        </xs:schema>"#;
2215        let mut parser = SchemaParser::from_reader(xml.as_bytes());
2216        let mut schema = parser.parse().unwrap();
2217
2218        assert_eq!(schema.elements.len(), 1);
2219        let element = schema.elements.remove(0);
2220        assert_eq!(
2221            element,
2222            Element {
2223                name: "address".to_string(),
2224                id: None,
2225                type_name: None,
2226                substitution_group: Some(QName {
2227                    prefix: Some(NamespacePrefix::from("xbrli")),
2228                    local_name: "tuple".to_string(),
2229                }),
2230                is_nillable: false,
2231                is_abstract: false,
2232                period_type: None,
2233                balance: None,
2234                complex_type: Some(ComplexType {
2235                    name: None,
2236                    mixed: false,
2237                    attributes: vec![],
2238                    any_attribute: None,
2239                    content: Some(ComplexTypeContent::ComplexContent(ComplexContent {
2240                        derivation: None,
2241                        particle: Some(Particle::Sequence {
2242                            children: vec![
2243                                Particle::Element {
2244                                    element: ElementParticle::Ref(QName {
2245                                        prefix: Some(NamespacePrefix::from("my")),
2246                                        local_name: "itemA".to_string(),
2247                                    }),
2248                                    occurs: Occurrence {
2249                                        min: 2,
2250                                        max: Some(2)
2251                                    },
2252                                },
2253                                Particle::Element {
2254                                    element: ElementParticle::Ref(QName {
2255                                        prefix: Some(NamespacePrefix::from("my")),
2256                                        local_name: "itemB".to_string(),
2257                                    }),
2258                                    occurs: Occurrence { min: 0, max: None },
2259                                },
2260                            ],
2261                            occurs: Occurrence {
2262                                min: 1,
2263                                max: Some(1)
2264                            },
2265                        }),
2266                    })),
2267                }),
2268            }
2269        );
2270    }
2271
2272    #[test]
2273    fn test_parse_tuple_element_with_choice() {
2274        let xml = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
2275                            targetNamespace="http://example.com"
2276                            xmlns="http://example.com"
2277                            xmlns:xbrli="http://www.xbrl.org/2003/instance">
2278                            <xs:element name="MyTuple" substitutionGroup="xbrli:tuple"
2279                                xmlns:xs="http://www.w3.org/2001/XMLSchema">
2280                                <xs:complexType>
2281                                    <xs:choice>
2282                                        <xs:element ref="my:optA" />
2283                                        <xs:element ref="my:optB" />
2284                                    </xs:choice>
2285                                </xs:complexType>
2286                            </xs:element>
2287                        </xs:schema>"#;
2288        let mut parser = SchemaParser::from_reader(xml.as_bytes());
2289        let mut schema = parser.parse().unwrap();
2290
2291        assert_eq!(schema.elements.len(), 1);
2292        let element = schema.elements.remove(0);
2293        assert_eq!(
2294            element,
2295            Element {
2296                name: "MyTuple".to_string(),
2297                id: None,
2298                type_name: None,
2299                substitution_group: Some(QName {
2300                    prefix: Some(NamespacePrefix::from("xbrli")),
2301                    local_name: "tuple".to_string(),
2302                }),
2303                is_nillable: false,
2304                is_abstract: false,
2305                period_type: None,
2306                balance: None,
2307                complex_type: Some(ComplexType {
2308                    name: None,
2309                    mixed: false,
2310                    attributes: vec![],
2311                    any_attribute: None,
2312                    content: Some(ComplexTypeContent::ComplexContent(ComplexContent {
2313                        derivation: None,
2314                        particle: Some(Particle::Choice {
2315                            children: vec![
2316                                Particle::Element {
2317                                    element: ElementParticle::Ref(QName {
2318                                        prefix: Some(NamespacePrefix::from("my")),
2319                                        local_name: "optA".to_string(),
2320                                    }),
2321                                    occurs: Occurrence {
2322                                        min: 1,
2323                                        max: Some(1)
2324                                    },
2325                                },
2326                                Particle::Element {
2327                                    element: ElementParticle::Ref(QName {
2328                                        prefix: Some(NamespacePrefix::from("my")),
2329                                        local_name: "optB".to_string(),
2330                                    }),
2331                                    occurs: Occurrence {
2332                                        min: 1,
2333                                        max: Some(1)
2334                                    },
2335                                },
2336                            ],
2337                            occurs: Occurrence {
2338                                min: 1,
2339                                max: Some(1)
2340                            },
2341                        }),
2342                    })),
2343                }),
2344            }
2345        );
2346    }
2347
2348    #[test]
2349    fn test_parse_element_particle_with_inline_complex_type() {
2350        let xml = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
2351                                targetNamespace="http://example.com"
2352                                xmlns:my="http://example.com"
2353                                xmlns:xbrli="http://www.xbrl.org/2003/instance">
2354                                <xs:element name="AddressTuple" substitutionGroup="xbrli:tuple">
2355                                    <xs:complexType>
2356                                        <xs:sequence>
2357                                            <xs:element name="street">
2358                                                <xs:complexType>
2359                                                    <xs:sequence>
2360                                                        <xs:element ref="my:line1" />
2361                                                    </xs:sequence>
2362                                                </xs:complexType>
2363                                            </xs:element>
2364                                        </xs:sequence>
2365                                    </xs:complexType>
2366                                </xs:element>
2367                            </xs:schema>"#;
2368        let mut parser = SchemaParser::from_reader(xml.as_bytes());
2369        let schema = parser.parse().unwrap();
2370
2371        let element = &schema.elements[0];
2372        assert_eq!(
2373            element,
2374            &Element {
2375                name: "AddressTuple".to_string(),
2376                id: None,
2377                type_name: None,
2378                substitution_group: Some(QName {
2379                    prefix: Some(NamespacePrefix::from("xbrli")),
2380                    local_name: "tuple".to_string(),
2381                }),
2382                is_nillable: false,
2383                is_abstract: false,
2384                period_type: None,
2385                balance: None,
2386                complex_type: Some(ComplexType {
2387                    name: None,
2388                    mixed: false,
2389                    attributes: vec![],
2390                    any_attribute: None,
2391                    content: Some(ComplexTypeContent::ComplexContent(ComplexContent {
2392                        derivation: None,
2393                        particle: Some(Particle::Sequence {
2394                            children: vec![Particle::Element {
2395                                element: ElementParticle::Decl(ElementDecl {
2396                                    name: "street".to_string(),
2397                                    type_name: None,
2398                                    inline_type: Some(Box::new(ComplexType {
2399                                        name: None,
2400                                        mixed: false,
2401                                        attributes: vec![],
2402                                        any_attribute: None,
2403                                        content: Some(ComplexTypeContent::ComplexContent(
2404                                            ComplexContent {
2405                                                derivation: None,
2406                                                particle: Some(Particle::Sequence {
2407                                                    children: vec![Particle::Element {
2408                                                        element: ElementParticle::Ref(QName {
2409                                                            prefix: Some(NamespacePrefix::from(
2410                                                                "my"
2411                                                            )),
2412                                                            local_name: "line1".to_string(),
2413                                                        }),
2414                                                        occurs: Occurrence {
2415                                                            min: 1,
2416                                                            max: Some(1)
2417                                                        },
2418                                                    }],
2419                                                    occurs: Occurrence {
2420                                                        min: 1,
2421                                                        max: Some(1)
2422                                                    },
2423                                                }),
2424                                            }
2425                                        )),
2426                                    })),
2427                                }),
2428                                occurs: Occurrence {
2429                                    min: 1,
2430                                    max: Some(1)
2431                                },
2432                            }],
2433                            occurs: Occurrence {
2434                                min: 1,
2435                                max: Some(1)
2436                            },
2437                        }),
2438                    })),
2439                }),
2440            }
2441        );
2442    }
2443
2444    // XSD constraint maxLength doesn't have a specific meaning in XBRL, and
2445    // won't be parsed.
2446    #[test]
2447    fn test_parse_simple_type_restriction() {
2448        let xml = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
2449                                targetNamespace="http://example.com"
2450                                xmlns="http://example.com">
2451                                <xs:simpleType name="myStringType">
2452                                    <xs:restriction base="xs:string">
2453                                        <xs:maxLength value="100" />
2454                                    </xs:restriction>
2455                                </xs:simpleType>
2456                            </xs:schema>"#;
2457        let mut parser = SchemaParser::from_reader(xml.as_bytes());
2458        let schema = parser.parse().unwrap();
2459
2460        assert_eq!(schema.simple_types.len(), 1);
2461        let simple_type = &schema.simple_types[0];
2462        assert_eq!(
2463            *simple_type,
2464            SimpleType {
2465                name: Some("myStringType".to_string()),
2466                base: Some(QName {
2467                    prefix: Some(NamespacePrefix::from("xs")),
2468                    local_name: "string".to_string()
2469                }),
2470                enumerations: vec![],
2471            }
2472        );
2473    }
2474
2475    #[test]
2476    fn test_parse_simple_type_enumeration() {
2477        let xml = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
2478                                targetNamespace="http://example.com"
2479                                xmlns="http://example.com">
2480                                <xs:simpleType name="StatusType">
2481                                    <xs:restriction base="xs:string">
2482                                        <xs:enumeration value="Open" />
2483                                        <xs:enumeration value="Closed" />
2484                                    </xs:restriction>
2485                                </xs:simpleType>
2486                            </xs:schema>"#;
2487        let mut parser = SchemaParser::from_reader(xml.as_bytes());
2488        let schema = parser.parse().unwrap();
2489
2490        assert_eq!(schema.simple_types.len(), 1);
2491        let simple_type = &schema.simple_types[0];
2492        assert_eq!(
2493            *simple_type,
2494            SimpleType {
2495                name: Some("StatusType".to_string()),
2496                base: Some(QName {
2497                    prefix: Some(NamespacePrefix::from("xs")),
2498                    local_name: "string".to_string()
2499                }),
2500                enumerations: vec!["Open".to_string(), "Closed".to_string()],
2501            }
2502        );
2503    }
2504
2505    #[test]
2506    fn test_parse_complex_type_empty() {
2507        let xml = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
2508                            targetNamespace="http://example.com"
2509                            xmlns="http://example.com"
2510                            xmlns:xbrli="http://www.xbrl.org/2003/instance">
2511                            <xs:complexType name="emptyType"
2512                                xmlns:xs="http://www.w3.org/2001/XMLSchema">
2513                        </xs:complexType>
2514                        </xs:schema>"#;
2515        let mut parser = SchemaParser::from_reader(xml.as_bytes());
2516        let schema = parser.parse().unwrap();
2517
2518        assert_eq!(schema.complex_types.len(), 1);
2519        let complex_type = &schema.complex_types[0];
2520        assert_eq!(
2521            complex_type,
2522            &ComplexType {
2523                name: Some("emptyType".to_string()),
2524                mixed: false,
2525                attributes: vec![],
2526                any_attribute: None,
2527                content: None,
2528            }
2529        );
2530    }
2531
2532    #[test]
2533    fn test_parse_complex_type_invalid_restriction() {
2534        let xml = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
2535                            targetNamespace="http://example.com"
2536                            xmlns="http://example.com"
2537                            xmlns:xbrli="http://www.xbrl.org/2003/instance">
2538                            <xs:complexType xmlns:xs="http://www.w3.org/2001/XMLSchema">
2539                                <xs:restriction base="xbrli:decimalItemType" />
2540                            </xs:complexType>
2541                        </xs:schema>"#;
2542        let mut parser = SchemaParser::from_reader(xml.as_bytes());
2543        let res = parser.parse();
2544
2545        assert_matches!(res, Err(XbrlError::InvalidSchemaDocument { .. }));
2546    }
2547
2548    #[test]
2549    fn test_parse_complex_type_invalid_extension() {
2550        let xml = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
2551                            targetNamespace="http://example.com"
2552                            xmlns="http://example.com"
2553                            xmlns:xbrli="http://www.xbrl.org/2003/instance">
2554                            <xs:complexType xmlns:xs="http://www.w3.org/2001/XMLSchema">
2555                                <xs:extension base="xbrli:decimalItemType" />
2556                            </xs:complexType>
2557                        </xs:schema>"#;
2558        let mut parser = SchemaParser::from_reader(xml.as_bytes());
2559        let res = parser.parse();
2560
2561        assert_matches!(res, Err(XbrlError::InvalidSchemaDocument { .. }));
2562    }
2563
2564    #[test]
2565    fn test_parse_complex_type_with_simple_content_and_extension() {
2566        let xml = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
2567                            targetNamespace="http://example.com"
2568                            xmlns="http://example.com"
2569                            xmlns:xbrli="http://www.xbrl.org/2003/instance">
2570                            <xs:complexType name="monetaryItemType">
2571                                <xs:simpleContent>
2572                                    <xs:extension base="xbrli:decimalItemType">
2573                                        <xs:attribute ref="xbrli:unitRef" use="required" />
2574                                        <xs:attribute ref="xbrli:decimals" />
2575                                    </xs:extension>
2576                                </xs:simpleContent>
2577                            </xs:complexType>
2578                        </xs:schema>"#;
2579        let mut parser = SchemaParser::from_reader(xml.as_bytes());
2580        let schema = parser.parse().unwrap();
2581
2582        assert_eq!(schema.complex_types.len(), 1);
2583        let complex_type = &schema.complex_types[0];
2584        assert_eq!(
2585            complex_type,
2586            &ComplexType {
2587                name: Some("monetaryItemType".to_string()),
2588                mixed: false,
2589                attributes: vec![
2590                    AttributeUse {
2591                        ref_name: "xbrli:unitRef".to_string(),
2592                        required: true,
2593                    },
2594                    AttributeUse {
2595                        ref_name: "xbrli:decimals".to_string(),
2596                        required: false,
2597                    },
2598                ],
2599                any_attribute: None,
2600                content: Some(ComplexTypeContent::SimpleContent(SimpleContent {
2601                    base: QName {
2602                        prefix: Some(NamespacePrefix::from("xbrli")),
2603                        local_name: "decimalItemType".to_string(),
2604                    },
2605                    derivation: DerivationKind::Extension,
2606                })),
2607            }
2608        );
2609    }
2610
2611    #[test]
2612    fn test_parse_complex_type_with_simple_content_and_restriction() {
2613        let xml = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
2614                            targetNamespace="http://example.com"
2615                            xmlns="http://example.com"
2616                            xmlns:xbrli="http://www.xbrl.org/2003/instance">
2617                            <xs:complexType name="restrictedDecimal">
2618                                <xs:simpleContent>
2619                                    <xs:restriction base="xbrli:decimalItemType">
2620                                        <xs:attribute ref="xbrli:unitRef" use="required" />
2621                                    </xs:restriction>
2622                                </xs:simpleContent>
2623                            </xs:complexType>
2624                        </xs:schema>"#;
2625        let mut parser = SchemaParser::from_reader(xml.as_bytes());
2626        let schema = parser.parse().unwrap();
2627
2628        assert_eq!(schema.complex_types.len(), 1);
2629        let complex_type = &schema.complex_types[0];
2630        assert_eq!(
2631            complex_type,
2632            &ComplexType {
2633                name: Some("restrictedDecimal".to_string()),
2634                mixed: false,
2635                attributes: vec![AttributeUse {
2636                    ref_name: "xbrli:unitRef".to_string(),
2637                    required: true,
2638                }],
2639                any_attribute: None,
2640                content: Some(ComplexTypeContent::SimpleContent(SimpleContent {
2641                    base: QName {
2642                        prefix: Some(NamespacePrefix::from("xbrli")),
2643                        local_name: "decimalItemType".to_string(),
2644                    },
2645                    derivation: DerivationKind::Restriction,
2646                })),
2647            }
2648        );
2649    }
2650
2651    #[test]
2652    fn test_parse_complex_type_with_complex_content_and_extension() {
2653        let xml = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
2654                            targetNamespace="http://example.com"
2655                            xmlns="http://example.com">
2656                            <!-- Base complex type -->
2657                            <xs:complexType name="baseAccountType">
2658                                <xs:sequence>
2659                                    <xs:element ref="xs:name" />
2660                                </xs:sequence>
2661                            </xs:complexType>
2662                            <!-- Derived type extending the base -->
2663                            <xs:complexType name="extendedAccountType">
2664                                <xs:complexContent>
2665                                    <xs:extension base="baseAccountType">
2666                                        <xs:sequence>
2667                                            <xs:element ref="xs:balance" />
2668                                        </xs:sequence>
2669                                        <xs:attribute ref="currency" />
2670                                    </xs:extension>
2671                                </xs:complexContent>
2672                            </xs:complexType>
2673                        </xs:schema>"#;
2674        let mut parser = SchemaParser::from_reader(xml.as_bytes());
2675        let schema = parser.parse().unwrap();
2676
2677        assert_eq!(schema.complex_types.len(), 2);
2678        let base_type = &schema.complex_types[0];
2679        assert_eq!(
2680            base_type,
2681            &ComplexType {
2682                name: Some("baseAccountType".to_string()),
2683                mixed: false,
2684                attributes: vec![],
2685                any_attribute: None,
2686                content: Some(ComplexTypeContent::ComplexContent(ComplexContent {
2687                    derivation: None,
2688                    particle: Some(Particle::Sequence {
2689                        children: vec![Particle::Element {
2690                            element: ElementParticle::Ref(QName {
2691                                prefix: Some(NamespacePrefix::from("xs")),
2692                                local_name: "name".to_string(),
2693                            }),
2694                            occurs: Occurrence {
2695                                min: 1,
2696                                max: Some(1)
2697                            },
2698                        }],
2699                        occurs: Occurrence {
2700                            min: 1,
2701                            max: Some(1)
2702                        },
2703                    }),
2704                })),
2705            }
2706        );
2707        let complex_type = &schema.complex_types[1];
2708        assert_eq!(
2709            complex_type,
2710            &ComplexType {
2711                name: Some("extendedAccountType".to_string()),
2712                mixed: false,
2713                attributes: vec![AttributeUse {
2714                    ref_name: "currency".to_string(),
2715                    required: false,
2716                }],
2717                any_attribute: None,
2718                content: Some(ComplexTypeContent::ComplexContent(ComplexContent {
2719                    derivation: Some(Derivation::Extension(QName {
2720                        prefix: None,
2721                        local_name: "baseAccountType".to_string(),
2722                    })),
2723                    particle: Some(Particle::Sequence {
2724                        children: vec![Particle::Element {
2725                            element: ElementParticle::Ref(QName {
2726                                prefix: Some(NamespacePrefix::from("xs")),
2727                                local_name: "balance".to_string(),
2728                            }),
2729                            occurs: Occurrence {
2730                                min: 1,
2731                                max: Some(1)
2732                            },
2733                        }],
2734                        occurs: Occurrence {
2735                            min: 1,
2736                            max: Some(1)
2737                        },
2738                    }),
2739                })),
2740            }
2741        );
2742    }
2743
2744    #[test]
2745    fn test_parse_complex_type_with_complex_content_and_restriction() {
2746        let xml = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
2747                            targetNamespace="http://example.com"
2748                            xmlns="http://example.com">
2749                            <!-- Base complex type -->
2750                            <xs:complexType name="baseAccountType">
2751                                <xs:sequence>
2752                                    <xs:element ref="xs:name" />
2753                                    <xs:element ref="xs:balance" />
2754                                </xs:sequence>
2755                                <xs:attribute ref="currency" />
2756                            </xs:complexType>
2757                            <!-- Restricted type -->
2758                            <xs:complexType name="restrictedAccountType">
2759                                <xs:complexContent>
2760                                    <xs:restriction base="baseAccountType">
2761                                        <xs:sequence>
2762                                            <xs:element ref="xs:name" />
2763                                        </xs:sequence>
2764                                        <xs:attribute ref="currency" use="required" />
2765                                    </xs:restriction>
2766                                </xs:complexContent>
2767                            </xs:complexType>
2768                        </xs:schema>"#;
2769        let mut parser = SchemaParser::from_reader(xml.as_bytes());
2770        let schema = parser.parse().unwrap();
2771
2772        let complex_type = &schema.complex_types[0];
2773        assert_eq!(
2774            complex_type,
2775            &ComplexType {
2776                name: Some("baseAccountType".to_string()),
2777                mixed: false,
2778                attributes: vec![AttributeUse {
2779                    ref_name: "currency".to_string(),
2780                    required: false,
2781                }],
2782                any_attribute: None,
2783                content: Some(ComplexTypeContent::ComplexContent(ComplexContent {
2784                    derivation: None,
2785                    particle: Some(Particle::Sequence {
2786                        children: vec![
2787                            Particle::Element {
2788                                element: ElementParticle::Ref(QName {
2789                                    prefix: Some(NamespacePrefix::from("xs")),
2790                                    local_name: "name".to_string(),
2791                                }),
2792                                occurs: Occurrence {
2793                                    min: 1,
2794                                    max: Some(1)
2795                                },
2796                            },
2797                            Particle::Element {
2798                                element: ElementParticle::Ref(QName {
2799                                    prefix: Some(NamespacePrefix::from("xs")),
2800                                    local_name: "balance".to_string(),
2801                                }),
2802                                occurs: Occurrence {
2803                                    min: 1,
2804                                    max: Some(1)
2805                                },
2806                            },
2807                        ],
2808                        occurs: Occurrence {
2809                            min: 1,
2810                            max: Some(1)
2811                        },
2812                    }),
2813                })),
2814            }
2815        );
2816        let restricted_type = &schema.complex_types[1];
2817        assert_eq!(
2818            restricted_type,
2819            &ComplexType {
2820                name: Some("restrictedAccountType".to_string()),
2821                mixed: false,
2822                attributes: vec![AttributeUse {
2823                    ref_name: "currency".to_string(),
2824                    required: true,
2825                }],
2826                any_attribute: None,
2827                content: Some(ComplexTypeContent::ComplexContent(ComplexContent {
2828                    derivation: Some(Derivation::Restriction(QName {
2829                        prefix: None,
2830                        local_name: "baseAccountType".to_string(),
2831                    })),
2832                    particle: Some(Particle::Sequence {
2833                        children: vec![Particle::Element {
2834                            element: ElementParticle::Ref(QName {
2835                                prefix: Some(NamespacePrefix::from("xs")),
2836                                local_name: "name".to_string(),
2837                            }),
2838                            occurs: Occurrence {
2839                                min: 1,
2840                                max: Some(1)
2841                            },
2842                        }],
2843                        occurs: Occurrence {
2844                            min: 1,
2845                            max: Some(1)
2846                        },
2847                    }),
2848                })),
2849            }
2850        );
2851    }
2852
2853    #[test]
2854    fn test_parse_complex_type_with_mixed_type() {
2855        let xml = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
2856                                targetNamespace="http://example.com"
2857                                xmlns="http://example.com">
2858                                <xs:complexType name="MixedType" mixed="true">
2859                                    <xs:sequence>
2860                                        <xs:element name="child" type="xs:string" />
2861                                    </xs:sequence>
2862                                </xs:complexType>
2863                            </xs:schema>"#;
2864        let mut parser = SchemaParser::from_reader(xml.as_bytes());
2865        let schema = parser.parse().unwrap();
2866
2867        let complex_type = &schema.complex_types[0];
2868        assert_eq!(
2869            complex_type,
2870            &ComplexType {
2871                name: Some("MixedType".to_string()),
2872                mixed: true,
2873                attributes: vec![],
2874                any_attribute: None,
2875                content: Some(ComplexTypeContent::ComplexContent(ComplexContent {
2876                    derivation: None,
2877                    particle: Some(Particle::Sequence {
2878                        children: vec![Particle::Element {
2879                            element: ElementParticle::Decl(ElementDecl {
2880                                name: "child".to_string(),
2881                                type_name: Some(QName {
2882                                    prefix: Some(NamespacePrefix::from("xs")),
2883                                    local_name: "string".to_string(),
2884                                }),
2885                                inline_type: None,
2886                            }),
2887                            occurs: Occurrence {
2888                                min: 1,
2889                                max: Some(1)
2890                            },
2891                        }],
2892                        occurs: Occurrence {
2893                            min: 1,
2894                            max: Some(1)
2895                        },
2896                    }),
2897                })),
2898            }
2899        );
2900    }
2901
2902    #[test]
2903    fn test_parse_complex_type_with_any_attribute() {
2904        let xml = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
2905                                targetNamespace="http://example.com"
2906                                xmlns="http://example.com">
2907                                <!-- ##any -->
2908                                <xs:complexType name="AnyAttrType">
2909                                    <xs:sequence />
2910                                    <xs:anyAttribute />
2911                                </xs:complexType>
2912                                <!-- specific namespace -->
2913                                <xs:complexType name="SpecificAttrType">
2914                                    <xs:sequence />
2915                                    <xs:anyAttribute namespace="http://example.com/other" />
2916                                </xs:complexType>
2917                            </xs:schema>"#;
2918        let mut parser = SchemaParser::from_reader(xml.as_bytes());
2919        let schema = parser.parse().unwrap();
2920
2921        let complex_type = &schema.complex_types[0];
2922        assert_eq!(
2923            complex_type,
2924            &ComplexType {
2925                name: Some("AnyAttrType".to_string()),
2926                mixed: false,
2927                attributes: vec![],
2928                any_attribute: Some(AnyAttribute {
2929                    namespace: AnyAttributeNamespace::Any,
2930                }),
2931                content: Some(ComplexTypeContent::ComplexContent(ComplexContent {
2932                    derivation: None,
2933                    particle: Some(Particle::Sequence {
2934                        children: vec![],
2935                        occurs: Occurrence {
2936                            min: 1,
2937                            max: Some(1)
2938                        },
2939                    }),
2940                })),
2941            }
2942        );
2943    }
2944
2945    #[test]
2946    fn test_parse_complex_type_with_group_references() {
2947        let xml = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
2948                                targetNamespace="http://example.com"
2949                                xmlns="http://example.com">
2950                                <xs:group name="CommonGroup">
2951                                    <xs:sequence>
2952                                        <xs:element name="a" type="xs:string" />
2953                                        <xs:element name="b" type="xs:string" />
2954                                    </xs:sequence>
2955                                </xs:group>
2956                                <xs:complexType name="UsesGroup">
2957                                    <xs:sequence>
2958                                        <xs:group ref="CommonGroup" />
2959                                    </xs:sequence>
2960                                </xs:complexType>
2961                            </xs:schema>"#;
2962        let mut parser = SchemaParser::from_reader(xml.as_bytes());
2963        let schema = parser.parse().unwrap();
2964
2965        let complex_type = &schema.complex_types[0];
2966        assert_eq!(
2967            complex_type,
2968            &ComplexType {
2969                name: Some("UsesGroup".to_string()),
2970                mixed: false,
2971                attributes: vec![],
2972                any_attribute: None,
2973                content: Some(ComplexTypeContent::ComplexContent(ComplexContent {
2974                    derivation: None,
2975                    particle: Some(Particle::Sequence {
2976                        children: vec![Particle::Group {
2977                            group: GroupParticle::Ref(QName {
2978                                prefix: None,
2979                                local_name: "CommonGroup".to_string()
2980                            }),
2981                            occurs: Occurrence {
2982                                min: 1,
2983                                max: Some(1)
2984                            }
2985                        }],
2986                        occurs: Occurrence {
2987                            min: 1,
2988                            max: Some(1)
2989                        },
2990                    }),
2991                })),
2992            }
2993        );
2994    }
2995
2996    #[test]
2997    fn test_parse_complex_type_with_anonymous_child_element() {
2998        let xml = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
2999                                targetNamespace="http://example.com"
3000                                xmlns="http://example.com">
3001                                <xs:complexType name="AnonymousChildrenType">
3002                                    <xs:sequence>
3003                                        <xs:element name="inlineChild" type="xs:string" />
3004                                    </xs:sequence>
3005                                </xs:complexType>
3006                            </xs:schema>"#;
3007        let mut parser = SchemaParser::from_reader(xml.as_bytes());
3008        let schema = parser.parse().unwrap();
3009
3010        let complex_type = &schema.complex_types[0];
3011        assert_eq!(
3012            complex_type,
3013            &ComplexType {
3014                name: Some("AnonymousChildrenType".to_string()),
3015                mixed: false,
3016                attributes: vec![],
3017                any_attribute: None,
3018                content: Some(ComplexTypeContent::ComplexContent(ComplexContent {
3019                    derivation: None,
3020                    particle: Some(Particle::Sequence {
3021                        children: vec![Particle::Element {
3022                            element: ElementParticle::Decl(ElementDecl {
3023                                name: "inlineChild".to_string(),
3024                                type_name: Some(QName {
3025                                    prefix: Some(NamespacePrefix::from("xs")),
3026                                    local_name: "string".to_string(),
3027                                }),
3028                                inline_type: None,
3029                            }),
3030                            occurs: Occurrence {
3031                                min: 1,
3032                                max: Some(1)
3033                            },
3034                        }],
3035                        occurs: Occurrence {
3036                            min: 1,
3037                            max: Some(1)
3038                        },
3039                    }),
3040                })),
3041            }
3042        );
3043    }
3044
3045    #[test]
3046    fn test_parse_complex_type_with_referenced_children() {
3047        let xml = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
3048                                targetNamespace="http://example.com"
3049                                xmlns="http://example.com">
3050                                <xs:element name="Child" type="xs:string" />
3051                                <xs:complexType name="RefChildrenType">
3052                                    <xs:sequence>
3053                                        <xs:element ref="Child" />
3054                                    </xs:sequence>
3055                                </xs:complexType>
3056                            </xs:schema>"#;
3057        let mut parser = SchemaParser::from_reader(xml.as_bytes());
3058        let schema = parser.parse().unwrap();
3059
3060        let complex_type = &schema.complex_types[0];
3061        assert_eq!(
3062            complex_type,
3063            &ComplexType {
3064                name: Some("RefChildrenType".to_string()),
3065                mixed: false,
3066                attributes: vec![],
3067                any_attribute: None,
3068                content: Some(ComplexTypeContent::ComplexContent(ComplexContent {
3069                    derivation: None,
3070                    particle: Some(Particle::Sequence {
3071                        children: vec![Particle::Element {
3072                            element: ElementParticle::Ref(QName {
3073                                prefix: None,
3074                                local_name: "Child".to_string(),
3075                            }),
3076                            occurs: Occurrence {
3077                                min: 1,
3078                                max: Some(1)
3079                            },
3080                        }],
3081                        occurs: Occurrence {
3082                            min: 1,
3083                            max: Some(1)
3084                        },
3085                    }),
3086                })),
3087            }
3088        );
3089    }
3090
3091    #[test]
3092    fn test_parse_complex_type_with_direct_sequence_and_attribute() {
3093        let xml = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
3094                                targetNamespace="http://example.com"
3095                                xmlns="http://example.com">
3096                                <xs:complexType name="SequenceWithAttr">
3097                                    <xs:sequence>
3098                                        <xs:element ref="child" />
3099                                    </xs:sequence>
3100                                    <xs:attribute ref="lang" />
3101                                </xs:complexType>
3102                            </xs:schema>"#;
3103        let mut parser = SchemaParser::from_reader(xml.as_bytes());
3104        let schema = parser.parse().unwrap();
3105
3106        let complex_type = &schema.complex_types[0];
3107        assert_eq!(
3108            complex_type,
3109            &ComplexType {
3110                name: Some("SequenceWithAttr".to_string()),
3111                mixed: false,
3112                attributes: vec![AttributeUse {
3113                    ref_name: "lang".to_string(),
3114                    required: false,
3115                }],
3116                any_attribute: None,
3117                content: Some(ComplexTypeContent::ComplexContent(ComplexContent {
3118                    derivation: None,
3119                    particle: Some(Particle::Sequence {
3120                        children: vec![Particle::Element {
3121                            element: ElementParticle::Ref(QName {
3122                                prefix: None,
3123                                local_name: "child".to_string(),
3124                            }),
3125                            occurs: Occurrence {
3126                                min: 1,
3127                                max: Some(1)
3128                            },
3129                        }],
3130                        occurs: Occurrence {
3131                            min: 1,
3132                            max: Some(1)
3133                        },
3134                    }),
3135                })),
3136            }
3137        );
3138    }
3139}