instant_xml/
lib.rs

1use std::{borrow::Cow, fmt};
2
3use thiserror::Error;
4
5pub use macros::{FromXml, ToXml};
6
7#[doc(hidden)]
8pub mod de;
9mod impls;
10use de::Context;
11pub use de::Deserializer;
12pub use impls::{display_to_xml, from_xml_str, OptionAccumulator};
13#[doc(hidden)]
14pub mod ser;
15pub use ser::Serializer;
16
17pub trait ToXml {
18    fn serialize<W: fmt::Write + ?Sized>(
19        &self,
20        field: Option<Id<'_>>,
21        serializer: &mut Serializer<W>,
22    ) -> Result<(), Error>;
23
24    fn present(&self) -> bool {
25        true
26    }
27}
28
29impl<T: ToXml + ?Sized> ToXml for &T {
30    fn serialize<W: fmt::Write + ?Sized>(
31        &self,
32        field: Option<Id<'_>>,
33        serializer: &mut Serializer<W>,
34    ) -> Result<(), Error> {
35        (*self).serialize(field, serializer)
36    }
37}
38
39pub trait FromXml<'xml>: Sized {
40    fn matches(id: Id<'_>, field: Option<Id<'_>>) -> bool;
41
42    fn deserialize<'cx>(
43        into: &mut Self::Accumulator,
44        field: &'static str,
45        deserializer: &mut Deserializer<'cx, 'xml>,
46    ) -> Result<(), Error>;
47
48    type Accumulator: Accumulate<Self>;
49    const KIND: Kind;
50}
51
52/// A type implementing `Accumulate<T>` is used to accumulate a value of type `T`.
53pub trait Accumulate<T>: Default {
54    fn try_done(self, field: &'static str) -> Result<T, Error>;
55}
56
57impl<T> Accumulate<T> for Option<T> {
58    fn try_done(self, field: &'static str) -> Result<T, Error> {
59        self.ok_or(Error::MissingValue(field))
60    }
61}
62
63impl<T> Accumulate<Vec<T>> for Vec<T> {
64    fn try_done(self, _: &'static str) -> Result<Vec<T>, Error> {
65        Ok(self)
66    }
67}
68
69impl<'a, T> Accumulate<Cow<'a, [T]>> for Vec<T>
70where
71    [T]: ToOwned<Owned = Vec<T>>,
72{
73    fn try_done(self, _: &'static str) -> Result<Cow<'a, [T]>, Error> {
74        Ok(Cow::Owned(self))
75    }
76}
77
78impl<T> Accumulate<Option<T>> for Option<T> {
79    fn try_done(self, _: &'static str) -> Result<Option<T>, Error> {
80        Ok(self)
81    }
82}
83
84pub fn from_str<'xml, T: FromXml<'xml>>(input: &'xml str) -> Result<T, Error> {
85    let (mut context, root) = Context::new(input)?;
86    let id = context.element_id(&root)?;
87
88    if !T::matches(id, None) {
89        return Err(Error::UnexpectedValue(match id.ns.is_empty() {
90            true => format!("unexpected root element {:?}", id.name),
91            false => format!(
92                "unexpected root element {:?} in namespace {:?}",
93                id.name, id.ns
94            ),
95        }));
96    }
97
98    let mut value = T::Accumulator::default();
99    T::deserialize(
100        &mut value,
101        "<root element>",
102        &mut Deserializer::new(root, &mut context),
103    )?;
104    value.try_done("<root element>")
105}
106
107pub fn to_string(value: &(impl ToXml + ?Sized)) -> Result<String, Error> {
108    let mut output = String::new();
109    to_writer(value, &mut output)?;
110    Ok(output)
111}
112
113pub fn to_writer(
114    value: &(impl ToXml + ?Sized),
115    output: &mut (impl fmt::Write + ?Sized),
116) -> Result<(), Error> {
117    value.serialize(None, &mut Serializer::new(output))
118}
119
120pub trait FromXmlOwned: for<'xml> FromXml<'xml> {}
121
122impl<T> FromXmlOwned for T where T: for<'xml> FromXml<'xml> {}
123
124#[derive(Clone, Debug, Eq, Error, PartialEq)]
125pub enum Error {
126    #[error("format: {0}")]
127    Format(#[from] fmt::Error),
128    #[error("invalid entity: {0}")]
129    InvalidEntity(String),
130    #[error("parse: {0}")]
131    Parse(#[from] xmlparser::Error),
132    #[error("other: {0}")]
133    Other(std::string::String),
134    #[error("unexpected end of stream")]
135    UnexpectedEndOfStream,
136    #[error("unexpected value: '{0}'")]
137    UnexpectedValue(String),
138    #[error("unexpected tag: {0}")]
139    UnexpectedTag(String),
140    #[error("missing tag")]
141    MissingTag,
142    #[error("missing value: {0}")]
143    MissingValue(&'static str),
144    #[error("unexpected token: {0}")]
145    UnexpectedToken(String),
146    #[error("unknown prefix: {0}")]
147    UnknownPrefix(String),
148    #[error("unexpected node: {0}")]
149    UnexpectedNode(String),
150    #[error("unexpected state: {0}")]
151    UnexpectedState(&'static str),
152    #[error("expected scalar, found {0}")]
153    ExpectedScalar(String),
154    #[error("duplicate value for {0}")]
155    DuplicateValue(&'static str),
156}
157
158#[derive(Copy, Clone, Debug, Eq, PartialEq)]
159pub enum Kind {
160    Scalar,
161    Element,
162}
163
164#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
165pub struct Id<'a> {
166    pub ns: &'a str,
167    pub name: &'a str,
168}