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