async_xml/
util.rs

1//! Miscellaneous helper types
2
3use crate::{
4    reader::{FromXml, XmlFromStr},
5    Error, Visitor,
6};
7use std::{
8    ops::{Deref, DerefMut},
9    str::FromStr,
10};
11use tokio::io::AsyncBufRead;
12
13/// A vector expecting space-separated items.
14#[derive(Debug, Clone, PartialEq)]
15pub struct XmlVec<T> {
16    items: Vec<T>,
17}
18
19impl<T, E> FromStr for XmlVec<T>
20where
21    T: FromStr<Err = E>,
22{
23    type Err = E;
24
25    fn from_str(s: &str) -> Result<Self, Self::Err> {
26        let mut items = Vec::new();
27        for split in s.split(' ') {
28            let item = T::from_str(split)?;
29            items.push(item);
30        }
31        Ok(Self { items })
32    }
33}
34
35impl<T> XmlFromStr for XmlVec<T> where T: FromStr {}
36
37impl<T> Deref for XmlVec<T> {
38    type Target = Vec<T>;
39
40    fn deref(&self) -> &Self::Target {
41        &self.items
42    }
43}
44
45impl<T> DerefMut for XmlVec<T> {
46    fn deref_mut(&mut self) -> &mut Self::Target {
47        &mut self.items
48    }
49}
50
51impl<T> From<XmlVec<T>> for Vec<T> {
52    fn from(v: XmlVec<T>) -> Self {
53        v.items
54    }
55}
56
57impl<T> From<Vec<T>> for XmlVec<T> {
58    fn from(v: Vec<T>) -> Self {
59        Self { items: v }
60    }
61}
62
63/// An XML node that isn't deserialized into a more specific type.
64#[derive(Debug, Clone, PartialEq, Default)]
65pub struct XmlNode {
66    /// Tag name of the node.
67    pub name: String,
68    /// Attributes of the node.
69    pub attributes: Vec<XmlAttribute>,
70    /// Text content of the node.
71    pub text: Option<String>,
72    /// Child nodes.
73    pub children: Vec<XmlNode>,
74}
75
76/// An attribute of an [`XmlNode`].
77#[derive(Debug, Clone, PartialEq)]
78pub struct XmlAttribute {
79    /// Attribute name.
80    pub name: String,
81    /// Attribute value.
82    pub value: String,
83}
84
85#[async_trait::async_trait(?Send)]
86impl<B> Visitor<B> for XmlNode
87where
88    B: AsyncBufRead + Unpin,
89{
90    type Output = Self;
91
92    fn visit_tag(&mut self, name: &str) -> Result<(), Error> {
93        tracing::trace!("XmlNode deserializing element <{}>", name);
94        self.name = name.to_string();
95        Ok(())
96    }
97
98    fn visit_attribute(&mut self, name: &str, value: &str) -> Result<(), Error> {
99        self.attributes.push(XmlAttribute {
100            name: name.into(),
101            value: value.into(),
102        });
103        Ok(())
104    }
105
106    fn visit_text(&mut self, text: &str) -> Result<(), Error> {
107        self.text = Some(text.into());
108        Ok(())
109    }
110
111    async fn visit_child(
112        &mut self,
113        _name: &str,
114        reader: &mut crate::PeekingReader<B>,
115    ) -> Result<(), Error> {
116        self.children.push(
117            reader
118                .deserialize()
119                .await
120                .map_err(|e| Error::InnerDeserialiaztionError(self.name.clone(), Box::new(e)))?,
121        );
122        Ok(())
123    }
124
125    fn build(self) -> Result<Self::Output, Error> {
126        tracing::trace!("XmlNode done deserializing element <{}>", self.name);
127        Ok(self)
128    }
129}
130
131impl<B> FromXml<B> for XmlNode
132where
133    B: AsyncBufRead + Unpin,
134{
135    type Visitor = Self;
136}
137
138/// A generic visitor that will discard all errors thrown during build
139///
140/// This visitor can be wrapped around any existing visitor and its output type will be the wrapped visitor's output
141/// type wrapped in an [`Option`]. Errors thrown during build will be discarded and a [`None`]-value will be returned.
142pub struct DiscardErrorVisitor<V, B>
143where
144    B: AsyncBufRead + Unpin,
145    V: Visitor<B>,
146{
147    inner_visitor: V,
148    _phantom: core::marker::PhantomData<B>,
149}
150
151impl<V, B> Default for DiscardErrorVisitor<V, B>
152where
153    B: AsyncBufRead + Unpin,
154    V: Visitor<B> + Default,
155{
156    fn default() -> Self {
157        Self {
158            inner_visitor: V::default(),
159            _phantom: core::marker::PhantomData,
160        }
161    }
162}
163
164#[async_trait::async_trait(?Send)]
165impl<V, B> Visitor<B> for DiscardErrorVisitor<V, B>
166where
167    B: AsyncBufRead + Unpin,
168    V: Visitor<B>,
169{
170    type Output = Option<V::Output>;
171
172    fn start_name() -> Option<&'static str> {
173        V::start_name()
174    }
175
176    fn visit_attribute(&mut self, name: &str, value: &str) -> Result<(), Error> {
177        self.inner_visitor.visit_attribute(name, value)
178    }
179
180    async fn visit_child(
181        &mut self,
182        name: &str,
183        reader: &mut crate::PeekingReader<B>,
184    ) -> Result<(), Error> {
185        self.inner_visitor.visit_child(name, reader).await
186    }
187
188    fn visit_text(&mut self, text: &str) -> Result<(), Error> {
189        self.inner_visitor.visit_text(text)
190    }
191
192    fn build(self) -> Result<Self::Output, Error> {
193        match self.inner_visitor.build() {
194            Ok(t) => Ok(Some(t)),
195            Err(e) => {
196                tracing::trace!("discarding build error: {:?}", e);
197                Ok(None)
198            }
199        }
200    }
201}