xmlity_quick_xml/
lib.rs

1//! # XMLity Quick XML
2//!
3//! This crate contains a reference implementation of the `xmlity` crate using the `quick-xml` crate. It is the intention to keep this crate up to date with the latest version of `quick-xml` and `xmlity`.
4use ::quick_xml::name::ResolveResult;
5use core::str;
6
7use xmlity::{ser::IncludePrefix, ExpandedName, LocalName, Prefix, QName, XmlNamespace};
8
9pub mod de;
10pub mod ser;
11
12pub use de::{from_str, Deserializer};
13use quick_xml::name::{LocalName as QuickLocalName, Prefix as QuickPrefix, QName as QuickName};
14pub use ser::{to_string, Serializer};
15
16#[derive(Debug, thiserror::Error)]
17pub enum Error {
18    #[error("Quick XML error: {0}")]
19    QuickXml(#[from] quick_xml::Error),
20    #[error("Attribute error: {0}")]
21    AttrError(#[from] quick_xml::events::attributes::AttrError),
22    #[error("IO error: {0}")]
23    Io(#[from] std::io::Error),
24    #[error("Unexpected: {0}")]
25    Unexpected(xmlity::de::Unexpected),
26    #[error("Custom: {0}")]
27    Custom(String),
28    #[error("Wrong name: expected {expected:?}, got {actual:?}")]
29    WrongName {
30        actual: Box<ExpandedName<'static>>,
31        expected: Box<ExpandedName<'static>>,
32    },
33    #[error("Unknown child")]
34    UnknownChild,
35    #[error("Invalid UTF-8: {0}")]
36    InvalidUtf8(#[from] std::string::FromUtf8Error),
37    #[error("Invalid string")]
38    InvalidString,
39    #[error("Missing field: {field}")]
40    MissingField { field: String },
41    #[error("No possible variant: {ident}")]
42    NoPossibleVariant { ident: String },
43    #[error("Missing data")]
44    MissingData,
45}
46
47impl xmlity::de::Error for Error {
48    fn custom<T: ToString>(msg: T) -> Self {
49        Error::Custom(msg.to_string())
50    }
51
52    fn wrong_name(actual: &ExpandedName<'_>, expected: &ExpandedName<'_>) -> Self {
53        Error::WrongName {
54            actual: Box::new(actual.clone().into_owned()),
55            expected: Box::new(expected.clone().into_owned()),
56        }
57    }
58
59    fn unexpected_visit<T>(unexpected: xmlity::de::Unexpected, _expected: &T) -> Self {
60        Error::Unexpected(unexpected)
61    }
62
63    fn missing_field(field: &str) -> Self {
64        Error::MissingField {
65            field: field.to_string(),
66        }
67    }
68
69    fn no_possible_variant(ident: &str) -> Self {
70        Error::NoPossibleVariant {
71            ident: ident.to_string(),
72        }
73    }
74
75    fn missing_data() -> Self {
76        Error::MissingData
77    }
78
79    fn unknown_child() -> Self {
80        Error::UnknownChild
81    }
82
83    fn invalid_string() -> Self {
84        Error::InvalidString
85    }
86}
87
88impl xmlity::ser::Error for Error {
89    fn custom<T: ToString>(msg: T) -> Self {
90        Error::Custom(msg.to_string())
91    }
92}
93
94pub trait HasQuickXmlAlternative {
95    type QuickXmlAlternative;
96
97    fn from_quick_xml(quick_xml: Self::QuickXmlAlternative) -> Self;
98}
99
100impl<'a> HasQuickXmlAlternative for QName<'a> {
101    type QuickXmlAlternative = QuickName<'a>;
102
103    fn from_quick_xml(quick_xml: Self::QuickXmlAlternative) -> Self {
104        QName::new(
105            quick_xml.prefix().map(Prefix::from_quick_xml),
106            LocalName::from_quick_xml(quick_xml.local_name()),
107        )
108    }
109}
110
111impl<'a> HasQuickXmlAlternative for Prefix<'a> {
112    type QuickXmlAlternative = QuickPrefix<'a>;
113    fn from_quick_xml(quick_xml: Self::QuickXmlAlternative) -> Self {
114        Self::new(str::from_utf8(quick_xml.into_inner()).expect("prefix should be valid utf8"))
115            .expect("A quick xml prefix should be valid")
116    }
117}
118
119impl<'a> HasQuickXmlAlternative for LocalName<'a> {
120    type QuickXmlAlternative = QuickLocalName<'a>;
121    fn from_quick_xml(quick_xml: Self::QuickXmlAlternative) -> Self {
122        Self::new(str::from_utf8(quick_xml.into_inner()).expect("local name should be valid utf8"))
123            .expect("A quick xml local name should be valid")
124    }
125}
126
127pub struct OwnedQuickName(Vec<u8>);
128
129impl OwnedQuickName {
130    pub fn new(name: &QName<'_>) -> Self {
131        Self(name.to_string().into_bytes())
132    }
133
134    pub fn as_ref(&self) -> QuickName<'_> {
135        QuickName(&self.0[..])
136    }
137}
138
139pub fn xml_namespace_from_resolve_result(value: ResolveResult<'_>) -> Option<XmlNamespace<'_>> {
140    match value {
141        ResolveResult::Bound(namespace) => Some(
142            XmlNamespace::new(str::from_utf8(namespace.0).expect("namespace should be valid utf8"))
143                .unwrap(),
144        ),
145        _ => None,
146    }
147}
148
149#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
150pub struct Attribute<'a> {
151    pub name: ExpandedName<'a>,
152    pub value: String,
153    pub enforce_prefix: IncludePrefix,
154    pub preferred_prefix: Option<Prefix<'a>>,
155}
156
157impl<'a> Attribute<'a> {
158    pub fn resolve(self, resolved_prefix: Option<Prefix<'a>>) -> ResolvedAttribute<'a> {
159        ResolvedAttribute {
160            name: self.name.to_q_name(resolved_prefix),
161            value: self.value,
162        }
163    }
164}
165
166#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
167pub struct ResolvedAttribute<'a> {
168    pub name: QName<'a>,
169    pub value: String,
170}
171
172fn declaration_into_attribute(xmlns: XmlnsDeclaration<'_>) -> ResolvedAttribute<'_> {
173    ResolvedAttribute {
174        name: XmlnsDeclaration::xmlns_qname(xmlns.prefix),
175        value: xmlns.namespace.as_str().to_owned(),
176    }
177}
178
179/// An XML namespace declaration/singular mapping from a prefix to a namespace.
180#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
181pub struct XmlnsDeclaration<'a> {
182    pub prefix: Prefix<'a>,
183    pub namespace: XmlNamespace<'a>,
184}
185
186impl<'a> XmlnsDeclaration<'a> {
187    pub fn new(prefix: Prefix<'a>, namespace: XmlNamespace<'a>) -> Self {
188        Self { prefix, namespace }
189    }
190
191    pub fn into_owned(self) -> XmlnsDeclaration<'static> {
192        XmlnsDeclaration {
193            prefix: self.prefix.into_owned(),
194            namespace: self.namespace.into_owned(),
195        }
196    }
197
198    pub fn prefix(&self) -> &Prefix<'a> {
199        &self.prefix
200    }
201
202    pub fn namespace(&self) -> &XmlNamespace<'a> {
203        &self.namespace
204    }
205
206    /// Returns the QName for the XML namespace declaration.
207    pub fn xmlns_qname(prefix: Prefix<'_>) -> QName<'_> {
208        if prefix.is_default() {
209            QName::new(
210                None,
211                LocalName::new("xmlns").expect("xmlns is a valid local name"),
212            )
213        } else {
214            QName::new(
215                Some(Prefix::new("xmlns").expect("xmlns is a valid prefix")),
216                LocalName::from(prefix),
217            )
218        }
219    }
220}