async_xml/reader/
impls.rs

1//! Visitors and implementations for deserialization some standard library types
2
3use super::{FromXml, PeekingReader, Visitor};
4use crate::Error;
5use std::{marker::PhantomData, str::FromStr};
6use tokio::io::AsyncBufRead;
7
8impl<B, T> FromXml<B> for Option<T>
9where
10    B: AsyncBufRead + Unpin,
11    T: FromXml<B>,
12{
13    type Visitor = OptionalVisitor<T, B>;
14}
15
16/// A visitor for deserializing empty XML elements as an [`Option::None`]
17///
18/// All `visit_*` calls will be forwarded to an inner visitor but [`build()`](Self::build) will return [`None`]
19/// if no `visit_*` methods have been called and the inner visitor returns an error on building.
20pub struct OptionalVisitor<T, B>
21where
22    B: AsyncBufRead + Unpin,
23    T: FromXml<B>,
24{
25    empty: bool,
26    inner_visitor: T::Visitor,
27}
28
29impl<T, B> Default for OptionalVisitor<T, B>
30where
31    B: AsyncBufRead + Unpin,
32    T: FromXml<B>,
33{
34    fn default() -> Self {
35        Self {
36            empty: true,
37            inner_visitor: T::Visitor::default(),
38        }
39    }
40}
41
42#[async_trait::async_trait(?Send)]
43impl<B, T> Visitor<B> for OptionalVisitor<T, B>
44where
45    B: AsyncBufRead + Unpin,
46    T: FromXml<B>,
47{
48    type Output = Option<T>;
49
50    fn start_name() -> Option<&'static str> {
51        T::Visitor::start_name()
52    }
53
54    fn visit_attribute(&mut self, name: &str, value: &str) -> Result<(), Error> {
55        self.empty = false;
56        self.inner_visitor.visit_attribute(name, value)
57    }
58
59    async fn visit_child(
60        &mut self,
61        name: &str,
62        reader: &mut PeekingReader<B>,
63    ) -> Result<(), Error> {
64        self.empty = false;
65        self.inner_visitor.visit_child(name, reader).await
66    }
67
68    fn visit_text(&mut self, text: &str) -> Result<(), Error> {
69        self.empty = false;
70        self.inner_visitor.visit_text(text)
71    }
72
73    fn build(self) -> Result<Self::Output, Error> {
74        match self.inner_visitor.build() {
75            Ok(t) => Ok(Some(t)),
76            Err(_) if self.empty => Ok(None),
77            Err(e) => Err(e),
78        }
79    }
80}
81
82/// Marker trait for auto-implementing the [`FromXml`] trait based on the [`FromStr`]-implementation of a type.
83pub trait XmlFromStr {}
84
85impl<B, T, E> FromXml<B> for T
86where
87    B: AsyncBufRead + Unpin,
88    T: XmlFromStr + FromStr<Err = E>,
89    E: std::fmt::Display,
90{
91    type Visitor = FromStringVisitor<T>;
92}
93
94/// A visitor for deserializing types implementing [`FromStr`]
95///
96/// If the XML element contains attributes or child elements, this visitor will return an error.
97pub struct FromStringVisitor<T>
98where
99    T: FromStr,
100{
101    data: Option<T>,
102}
103
104impl<T, E> Default for FromStringVisitor<T>
105where
106    T: XmlFromStr + FromStr<Err = E>,
107    E: std::fmt::Display,
108{
109    fn default() -> Self {
110        Self { data: None }
111    }
112}
113
114#[async_trait::async_trait(?Send)]
115impl<B, T, E> Visitor<B> for FromStringVisitor<T>
116where
117    B: AsyncBufRead + Unpin,
118    T: XmlFromStr + FromStr<Err = E>,
119    E: std::fmt::Display,
120{
121    type Output = T;
122
123    fn start_name() -> Option<&'static str> {
124        None
125    }
126
127    fn visit_text(&mut self, text: &str) -> Result<(), Error> {
128        if self.data.is_some() {
129            return Err(Error::DoubleText);
130        }
131        match T::from_str(text) {
132            Ok(t) => self.data = Some(t),
133            Err(e) => return Err(Error::Deserialization(e.to_string())),
134        }
135        Ok(())
136    }
137
138    fn build(self) -> Result<Self::Output, Error> {
139        let data = if let Some(data) = self.data {
140            data
141        } else {
142            return Err(Error::MissingText);
143        };
144        Ok(data)
145    }
146}
147
148/// Generic visitor for forwarding all calls to an inner visitor and converting into the target
149/// type using its [`From`] implementation.
150pub struct FromVisitor<B, Target, FromType>
151where
152    B: AsyncBufRead + Unpin,
153    Target: From<FromType>,
154    FromType: FromXml<B>,
155{
156    inner: FromType::Visitor,
157    _target: PhantomData<Target>,
158}
159
160impl<B, Target, FromType> Default for FromVisitor<B, Target, FromType>
161where
162    B: AsyncBufRead + Unpin,
163    Target: From<FromType>,
164    FromType: FromXml<B>,
165{
166    fn default() -> Self {
167        Self {
168            inner: FromType::Visitor::default(),
169            _target: PhantomData,
170        }
171    }
172}
173
174#[async_trait::async_trait(?Send)]
175impl<B, Target, FromType> Visitor<B> for FromVisitor<B, Target, FromType>
176where
177    B: AsyncBufRead + Unpin,
178    Target: From<FromType>,
179    FromType: FromXml<B>,
180{
181    type Output = Target;
182
183    fn start_name() -> Option<&'static str> {
184        None
185    }
186
187    fn visit_tag(&mut self, name: &str) -> Result<(), Error> {
188        self.inner.visit_tag(name)
189    }
190
191    fn visit_text(&mut self, text: &str) -> Result<(), Error> {
192        self.inner.visit_text(text)
193    }
194
195    fn visit_attribute(&mut self, name: &str, value: &str) -> Result<(), Error> {
196        self.inner.visit_attribute(name, value)
197    }
198
199    async fn visit_child(
200        &mut self,
201        name: &str,
202        reader: &mut PeekingReader<B>,
203    ) -> Result<(), Error> {
204        self.inner.visit_child(name, reader).await
205    }
206
207    fn build(self) -> Result<Self::Output, Error> {
208        let from = self.inner.build()?;
209        Ok(from.into())
210    }
211}
212
213/// Generic visitor for forwarding all calls to an inner visitor and converting into the target
214/// type using its [`TryFrom`] implementation.
215pub struct TryFromVisitor<B, Target, FromType, E>
216where
217    B: AsyncBufRead + Unpin,
218    Target: TryFrom<FromType, Error = E>,
219    FromType: FromXml<B>,
220{
221    inner: FromType::Visitor,
222    _target: PhantomData<Target>,
223}
224
225impl<B, Target, FromType, E> Default for TryFromVisitor<B, Target, FromType, E>
226where
227    B: AsyncBufRead + Unpin,
228    Target: TryFrom<FromType, Error = E>,
229    FromType: FromXml<B>,
230{
231    fn default() -> Self {
232        Self {
233            inner: FromType::Visitor::default(),
234            _target: PhantomData,
235        }
236    }
237}
238
239#[async_trait::async_trait(?Send)]
240impl<B, Target, FromType, E> Visitor<B> for TryFromVisitor<B, Target, FromType, E>
241where
242    B: AsyncBufRead + Unpin,
243    Target: TryFrom<FromType, Error = E>,
244    FromType: FromXml<B>,
245    E: std::fmt::Display,
246{
247    type Output = Target;
248
249    fn start_name() -> Option<&'static str> {
250        None
251    }
252
253    fn visit_tag(&mut self, name: &str) -> Result<(), Error> {
254        self.inner.visit_tag(name)
255    }
256
257    fn visit_text(&mut self, text: &str) -> Result<(), Error> {
258        self.inner.visit_text(text)
259    }
260
261    fn visit_attribute(&mut self, name: &str, value: &str) -> Result<(), Error> {
262        self.inner.visit_attribute(name, value)
263    }
264
265    async fn visit_child(
266        &mut self,
267        name: &str,
268        reader: &mut PeekingReader<B>,
269    ) -> Result<(), Error> {
270        self.inner.visit_child(name, reader).await
271    }
272
273    fn build(self) -> Result<Self::Output, Error> {
274        let from = self.inner.build()?;
275        from.try_into()
276            .map_err(|e| Error::Deserialization(format!("error converting: {}", e)))
277    }
278}
279
280impl XmlFromStr for String {}
281impl XmlFromStr for i8 {}
282impl XmlFromStr for i16 {}
283impl XmlFromStr for i32 {}
284impl XmlFromStr for i64 {}
285impl XmlFromStr for u8 {}
286impl XmlFromStr for u16 {}
287impl XmlFromStr for u32 {}
288impl XmlFromStr for u64 {}
289impl XmlFromStr for u128 {}
290impl XmlFromStr for bool {}
291impl XmlFromStr for char {}
292impl XmlFromStr for &str {}
293
294impl<T: XmlFromStr> XmlFromStr for &'static T {}