1use std::fmt;
4use thiserror::Error;
5
6#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct XmlPath(String);
9
10impl XmlPath {
11 pub(crate) fn new(path: String) -> Self {
12 Self(path)
13 }
14
15 #[must_use]
17 pub fn as_str(&self) -> &str {
18 &self.0
19 }
20}
21
22impl fmt::Display for XmlPath {
23 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
24 formatter.write_str(&self.0)
25 }
26}
27
28#[derive(Debug, Error)]
30#[non_exhaustive]
31pub enum XmlError {
32 #[error("malformed XML: {0}")]
34 Malformed(String),
35 #[error("unparseable entity id {0:?}")]
37 BadId(String),
38 #[error("invalid {kind} scalar {value:?}")]
40 InvalidScalar {
41 kind: String,
43 value: String,
45 },
46 #[error("unknown value kind {0:?}")]
48 UnknownKind(String),
49 #[error("element `{element}` has namespace {found:?}; expected `{expected}`")]
51 Namespace {
52 element: String,
54 expected: &'static str,
56 found: Option<String>,
58 },
59 #[error("ifcXML profile expects schema `{expected}`, found {found:?}")]
61 Profile {
62 expected: &'static str,
64 found: Option<String>,
66 },
67 #[error("strict ifcXML requires root `ifcXML`, found {found:?}")]
69 Root {
70 found: Option<String>,
72 },
73 #[error("{path}: {source}")]
75 At {
76 path: XmlPath,
78 #[source]
80 source: Box<XmlError>,
81 },
82 #[error("write failed: {0}")]
84 Write(String),
85}
86
87impl XmlError {
88 #[must_use]
90 pub fn path(&self) -> Option<&XmlPath> {
91 match self {
92 Self::At { path, .. } => Some(path),
93 _ => None,
94 }
95 }
96
97 #[must_use]
99 pub fn root_cause(&self) -> &Self {
100 match self {
101 Self::At { source, .. } => source.root_cause(),
102 error => error,
103 }
104 }
105
106 pub(crate) fn at(self, path: String) -> Self {
107 match self {
108 Self::At { .. } => self,
109 source => Self::At {
110 path: XmlPath::new(path),
111 source: Box::new(source),
112 },
113 }
114 }
115}
116
117impl From<quick_xml::Error> for XmlError {
118 fn from(error: quick_xml::Error) -> Self {
119 Self::Malformed(error.to_string())
120 }
121}