1use std::{error::Error, fmt, str::FromStr};
36
37use crate::{
38 schema::DEFINITIONS, AnyUri, Attribute, Base, Base64Binary, Concept, DataTemplate, DataType,
39 DataTypeConstraint, DataTypeName, DataValue, DateTime, Decimal, Dimension, Element,
40 GroupOfProperties, Guid, Language, MultiLanguageText, Node, NonNegativeInteger, ObjectType,
41 Property, QuantityKind, Rational, Reference, ReferenceDocument, Scale, Subject, Unit,
42 ValueError, ValueList, NAMESPACE,
43};
44
45const PREFIX: &str = "dt";
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
49#[non_exhaustive]
50pub enum CodecErrorKind {
51 MissingAttribute,
53 MissingChild,
55 TooManyChildren,
57 UnknownChild,
59 UnknownAttribute,
62 InvalidValue,
64 UnexpectedContent,
66}
67
68#[derive(Debug, Clone, PartialEq, Eq)]
70pub struct CodecError {
71 kind: CodecErrorKind,
72 path: String,
73 detail: String,
74}
75
76impl CodecError {
77 fn new(kind: CodecErrorKind, path: impl Into<String>, detail: impl Into<String>) -> Self {
78 Self {
79 kind,
80 path: path.into(),
81 detail: detail.into(),
82 }
83 }
84
85 #[must_use]
87 pub const fn kind(&self) -> CodecErrorKind {
88 self.kind
89 }
90
91 #[must_use]
94 pub fn path(&self) -> &str {
95 &self.path
96 }
97
98 #[must_use]
100 pub fn detail(&self) -> &str {
101 &self.detail
102 }
103
104 fn nested(mut self, parent: &str) -> Self {
105 self.path = if self.path.is_empty() {
106 parent.to_owned()
107 } else {
108 format!("{parent}/{}", self.path)
109 };
110 self
111 }
112}
113
114impl fmt::Display for CodecError {
115 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
116 write!(
117 formatter,
118 "{:?} at `{}`: {}",
119 self.kind, self.path, self.detail
120 )
121 }
122}
123
124impl Error for CodecError {}
125
126fn declared_children(handle: &str) -> &'static [crate::schema::ChildRule] {
132 DEFINITIONS
133 .iter()
134 .find(|definition| definition.handle == handle)
135 .map_or(&[], |definition| definition.children)
136}
137
138struct ChildWriter {
140 handle: &'static str,
141 children: Vec<(&'static str, Element)>,
142}
143
144impl ChildWriter {
145 const fn new(handle: &'static str) -> Self {
146 Self {
147 handle,
148 children: Vec::new(),
149 }
150 }
151
152 fn push(&mut self, name: &'static str, element: Element) {
153 self.children.push((name, element));
154 }
155
156 fn texts(&mut self, name: &'static str, values: &[MultiLanguageText]) {
157 for value in values {
158 self.push(name, multilingual(name, value));
159 }
160 }
161
162 fn references(&mut self, name: &'static str, values: &[Reference]) {
163 for value in values {
164 self.push(name, reference(name, value));
165 }
166 }
167
168 fn simple<'a>(&mut self, name: &'static str, values: impl IntoIterator<Item = &'a str>) {
169 for value in values {
170 self.push(name, dt_element(name).with_text(value));
171 }
172 }
173
174 fn finish(self, mut element: Element) -> Element {
179 let order = declared_children(self.handle);
180 debug_assert!(
181 self.children
182 .iter()
183 .all(|(name, _)| order.iter().any(|rule| rule.name == *name)),
184 "codec wrote a child not declared on {}",
185 self.handle
186 );
187 let mut children = self.children;
188 for rule in order {
189 let (matching, rest): (Vec<_>, Vec<_>) = children
190 .into_iter()
191 .partition(|(name, _)| *name == rule.name);
192 children = rest;
193 for (_, child) in matching {
194 element = element.with_child(child);
195 }
196 }
197 element
198 }
199}
200
201fn dt_element(local_name: &str) -> Element {
202 Element::new(Some(NAMESPACE), Some(PREFIX), local_name)
203}
204
205fn dt_attribute(local_name: &str, value: &str) -> Attribute {
206 Attribute::new(Some(PREFIX), local_name, Some(NAMESPACE), value)
207}
208
209fn plain_attribute(local_name: &str, value: &str) -> Attribute {
210 Attribute::new(None, local_name, None, value)
211}
212
213fn multilingual(name: &str, value: &MultiLanguageText) -> Element {
214 dt_element(name)
215 .with_attribute(plain_attribute("language", value.language()))
216 .with_text(value.text())
217}
218
219fn reference(name: &str, value: &Reference) -> Element {
220 let mut element = dt_element(name);
221 if let Some(uri) = value.uri() {
222 element = element.with_attribute(dt_attribute("referenceURI", uri));
223 }
224 if let Some(guid) = value.guid() {
225 element = element.with_attribute(dt_attribute("GUID", guid.as_str()));
226 }
227 element
228}
229
230fn concept_attributes(element: Element, concept: &Concept) -> Element {
235 let mut element = element;
236 if let Some(about) = concept.about_value() {
237 element = element.with_attribute(dt_attribute("about", about.as_str()));
238 }
239 element
240 .with_attribute(dt_attribute("GUID", concept.guid().as_str()))
241 .with_attribute(plain_attribute(
242 "dateOfCreation",
243 concept.date_of_creation_value().as_str(),
244 ))
245}
246
247fn write_concept(children: &mut ChildWriter, concept: &Concept) {
248 children.texts("Name", concept.names());
249 children.texts("Definition", concept.definitions());
250 children.references("ReferenceDocumentRef", concept.reference_document_refs());
251 children.texts("Description", concept.descriptions());
252 children.texts("Example", concept.examples());
253 children.references("SimilarToRef", concept.similar_to_refs());
254 children.simple(
255 "LanguageOfCreator",
256 concept.languages_of_creator().iter().map(Language::as_str),
257 );
258 children.simple(
259 "CountryOfOrigin",
260 concept.countries_of_origin().iter().map(String::as_str),
261 );
262 children.simple(
263 "VisualRepresentation",
264 concept
265 .visual_representations()
266 .iter()
267 .map(Base64Binary::as_str),
268 );
269 children.simple(
270 "MajorVersion",
271 concept
272 .major_versions()
273 .iter()
274 .map(NonNegativeInteger::as_str),
275 );
276 children.simple(
277 "MinorVersion",
278 concept
279 .minor_versions()
280 .iter()
281 .map(NonNegativeInteger::as_str),
282 );
283 children.simple("Status", concept.statuses().iter().map(String::as_str));
284 children.references("ReplacedObjectsRef", concept.replaced_objects_refs());
285 children.simple(
286 "DeprecationExplanation",
287 concept
288 .deprecation_explanations()
289 .iter()
290 .map(String::as_str),
291 );
292 children.references("DictionaryRef", concept.dictionary_refs());
293}
294
295fn write_subject(children: &mut ChildWriter, subject: &Subject) {
296 write_concept(children, subject.concept());
297 children.references("HasPartRef", subject.has_part_refs());
298 children.references("IsSubtypeOfRef", subject.is_subtype_of_refs());
299}
300
301fn encode(
302 local_name: &'static str,
303 handle: &'static str,
304 concept: &Concept,
305 fill: impl FnOnce(&mut ChildWriter),
306) -> Element {
307 let mut children = ChildWriter::new(handle);
308 fill(&mut children);
309 children.finish(concept_attributes(dt_element(local_name), concept))
310}
311
312fn data_type_element(value: &DataType) -> Element {
313 let mut element = dt_element("DataType");
314 if let Some(name) = value.name() {
315 element = element.with_attribute(plain_attribute("name", name.as_str()));
316 }
317 let mut children = ChildWriter::new("DataTypeType");
318 for constraint in value.constraints() {
319 let (name, bound) = match constraint {
320 DataTypeConstraint::MinInclusive(bound) => ("MinInclusive", bound),
321 DataTypeConstraint::MinExclusive(bound) => ("MinExclusive", bound),
322 DataTypeConstraint::MaxInclusive(bound) => ("MaxInclusive", bound),
323 DataTypeConstraint::MaxExclusive(bound) => ("MaxExclusive", bound),
324 };
325 children.push(
326 name,
327 dt_element(name).with_attribute(plain_attribute("value", bound)),
328 );
329 }
330 for format in value.data_formats() {
331 children.push(
332 "DataFormat",
333 dt_element("DataFormat").with_attribute(plain_attribute("value", format)),
334 );
335 }
336 for list in value.possible_values() {
337 let mut values = dt_element("ValueList")
338 .with_attribute(plain_attribute("language", list.language().as_str()));
339 for item in list.values() {
340 let mut entry = dt_element("Value");
341 if let Some(order) = item.order() {
342 entry = entry.with_attribute(plain_attribute("order", &order.to_string()));
343 }
344 values = values.with_child(entry.with_text(item.value()));
345 }
346 children.push(
347 "PossibleValues",
348 dt_element("PossibleValues").with_child(values),
349 );
350 }
351 children.finish(element)
352}
353
354impl Concept {
355 #[must_use]
358 pub fn to_element_named(&self, local_name: &'static str) -> Element {
359 encode(local_name, "ConceptType", self, |children| {
360 write_concept(children, self);
361 })
362 }
363}
364
365impl Subject {
366 #[must_use]
368 pub fn to_element(&self) -> Element {
369 encode("Subject", "SubjectType", self.concept(), |children| {
370 write_subject(children, self);
371 })
372 }
373}
374
375impl ObjectType {
376 #[must_use]
378 pub fn to_element(&self) -> Element {
379 let subject = self.subject();
380 encode(
381 "ObjectType",
382 "ObjectTypeType",
383 subject.concept(),
384 |children| {
385 write_subject(children, subject);
386 },
387 )
388 }
389}
390
391impl GroupOfProperties {
392 #[must_use]
394 pub fn to_element(&self) -> Element {
395 let subject = self.subject();
396 encode(
397 "GroupOfProperties",
398 "GroupOfPropertiesType",
399 subject.concept(),
400 |children| {
401 write_subject(children, subject);
402 children.references("HasPropertyRef", self.property_refs());
403 },
404 )
405 }
406}
407
408impl DataTemplate {
409 #[must_use]
411 pub fn to_element(&self) -> Element {
412 let subject = self.subject();
413 encode(
414 "DataTemplate",
415 "DataTemplateType",
416 subject.concept(),
417 |children| {
418 write_subject(children, subject);
419 children.references("HasObjectTypeRef", self.object_type_refs());
420 children.references("HasPropertyRef", self.property_refs());
421 children.references("HasGroupOfPropertiesRef", self.group_of_properties_refs());
422 },
423 )
424 }
425}
426
427impl Property {
428 #[must_use]
430 pub fn to_element(&self) -> Element {
431 encode("Property", "PropertyType", self.concept(), |children| {
432 write_concept(children, self.concept());
433 children.push("DataType", data_type_element(self.data_type()));
434 children.simple("Symbol", self.symbols().iter().map(String::as_str));
435 children.references("DimensionRef", self.dimension_refs());
436 children.references("UnitRef", self.unit_refs());
437 children.references("QuantityKindRef", self.quantity_kind_refs());
438 children.references("IsDependentOnRef", self.dependency_refs());
439 children.references(
440 "IsSpecializationOfRef",
441 self.specialization_ref().map_or(&[], std::slice::from_ref),
442 );
443 })
444 }
445}
446
447impl QuantityKind {
448 #[must_use]
450 pub fn to_element(&self) -> Element {
451 encode(
452 "QuantityKind",
453 "QuantityKindType",
454 self.concept(),
455 |children| {
456 write_concept(children, self.concept());
457 children.references("DimensionRef", std::slice::from_ref(self.dimension_ref()));
458 },
459 )
460 }
461}
462
463pub(crate) const DIMENSION_EXPONENTS: [&str; 7] = [
465 "DimensionExponentForAmountOfSubstance",
466 "DimensionExponentForElectricCurrent",
467 "DimensionExponentForLength",
468 "DimensionExponentForLuminousIntensity",
469 "DimensionExponentForMass",
470 "DimensionExponentForThermodynamicTemperature",
471 "DimensionExponentForTime",
472];
473
474impl Dimension {
475 #[must_use]
477 pub fn to_element(&self) -> Element {
478 encode("Dimension", "DimensionType", self.concept(), |children| {
479 write_concept(children, self.concept());
480 for (name, exponent) in DIMENSION_EXPONENTS.into_iter().zip(self.exponents()) {
481 children.simple(name, [exponent.as_str()]);
482 }
483 })
484 }
485}
486
487impl Unit {
488 #[must_use]
490 pub fn to_element(&self) -> Element {
491 encode("Unit", "UnitType", self.concept(), |children| {
492 write_concept(children, self.concept());
493 children.texts("Symbol", self.symbols());
494 children.references("DimensionRef", std::slice::from_ref(self.dimension_ref()));
495 children.simple("Scale", [self.scale().as_str()]);
496 children.simple("Base", [self.base().as_str()]);
497 children.simple("Coefficient", [self.coefficient().as_str()]);
498 children.simple("Offset", [self.offset().as_str()]);
499 })
500 }
501}
502
503impl ReferenceDocument {
504 #[must_use]
506 pub fn to_element(&self) -> Element {
507 encode(
508 "ReferenceDocument",
509 "ReferenceDocumentType",
510 self.concept(),
511 |children| {
512 write_concept(children, self.concept());
513 children.simple(
514 "DateOfPublication",
515 self.date_of_publication().map(DateTime::as_str),
516 );
517 children.simple("Author", self.author());
518 children.simple("ISBN", self.isbn());
519 children.simple("Language", self.languages().iter().map(Language::as_str));
520 children.simple("Publisher", self.publisher());
521 children.simple("URI", self.uri());
522 },
523 )
524 }
525}
526
527struct Reader<'a> {
534 element: &'a Element,
535 consumed: Vec<bool>,
536}
537
538impl<'a> Reader<'a> {
539 fn new(element: &'a Element) -> Result<Self, CodecError> {
540 for node in element.nodes() {
541 if let Node::Text(text) | Node::CData(text) = node {
542 if !text.chars().all(|c| matches!(c, ' ' | '\t' | '\r' | '\n')) {
543 return Err(CodecError::new(
544 CodecErrorKind::UnexpectedContent,
545 "",
546 "character data in element-only content",
547 ));
548 }
549 }
550 }
551 let count = element.children().count();
552 Ok(Self {
553 element,
554 consumed: vec![false; count],
555 })
556 }
557
558 fn take(&mut self, name: &str) -> Vec<(String, &'a Element)> {
560 let mut index = 0;
561 let mut found = Vec::new();
562 for (position, child) in self.element.children().enumerate() {
563 if child.namespace_uri() == Some(NAMESPACE) && child.local_name() == name {
564 index += 1;
565 self.consumed[position] = true;
566 found.push((format!("{name}[{index}]"), child));
567 }
568 }
569 found
570 }
571
572 fn texts(&mut self, name: &str) -> Result<Vec<MultiLanguageText>, CodecError> {
573 self.take(name)
574 .into_iter()
575 .map(|(path, child)| read_multilingual(child).map_err(|error| error.nested(&path)))
576 .collect()
577 }
578
579 fn references(&mut self, name: &str) -> Result<Vec<Reference>, CodecError> {
580 self.take(name)
581 .into_iter()
582 .map(|(path, child)| read_reference(child).map_err(|error| error.nested(&path)))
583 .collect()
584 }
585
586 fn simple<T>(
587 &mut self,
588 name: &str,
589 parse: impl Fn(&str) -> Result<T, ValueError>,
590 ) -> Result<Vec<T>, CodecError> {
591 self.take(name)
592 .into_iter()
593 .map(|(path, child)| {
594 refuse_unrepresented_attributes(child, &[]).map_err(|error| error.nested(&path))?;
595 let text = simple_text(child).map_err(|error| error.nested(&path))?;
596 parse(&text).map_err(|error| invalid(&path, &error))
597 })
598 .collect()
599 }
600
601 fn strings(&mut self, name: &str) -> Result<Vec<String>, CodecError> {
602 self.simple(name, |text| Ok(text.to_owned()))
603 }
604
605 fn at_most_one<T>(name: &str, mut values: Vec<T>) -> Result<Option<T>, CodecError> {
606 if values.len() > 1 {
607 return Err(CodecError::new(
608 CodecErrorKind::TooManyChildren,
609 format!("{name}[2]"),
610 format!(
611 "`{name}` occurs {} times; at most one is declared",
612 values.len()
613 ),
614 ));
615 }
616 Ok(values.pop())
617 }
618
619 fn exactly_one<T>(name: &str, values: Vec<T>) -> Result<T, CodecError> {
620 Self::at_most_one(name, values)?.ok_or_else(|| {
621 CodecError::new(
622 CodecErrorKind::MissingChild,
623 "",
624 format!("required child `{name}` is missing"),
625 )
626 })
627 }
628
629 fn finish(self) -> Result<(), CodecError> {
631 let unknown = self
632 .element
633 .children()
634 .zip(&self.consumed)
635 .find(|(_, consumed)| !**consumed);
636 match unknown {
637 None => Ok(()),
638 Some((child, _)) => Err(CodecError::new(
639 CodecErrorKind::UnknownChild,
640 child.qname(),
641 format!(
642 "`{}` ({}) is not represented by the owned model",
643 child.local_name(),
644 child.namespace_uri().unwrap_or("no namespace")
645 ),
646 )),
647 }
648 }
649}
650
651type AttributeName = (Option<&'static str>, &'static str);
653
654const CONCEPT_ATTRIBUTES: &[AttributeName] = &[
658 (Some(NAMESPACE), "about"),
659 (Some(NAMESPACE), "GUID"),
660 (None, "dateOfCreation"),
661];
662const REFERENCE_ATTRIBUTES: &[AttributeName] =
663 &[(Some(NAMESPACE), "GUID"), (Some(NAMESPACE), "referenceURI")];
664const LANGUAGE_ATTRIBUTE: &[AttributeName] = &[(None, "language")];
665const VALUE_ATTRIBUTE: &[AttributeName] = &[(None, "value")];
666
667fn refuse_unrepresented_attributes(
670 element: &Element,
671 allowed: &[AttributeName],
672) -> Result<(), CodecError> {
673 for attribute in element.attributes() {
674 if attribute.qname() == "xmlns" || attribute.prefix() == Some("xmlns") {
675 continue;
676 }
677 let namespace = attribute.namespace_uri();
678 let local_name = attribute.local_name();
679 if !allowed
680 .iter()
681 .any(|(ns, name)| *ns == namespace && *name == local_name)
682 {
683 return Err(CodecError::new(
684 CodecErrorKind::UnknownAttribute,
685 format!("@{}", attribute.qname()),
686 format!(
687 "attribute `{local_name}` ({}) is not represented by the owned model",
688 namespace.unwrap_or("no namespace")
689 ),
690 ));
691 }
692 }
693 Ok(())
694}
695
696fn empty_content(element: &Element) -> Result<(), CodecError> {
698 let text = simple_text(element)?;
699 if text.chars().all(|c| matches!(c, ' ' | '\t' | '\r' | '\n')) {
700 Ok(())
701 } else {
702 Err(CodecError::new(
703 CodecErrorKind::UnexpectedContent,
704 "",
705 "character data where empty content is declared",
706 ))
707 }
708}
709
710fn invalid(path: &str, error: &ValueError) -> CodecError {
711 CodecError::new(CodecErrorKind::InvalidValue, path, error.to_string())
712}
713
714fn simple_text(element: &Element) -> Result<String, CodecError> {
716 if element.children().next().is_some() {
717 return Err(CodecError::new(
718 CodecErrorKind::UnexpectedContent,
719 "",
720 "element content where simple content is declared",
721 ));
722 }
723 Ok(element.direct_text())
724}
725
726fn read_multilingual(element: &Element) -> Result<MultiLanguageText, CodecError> {
727 refuse_unrepresented_attributes(element, LANGUAGE_ATTRIBUTE)?;
728 let language = element.attribute_ns(None, "language").ok_or_else(|| {
729 CodecError::new(
730 CodecErrorKind::MissingAttribute,
731 "@language",
732 "required attribute `language` is missing",
733 )
734 })?;
735 let text = simple_text(element)?;
736 MultiLanguageText::new(language, text).map_err(|error| invalid("@language", &error))
737}
738
739fn read_reference(element: &Element) -> Result<Reference, CodecError> {
740 refuse_unrepresented_attributes(element, REFERENCE_ATTRIBUTES)?;
741 empty_content(element)?;
742 let guid = element
743 .attribute_ns(Some(NAMESPACE), "GUID")
744 .map(Guid::from_str)
745 .transpose()
746 .map_err(|error| invalid("@GUID", &error))?;
747 let uri = element
748 .attribute_ns(Some(NAMESPACE), "referenceURI")
749 .map(AnyUri::from_str)
750 .transpose()
751 .map_err(|error| invalid("@referenceURI", &error))?;
752 Ok(Reference::new(guid, uri))
753}
754
755fn required_attribute<'a>(
756 element: &'a Element,
757 namespace: Option<&str>,
758 name: &str,
759) -> Result<&'a str, CodecError> {
760 element.attribute_ns(namespace, name).ok_or_else(|| {
761 CodecError::new(
762 CodecErrorKind::MissingAttribute,
763 format!("@{name}"),
764 format!("required attribute `{name}` is missing"),
765 )
766 })
767}
768
769fn read_concept(element: &Element, children: &mut Reader<'_>) -> Result<Concept, CodecError> {
770 refuse_unrepresented_attributes(element, CONCEPT_ATTRIBUTES)?;
771 let guid = Guid::from_str(required_attribute(element, Some(NAMESPACE), "GUID")?)
772 .map_err(|error| invalid("@GUID", &error))?;
773 let date = DateTime::from_str(required_attribute(element, None, "dateOfCreation")?)
774 .map_err(|error| invalid("@dateOfCreation", &error))?;
775 let mut concept = Concept::from_identity(guid, date);
776 if let Some(about) = element.attribute_ns(Some(NAMESPACE), "about") {
777 concept.set_about(Some(
778 AnyUri::from_str(about).map_err(|error| invalid("@about", &error))?,
779 ));
780 }
781 for value in children.texts("Name")? {
782 concept.add_name(value);
783 }
784 for value in children.texts("Definition")? {
785 concept.add_definition(value);
786 }
787 for value in children.references("ReferenceDocumentRef")? {
788 concept.add_reference_document_ref(value);
789 }
790 for value in children.texts("Description")? {
791 concept.add_description(value);
792 }
793 for value in children.texts("Example")? {
794 concept.add_example(value);
795 }
796 for value in children.references("SimilarToRef")? {
797 concept.add_similar_to_ref(value);
798 }
799 for value in children.simple("LanguageOfCreator", Language::from_str)? {
800 concept.add_language_of_creator(value);
801 }
802 for value in children.strings("CountryOfOrigin")? {
803 concept.add_country_of_origin(value);
804 }
805 for value in children.simple("VisualRepresentation", Base64Binary::from_str)? {
806 concept.add_visual_representation(value);
807 }
808 for value in children.simple("MajorVersion", NonNegativeInteger::from_str)? {
809 concept.add_major_version(value);
810 }
811 for value in children.simple("MinorVersion", NonNegativeInteger::from_str)? {
812 concept.add_minor_version(value);
813 }
814 for value in children.strings("Status")? {
815 concept.add_status(value);
816 }
817 for value in children.references("ReplacedObjectsRef")? {
818 concept.add_replaced_objects_ref(value);
819 }
820 for value in children.strings("DeprecationExplanation")? {
821 concept.add_deprecation_explanation(value);
822 }
823 for value in children.references("DictionaryRef")? {
824 concept.add_dictionary_ref(value);
825 }
826 Ok(concept)
827}
828
829fn read_subject(element: &Element, children: &mut Reader<'_>) -> Result<Subject, CodecError> {
830 let mut subject = Subject::new(read_concept(element, children)?);
831 for value in children.references("HasPartRef")? {
832 subject.add_has_part_ref(value);
833 }
834 for value in children.references("IsSubtypeOfRef")? {
835 subject.add_is_subtype_of_ref(value);
836 }
837 Ok(subject)
838}
839
840fn read_data_type(element: &Element) -> Result<DataType, CodecError> {
841 refuse_unrepresented_attributes(element, &[(None, "name")])?;
842 let mut children = Reader::new(element)?;
843 let mut value = DataType::new(element.attribute_ns(None, "name").map(DataTypeName::from));
844 for (position, child) in element.children().enumerate() {
847 if child.namespace_uri() != Some(NAMESPACE) {
848 continue;
849 }
850 let make: fn(String) -> DataTypeConstraint = match child.local_name() {
851 "MinInclusive" => DataTypeConstraint::MinInclusive,
852 "MinExclusive" => DataTypeConstraint::MinExclusive,
853 "MaxInclusive" => DataTypeConstraint::MaxInclusive,
854 "MaxExclusive" => DataTypeConstraint::MaxExclusive,
855 _ => continue,
856 };
857 children.consumed[position] = true;
858 let path = format!("{}[{}]", child.local_name(), position + 1);
859 refuse_unrepresented_attributes(child, VALUE_ATTRIBUTE)
860 .map_err(|error| error.nested(&path))?;
861 let bound = child.attribute_ns(None, "value").ok_or_else(|| {
862 CodecError::new(
863 CodecErrorKind::MissingAttribute,
864 format!("{path}/@value"),
865 "a bound without `value` cannot be represented",
866 )
867 })?;
868 simple_text(child).map_err(|error| error.nested(&path))?;
869 empty_content(child).map_err(|error| error.nested(&path))?;
870 value.add_constraint(make(bound.to_owned()));
871 }
872 for (path, child) in children.take("DataFormat") {
873 refuse_unrepresented_attributes(child, VALUE_ATTRIBUTE)
874 .map_err(|error| error.nested(&path))?;
875 empty_content(child).map_err(|error| error.nested(&path))?;
876 let format = child.attribute_ns(None, "value").ok_or_else(|| {
877 CodecError::new(
878 CodecErrorKind::MissingAttribute,
879 format!("{path}/@value"),
880 "a data format without `value` cannot be represented",
881 )
882 })?;
883 value.add_data_format(format);
884 }
885 for (path, possible) in children.take("PossibleValues") {
886 refuse_unrepresented_attributes(possible, &[]).map_err(|error| error.nested(&path))?;
887 let mut lists = Reader::new(possible).map_err(|error| error.nested(&path))?;
888 for (list_path, list) in lists.take("ValueList") {
889 let nested = format!("{path}/{list_path}");
890 value
891 .add_possible_values(read_value_list(list).map_err(|error| error.nested(&nested))?);
892 }
893 lists.finish().map_err(|error| error.nested(&path))?;
894 }
895 children.finish()?;
896 Ok(value)
897}
898
899fn read_value_list(element: &Element) -> Result<ValueList, CodecError> {
900 refuse_unrepresented_attributes(element, LANGUAGE_ATTRIBUTE)?;
901 let language = Language::from_str(required_attribute(element, None, "language")?)
902 .map_err(|error| invalid("@language", &error))?;
903 let mut children = Reader::new(element)?;
904 let mut values = Vec::new();
905 for (path, child) in children.take("Value") {
906 refuse_unrepresented_attributes(child, &[(None, "order")])
907 .map_err(|error| error.nested(&path))?;
908 let order = child
909 .attribute_ns(None, "order")
910 .map(|order| {
911 order.trim().parse::<i32>().map_err(|_| {
912 CodecError::new(
913 CodecErrorKind::InvalidValue,
914 format!("{path}/@order"),
915 format!("`{order}` is not an xs:int"),
916 )
917 })
918 })
919 .transpose()?;
920 let text = simple_text(child).map_err(|error| error.nested(&path))?;
921 values.push(DataValue::new(text, order));
922 }
923 children.finish()?;
924 let mut values = values.into_iter();
925 let first = values.next().ok_or_else(|| {
926 CodecError::new(
927 CodecErrorKind::MissingChild,
928 "",
929 "a value list requires at least one `Value`",
930 )
931 })?;
932 let mut list = ValueList::new(language, first);
933 for value in values {
934 list.add_value(value);
935 }
936 Ok(list)
937}
938
939fn decode<T>(
945 element: &Element,
946 read: impl FnOnce(&mut Reader<'_>) -> Result<T, CodecError>,
947) -> Result<T, CodecError> {
948 if !matches!(element.namespace_uri(), None | Some(NAMESPACE)) {
949 return Err(CodecError::new(
950 CodecErrorKind::UnexpectedContent,
951 "",
952 format!(
953 "`{}` is in namespace {}, not ISO 23387",
954 element.local_name(),
955 element.namespace_uri().unwrap_or_default()
956 ),
957 ));
958 }
959 let mut children = Reader::new(element)?;
960 let value = read(&mut children)?;
961 children.finish()?;
962 Ok(value)
963}
964
965impl Concept {
966 pub fn from_element(element: &Element) -> Result<Self, CodecError> {
968 decode(element, |children| read_concept(element, children))
969 }
970}
971
972impl Subject {
973 pub fn from_element(element: &Element) -> Result<Self, CodecError> {
975 decode(element, |children| read_subject(element, children))
976 }
977}
978
979impl ObjectType {
980 pub fn from_element(element: &Element) -> Result<Self, CodecError> {
982 decode(element, |children| {
983 read_subject(element, children).map(Self::new)
984 })
985 }
986}
987
988impl GroupOfProperties {
989 pub fn from_element(element: &Element) -> Result<Self, CodecError> {
991 decode(element, |children| {
992 let subject = read_subject(element, children)?;
993 let mut refs = children.references("HasPropertyRef")?.into_iter();
994 let first = refs.next().ok_or_else(|| {
997 CodecError::new(
998 CodecErrorKind::MissingChild,
999 "",
1000 "required child `HasPropertyRef` is missing",
1001 )
1002 })?;
1003 let mut group = Self::new(subject, first);
1004 for value in refs {
1005 group.add_property_ref(value);
1006 }
1007 Ok(group)
1008 })
1009 }
1010}
1011
1012impl DataTemplate {
1013 pub fn from_element(element: &Element) -> Result<Self, CodecError> {
1015 decode(element, |children| {
1016 let mut template = Self::new(read_subject(element, children)?);
1017 for value in children.references("HasObjectTypeRef")? {
1018 template.add_object_type_ref(value);
1019 }
1020 for value in children.references("HasPropertyRef")? {
1021 template.add_property_ref(value);
1022 }
1023 for value in children.references("HasGroupOfPropertiesRef")? {
1024 template.add_group_of_properties_ref(value);
1025 }
1026 Ok(template)
1027 })
1028 }
1029}
1030
1031impl Property {
1032 pub fn from_element(element: &Element) -> Result<Self, CodecError> {
1034 decode(element, |children| {
1035 let concept = read_concept(element, children)?;
1036 let data_type = Reader::exactly_one("DataType", children.take("DataType"))?;
1037 let data_type =
1038 read_data_type(data_type.1).map_err(|error| error.nested(&data_type.0))?;
1039 let mut property = Self::new(concept, data_type);
1040 for value in children.strings("Symbol")? {
1041 property.add_symbol(value);
1042 }
1043 for value in children.references("DimensionRef")? {
1044 property.add_dimension_ref(value);
1045 }
1046 for value in children.references("UnitRef")? {
1047 property.add_unit_ref(value);
1048 }
1049 for value in children.references("QuantityKindRef")? {
1050 property.add_quantity_kind_ref(value);
1051 }
1052 for value in children.references("IsDependentOnRef")? {
1053 property.add_dependency_ref(value);
1054 }
1055 property.set_specialization_ref(Reader::at_most_one(
1056 "IsSpecializationOfRef",
1057 children.references("IsSpecializationOfRef")?,
1058 )?);
1059 Ok(property)
1060 })
1061 }
1062}
1063
1064impl QuantityKind {
1065 pub fn from_element(element: &Element) -> Result<Self, CodecError> {
1067 decode(element, |children| {
1068 let concept = read_concept(element, children)?;
1069 let dimension =
1070 Reader::exactly_one("DimensionRef", children.references("DimensionRef")?)?;
1071 Ok(Self::new(concept, dimension))
1072 })
1073 }
1074}
1075
1076impl Dimension {
1077 pub fn from_element(element: &Element) -> Result<Self, CodecError> {
1079 decode(element, |children| {
1080 let concept = read_concept(element, children)?;
1081 let mut exponents = Vec::with_capacity(DIMENSION_EXPONENTS.len());
1082 for name in DIMENSION_EXPONENTS {
1083 exponents.push(Reader::exactly_one(
1084 name,
1085 children.simple(name, Decimal::from_str)?,
1086 )?);
1087 }
1088 let exponents: [Decimal; 7] = exponents
1089 .try_into()
1090 .unwrap_or_else(|_| unreachable!("seven names yield seven exponents"));
1091 Ok(Self::new(concept, exponents))
1092 })
1093 }
1094}
1095
1096impl Unit {
1097 pub fn from_element(element: &Element) -> Result<Self, CodecError> {
1099 decode(element, |children| {
1100 let concept = read_concept(element, children)?;
1101 let symbols = children.texts("Symbol")?;
1102 let dimension =
1103 Reader::exactly_one("DimensionRef", children.references("DimensionRef")?)?;
1104 let scale = Reader::exactly_one("Scale", children.strings("Scale")?)?;
1105 let base = Reader::exactly_one("Base", children.strings("Base")?)?;
1106 let coefficient = Reader::exactly_one(
1107 "Coefficient",
1108 children.simple("Coefficient", Rational::from_str)?,
1109 )?;
1110 let offset =
1111 Reader::exactly_one("Offset", children.simple("Offset", Rational::from_str)?)?;
1112 let mut unit = Self::new(
1113 concept,
1114 dimension,
1115 Scale::from(scale.as_str()),
1116 Base::from(base.as_str()),
1117 coefficient,
1118 offset,
1119 );
1120 for symbol in symbols {
1121 unit.add_symbol(symbol);
1122 }
1123 Ok(unit)
1124 })
1125 }
1126}
1127
1128impl ReferenceDocument {
1129 pub fn from_element(element: &Element) -> Result<Self, CodecError> {
1131 decode(element, |children| {
1132 let concept = read_concept(element, children)?;
1133 let mut languages = children.simple("Language", Language::from_str)?.into_iter();
1134 let first = languages.next().ok_or_else(|| {
1135 CodecError::new(
1136 CodecErrorKind::MissingChild,
1137 "",
1138 "required child `Language` is missing",
1139 )
1140 })?;
1141 let mut document = Self::new(concept, first);
1142 for language in languages {
1143 document.add_language(language);
1144 }
1145 document.set_date_of_publication(Reader::at_most_one(
1146 "DateOfPublication",
1147 children.simple("DateOfPublication", DateTime::from_str)?,
1148 )?);
1149 document.set_author(Reader::at_most_one("Author", children.strings("Author")?)?);
1150 document.set_isbn(Reader::at_most_one("ISBN", children.strings("ISBN")?)?);
1151 document.set_publisher(Reader::at_most_one(
1152 "Publisher",
1153 children.strings("Publisher")?,
1154 )?);
1155 document.set_uri(Reader::at_most_one(
1156 "URI",
1157 children.simple("URI", AnyUri::from_str)?,
1158 )?);
1159 Ok(document)
1160 })
1161 }
1162}
1163
1164#[cfg(test)]
1165mod drift {
1166 use super::*;
1171 use crate::schema::DEFINITIONS;
1172
1173 fn declared(handle: &str) -> Vec<&'static str> {
1174 let definition = DEFINITIONS
1175 .iter()
1176 .find(|definition| definition.handle == handle)
1177 .unwrap_or_else(|| panic!("schema tables no longer define {handle}"));
1178 definition.children.iter().map(|rule| rule.name).collect()
1179 }
1180
1181 fn written(element: &Element) -> Vec<String> {
1182 let mut names: Vec<String> = element
1183 .children()
1184 .map(|child| child.local_name().to_owned())
1185 .collect();
1186 names.dedup();
1187 names
1188 }
1189
1190 fn assert_writes_every_declared_child(handle: &str, element: &Element) {
1191 let written = written(element);
1192 let missing: Vec<_> = declared(handle)
1193 .into_iter()
1194 .filter(|name| !written.iter().any(|w| w == name))
1195 .collect();
1196 assert!(
1197 missing.is_empty(),
1198 "{handle}: codec never writes {missing:?}"
1199 );
1200 }
1201
1202 use crate::{
1204 Base, Base64Binary, DataTypeConstraint, DataTypeName, DataValue, Decimal, Language,
1205 NonNegativeInteger, Rational, Scale, ValueList,
1206 };
1207 include!("../tests/support/maximal.rs");
1208
1209 #[test]
1210 fn codec_writes_every_child_the_schema_declares() {
1211 let cases = [
1212 (
1213 "ObjectTypeType",
1214 ObjectType::new(maximal_subject(1)).to_element(),
1215 ),
1216 ("PropertyType", maximal_property().to_element()),
1217 ("GroupOfPropertiesType", maximal_group().to_element()),
1218 ("DataTemplateType", maximal_template().to_element()),
1219 ("QuantityKindType", maximal_quantity_kind().to_element()),
1220 ("DimensionType", maximal_dimension().to_element()),
1221 ("UnitType", maximal_unit().to_element()),
1222 (
1223 "ReferenceDocumentType",
1224 maximal_reference_document().to_element(),
1225 ),
1226 ];
1227 for (handle, element) in &cases {
1228 assert_writes_every_declared_child(handle, element);
1229 }
1230 }
1231}