sonos/
xmlutil.rs

1use crate::upnp::{DecodeXml, EncodeXml};
2use instant_xml::{Deserializer, FromXml, Id, Kind, ToXml};
3
4/// This is a wrapper container that can be used to adapt a
5/// scalar embedded xml string value into a more rich Rust
6/// type representation.
7#[derive(Debug, PartialEq, Clone, Default)]
8pub struct DecodeXmlString<T>(pub Option<T>)
9where
10    T: DecodeXml;
11
12impl<'xml, T> FromXml<'xml> for DecodeXmlString<T>
13where
14    T: DecodeXml,
15{
16    #[inline]
17    fn matches(id: Id<'_>, field: Option<Id<'_>>) -> bool {
18        match field {
19            Some(field) => id == field,
20            None => false,
21        }
22    }
23
24    fn deserialize<'cx>(
25        target: &mut <Self as FromXml<'_>>::Accumulator,
26        field: &'static str,
27        deserializer: &mut Deserializer<'cx, '_>,
28    ) -> std::result::Result<(), instant_xml::Error> {
29        if target.is_some() {
30            return Err(instant_xml::Error::DuplicateValue(field));
31        }
32
33        match deserializer.take_str()? {
34            Some(value) => {
35                // eprintln!("decode: {value}");
36
37                let is_empty = value.trim().is_empty() || value == "NOT_IMPLEMENTED";
38
39                if !is_empty {
40                    let parsed = T::decode_xml(&value).map_err(|err| {
41                        instant_xml::Error::Other(format!(
42                            "failed to decode_xml for {field}: `{err:#}` {value}"
43                        ))
44                    })?;
45                    target.replace(DecodeXmlString(Some(parsed)));
46                }
47                Ok(())
48            }
49            None => {
50                // There is no value
51                Ok(())
52            }
53        }
54    }
55
56    type Accumulator = Option<Self>;
57    // We appear to be a string in the doc
58    const KIND: Kind = Kind::Scalar;
59}
60
61impl<T> ToXml for DecodeXmlString<T>
62where
63    T: DecodeXml,
64    T: EncodeXml,
65{
66    fn serialize<W>(
67        &self,
68        id: Option<Id<'_>>,
69        serializer: &mut instant_xml::Serializer<'_, W>,
70    ) -> std::result::Result<(), instant_xml::Error>
71    where
72        W: std::fmt::Write + ?Sized,
73    {
74        let encoded = match &self.0 {
75            Some(inner) => inner.encode_xml()?,
76            None => String::new(),
77        };
78        encoded.serialize(id, serializer)
79    }
80}
81
82impl<T: DecodeXml> DecodeXmlString<T> {
83    pub fn into_inner(self) -> Option<T> {
84        self.0
85    }
86}
87
88impl<T: DecodeXml> std::ops::Deref for DecodeXmlString<T> {
89    type Target = Option<T>;
90    fn deref(&self) -> &Option<T> {
91        &self.0
92    }
93}
94
95impl<T: DecodeXml> From<T> for DecodeXmlString<T> {
96    fn from(value: T) -> DecodeXmlString<T> {
97        DecodeXmlString(Some(value))
98    }
99}
100
101impl<T: DecodeXml> From<Option<T>> for DecodeXmlString<T> {
102    fn from(value: Option<T>) -> DecodeXmlString<T> {
103        DecodeXmlString(value)
104    }
105}