1use std::{
8 self,
9 borrow::Cow,
10 fmt,
11 io::{Read, Write},
12 str::FromStr,
13 sync::LazyLock,
14};
15
16use crate::{
17 byte_string::ByteString,
18 encoding::{BinaryDecodable, BinaryEncodable, EncodingResult},
19 guid::Guid,
20 node_id::{Identifier, NodeId},
21 read_u16, read_u32, read_u8,
22 status_code::StatusCode,
23 string::*,
24 write_u16, write_u32, write_u8, Context, Error, NamespaceMap, UaNullable,
25};
26
27#[derive(PartialEq, Debug, Clone, Eq, Hash, Default)]
29pub struct ExpandedNodeId {
30 pub node_id: NodeId,
32 pub namespace_uri: UAString,
34 pub server_index: u32,
36}
37
38impl UaNullable for ExpandedNodeId {
39 fn is_ua_null(&self) -> bool {
40 self.is_null()
41 }
42}
43
44#[cfg(feature = "json")]
45mod json {
46 use std::io::{Read, Write};
72 use std::str::FromStr;
73
74 use crate::{json::*, ByteString, Error, Guid};
75
76 use super::{ExpandedNodeId, Identifier, NodeId, UAString};
77 enum RawIdentifier {
78 String(String),
79 Integer(u32),
80 }
81
82 impl JsonEncodable for ExpandedNodeId {
83 fn encode(
84 &self,
85 stream: &mut JsonStreamWriter<&mut dyn Write>,
86 ctx: &crate::json::Context<'_>,
87 ) -> super::EncodingResult<()> {
88 stream.begin_object()?;
89 match &self.node_id.identifier {
90 super::Identifier::Numeric(n) => {
91 stream.name("Id")?;
92 stream.number_value(*n)?;
93 }
94 super::Identifier::String(uastring) => {
95 stream.name("IdType")?;
96 stream.number_value(1)?;
97 stream.name("Id")?;
98 JsonEncodable::encode(uastring, stream, ctx)?;
99 }
100 super::Identifier::Guid(guid) => {
101 stream.name("IdType")?;
102 stream.number_value(2)?;
103 stream.name("Id")?;
104 JsonEncodable::encode(guid, stream, ctx)?;
105 }
106 super::Identifier::ByteString(byte_string) => {
107 stream.name("IdType")?;
108 stream.number_value(3)?;
109 stream.name("Id")?;
110 JsonEncodable::encode(byte_string, stream, ctx)?;
111 }
112 }
113 if !self.namespace_uri.is_null() {
114 stream.name("Namespace")?;
115 stream.string_value(self.namespace_uri.as_ref())?;
116 } else if self.node_id.namespace != 0 {
117 stream.name("Namespace")?;
118 stream.number_value(self.node_id.namespace)?;
119 }
120 if self.server_index != 0 {
121 stream.name("ServerUri")?;
122 stream.number_value(self.server_index)?;
123 }
124 stream.end_object()?;
125 Ok(())
126 }
127 }
128
129 impl JsonDecodable for ExpandedNodeId {
130 fn decode(
131 stream: &mut JsonStreamReader<&mut dyn Read>,
132 _ctx: &Context<'_>,
133 ) -> super::EncodingResult<Self> {
134 match stream.peek()? {
135 ValueType::Null => {
136 stream.next_null()?;
137 return Ok(Self::null());
138 }
139 _ => stream.begin_object()?,
140 }
141
142 let mut id_type: Option<u16> = None;
143 let mut namespace: Option<RawIdentifier> = None;
144 let mut value: Option<RawIdentifier> = None;
145 let mut server_uri: Option<u32> = None;
146
147 while stream.has_next()? {
148 match stream.next_name()? {
149 "IdType" => {
150 id_type = Some(stream.next_number()??);
151 }
152 "Namespace" => match stream.peek()? {
153 ValueType::Null => {
154 stream.next_null()?;
155 namespace = Some(RawIdentifier::Integer(0));
156 }
157 ValueType::Number => {
158 namespace = Some(RawIdentifier::Integer(stream.next_number()??));
159 }
160 _ => {
161 namespace = Some(RawIdentifier::String(stream.next_string()?));
162 }
163 },
164 "ServerUri" => {
165 server_uri = Some(stream.next_number()??);
166 }
167 "Id" => match stream.peek()? {
168 ValueType::Null => {
169 stream.next_null()?;
170 value = Some(RawIdentifier::Integer(0));
171 }
172 ValueType::Number => {
173 value = Some(RawIdentifier::Integer(stream.next_number()??));
174 }
175 _ => {
176 value = Some(RawIdentifier::String(stream.next_string()?));
177 }
178 },
179 _ => stream.skip_value()?,
180 }
181 }
182
183 let identifier = match id_type {
184 Some(1) => {
185 let Some(RawIdentifier::String(s)) = value else {
186 return Err(Error::decoding("Invalid NodeId, empty identifier"));
187 };
188 let s = UAString::from(s);
189 if s.is_null() || s.is_empty() {
190 return Err(Error::decoding("Invalid NodeId, empty identifier"));
191 }
192 Identifier::String(s)
193 }
194 Some(2) => {
195 let Some(RawIdentifier::String(s)) = value else {
196 return Err(Error::decoding("Invalid NodeId, empty identifier"));
197 };
198 let s = Guid::from_str(&s)
199 .map_err(|_| Error::decoding("Unable to decode GUID identifier"))?;
200 Identifier::Guid(s)
201 }
202 Some(3) => {
203 let Some(RawIdentifier::String(s)) = value else {
204 return Err(Error::decoding("Invalid NodeId, empty identifier"));
205 };
206 let s: ByteString = ByteString::from_base64(&s)
207 .ok_or_else(|| Error::decoding("Unable to decode bytestring identifier"))?;
208 Identifier::ByteString(s)
209 }
210 None | Some(0) => {
211 let Some(RawIdentifier::Integer(s)) = value else {
212 return Err(Error::decoding("Invalid NodeId, empty identifier"));
213 };
214 Identifier::Numeric(s)
215 }
216 Some(r) => {
217 return Err(Error::decoding(format!(
218 "Failed to deserialize NodeId, got unexpected IdType {r}"
219 )));
220 }
221 };
222
223 let (namespace_uri, namespace) = match namespace {
224 Some(RawIdentifier::String(s)) => (Some(s), 0u16),
225 Some(RawIdentifier::Integer(s)) => (None, s.try_into().map_err(Error::decoding)?),
226 None => (None, 0),
227 };
228
229 stream.end_object()?;
230 Ok(ExpandedNodeId {
231 node_id: NodeId {
232 namespace,
233 identifier,
234 },
235 namespace_uri: namespace_uri.into(),
236 server_index: server_uri.unwrap_or_default(),
237 })
238 }
239 }
240}
241
242#[cfg(feature = "xml")]
243mod xml {
244 use crate::{xml::*, NodeId, UAString};
247 use std::io::{Read, Write};
248
249 use super::ExpandedNodeId;
250
251 impl XmlType for ExpandedNodeId {
252 const TAG: &'static str = "ExpandedNodeId";
253 }
254
255 impl XmlEncodable for ExpandedNodeId {
256 fn encode(
257 &self,
258 writer: &mut XmlStreamWriter<&mut dyn Write>,
259 context: &Context<'_>,
260 ) -> EncodingResult<()> {
261 let Some(node_id) = context.namespaces().resolve_node_id(self) else {
262 return Err(Error::encoding(
263 "Unable to resolve ExpandedNodeId, invalid namespace",
264 ));
265 };
266 node_id.encode(writer, context)
267 }
268 }
269
270 impl XmlDecodable for ExpandedNodeId {
271 fn decode(
272 reader: &mut XmlStreamReader<&mut dyn Read>,
273 context: &Context<'_>,
274 ) -> EncodingResult<Self> {
275 let node_id = NodeId::decode(reader, context)?;
276 Ok(ExpandedNodeId {
277 node_id,
278 namespace_uri: UAString::null(),
279 server_index: 0,
280 })
281 }
282 }
283}
284
285impl BinaryEncodable for ExpandedNodeId {
286 fn byte_len(&self, ctx: &crate::Context<'_>) -> usize {
287 let mut size = self.node_id.byte_len(ctx);
288 if !self.namespace_uri.is_null() {
289 size += self.namespace_uri.byte_len(ctx);
290 }
291 if self.server_index != 0 {
292 size += self.server_index.byte_len(ctx);
293 }
294 size
295 }
296
297 fn encode<S: Write + ?Sized>(&self, stream: &mut S, ctx: &Context<'_>) -> EncodingResult<()> {
298 let mut data_encoding = 0;
299 if !self.namespace_uri.is_null() {
300 data_encoding |= 0x80;
301 }
302 if self.server_index != 0 {
303 data_encoding |= 0x40;
304 }
305
306 match &self.node_id.identifier {
308 Identifier::Numeric(value) => {
309 if self.node_id.namespace == 0 && *value <= 255 {
310 write_u8(stream, data_encoding)?;
312 write_u8(stream, *value as u8)?;
313 } else if self.node_id.namespace <= 255 && *value <= 65535 {
314 write_u8(stream, data_encoding | 0x1)?;
316 write_u8(stream, self.node_id.namespace as u8)?;
317 write_u16(stream, *value as u16)?;
318 } else {
319 write_u8(stream, data_encoding | 0x2)?;
321 write_u16(stream, self.node_id.namespace)?;
322 write_u32(stream, *value)?;
323 }
324 }
325 Identifier::String(value) => {
326 write_u8(stream, data_encoding | 0x3)?;
327 write_u16(stream, self.node_id.namespace)?;
328 value.encode(stream, ctx)?;
329 }
330 Identifier::Guid(value) => {
331 write_u8(stream, data_encoding | 0x4)?;
332 write_u16(stream, self.node_id.namespace)?;
333 value.encode(stream, ctx)?;
334 }
335 Identifier::ByteString(ref value) => {
336 write_u8(stream, data_encoding | 0x5)?;
337 write_u16(stream, self.node_id.namespace)?;
338 value.encode(stream, ctx)?;
339 }
340 }
341 if !self.namespace_uri.is_null() {
342 self.namespace_uri.encode(stream, ctx)?;
343 }
344 if self.server_index != 0 {
345 self.server_index.encode(stream, ctx)?;
346 }
347 Ok(())
348 }
349}
350
351impl BinaryDecodable for ExpandedNodeId {
352 fn decode<S: Read + ?Sized>(stream: &mut S, ctx: &Context<'_>) -> EncodingResult<Self> {
353 let data_encoding = read_u8(stream)?;
354 let identifier = data_encoding & 0x0f;
355 let node_id = match identifier {
356 0x0 => {
357 let value = read_u8(stream)?;
358 NodeId::new(0, u32::from(value))
359 }
360 0x1 => {
361 let namespace = read_u8(stream)?;
362 let value = read_u16(stream)?;
363 NodeId::new(u16::from(namespace), u32::from(value))
364 }
365 0x2 => {
366 let namespace = read_u16(stream)?;
367 let value = read_u32(stream)?;
368 NodeId::new(namespace, value)
369 }
370 0x3 => {
371 let namespace = read_u16(stream)?;
372 let value = UAString::decode(stream, ctx)?;
373 NodeId::new(namespace, value)
374 }
375 0x4 => {
376 let namespace = read_u16(stream)?;
377 let value = Guid::decode(stream, ctx)?;
378 NodeId::new(namespace, value)
379 }
380 0x5 => {
381 let namespace = read_u16(stream)?;
382 let value = ByteString::decode(stream, ctx)?;
383 NodeId::new(namespace, value)
384 }
385 _ => {
386 return Err(Error::encoding(format!(
387 "Unrecognized expanded node id type {identifier}"
388 )));
389 }
390 };
391
392 let namespace_uri = if data_encoding & 0x80 != 0 {
394 UAString::decode(stream, ctx)?
395 } else {
396 UAString::null()
397 };
398 let server_index = if data_encoding & 0x40 != 0 {
399 u32::decode(stream, ctx)?
400 } else {
401 0
402 };
403
404 Ok(ExpandedNodeId {
405 node_id,
406 namespace_uri,
407 server_index,
408 })
409 }
410}
411
412impl From<&NodeId> for ExpandedNodeId {
413 fn from(value: &NodeId) -> Self {
414 value.clone().into()
415 }
416}
417
418impl From<(NodeId, u32)> for ExpandedNodeId {
419 fn from(v: (NodeId, u32)) -> Self {
420 ExpandedNodeId {
421 node_id: v.0,
422 namespace_uri: UAString::null(),
423 server_index: v.1,
424 }
425 }
426}
427
428impl<T> From<(T, &str)> for ExpandedNodeId
429where
430 T: Into<NodeId>,
431{
432 fn from(value: (T, &str)) -> Self {
433 ExpandedNodeId {
434 node_id: value.0.into(),
435 namespace_uri: value.1.into(),
436 server_index: 0,
437 }
438 }
439}
440
441impl From<NodeId> for ExpandedNodeId {
442 fn from(v: NodeId) -> Self {
443 ExpandedNodeId {
444 node_id: v,
445 namespace_uri: UAString::null(),
446 server_index: 0,
447 }
448 }
449}
450
451impl fmt::Display for ExpandedNodeId {
452 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
453 if self.namespace_uri.is_empty() {
455 write!(f, "svr={};{}", self.server_index, self.node_id)
457 } else {
458 let namespace_uri = String::from(self.namespace_uri.as_ref())
460 .replace('%', "%25")
461 .replace(';', "%3b");
462 write!(
464 f,
465 "svr={};nsu={};{}",
466 self.server_index, namespace_uri, self.node_id.identifier
467 )
468 }
469 }
470}
471
472impl FromStr for ExpandedNodeId {
473 type Err = StatusCode;
474 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
475 use regex::Regex;
476
477 static RE: LazyLock<Regex> = LazyLock::new(|| {
484 Regex::new(
485 r"^svr=(?P<svr>[0-9]+);(ns=(?P<ns>[0-9]+)|nsu=(?P<nsu>[^;]+));(?P<t>[isgb]=.+)$",
486 )
487 .unwrap()
488 });
489
490 let captures = RE.captures(s).ok_or(StatusCode::BadNodeIdInvalid)?;
491
492 let server_index = captures
494 .name("svr")
495 .ok_or(StatusCode::BadNodeIdInvalid)
496 .and_then(|server_index| {
497 server_index
498 .as_str()
499 .parse::<u32>()
500 .map_err(|_| StatusCode::BadNodeIdInvalid)
501 })?;
502
503 let namespace_uri = if let Some(nsu) = captures.name("nsu") {
505 let nsu = String::from(nsu.as_str())
507 .replace("%3b", ";")
508 .replace("%25", "%");
509 UAString::from(nsu)
510 } else {
511 UAString::null()
512 };
513
514 let namespace = if let Some(ns) = captures.name("ns") {
515 ns.as_str()
516 .parse::<u16>()
517 .map_err(|_| StatusCode::BadNodeIdInvalid)?
518 } else {
519 0
520 };
521
522 let t = captures.name("t").unwrap();
524 Identifier::from_str(t.as_str())
525 .map(|t| ExpandedNodeId {
526 server_index,
527 namespace_uri,
528 node_id: NodeId::new(namespace, t),
529 })
530 .map_err(|_| StatusCode::BadNodeIdInvalid)
531 }
532}
533
534impl ExpandedNodeId {
535 pub fn new<T>(value: T) -> ExpandedNodeId
537 where
538 T: 'static + Into<ExpandedNodeId>,
539 {
540 value.into()
541 }
542
543 pub fn new_with_namespace(namespace: &str, value: impl Into<Identifier> + 'static) -> Self {
545 Self {
546 namespace_uri: namespace.into(),
547 node_id: NodeId::new(0, value),
548 server_index: 0,
549 }
550 }
551
552 pub fn null() -> ExpandedNodeId {
554 Self::new(NodeId::null())
555 }
556
557 pub fn is_null(&self) -> bool {
559 self.node_id.is_null()
560 }
561
562 pub fn try_resolve<'a>(&'a self, namespaces: &NamespaceMap) -> Option<Cow<'a, NodeId>> {
568 if self.server_index != 0 {
569 return None;
570 }
571 if let Some(uri) = self.namespace_uri.value() {
572 let idx = namespaces.get_index(uri)?;
573 Some(Cow::Owned(NodeId {
574 namespace: idx,
575 identifier: self.node_id.identifier.clone(),
576 }))
577 } else {
578 Some(Cow::Borrowed(&self.node_id))
579 }
580 }
581}