opcua_types/variant/
xml.rs

1use crate::{
2    xml::*, Array, ByteString, DataValue, DateTime, DiagnosticInfo, ExpandedNodeId,
3    ExtensionObject, Guid, LocalizedText, NodeId, QualifiedName, StatusCode, UAString,
4};
5
6use super::{Variant, VariantScalarTypeId};
7
8impl XmlType for Variant {
9    const TAG: &'static str = "Variant";
10}
11impl VariantScalarTypeId {
12    /// Get the XML name of a variant type.
13    pub fn xml_name(&self) -> &'static str {
14        match self {
15            VariantScalarTypeId::Boolean => "Boolean",
16            VariantScalarTypeId::SByte => "SByte",
17            VariantScalarTypeId::Byte => "Byte",
18            VariantScalarTypeId::Int16 => "Int16",
19            VariantScalarTypeId::UInt16 => "UInt16",
20            VariantScalarTypeId::Int32 => "Int32",
21            VariantScalarTypeId::UInt32 => "UInt32",
22            VariantScalarTypeId::Int64 => "Int64",
23            VariantScalarTypeId::UInt64 => "UInt64",
24            VariantScalarTypeId::Float => "Float",
25            VariantScalarTypeId::Double => "Double",
26            VariantScalarTypeId::String => "String",
27            VariantScalarTypeId::DateTime => "DateTime",
28            VariantScalarTypeId::Guid => "Guid",
29            VariantScalarTypeId::ByteString => "ByteString",
30            VariantScalarTypeId::XmlElement => "XmlElement",
31            VariantScalarTypeId::NodeId => "NodeId",
32            VariantScalarTypeId::ExpandedNodeId => "ExpandedNodeId",
33            VariantScalarTypeId::StatusCode => "StatusCode",
34            VariantScalarTypeId::QualifiedName => "QualifiedName",
35            VariantScalarTypeId::LocalizedText => "LocalizedText",
36            VariantScalarTypeId::ExtensionObject => "ExtensionObject",
37            VariantScalarTypeId::DataValue => "DataValue",
38            VariantScalarTypeId::Variant => "Variant",
39            VariantScalarTypeId::DiagnosticInfo => "DiagnosticInfo",
40        }
41    }
42
43    /// Get a variant type ID from the XML name of the variant type.
44    pub fn from_xml_name(name: &str) -> Option<Self> {
45        Some(match name {
46            "Boolean" => VariantScalarTypeId::Boolean,
47            "SByte" => VariantScalarTypeId::SByte,
48            "Byte" => VariantScalarTypeId::Byte,
49            "Int16" => VariantScalarTypeId::Int16,
50            "UInt16" => VariantScalarTypeId::UInt16,
51            "Int32" => VariantScalarTypeId::Int32,
52            "UInt32" => VariantScalarTypeId::UInt32,
53            "Int64" => VariantScalarTypeId::Int64,
54            "UInt64" => VariantScalarTypeId::UInt64,
55            "Float" => VariantScalarTypeId::Float,
56            "Double" => VariantScalarTypeId::Double,
57            "String" => VariantScalarTypeId::String,
58            "DateTime" => VariantScalarTypeId::DateTime,
59            "Guid" => VariantScalarTypeId::Guid,
60            "ByteString" => VariantScalarTypeId::ByteString,
61            "XmlElement" => VariantScalarTypeId::XmlElement,
62            "NodeId" => VariantScalarTypeId::NodeId,
63            "ExpandedNodeId" => VariantScalarTypeId::ExpandedNodeId,
64            "StatusCode" => VariantScalarTypeId::StatusCode,
65            "QualifiedName" => VariantScalarTypeId::QualifiedName,
66            "LocalizedText" => VariantScalarTypeId::LocalizedText,
67            "ExtensionObject" => VariantScalarTypeId::ExtensionObject,
68            "DataValue" => VariantScalarTypeId::DataValue,
69            "Variant" => VariantScalarTypeId::Variant,
70            "DiagnosticInfo" => VariantScalarTypeId::DiagnosticInfo,
71            _ => return None,
72        })
73    }
74}
75
76impl Variant {
77    /// Get a default variant of the given type.
78    pub fn get_variant_default(ty: VariantScalarTypeId) -> Variant {
79        match ty {
80            VariantScalarTypeId::Boolean => Variant::Boolean(Default::default()),
81            VariantScalarTypeId::SByte => Variant::SByte(Default::default()),
82            VariantScalarTypeId::Byte => Variant::Byte(Default::default()),
83            VariantScalarTypeId::Int16 => Variant::Int16(Default::default()),
84            VariantScalarTypeId::UInt16 => Variant::UInt16(Default::default()),
85            VariantScalarTypeId::Int32 => Variant::Int32(Default::default()),
86            VariantScalarTypeId::UInt32 => Variant::UInt32(Default::default()),
87            VariantScalarTypeId::Int64 => Variant::Int64(Default::default()),
88            VariantScalarTypeId::UInt64 => Variant::UInt64(Default::default()),
89            VariantScalarTypeId::Float => Variant::Float(Default::default()),
90            VariantScalarTypeId::Double => Variant::Double(Default::default()),
91            VariantScalarTypeId::String => Variant::String(Default::default()),
92            VariantScalarTypeId::DateTime => Variant::DateTime(Default::default()),
93            VariantScalarTypeId::Guid => Variant::Guid(Default::default()),
94            VariantScalarTypeId::ByteString => Variant::ByteString(Default::default()),
95            VariantScalarTypeId::XmlElement => Variant::XmlElement(Default::default()),
96            VariantScalarTypeId::NodeId => Variant::NodeId(Default::default()),
97            VariantScalarTypeId::ExpandedNodeId => Variant::ExpandedNodeId(Default::default()),
98            VariantScalarTypeId::StatusCode => Variant::StatusCode(Default::default()),
99            VariantScalarTypeId::QualifiedName => Variant::QualifiedName(Default::default()),
100            VariantScalarTypeId::LocalizedText => Variant::LocalizedText(Default::default()),
101            VariantScalarTypeId::ExtensionObject => Variant::ExtensionObject(Default::default()),
102            VariantScalarTypeId::DataValue => Variant::DataValue(Default::default()),
103            VariantScalarTypeId::Variant => Variant::Variant(Default::default()),
104            VariantScalarTypeId::DiagnosticInfo => Variant::DiagnosticInfo(Default::default()),
105        }
106    }
107
108    /// Decode an XML variant value from stream, consuming the rest of the current element.
109    pub fn xml_decode_variant_value(
110        stream: &mut XmlStreamReader<&mut dyn std::io::Read>,
111        context: &Context<'_>,
112        key: &str,
113    ) -> EncodingResult<Self> {
114        if let Some(ty) = key.strip_prefix("ListOf") {
115            let ty = VariantScalarTypeId::from_xml_name(ty)
116                .ok_or_else(|| Error::decoding(format!("Invalid variant contents: {key}")))?;
117            let mut vec = Vec::new();
118            stream.iter_children_include_empty(
119                |key, stream, context| {
120                    let Some(stream) = stream else {
121                        let ty = VariantScalarTypeId::from_xml_name(&key).ok_or_else(|| {
122                            Error::decoding(format!("Invalid variant contents: {key}"))
123                        })?;
124                        vec.push(Self::get_variant_default(ty));
125                        return Ok(());
126                    };
127                    let r = Variant::xml_decode_variant_value(stream, context, &key)?;
128                    vec.push(r);
129                    Ok(())
130                },
131                context,
132            )?;
133            Ok(Self::Array(Box::new(
134                Array::new(ty, vec).map_err(Error::decoding)?,
135            )))
136        } else if key == "Matrix" {
137            let mut dims = Vec::new();
138            let mut elems = Vec::new();
139            stream.iter_children(
140                |key, stream, context| match key.as_str() {
141                    "Dimensions" => {
142                        dims = Vec::<i32>::decode(stream, context)?;
143                        Ok(())
144                    }
145                    "Elements" => stream.iter_children_include_empty(
146                        |key, stream, context| {
147                            let Some(stream) = stream else {
148                                let ty =
149                                    VariantScalarTypeId::from_xml_name(&key).ok_or_else(|| {
150                                        Error::decoding(format!("Invalid variant contents: {key}"))
151                                    })?;
152                                elems.push(Self::get_variant_default(ty));
153                                return Ok(());
154                            };
155                            let r = Variant::xml_decode_variant_value(stream, context, &key)?;
156                            elems.push(r);
157                            Ok(())
158                        },
159                        context,
160                    ),
161                    r => Err(Error::decoding(format!(
162                        "Invalid field in Matrix content: {r}"
163                    ))),
164                },
165                context,
166            )?;
167            // If you have an empty matrix there's no actual way to determine the type.
168            let scalar_type = elems
169                .first()
170                .and_then(|v| v.scalar_type_id())
171                .unwrap_or(VariantScalarTypeId::Int32);
172            Ok(Self::Array(Box::new(
173                Array::new_multi(
174                    scalar_type,
175                    elems,
176                    dims.into_iter()
177                        .map(|d| d.try_into())
178                        .collect::<Result<Vec<_>, _>>()
179                        .map_err(|_| {
180                            Error::decoding("Invalid array dimensions, must all be non-negative")
181                        })?,
182                )
183                .map_err(Error::decoding)?,
184            )))
185        } else {
186            Ok(match key {
187                "Boolean" => Self::Boolean(XmlDecodable::decode(stream, context)?),
188                "SByte" => Self::SByte(XmlDecodable::decode(stream, context)?),
189                "Byte" => Self::Byte(XmlDecodable::decode(stream, context)?),
190                "Int16" => Self::Int16(XmlDecodable::decode(stream, context)?),
191                "UInt16" => Self::UInt16(XmlDecodable::decode(stream, context)?),
192                "Int32" => Self::Int32(XmlDecodable::decode(stream, context)?),
193                "UInt32" => Self::UInt32(XmlDecodable::decode(stream, context)?),
194                "Int64" => Self::Int64(XmlDecodable::decode(stream, context)?),
195                "UInt64" => Self::UInt64(XmlDecodable::decode(stream, context)?),
196                "Float" => Self::Float(XmlDecodable::decode(stream, context)?),
197                "Double" => Self::Double(XmlDecodable::decode(stream, context)?),
198                "String" => Self::String(XmlDecodable::decode(stream, context)?),
199                "DateTime" => Self::DateTime(XmlDecodable::decode(stream, context)?),
200                "Guid" => Self::Guid(XmlDecodable::decode(stream, context)?),
201                "ByteString" => Self::ByteString(XmlDecodable::decode(stream, context)?),
202                "XmlElement" => Self::XmlElement(XmlDecodable::decode(stream, context)?),
203                "NodeId" => Self::NodeId(XmlDecodable::decode(stream, context)?),
204                "ExpandedNodeId" => Self::ExpandedNodeId(XmlDecodable::decode(stream, context)?),
205                "StatusCode" => Self::StatusCode(XmlDecodable::decode(stream, context)?),
206                "QualifiedName" => Self::QualifiedName(XmlDecodable::decode(stream, context)?),
207                "LocalizedText" => Self::LocalizedText(XmlDecodable::decode(stream, context)?),
208                "ExtensionObject" => Self::ExtensionObject(XmlDecodable::decode(stream, context)?),
209                "DataValue" => Self::DataValue(XmlDecodable::decode(stream, context)?),
210                "Variant" => Self::Variant(XmlDecodable::decode(stream, context)?),
211                "DiagnosticInfo" => Self::DiagnosticInfo(XmlDecodable::decode(stream, context)?),
212                r => return Err(Error::decoding(format!("Invalid variant type {r}"))),
213            })
214        }
215    }
216}
217
218impl XmlEncodable for Variant {
219    fn encode(
220        &self,
221        stream: &mut XmlStreamWriter<&mut dyn std::io::Write>,
222        ctx: &Context<'_>,
223    ) -> EncodingResult<()> {
224        match self {
225            Variant::Empty => return Ok(()),
226            Variant::Boolean(v) => stream.encode_child(bool::TAG, v, ctx)?,
227            Variant::SByte(v) => stream.encode_child(i8::TAG, v, ctx)?,
228            Variant::Byte(v) => stream.encode_child(u8::TAG, v, ctx)?,
229            Variant::Int16(v) => stream.encode_child(i16::TAG, v, ctx)?,
230            Variant::UInt16(v) => stream.encode_child(u16::TAG, v, ctx)?,
231            Variant::Int32(v) => stream.encode_child(i32::TAG, v, ctx)?,
232            Variant::UInt32(v) => stream.encode_child(u32::TAG, v, ctx)?,
233            Variant::Int64(v) => stream.encode_child(i64::TAG, v, ctx)?,
234            Variant::UInt64(v) => stream.encode_child(u64::TAG, v, ctx)?,
235            Variant::Float(v) => stream.encode_child(f32::TAG, v, ctx)?,
236            Variant::Double(v) => stream.encode_child(f64::TAG, v, ctx)?,
237            Variant::String(v) => stream.encode_child(UAString::TAG, v, ctx)?,
238            Variant::DateTime(v) => stream.encode_child(DateTime::TAG, v, ctx)?,
239            Variant::Guid(v) => stream.encode_child(Guid::TAG, v, ctx)?,
240            Variant::StatusCode(v) => stream.encode_child(StatusCode::TAG, v, ctx)?,
241            Variant::ByteString(v) => stream.encode_child(ByteString::TAG, v, ctx)?,
242            Variant::XmlElement(v) => stream.encode_child(crate::XmlElement::TAG, v, ctx)?,
243            Variant::QualifiedName(v) => stream.encode_child(QualifiedName::TAG, v, ctx)?,
244            Variant::LocalizedText(v) => stream.encode_child(LocalizedText::TAG, v, ctx)?,
245            Variant::NodeId(v) => stream.encode_child(NodeId::TAG, v, ctx)?,
246            Variant::ExpandedNodeId(v) => stream.encode_child(ExpandedNodeId::TAG, v, ctx)?,
247            Variant::ExtensionObject(v) => stream.encode_child(ExtensionObject::TAG, v, ctx)?,
248            Variant::Variant(v) => stream.encode_child(Variant::TAG, v, ctx)?,
249            Variant::DataValue(v) => stream.encode_child(DataValue::TAG, v, ctx)?,
250            Variant::DiagnosticInfo(v) => stream.encode_child(DiagnosticInfo::TAG, v, ctx)?,
251            Variant::Array(v) => {
252                let xml_name = v.value_type.xml_name();
253                if let Some(dims) = v.dimensions.as_ref() {
254                    if dims.len() > 1 {
255                        stream.write_start("Matrix")?;
256                        // For some incredibly annoying reason, OPC-UA insists that dimensions be
257                        // encoded as _signed_ integers. For other encoders it's irrelevant,
258                        // but it matters for XML.
259                        let dims: Vec<_> = dims.iter().map(|d| *d as i32).collect();
260                        stream.encode_child("Dimensions", &dims, ctx)?;
261
262                        stream.write_start("Elements")?;
263                        for item in &v.values {
264                            item.encode(stream, ctx)?;
265                        }
266                        stream.write_end("Elements")?;
267                        stream.write_end("Matrix")?;
268                        return Ok(());
269                    }
270                }
271                let tag_name = format!("ListOf{xml_name}");
272                stream.write_start(&tag_name)?;
273                for item in &v.values {
274                    item.encode(stream, ctx)?;
275                }
276                stream.write_end(&tag_name)?;
277            }
278        }
279
280        Ok(())
281    }
282}
283
284impl XmlDecodable for Variant {
285    fn decode(
286        stream: &mut XmlStreamReader<&mut dyn std::io::Read>,
287        context: &Context<'_>,
288    ) -> Result<Self, Error> {
289        stream
290            .get_first_child(
291                |key, stream, ctx| Self::xml_decode_variant_value(stream, ctx, &key),
292                context,
293            )
294            .map(|v| v.unwrap_or(Variant::Empty))
295    }
296}