use std::{
io::{Cursor, Read, Write},
str::{from_utf8, Utf8Error},
};
use opcua_xml::{XmlReadError, XmlStreamReader, XmlStreamWriter, XmlWriteError};
use crate::{Context, EncodingResult, Error, UaNullable};
impl From<XmlReadError> for Error {
fn from(value: XmlReadError) -> Self {
Self::decoding(value)
}
}
impl From<XmlWriteError> for Error {
fn from(value: XmlWriteError) -> Self {
Self::encoding(value)
}
}
impl From<Utf8Error> for Error {
fn from(value: Utf8Error) -> Self {
Self::decoding(value)
}
}
pub trait XmlType {
const TAG: &'static str;
fn tag(&self) -> &str {
Self::TAG
}
}
pub trait XmlDecodable: XmlType {
fn decode(
read: &mut XmlStreamReader<&mut dyn Read>,
context: &Context<'_>,
) -> Result<Self, Error>
where
Self: Sized;
}
pub trait XmlEncodable: XmlType + UaNullable {
fn encode(
&self,
writer: &mut XmlStreamWriter<&mut dyn Write>,
context: &Context<'_>,
) -> EncodingResult<()>;
}
pub trait XmlWriteExt {
fn encode_child<T: XmlEncodable + ?Sized>(
&mut self,
tag: &str,
value: &T,
context: &Context<'_>,
) -> EncodingResult<()>;
}
impl XmlWriteExt for XmlStreamWriter<&mut dyn Write> {
fn encode_child<T: XmlEncodable + ?Sized>(
&mut self,
tag: &str,
value: &T,
context: &Context<'_>,
) -> EncodingResult<()> {
self.write_start(tag)?;
value.encode(self, context)?;
self.write_end(tag)?;
Ok(())
}
}
pub trait XmlReadExt {
fn iter_children_include_empty(
&mut self,
process: impl FnMut(String, Option<&mut Self>, &Context<'_>) -> EncodingResult<()>,
context: &Context<'_>,
) -> EncodingResult<()>;
fn iter_children(
&mut self,
cb: impl FnMut(String, &mut Self, &Context<'_>) -> EncodingResult<()>,
context: &Context<'_>,
) -> EncodingResult<()>;
fn get_single_child<T>(
&mut self,
tag: &str,
cb: impl FnMut(&mut Self, &Context<'_>) -> Result<T, Error>,
context: &Context<'_>,
) -> EncodingResult<Option<T>>;
fn decode_single_child<T: XmlDecodable>(
&mut self,
tag: &str,
context: &Context<'_>,
) -> Result<Option<T>, Error>;
fn get_first_child<T>(
&mut self,
cb: impl FnOnce(String, &mut Self, &Context<'_>) -> Result<T, Error>,
context: &Context<'_>,
) -> EncodingResult<Option<T>>;
}
impl XmlReadExt for XmlStreamReader<&mut dyn Read> {
fn iter_children_include_empty(
&mut self,
mut process: impl FnMut(String, Option<&mut Self>, &Context<'_>) -> EncodingResult<()>,
context: &Context<'_>,
) -> EncodingResult<()> {
loop {
match self.next_event()? {
opcua_xml::events::Event::Start(s) => {
let local_name = s.local_name();
let name = from_utf8(local_name.as_ref())?;
process(name.to_owned(), Some(self), context)?;
}
opcua_xml::events::Event::Empty(s) => {
let local_name = s.local_name();
let name = from_utf8(local_name.as_ref())?;
process(name.to_owned(), None, context)?;
}
opcua_xml::events::Event::End(_) | opcua_xml::events::Event::Eof => {
return Ok(());
}
_ => (),
}
}
}
fn iter_children(
&mut self,
mut process: impl FnMut(String, &mut Self, &Context<'_>) -> EncodingResult<()>,
context: &Context<'_>,
) -> EncodingResult<()> {
loop {
match self.next_event()? {
opcua_xml::events::Event::Start(s) => {
let local_name = s.local_name();
let name = from_utf8(local_name.as_ref())?;
process(name.to_owned(), self, context)?;
}
opcua_xml::events::Event::End(_) | opcua_xml::events::Event::Eof => {
return Ok(());
}
_ => (),
}
}
}
fn get_single_child<T>(
&mut self,
tag: &str,
cb: impl FnOnce(&mut Self, &Context<'_>) -> Result<T, Error>,
context: &Context<'_>,
) -> EncodingResult<Option<T>> {
let mut cb = Some(cb);
let mut res = None;
self.iter_children(
|key, reader, ctx| {
if tag == key {
if let Some(cb) = cb.take() {
res = Some(cb(reader, ctx)?);
return Ok(());
}
}
reader.skip_value()?;
Ok(())
},
context,
)?;
Ok(res)
}
fn decode_single_child<T: XmlDecodable>(
&mut self,
tag: &str,
context: &Context<'_>,
) -> EncodingResult<Option<T>> {
self.get_single_child(tag, |reader, ctx| T::decode(reader, ctx), context)
}
fn get_first_child<T>(
&mut self,
cb: impl FnOnce(String, &mut Self, &Context<'_>) -> Result<T, Error>,
context: &Context<'_>,
) -> EncodingResult<Option<T>> {
let mut cb = Some(cb);
let mut res = None;
self.iter_children(
|key, reader, ctx| {
if let Some(cb) = cb.take() {
res = Some(cb(key, reader, ctx)?);
return Ok(());
}
reader.skip_value()?;
Ok(())
},
context,
)?;
Ok(res)
}
}
pub fn to_bytes<T: XmlEncodable>(value: &T, ctx: &Context<'_>) -> EncodingResult<Vec<u8>> {
let mut res = Vec::new();
let mut stream = Cursor::new(&mut res);
let mut writer = XmlStreamWriter::new(&mut stream as &mut dyn Write);
value.encode(&mut writer, ctx)?;
Ok(res)
}
pub fn to_string<T: XmlEncodable>(value: &T, ctx: &Context<'_>) -> EncodingResult<String> {
let bytes = to_bytes(value, ctx)?;
String::from_utf8(bytes).map_err(Error::decoding)
}
pub fn from_bytes<T: XmlDecodable>(data: &[u8], ctx: &Context<'_>) -> EncodingResult<T> {
let mut cursor = Cursor::new(data);
let mut reader = XmlStreamReader::new(&mut cursor as &mut dyn Read);
T::decode(&mut reader, ctx)
}