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