1mod builtins;
6mod encoding;
7
8pub use crate::{Context, EncodingResult, Error};
9pub use encoding::{XmlDecodable, XmlEncodable, XmlReadExt, XmlType, XmlWriteExt};
10pub use opcua_xml::{XmlStreamReader, XmlStreamWriter};
11
12use std::{
13 io::{Cursor, Read},
14 str::{from_utf8, FromStr},
15};
16
17pub use opcua_xml::schema::opc_ua_types::XmlElement;
18use tracing::warn;
19
20use crate::{
21 Array, ByteString, ExpandedNodeId, ExtensionObject, LocalizedText, NodeId, QualifiedName,
22 StatusCode, UninitializedIndex, Variant, VariantScalarTypeId,
23};
24
25impl From<UninitializedIndex> for Error {
26 fn from(value: UninitializedIndex) -> Self {
27 Self::decoding(format!("Uninitialized index {}", value.0))
28 }
29}
30
31fn mk_node_id(node_id: &opc_ua_types::NodeId, ctx: &Context<'_>) -> Result<NodeId, Error> {
32 let Some(idf) = &node_id.identifier else {
33 return Ok(NodeId::null());
34 };
35 let Ok(mut parsed) = NodeId::from_str(idf) else {
36 return Err(Error::decoding(format!("Invalid node ID: {idf}")));
37 };
38 parsed.namespace = ctx.resolve_namespace_index(parsed.namespace)?;
39 Ok(parsed)
40}
41
42use opcua_xml::{
43 events::Event,
44 schema::opc_ua_types::{self, Variant as XmlVariant},
45};
46
47pub(crate) fn enter_first_tag(
49 stream: &mut XmlStreamReader<&mut dyn Read>,
50) -> EncodingResult<Option<String>> {
51 loop {
52 match stream.next_event()? {
53 Event::Start(s) => {
54 let local_name = s.local_name();
55 let name = from_utf8(local_name.as_ref())?;
56 return Ok(Some(name.to_owned()));
57 }
58 Event::End(_) | Event::Eof | Event::Empty(_) => return Ok(None),
59 _ => (),
60 }
61 }
62}
63
64fn mk_extension_object(
65 val: &opc_ua_types::ExtensionObject,
66 ctx: &Context<'_>,
67) -> EncodingResult<ExtensionObject> {
68 let Some(body) = &val.body else {
69 return Ok(ExtensionObject::null());
70 };
71 let Some(data) = &body.raw else {
72 return Ok(ExtensionObject::null());
73 };
74 let Some(type_id) = &val.type_id else {
75 return Err(Error::decoding("Extension object missing type ID"));
76 };
77 let node_id = mk_node_id(type_id, ctx)?;
78 let mut cursor = Cursor::new(data.as_bytes());
79 let mut stream = XmlStreamReader::new(&mut cursor as &mut dyn Read);
80 if let Some(name) = enter_first_tag(&mut stream)? {
82 ctx.load_from_xml(&node_id, &mut stream, &name)
83 } else {
84 Ok(ExtensionObject::null())
85 }
86}
87
88impl Variant {
89 pub fn from_nodeset(val: &XmlVariant, ctx: &Context<'_>) -> EncodingResult<Variant> {
93 Ok(match val {
94 XmlVariant::Boolean(v) => (*v).into(),
95 XmlVariant::ListOfBoolean(v) => v.into(),
96 XmlVariant::SByte(v) => (*v).into(),
97 XmlVariant::ListOfSByte(v) => v.into(),
98 XmlVariant::Byte(v) => (*v).into(),
99 XmlVariant::ListOfByte(v) => v.into(),
100 XmlVariant::Int16(v) => (*v).into(),
101 XmlVariant::ListOfInt16(v) => v.into(),
102 XmlVariant::UInt16(v) => (*v).into(),
103 XmlVariant::ListOfUInt16(v) => v.into(),
104 XmlVariant::Int32(v) => (*v).into(),
105 XmlVariant::ListOfInt32(v) => v.into(),
106 XmlVariant::UInt32(v) => (*v).into(),
107 XmlVariant::ListOfUInt32(v) => v.into(),
108 XmlVariant::Int64(v) => (*v).into(),
109 XmlVariant::ListOfInt64(v) => v.into(),
110 XmlVariant::UInt64(v) => (*v).into(),
111 XmlVariant::ListOfUInt64(v) => v.into(),
112 XmlVariant::Float(v) => (*v).into(),
113 XmlVariant::ListOfFloat(v) => v.into(),
114 XmlVariant::Double(v) => (*v).into(),
115 XmlVariant::ListOfDouble(v) => v.into(),
116 XmlVariant::String(v) => v.clone().into(),
117 XmlVariant::ListOfString(v) => v.into(),
118 XmlVariant::DateTime(v) => (*v).into(),
119 XmlVariant::ListOfDateTime(v) => v.into(),
120 XmlVariant::Guid(v) => (*v).into(),
121 XmlVariant::ListOfGuid(v) => v.into(),
122 XmlVariant::ByteString(b) => ByteString::from_base64(b.trim())
123 .unwrap_or_else(|| {
124 warn!("Invalid byte string: {b}");
125 ByteString::null()
126 })
127 .into(),
128 XmlVariant::ListOfByteString(v) => v
129 .iter()
130 .map(|b| {
131 ByteString::from_base64(b.trim()).unwrap_or_else(|| {
132 warn!("Invalid byte string: {b}");
133 ByteString::null()
134 })
135 })
136 .collect::<Vec<_>>()
137 .into(),
138 XmlVariant::XmlElement(vec) => Variant::XmlElement(
139 vec.iter()
140 .map(|v| v.to_string().trim().to_owned())
141 .collect::<String>()
142 .into(),
143 ),
144 XmlVariant::ListOfXmlElement(vec) => Variant::Array(Box::new(Array {
145 value_type: VariantScalarTypeId::XmlElement,
146 values: vec
147 .iter()
148 .map(|v| {
149 Variant::XmlElement(
150 v.iter()
151 .map(|vv| vv.to_string().trim().to_string())
152 .collect::<String>()
153 .into(),
154 )
155 })
156 .collect(),
157 dimensions: None,
158 })),
159 XmlVariant::QualifiedName(q) => QualifiedName::new(
160 ctx.resolve_namespace_index(q.namespace_index.unwrap_or(0))?,
161 q.name.as_deref().unwrap_or("").trim(),
162 )
163 .into(),
164 XmlVariant::ListOfQualifiedName(v) => v
165 .iter()
166 .map(|q| {
167 Ok(QualifiedName::new(
168 ctx.resolve_namespace_index(q.namespace_index.unwrap_or(0))?,
169 q.name.as_deref().unwrap_or("").trim(),
170 ))
171 })
172 .collect::<Result<Vec<QualifiedName>, Error>>()?
173 .into(),
174 XmlVariant::LocalizedText(l) => LocalizedText::new(
175 l.locale.as_deref().unwrap_or("").trim(),
176 l.text.as_deref().unwrap_or("").trim(),
177 )
178 .into(),
179 XmlVariant::ListOfLocalizedText(v) => v
180 .iter()
181 .map(|l| {
182 LocalizedText::new(
183 l.locale.as_deref().unwrap_or("").trim(),
184 l.text.as_deref().unwrap_or("").trim(),
185 )
186 })
187 .collect::<Vec<_>>()
188 .into(),
189 XmlVariant::NodeId(node_id) => mk_node_id(node_id, ctx)?.into(),
190 XmlVariant::ListOfNodeId(v) => v
191 .iter()
192 .map(|node_id| mk_node_id(node_id, ctx))
193 .collect::<Result<Vec<_>, _>>()?
194 .into(),
195 XmlVariant::ExpandedNodeId(node_id) => {
196 ExpandedNodeId::new(mk_node_id(node_id, ctx)?).into()
197 }
198 XmlVariant::ListOfExpandedNodeId(v) => v
199 .iter()
200 .map(|node_id| Ok(ExpandedNodeId::new(mk_node_id(node_id, ctx)?)))
201 .collect::<Result<Vec<_>, Error>>()?
202 .into(),
203 XmlVariant::ExtensionObject(val) => mk_extension_object(val, ctx)?.into(),
204 XmlVariant::ListOfExtensionObject(v) => v
205 .iter()
206 .map(|val| mk_extension_object(val, ctx))
207 .collect::<Result<Vec<_>, _>>()?
208 .into(),
209 XmlVariant::Variant(variant) => {
210 let inner = Variant::from_nodeset(variant, ctx)?;
211 Variant::Variant(Box::new(inner))
212 }
213 XmlVariant::ListOfVariant(vec) => Variant::Array(Box::new(Array {
214 value_type: VariantScalarTypeId::Variant,
215 values: vec
216 .iter()
217 .map(|v| Ok(Variant::Variant(Box::new(Variant::from_nodeset(v, ctx)?))))
218 .collect::<Result<Vec<_>, Error>>()?,
219 dimensions: None,
220 })),
221 XmlVariant::StatusCode(status_code) => StatusCode::from(status_code.code).into(),
222 XmlVariant::ListOfStatusCode(vec) => vec
223 .iter()
224 .map(|v| StatusCode::from(v.code))
225 .collect::<Vec<_>>()
226 .into(),
227 })
228 }
229}