opcua_xml/
lib.rs

1#![warn(missing_docs)]
2
3//! Core utilities for working with various OPC-UA XML schemas.
4//!
5//! This crate defines methods for decoding.
6//!
7//! - A subset of the XMLSchema schema in [schema::xml_schema].
8//! - XML schema for OPC-UA BSD files in [schema::opc_binary_schema]
9//! - XML schema for OPC-UA types defined in XSD files in [schema::opc_ua_types]
10//! - XML schema for NodeSet2 files in [schema::ua_node_set]
11//!
12//! XML parsing is done with the `roxmltree` crate.
13
14use ext::NodeExt;
15use roxmltree::Node;
16
17mod encoding;
18mod error;
19mod ext;
20pub mod schema;
21
22pub use encoding::{XmlReadError, XmlStreamReader, XmlStreamWriter, XmlWriteError};
23pub use quick_xml::events;
24
25pub use error::{XmlError, XmlErrorInner};
26pub use schema::opc_binary_schema::load_bsd_file;
27pub use schema::ua_node_set::load_nodeset2_file;
28pub use schema::xml_schema::load_xsd_schema;
29
30pub use schema::opc_ua_types::XmlElement;
31
32/// Get a type by loading it from a string containing an XML document.
33pub fn from_str<'a, T: XmlLoad<'a>>(data: &'a str) -> Result<T, XmlError> {
34    let doc = roxmltree::Document::parse(data).map_err(|e| XmlError {
35        span: 0..data.len(),
36        error: e.into(),
37    })?;
38    T::load(&doc.root().first_child().ok_or_else(|| XmlError {
39        span: doc.root().range(),
40        error: error::XmlErrorInner::MissingField("Root".to_owned()),
41    })?)
42}
43
44/// Trait for types that can be loaded from an XML node.
45pub trait XmlLoad<'input>: Sized {
46    /// Load Self from an XML node.
47    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError>;
48}
49
50/// Trait for types that can be loaded from an XML node body.
51pub trait FromValue: Sized {
52    /// Load Self from the body of a node. `v` is the value being parsed, `attr` and `node` are
53    /// given for context and error handling.
54    fn from_value(node: &Node<'_, '_>, attr: &str, v: &str) -> Result<Self, XmlError>;
55}
56
57macro_rules! from_int {
58    ($ty:ident) => {
59        impl FromValue for $ty {
60            fn from_value(node: &Node<'_, '_>, attr: &str, v: &str) -> Result<Self, XmlError> {
61                v.parse().map_err(|e| XmlError::parse_int(node, attr, e))
62            }
63        }
64    };
65}
66
67from_int!(i64);
68from_int!(u64);
69from_int!(i32);
70from_int!(u32);
71from_int!(i16);
72from_int!(u16);
73from_int!(i8);
74from_int!(u8);
75
76impl FromValue for String {
77    fn from_value(_node: &Node<'_, '_>, _attr: &str, v: &str) -> Result<Self, XmlError> {
78        Ok(v.to_owned())
79    }
80}
81
82impl FromValue for f64 {
83    fn from_value(node: &Node<'_, '_>, attr: &str, v: &str) -> Result<Self, XmlError> {
84        v.parse().map_err(|e| XmlError::parse_float(node, attr, e))
85    }
86}
87
88impl FromValue for f32 {
89    fn from_value(node: &Node<'_, '_>, attr: &str, v: &str) -> Result<Self, XmlError> {
90        v.parse().map_err(|e| XmlError::parse_float(node, attr, e))
91    }
92}
93
94impl FromValue for bool {
95    fn from_value(node: &Node<'_, '_>, attr: &str, v: &str) -> Result<Self, XmlError> {
96        v.parse().map_err(|e| XmlError::parse_bool(node, attr, e))
97    }
98}
99
100impl<'input, T> XmlLoad<'input> for T
101where
102    T: FromValue + Default,
103{
104    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
105        T::from_value(node, "content", node.try_contents().unwrap_or_default())
106    }
107}