1use quick_xml::{encoding::EncodingError, escape::EscapeError, events::attributes::AttrError};
5use std::{fmt, io};
6use thiserror::Error;
7
8#[derive(Debug, Error)]
13#[error("error serializing JUnit report")]
14pub struct SerializeError {
15 #[from]
16 inner: quick_xml::Error,
17}
18
19impl From<EncodingError> for SerializeError {
20 fn from(inner: EncodingError) -> Self {
21 Self {
22 inner: quick_xml::Error::Encoding(inner),
23 }
24 }
25}
26
27impl From<io::Error> for SerializeError {
28 fn from(inner: io::Error) -> Self {
29 Self {
30 inner: quick_xml::Error::from(inner),
31 }
32 }
33}
34
35#[derive(Clone, Debug, PartialEq, Eq)]
37pub enum PathElement {
38 TestSuites,
40 TestSuite(usize, Option<String>),
42 TestCase(usize, Option<String>),
44 Properties,
46 Property(usize),
48 Failure,
50 Error,
52 Skipped,
54 FlakyFailure,
56 FlakyError,
58 RerunFailure,
60 RerunError,
62 SystemOut,
64 SystemErr,
66 Attribute(String),
68}
69
70impl fmt::Display for PathElement {
71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 match self {
73 PathElement::TestSuites => write!(f, "testsuites"),
74 PathElement::TestSuite(idx, Some(name)) => {
75 write!(f, "testsuite[{}](\"{}\")", idx, name)
76 }
77 PathElement::TestSuite(idx, None) => write!(f, "testsuite[{}]", idx),
78 PathElement::TestCase(idx, Some(name)) => write!(f, "testcase[{}](\"{}\")", idx, name),
79 PathElement::TestCase(idx, None) => write!(f, "testcase[{}]", idx),
80 PathElement::Properties => write!(f, "properties"),
81 PathElement::Property(idx) => write!(f, "property[{}]", idx),
82 PathElement::Failure => write!(f, "failure"),
83 PathElement::Error => write!(f, "error"),
84 PathElement::Skipped => write!(f, "skipped"),
85 PathElement::FlakyFailure => write!(f, "flakyFailure"),
86 PathElement::FlakyError => write!(f, "flakyError"),
87 PathElement::RerunFailure => write!(f, "rerunFailure"),
88 PathElement::RerunError => write!(f, "rerunError"),
89 PathElement::SystemOut => write!(f, "system-out"),
90 PathElement::SystemErr => write!(f, "system-err"),
91 PathElement::Attribute(name) => write!(f, "@{}", name),
92 }
93 }
94}
95
96#[derive(Debug, Error)]
98#[non_exhaustive]
99pub enum DeserializeErrorKind {
100 #[error("error parsing XML")]
102 XmlError(#[from] quick_xml::Error),
103
104 #[error("invalid UTF-8")]
106 Utf8Error(#[from] std::str::Utf8Error),
107
108 #[error("invalid integer: {0}")]
110 ParseIntError(#[from] std::num::ParseIntError),
111
112 #[error("invalid float: {0}")]
114 ParseFloatError(#[from] std::num::ParseFloatError),
115
116 #[error("invalid timestamp: {0}")]
118 ParseTimestampError(String),
119
120 #[error("invalid duration: {0}")]
122 ParseDurationError(String),
123
124 #[error("invalid UUID: {0}")]
126 ParseUuidError(#[from] uuid::Error),
127
128 #[error("missing required attribute: {0}")]
130 MissingAttribute(String),
131
132 #[error("unexpected element: {0}")]
134 UnexpectedElement(String),
135
136 #[error("invalid structure: {0}")]
138 InvalidStructure(String),
139
140 #[error("I/O error")]
142 IoError(#[from] io::Error),
143
144 #[error("attribute error: {0}")]
146 AttrError(#[from] AttrError),
147
148 #[error("XML unescape error: {0}")]
150 EscapeError(#[from] EscapeError),
151}
152
153#[derive(Debug)]
158pub struct DeserializeError {
159 kind: DeserializeErrorKind,
161 path: Vec<PathElement>,
163}
164
165impl DeserializeError {
166 pub fn new(kind: DeserializeErrorKind, path: Vec<PathElement>) -> Self {
168 Self { kind, path }
169 }
170
171 pub fn kind(&self) -> &DeserializeErrorKind {
173 &self.kind
174 }
175
176 pub fn path(&self) -> &[PathElement] {
178 &self.path
179 }
180}
181
182impl fmt::Display for DeserializeError {
183 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184 if self.path.is_empty() {
185 write!(f, "{}", self.kind)
186 } else {
187 write!(f, "at ")?;
188 for (i, element) in self.path.iter().enumerate() {
189 if i > 0 {
190 write!(f, "/")?;
191 }
192 write!(f, "{}", element)?;
193 }
194 write!(f, ": {}", self.kind)
195 }
196 }
197}
198
199impl std::error::Error for DeserializeError {
200 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
201 self.kind.source()
202 }
203}