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
52pub 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}