opcua_types/xml/
encoding.rs

1use std::{
2    io::{Read, Write},
3    str::{from_utf8, Utf8Error},
4};
5
6use opcua_xml::{XmlReadError, XmlStreamReader, XmlStreamWriter, XmlWriteError};
7
8use crate::{Context, EncodingResult, Error, UaNullable};
9
10impl From<XmlReadError> for Error {
11    fn from(value: XmlReadError) -> Self {
12        Self::decoding(value)
13    }
14}
15
16impl From<XmlWriteError> for Error {
17    fn from(value: XmlWriteError) -> Self {
18        Self::encoding(value)
19    }
20}
21
22impl From<Utf8Error> for Error {
23    fn from(value: Utf8Error) -> Self {
24        Self::decoding(value)
25    }
26}
27
28/// Trait for XML type name. Used by both XmlDecodable and XmlEncodable.
29pub trait XmlType {
30    /// The static fallback tag for this type.
31    /// Convenience feature, but also used in nested types.
32    const TAG: &'static str;
33    /// The XML tag name for this type.
34    fn tag(&self) -> &str {
35        Self::TAG
36    }
37}
38
39/// Trait for types that can be decoded from XML.
40pub trait XmlDecodable: XmlType {
41    /// Decode a value from an XML stream.
42    fn decode(
43        read: &mut XmlStreamReader<&mut dyn Read>,
44        context: &Context<'_>,
45    ) -> Result<Self, Error>
46    where
47        Self: Sized;
48}
49
50/// Trait for types that can be encoded to XML.
51pub trait XmlEncodable: XmlType + UaNullable {
52    /// Encode a value to an XML stream.
53    fn encode(
54        &self,
55        writer: &mut XmlStreamWriter<&mut dyn Write>,
56        context: &Context<'_>,
57    ) -> EncodingResult<()>;
58}
59
60/// Extensions for XmlStreamWriter.
61pub trait XmlWriteExt {
62    /// Encode a value as a child element.
63    fn encode_child<T: XmlEncodable + ?Sized>(
64        &mut self,
65        tag: &str,
66        value: &T,
67        context: &Context<'_>,
68    ) -> EncodingResult<()>;
69}
70
71impl XmlWriteExt for XmlStreamWriter<&mut dyn Write> {
72    fn encode_child<T: XmlEncodable + ?Sized>(
73        &mut self,
74        tag: &str,
75        value: &T,
76        context: &Context<'_>,
77    ) -> EncodingResult<()> {
78        self.write_start(tag)?;
79        value.encode(self, context)?;
80        self.write_end(tag)?;
81
82        Ok(())
83    }
84}
85
86/// Extensions for XmlStreamReader.
87pub trait XmlReadExt {
88    /// Iterate over children, calling the provided callback for each tag.
89    /// The callback must consume the tag, unless no reader is provided,
90    /// in which case the tag is already closed.
91    fn iter_children_include_empty(
92        &mut self,
93        process: impl FnMut(String, Option<&mut Self>, &Context<'_>) -> EncodingResult<()>,
94        context: &Context<'_>,
95    ) -> EncodingResult<()>;
96    /// Iterate over children, calling the provided callback for each tag.
97    /// The callback must consume the tag.
98    fn iter_children(
99        &mut self,
100        cb: impl FnMut(String, &mut Self, &Context<'_>) -> EncodingResult<()>,
101        context: &Context<'_>,
102    ) -> EncodingResult<()>;
103
104    /// Call a callback for a single child element. This will consume the
105    /// current node.
106    fn get_single_child<T>(
107        &mut self,
108        tag: &str,
109        cb: impl FnMut(&mut Self, &Context<'_>) -> Result<T, Error>,
110        context: &Context<'_>,
111    ) -> EncodingResult<Option<T>>;
112
113    /// Decode a single child element. This will consume the
114    /// current node.
115    fn decode_single_child<T: XmlDecodable>(
116        &mut self,
117        tag: &str,
118        context: &Context<'_>,
119    ) -> Result<Option<T>, Error>;
120
121    /// Call a callback for the first child element, then skip the rest. This will
122    /// consume the current node.
123    fn get_first_child<T>(
124        &mut self,
125        cb: impl FnOnce(String, &mut Self, &Context<'_>) -> Result<T, Error>,
126        context: &Context<'_>,
127    ) -> EncodingResult<Option<T>>;
128}
129
130impl XmlReadExt for XmlStreamReader<&mut dyn Read> {
131    fn iter_children_include_empty(
132        &mut self,
133        mut process: impl FnMut(String, Option<&mut Self>, &Context<'_>) -> EncodingResult<()>,
134        context: &Context<'_>,
135    ) -> EncodingResult<()> {
136        loop {
137            match self.next_event()? {
138                opcua_xml::events::Event::Start(s) => {
139                    let local_name = s.local_name();
140                    let name = from_utf8(local_name.as_ref())?;
141                    process(name.to_owned(), Some(self), context)?;
142                }
143                opcua_xml::events::Event::Empty(s) => {
144                    let local_name = s.local_name();
145                    let name = from_utf8(local_name.as_ref())?;
146                    process(name.to_owned(), None, context)?;
147                }
148                opcua_xml::events::Event::End(_) | opcua_xml::events::Event::Eof => {
149                    return Ok(());
150                }
151                _ => (),
152            }
153        }
154    }
155
156    fn iter_children(
157        &mut self,
158        mut process: impl FnMut(String, &mut Self, &Context<'_>) -> EncodingResult<()>,
159        context: &Context<'_>,
160    ) -> EncodingResult<()> {
161        loop {
162            match self.next_event()? {
163                opcua_xml::events::Event::Start(s) => {
164                    let local_name = s.local_name();
165                    let name = from_utf8(local_name.as_ref())?;
166                    process(name.to_owned(), self, context)?;
167                }
168                opcua_xml::events::Event::End(_) | opcua_xml::events::Event::Eof => {
169                    return Ok(());
170                }
171                _ => (),
172            }
173        }
174    }
175
176    fn get_single_child<T>(
177        &mut self,
178        tag: &str,
179        cb: impl FnOnce(&mut Self, &Context<'_>) -> Result<T, Error>,
180        context: &Context<'_>,
181    ) -> EncodingResult<Option<T>> {
182        let mut cb = Some(cb);
183        let mut res = None;
184        self.iter_children(
185            |key, reader, ctx| {
186                if tag == key {
187                    if let Some(cb) = cb.take() {
188                        res = Some(cb(reader, ctx)?);
189                        return Ok(());
190                    }
191                }
192                reader.skip_value()?;
193                Ok(())
194            },
195            context,
196        )?;
197        Ok(res)
198    }
199
200    fn decode_single_child<T: XmlDecodable>(
201        &mut self,
202        tag: &str,
203        context: &Context<'_>,
204    ) -> EncodingResult<Option<T>> {
205        self.get_single_child(tag, |reader, ctx| T::decode(reader, ctx), context)
206    }
207
208    fn get_first_child<T>(
209        &mut self,
210        cb: impl FnOnce(String, &mut Self, &Context<'_>) -> Result<T, Error>,
211        context: &Context<'_>,
212    ) -> EncodingResult<Option<T>> {
213        let mut cb = Some(cb);
214        let mut res = None;
215        self.iter_children(
216            |key, reader, ctx| {
217                if let Some(cb) = cb.take() {
218                    res = Some(cb(key, reader, ctx)?);
219                    return Ok(());
220                }
221                reader.skip_value()?;
222                Ok(())
223            },
224            context,
225        )?;
226        Ok(res)
227    }
228}