• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

facet-rs / facet / 20132083919

11 Dec 2025 11:48AM UTC coverage: 57.906% (+0.04%) from 57.865%
20132083919

push

github

fasterthanlime
Regenerate showcases

7 of 7 new or added lines in 2 files covered. (100.0%)

796 existing lines in 4 files now uncovered.

28914 of 49933 relevant lines covered (57.91%)

6304.13 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

14.2
/facet-xml/src/error.rs
1
//! Error types for XML serialization and deserialization.
2

3
use std::{
4
    error::Error,
5
    fmt::{self, Display},
6
};
7

8
use facet_core::Def;
9
use facet_reflect::ReflectError;
10
use miette::SourceSpan;
11

12
/// Error type for XML deserialization.
13
#[derive(Debug)]
14
pub struct XmlError {
15
    /// The specific kind of error
16
    pub(crate) kind: XmlErrorKind,
17
    /// Source code for diagnostics
18
    pub(crate) source_code: Option<String>,
19
    /// Primary span where the error occurred
20
    pub(crate) span: Option<SourceSpan>,
21
}
22

23
impl XmlError {
24
    /// Returns a reference to the error kind for detailed error inspection.
25
    pub fn kind(&self) -> &XmlErrorKind {
12✔
26
        &self.kind
12✔
27
    }
12✔
28

29
    /// Create a new error with the given kind.
30
    pub(crate) fn new(kind: impl Into<XmlErrorKind>) -> Self {
24✔
31
        XmlError {
24✔
32
            kind: kind.into(),
24✔
33
            source_code: None,
24✔
34
            span: None,
24✔
35
        }
24✔
36
    }
24✔
37

38
    /// Attach source code to this error for diagnostics.
39
    pub(crate) fn with_source(mut self, source: impl Into<String>) -> Self {
21✔
40
        self.source_code = Some(source.into());
21✔
41
        self
21✔
42
    }
21✔
43

44
    /// Attach a span to this error for diagnostics.
45
    pub(crate) fn with_span(mut self, span: impl Into<SourceSpan>) -> Self {
9✔
46
        self.span = Some(span.into());
9✔
47
        self
9✔
48
    }
9✔
49
}
50

51
impl 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

58
impl Error for XmlError {}
59

60
impl<K: Into<XmlErrorKind>> From<K> for XmlError {
61
    fn from(value: K) -> Self {
1✔
62
        XmlError::new(value)
1✔
63
    }
1✔
64
}
65

66
/// Public phase indicator for [`XmlErrorKind::MissingXmlAnnotations`].
67
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
68
pub enum MissingAnnotationPhase {
69
    /// Error triggered while serializing.
70
    Serialize,
71
    /// Error triggered while deserializing.
72
    Deserialize,
73
}
74

75
/// Detailed classification of XML errors.
76
#[derive(Debug)]
77
#[non_exhaustive]
78
pub enum XmlErrorKind {
79
    // Deserialization errors
80
    /// The document shape is invalid (expected struct with element fields).
81
    InvalidDocumentShape(&'static Def),
82
    /// Failed to parse the XML document.
83
    Parse(String),
84
    /// Error from the reflection system during deserialization.
85
    Reflect(ReflectError),
86
    /// Encountered an unsupported shape during deserialization.
87
    UnsupportedShape(String),
88
    /// No field matches the given element name.
89
    NoMatchingElement(String),
90
    /// No field matches the given attribute name.
91
    NoMatchingAttribute(String),
92
    /// Unknown attribute encountered.
93
    UnknownAttribute {
94
        /// The unknown attribute name.
95
        attribute: String,
96
        /// List of expected attribute names.
97
        expected: Vec<&'static str>,
98
    },
99
    /// No text field found for text content.
100
    NoTextField,
101
    /// Unexpected text content.
102
    UnexpectedText,
103
    /// Unsupported value definition.
104
    UnsupportedValueDef(String),
105
    /// Value doesn't fit the expected shape.
106
    InvalidValueForShape(String),
107
    /// Solver error (ambiguous or no matching variant for flattened enum).
108
    Solver(facet_solver::SolverError),
109
    /// Schema construction error.
110
    SchemaError(facet_solver::SchemaError),
111
    /// Unexpected end of input.
112
    UnexpectedEof,
113
    /// Unexpected XML event.
114
    UnexpectedEvent(String),
115
    /// Missing required element.
116
    MissingElement(String),
117
    /// Missing required attribute.
118
    MissingAttribute(String),
119
    /// Invalid attribute value.
120
    InvalidAttributeValue {
121
        /// The attribute name.
122
        name: String,
123
        /// The invalid value.
124
        value: String,
125
        /// The expected type.
126
        expected_type: String,
127
    },
128

129
    /// Unknown field encountered when deny_unknown_fields is set.
130
    UnknownField {
131
        /// The unknown field name.
132
        field: String,
133
        /// List of expected field names.
134
        expected: Vec<&'static str>,
135
    },
136
    /// Invalid UTF-8 in input.
137
    InvalidUtf8(String),
138
    /// Base64 decoding error.
139
    Base64Decode(String),
140

141
    // Serialization errors
142
    /// IO error during serialization.
143
    Io(String),
144
    /// Expected a struct for XML document serialization.
145
    SerializeNotStruct,
146
    /// Expected a list for elements field.
147
    SerializeNotList,
148
    /// Unknown element type during serialization.
149
    SerializeUnknownElementType,
150
    /// Unknown value type during serialization.
151
    SerializeUnknownValueType,
152
    /// Struct fields lack XML annotations, so they cannot be mapped automatically.
153
    MissingXmlAnnotations {
154
        /// Fully-qualified type name of the struct.
155
        type_name: &'static str,
156
        /// Whether the failure happened while serializing or deserializing.
157
        phase: MissingAnnotationPhase,
158
        /// Offending fields paired with their Rust type identifiers.
159
        fields: Vec<(&'static str, &'static str)>,
160
    },
161
}
162

163
impl XmlErrorKind {
164
    /// Returns an error code for this error kind.
165
    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",
×
UNCOV
175
            XmlErrorKind::UnexpectedText => "xml::unexpected_text",
×
176
            XmlErrorKind::UnsupportedValueDef(_) => "xml::unsupported_value_def",
×
UNCOV
177
            XmlErrorKind::InvalidValueForShape(_) => "xml::invalid_value",
×
UNCOV
178
            XmlErrorKind::Solver(_) => "xml::solver",
×
UNCOV
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",
×
UNCOV
185
            XmlErrorKind::UnknownField { .. } => "xml::unknown_field",
×
UNCOV
186
            XmlErrorKind::InvalidUtf8(_) => "xml::invalid_utf8",
×
UNCOV
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",
×
UNCOV
193
            XmlErrorKind::MissingXmlAnnotations { .. } => "xml::missing_xml_annotations",
×
194
        }
195
    }
×
196
}
197

198
impl Display for XmlErrorKind {
199
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
×
UNCOV
200
        match self {
×
201
            XmlErrorKind::InvalidDocumentShape(def) => {
×
202
                write!(
×
UNCOV
203
                    f,
×
204
                    "invalid shape {def:#?} — expected struct with element/attribute fields"
205
                )
206
            }
UNCOV
207
            XmlErrorKind::Parse(msg) => write!(f, "XML parse error: {msg}"),
×
UNCOV
208
            XmlErrorKind::Reflect(reflect_error) => write!(f, "{reflect_error}"),
×
209
            XmlErrorKind::UnsupportedShape(msg) => write!(f, "unsupported shape: {msg}"),
×
UNCOV
210
            XmlErrorKind::NoMatchingElement(element_name) => {
×
UNCOV
211
                write!(f, "no matching field for element '{element_name}'")
×
212
            }
UNCOV
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 => {
UNCOV
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
            }
UNCOV
236
            XmlErrorKind::InvalidValueForShape(msg) => {
×
UNCOV
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}"),
×
UNCOV
241
            XmlErrorKind::UnexpectedEof => write!(f, "unexpected end of XML input"),
×
UNCOV
242
            XmlErrorKind::UnexpectedEvent(msg) => write!(f, "unexpected XML event: {msg}"),
×
243
            XmlErrorKind::MissingElement(name) => write!(f, "missing required element '{name}'"),
×
UNCOV
244
            XmlErrorKind::MissingAttribute(name) => {
×
UNCOV
245
                write!(f, "missing required attribute '{name}'")
×
246
            }
247
            XmlErrorKind::InvalidAttributeValue {
248
                name,
×
UNCOV
249
                value,
×
250
                expected_type,
×
251
            } => {
UNCOV
252
                write!(
×
253
                    f,
×
254
                    "invalid value '{value}' for attribute '{name}', expected {expected_type}"
255
                )
256
            }
257
            XmlErrorKind::UnknownField { field, expected } => {
×
UNCOV
258
                write!(
×
UNCOV
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}"),
×
UNCOV
266
            XmlErrorKind::Base64Decode(msg) => write!(f, "base64 decode error: {msg}"),
×
UNCOV
267
            XmlErrorKind::Io(msg) => write!(f, "IO error: {msg}"),
×
268
            XmlErrorKind::SerializeNotStruct => {
UNCOV
269
                write!(f, "expected struct for XML document serialization")
×
270
            }
271
            XmlErrorKind::SerializeNotList => {
UNCOV
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 => {
UNCOV
281
                write!(f, "cannot serialize value: unknown type")
×
282
            }
283
            XmlErrorKind::MissingXmlAnnotations {
UNCOV
284
                type_name,
×
285
                phase,
×
286
                fields,
×
287
            } => {
UNCOV
288
                let verb = match phase {
×
289
                    MissingAnnotationPhase::Serialize => "serialize",
×
290
                    MissingAnnotationPhase::Deserialize => "deserialize",
×
291
                };
292

293
                write!(
×
UNCOV
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, ", ")?;
×
UNCOV
300
                    }
×
301
                    write!(f, "{field}: {ty}")?;
×
302
                }
UNCOV
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

319
impl From<ReflectError> for XmlErrorKind {
320
    fn from(value: ReflectError) -> Self {
1✔
321
        Self::Reflect(value)
1✔
322
    }
1✔
323
}
324

325
impl From<facet_solver::SchemaError> for XmlErrorKind {
326
    fn from(value: facet_solver::SchemaError) -> Self {
×
327
        Self::SchemaError(value)
×
328
    }
×
329
}
330

331
// ============================================================================
332
// Diagnostic Implementation
333
// ============================================================================
334

335
impl miette::Diagnostic for XmlError {
336
    fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
×
337
        Some(Box::new(self.kind.code()))
×
338
    }
×
339

UNCOV
340
    fn source_code(&self) -> Option<&dyn miette::SourceCode> {
×
341
        self.source_code
×
UNCOV
342
            .as_ref()
×
UNCOV
343
            .map(|s| s as &dyn miette::SourceCode)
×
UNCOV
344
    }
×
345

UNCOV
346
    fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
×
UNCOV
347
        if let Some(span) = self.span {
×
UNCOV
348
            let label = match &self.kind {
×
UNCOV
349
                XmlErrorKind::UnknownAttribute { attribute, .. } => {
×
UNCOV
350
                    format!("unknown attribute `{attribute}`")
×
351
                }
UNCOV
352
                XmlErrorKind::NoMatchingElement(name) => {
×
UNCOV
353
                    format!("no field matches `{name}`")
×
354
                }
UNCOV
355
                XmlErrorKind::NoMatchingAttribute(name) => {
×
UNCOV
356
                    format!("no field matches attribute `{name}`")
×
357
                }
UNCOV
358
                XmlErrorKind::MissingElement(name) => {
×
UNCOV
359
                    format!("missing element `{name}`")
×
360
                }
UNCOV
361
                XmlErrorKind::MissingAttribute(name) => {
×
UNCOV
362
                    format!("missing attribute `{name}`")
×
363
                }
UNCOV
364
                XmlErrorKind::UnknownField { field, .. } => {
×
UNCOV
365
                    format!("unknown field `{field}`")
×
366
                }
UNCOV
367
                _ => "error occurred here".to_string(),
×
368
            };
UNCOV
369
            Some(Box::new(std::iter::once(miette::LabeledSpan::at(
×
UNCOV
370
                span, label,
×
UNCOV
371
            ))))
×
372
        } else {
UNCOV
373
            None
×
374
        }
UNCOV
375
    }
×
376

UNCOV
377
    fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
×
UNCOV
378
        match &self.kind {
×
UNCOV
379
            XmlErrorKind::UnknownAttribute { expected, .. } => Some(Box::new(format!(
×
UNCOV
380
                "expected one of: {}",
×
UNCOV
381
                expected.join(", ")
×
UNCOV
382
            ))),
×
UNCOV
383
            XmlErrorKind::NoTextField => Some(Box::new(
×
UNCOV
384
                "add #[facet(xml::text)] to a String field to capture text content",
×
UNCOV
385
            )),
×
UNCOV
386
            XmlErrorKind::UnknownField { expected, .. } => Some(Box::new(format!(
×
UNCOV
387
                "expected one of: {}",
×
UNCOV
388
                expected.join(", ")
×
UNCOV
389
            ))),
×
UNCOV
390
            _ => None,
×
391
        }
UNCOV
392
    }
×
393
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc