1use std::{
4 error::Error,
5 fmt::{self, Display},
6};
7
8use facet_core::Def;
9use facet_reflect::ReflectError;
10use miette::SourceSpan;
11
12#[derive(Debug)]
14pub struct XmlError {
15 pub(crate) kind: XmlErrorKind,
17 pub(crate) source_code: Option<String>,
19 pub(crate) span: Option<SourceSpan>,
21}
22
23impl XmlError {
24 pub fn kind(&self) -> &XmlErrorKind {
26 &self.kind
27 }
28
29 pub(crate) fn new(kind: impl Into<XmlErrorKind>) -> Self {
31 XmlError {
32 kind: kind.into(),
33 source_code: None,
34 span: None,
35 }
36 }
37
38 pub(crate) fn with_source(mut self, source: impl Into<String>) -> Self {
40 self.source_code = Some(source.into());
41 self
42 }
43
44 pub(crate) fn with_span(mut self, span: impl Into<SourceSpan>) -> Self {
46 self.span = Some(span.into());
47 self
48 }
49}
50
51impl Display for XmlError {
52 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
53 let kind = &self.kind;
54 write!(f, "{kind}")
55 }
56}
57
58impl Error for XmlError {}
59
60impl<K: Into<XmlErrorKind>> From<K> for XmlError {
61 fn from(value: K) -> Self {
62 XmlError::new(value)
63 }
64}
65
66#[derive(Debug, Clone, Copy, PartialEq, Eq)]
68pub enum MissingAnnotationPhase {
69 Serialize,
71 Deserialize,
73}
74
75#[derive(Debug)]
77#[non_exhaustive]
78pub enum XmlErrorKind {
79 InvalidDocumentShape(&'static Def),
82 Parse(String),
84 Reflect(ReflectError),
86 UnsupportedShape(String),
88 NoMatchingElement(String),
90 NoMatchingAttribute(String),
92 UnknownAttribute {
94 attribute: String,
96 expected: Vec<&'static str>,
98 },
99 NoTextField,
101 UnexpectedText,
103 UnsupportedValueDef(String),
105 InvalidValueForShape(String),
107 Solver(facet_solver::SolverError),
109 SchemaError(facet_solver::SchemaError),
111 UnexpectedEof,
113 UnexpectedEvent(String),
115 MissingElement(String),
117 MissingAttribute(String),
119 InvalidAttributeValue {
121 name: String,
123 value: String,
125 expected_type: String,
127 },
128
129 UnknownField {
131 field: String,
133 expected: Vec<&'static str>,
135 },
136 InvalidUtf8(String),
138 Base64Decode(String),
140
141 Io(String),
144 SerializeNotStruct,
146 SerializeNotList,
148 SerializeUnknownElementType,
150 SerializeUnknownValueType,
152 MissingXmlAnnotations {
154 type_name: &'static str,
156 phase: MissingAnnotationPhase,
158 fields: Vec<(&'static str, &'static str)>,
160 },
161}
162
163impl XmlErrorKind {
164 pub fn code(&self) -> &'static str {
166 match self {
167 XmlErrorKind::InvalidDocumentShape(_) => "xml::invalid_document_shape",
168 XmlErrorKind::Parse(_) => "xml::parse",
169 XmlErrorKind::Reflect(_) => "xml::reflect",
170 XmlErrorKind::UnsupportedShape(_) => "xml::unsupported_shape",
171 XmlErrorKind::NoMatchingElement(_) => "xml::no_matching_element",
172 XmlErrorKind::NoMatchingAttribute(_) => "xml::no_matching_attribute",
173 XmlErrorKind::UnknownAttribute { .. } => "xml::unknown_attribute",
174 XmlErrorKind::NoTextField => "xml::no_text_field",
175 XmlErrorKind::UnexpectedText => "xml::unexpected_text",
176 XmlErrorKind::UnsupportedValueDef(_) => "xml::unsupported_value_def",
177 XmlErrorKind::InvalidValueForShape(_) => "xml::invalid_value",
178 XmlErrorKind::Solver(_) => "xml::solver",
179 XmlErrorKind::SchemaError(_) => "xml::schema",
180 XmlErrorKind::UnexpectedEof => "xml::unexpected_eof",
181 XmlErrorKind::UnexpectedEvent(_) => "xml::unexpected_event",
182 XmlErrorKind::MissingElement(_) => "xml::missing_element",
183 XmlErrorKind::MissingAttribute(_) => "xml::missing_attribute",
184 XmlErrorKind::InvalidAttributeValue { .. } => "xml::invalid_attribute_value",
185 XmlErrorKind::UnknownField { .. } => "xml::unknown_field",
186 XmlErrorKind::InvalidUtf8(_) => "xml::invalid_utf8",
187 XmlErrorKind::Base64Decode(_) => "xml::base64_decode",
188 XmlErrorKind::Io(_) => "xml::io",
189 XmlErrorKind::SerializeNotStruct => "xml::serialize_not_struct",
190 XmlErrorKind::SerializeNotList => "xml::serialize_not_list",
191 XmlErrorKind::SerializeUnknownElementType => "xml::serialize_unknown_element_type",
192 XmlErrorKind::SerializeUnknownValueType => "xml::serialize_unknown_value_type",
193 XmlErrorKind::MissingXmlAnnotations { .. } => "xml::missing_xml_annotations",
194 }
195 }
196}
197
198impl Display for XmlErrorKind {
199 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
200 match self {
201 XmlErrorKind::InvalidDocumentShape(def) => {
202 write!(
203 f,
204 "invalid shape {def:#?} — expected struct with element/attribute fields"
205 )
206 }
207 XmlErrorKind::Parse(msg) => write!(f, "XML parse error: {msg}"),
208 XmlErrorKind::Reflect(reflect_error) => write!(f, "{reflect_error}"),
209 XmlErrorKind::UnsupportedShape(msg) => write!(f, "unsupported shape: {msg}"),
210 XmlErrorKind::NoMatchingElement(element_name) => {
211 write!(f, "no matching field for element '{element_name}'")
212 }
213 XmlErrorKind::NoMatchingAttribute(attr_name) => {
214 write!(f, "no matching field for attribute '{attr_name}'")
215 }
216 XmlErrorKind::UnknownAttribute {
217 attribute,
218 expected,
219 } => {
220 write!(
221 f,
222 "unknown attribute '{}', expected one of: {}",
223 attribute,
224 expected.join(", ")
225 )
226 }
227 XmlErrorKind::NoTextField => {
228 write!(f, "no field marked with xml::text to receive text content")
229 }
230 XmlErrorKind::UnexpectedText => {
231 write!(f, "unexpected text content")
232 }
233 XmlErrorKind::UnsupportedValueDef(msg) => {
234 write!(f, "unsupported value definition: {msg}")
235 }
236 XmlErrorKind::InvalidValueForShape(msg) => {
237 write!(f, "invalid value for shape: {msg}")
238 }
239 XmlErrorKind::Solver(e) => write!(f, "{e}"),
240 XmlErrorKind::SchemaError(e) => write!(f, "schema error: {e}"),
241 XmlErrorKind::UnexpectedEof => write!(f, "unexpected end of XML input"),
242 XmlErrorKind::UnexpectedEvent(msg) => write!(f, "unexpected XML event: {msg}"),
243 XmlErrorKind::MissingElement(name) => write!(f, "missing required element '{name}'"),
244 XmlErrorKind::MissingAttribute(name) => {
245 write!(f, "missing required attribute '{name}'")
246 }
247 XmlErrorKind::InvalidAttributeValue {
248 name,
249 value,
250 expected_type,
251 } => {
252 write!(
253 f,
254 "invalid value '{value}' for attribute '{name}', expected {expected_type}"
255 )
256 }
257 XmlErrorKind::UnknownField { field, expected } => {
258 write!(
259 f,
260 "unknown field '{}', expected one of: {}",
261 field,
262 expected.join(", ")
263 )
264 }
265 XmlErrorKind::InvalidUtf8(msg) => write!(f, "invalid UTF-8: {msg}"),
266 XmlErrorKind::Base64Decode(msg) => write!(f, "base64 decode error: {msg}"),
267 XmlErrorKind::Io(msg) => write!(f, "IO error: {msg}"),
268 XmlErrorKind::SerializeNotStruct => {
269 write!(f, "expected struct for XML document serialization")
270 }
271 XmlErrorKind::SerializeNotList => {
272 write!(f, "expected list for elements field")
273 }
274 XmlErrorKind::SerializeUnknownElementType => {
275 write!(
276 f,
277 "cannot determine element name for value (expected enum or struct with element_name)"
278 )
279 }
280 XmlErrorKind::SerializeUnknownValueType => {
281 write!(f, "cannot serialize value: unknown type")
282 }
283 XmlErrorKind::MissingXmlAnnotations {
284 type_name,
285 phase,
286 fields,
287 } => {
288 let verb = match phase {
289 MissingAnnotationPhase::Serialize => "serialize",
290 MissingAnnotationPhase::Deserialize => "deserialize",
291 };
292
293 write!(
294 f,
295 "{type_name} cannot {verb} because these fields lack XML annotations: "
296 )?;
297 for (idx, (field, ty)) in fields.iter().enumerate() {
298 if idx > 0 {
299 write!(f, ", ")?;
300 }
301 write!(f, "{field}: {ty}")?;
302 }
303 write!(
304 f,
305 ". Each field must opt into XML via one of:\n\
306 • #[facet(xml::attribute)] → <{type_name} field=\"…\" /> (attributes)\n\
307 • #[facet(xml::element)] → <{type_name}><field>…</field></{type_name}> (single child)\n\
308 • #[facet(xml::elements)] → <{type_name}><field>…</field>…</{type_name}> (lists of children)\n\
309 • #[facet(xml::text)] → <{type_name}>…</{type_name}> (text content)\n\
310 • #[facet(xml::element_name)] to capture the element/tag name itself.\n\
311 `#[facet(child)]` is accepted as shorthand for xml::element. \
312 Use #[facet(flatten)] or #[facet(skip*)] if the field should be omitted."
313 )
314 }
315 }
316 }
317}
318
319impl From<ReflectError> for XmlErrorKind {
320 fn from(value: ReflectError) -> Self {
321 Self::Reflect(value)
322 }
323}
324
325impl From<facet_solver::SchemaError> for XmlErrorKind {
326 fn from(value: facet_solver::SchemaError) -> Self {
327 Self::SchemaError(value)
328 }
329}
330
331impl miette::Diagnostic for XmlError {
336 fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
337 Some(Box::new(self.kind.code()))
338 }
339
340 fn source_code(&self) -> Option<&dyn miette::SourceCode> {
341 self.source_code
342 .as_ref()
343 .map(|s| s as &dyn miette::SourceCode)
344 }
345
346 fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
347 if let Some(span) = self.span {
348 let label = match &self.kind {
349 XmlErrorKind::UnknownAttribute { attribute, .. } => {
350 format!("unknown attribute `{attribute}`")
351 }
352 XmlErrorKind::NoMatchingElement(name) => {
353 format!("no field matches `{name}`")
354 }
355 XmlErrorKind::NoMatchingAttribute(name) => {
356 format!("no field matches attribute `{name}`")
357 }
358 XmlErrorKind::MissingElement(name) => {
359 format!("missing element `{name}`")
360 }
361 XmlErrorKind::MissingAttribute(name) => {
362 format!("missing attribute `{name}`")
363 }
364 XmlErrorKind::UnknownField { field, .. } => {
365 format!("unknown field `{field}`")
366 }
367 _ => "error occurred here".to_string(),
368 };
369 Some(Box::new(std::iter::once(miette::LabeledSpan::at(
370 span, label,
371 ))))
372 } else {
373 None
374 }
375 }
376
377 fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
378 match &self.kind {
379 XmlErrorKind::UnknownAttribute { expected, .. } => Some(Box::new(format!(
380 "expected one of: {}",
381 expected.join(", ")
382 ))),
383 XmlErrorKind::NoTextField => Some(Box::new(
384 "add #[facet(xml::text)] to a String field to capture text content",
385 )),
386 XmlErrorKind::UnknownField { expected, .. } => Some(Box::new(format!(
387 "expected one of: {}",
388 expected.join(", ")
389 ))),
390 _ => None,
391 }
392 }
393}