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#[derive(Debug, PartialEq, Eq, Default)]
21pub enum FormDefault {
22 Qualified,
25 #[default]
28 Unqualified,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq)]
35pub enum DerivationKind {
36 Extension,
46 Restriction,
56}
57
58#[derive(Debug, Clone, PartialEq, Eq)]
60pub enum CyclesAllowed {
61 Any,
63 Undirected,
65 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#[derive(Debug, PartialEq, Eq)]
99pub struct SchemaImport {
100 pub namespace: String,
102 pub schema_location: Option<String>,
104}
105
106#[derive(Debug, PartialEq, Eq)]
109pub struct SchemaInclude {
110 pub schema_location: String,
112}
113
114#[derive(Debug, PartialEq, Eq)]
116pub struct LinkbaseRef {
117 pub href: String,
121 pub role: Option<String>,
125 pub arcrole: Option<String>,
127 pub link_type: Option<String>,
129}
130
131#[derive(Debug, PartialEq, Eq)]
133pub struct RawRoleType {
134 pub id: String,
136 pub role_uri: RoleUri,
138 pub definition: Option<String>,
140 pub used_on: Vec<QName>,
142}
143
144#[derive(Debug, PartialEq, Eq)]
146pub struct RawArcroleType {
147 pub id: String,
149 pub arcrole_uri: ArcroleUri,
151 pub definition: Option<String>,
153 pub used_on: Vec<QName>,
155 pub cycles_allowed: Option<CyclesAllowed>,
157}
158
159#[derive(Debug, Clone, PartialEq, Eq)]
161pub struct AttributeUse {
162 pub ref_name: String,
164 pub required: bool,
166}
167
168#[derive(Debug, Clone, PartialEq, Eq)]
170pub struct AnyAttribute {
171 pub namespace: AnyAttributeNamespace,
174}
175
176#[derive(Debug, Clone, PartialEq, Eq)]
178pub enum AnyAttributeNamespace {
179 Any,
181 Other,
183 TargetNamespace,
185 List(Vec<String>),
188}
189
190#[derive(Debug, Clone, PartialEq, Eq)]
193pub struct Occurrence {
194 pub min: u32,
196 pub max: Option<u32>,
199}
200
201#[derive(Debug, Clone, PartialEq, Eq)]
204pub enum Derivation {
205 Extension(QName),
207 Restriction(QName),
209}
210
211#[derive(Debug, Clone, PartialEq, Eq)]
214pub struct ElementDecl {
215 pub name: String,
217 pub type_name: Option<QName>,
219 pub inline_type: Option<Box<ComplexType>>,
221}
222
223#[derive(Debug, Clone, PartialEq, Eq)]
225pub enum ElementParticle {
226 Ref(QName),
228 Decl(ElementDecl),
232}
233
234impl ElementParticle {
235 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#[derive(Debug, Clone, PartialEq, Eq)]
247pub struct GroupDef {
248 pub name: Option<QName>,
251 pub particle: Box<Particle>,
253}
254
255#[derive(Debug, Clone, PartialEq, Eq)]
257pub enum GroupParticle {
258 Ref(QName),
260 Def(GroupDef),
262}
263
264#[derive(Debug, Clone, PartialEq, Eq)]
269pub enum Particle {
270 Element {
273 element: ElementParticle,
274 occurs: Occurrence,
275 },
276 Sequence {
278 children: Vec<Particle>,
279 occurs: Occurrence,
280 },
281 Choice {
283 children: Vec<Particle>,
284 occurs: Occurrence,
285 },
286 Group {
289 group: GroupParticle,
290 occurs: Occurrence,
291 },
292}
293
294impl Particle {
295 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 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#[derive(Debug, Clone, PartialEq, Eq)]
330pub struct SimpleContent {
331 pub base: QName,
334 pub derivation: DerivationKind,
337}
338#[derive(Debug, Clone, PartialEq, Eq)]
341pub struct ComplexContent {
342 pub derivation: Option<Derivation>,
345 pub particle: Option<Particle>,
349}
350
351#[derive(Debug, PartialEq, Eq)]
353pub struct SimpleType {
354 pub name: Option<String>,
356 pub base: Option<QName>,
359 pub enumerations: Vec<String>,
364}
365
366#[derive(Debug, Clone, PartialEq, Eq)]
369pub enum ComplexTypeContent {
370 SimpleContent(SimpleContent),
373 ComplexContent(ComplexContent),
380 Empty,
383}
384
385#[derive(Debug, Clone, PartialEq, Eq)]
387pub struct ComplexType {
388 pub name: Option<String>,
390 pub mixed: bool,
393 pub attributes: Vec<AttributeUse>,
395 pub any_attribute: Option<AnyAttribute>,
397 pub content: Option<ComplexTypeContent>,
399}
400
401impl ComplexType {
402 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 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#[derive(Debug, PartialEq, Eq)]
427pub struct Element {
428 pub name: String,
430 pub id: Option<String>,
433 pub type_name: Option<QName>,
435 pub substitution_group: Option<QName>,
437 pub is_nillable: bool,
439 pub is_abstract: bool,
441 pub period_type: Option<PeriodType>,
443 pub balance: Option<Balance>,
445 pub complex_type: Option<ComplexType>,
447}
448
449#[derive(Debug, PartialEq, Eq)]
452pub struct RawSchema {
453 pub target_namespace: Option<String>,
455 pub namespaces: HashMap<NamespacePrefix, NamespaceUri>,
457 pub element_form_default: FormDefault,
459 pub attribute_form_default: FormDefault,
461 pub imports: Vec<SchemaImport>,
463 pub includes: Vec<SchemaInclude>,
465 pub linkbase_refs: Vec<LinkbaseRef>,
467 pub role_types: Vec<RawRoleType>,
469 pub arcrole_types: Vec<RawArcroleType>,
471 pub elements: Vec<Element>,
473 pub simple_types: Vec<SimpleType>,
475 pub complex_types: Vec<ComplexType>,
477}
478
479pub struct SchemaParser<R> {
481 path: Option<PathBuf>,
484 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 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 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 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 }
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 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 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 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 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 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 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 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 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 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 depth += 1;
1018 }
1019 Event::End(_) => {
1020 if depth == 0 {
1021 break;
1023 } else {
1024 depth -= 1;
1025 }
1026 }
1027 Event::Empty(_) => {
1028 }
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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 let name = decl_name.unwrap_or_default();
1550 let mut inline_type: Option<Box<ComplexType>> = None;
1551
1552 if !is_empty {
1553 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 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 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 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 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 #[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 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 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]
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 #[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}