1use std::{
8 self,
9 convert::TryFrom,
10 fmt,
11 io::{Read, Write},
12 str::FromStr,
13 sync::{
14 atomic::{AtomicUsize, Ordering},
15 LazyLock,
16 },
17};
18
19use crate::{
20 byte_string::ByteString,
21 encoding::{BinaryDecodable, BinaryEncodable, EncodingResult},
22 guid::Guid,
23 read_u16, read_u32, read_u8,
24 status_code::StatusCode,
25 string::*,
26 write_u16, write_u32, write_u8, DataTypeId, Error, MethodId, ObjectId, ObjectTypeId,
27 ReferenceTypeId, UaNullable, VariableId, VariableTypeId,
28};
29
30#[derive(Eq, PartialEq, Clone, Debug, Hash)]
32pub enum Identifier {
33 Numeric(u32),
35 String(UAString),
37 Guid(Guid),
39 ByteString(ByteString),
41}
42
43impl fmt::Display for Identifier {
44 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
45 match self {
46 Identifier::Numeric(v) => write!(f, "i={}", *v),
47 Identifier::String(v) => write!(f, "s={v}"),
48 Identifier::Guid(v) => write!(f, "g={v:?}"),
49 Identifier::ByteString(v) => write!(f, "b={}", v.as_base64()),
50 }
51 }
52}
53
54impl FromStr for Identifier {
55 type Err = ();
56
57 fn from_str(s: &str) -> Result<Self, Self::Err> {
58 if s.len() < 2 {
59 Err(())
60 } else {
61 let k = &s[..2];
62 let v = &s[2..];
63 match k {
64 "i=" => v.parse::<u32>().map(|v| v.into()).map_err(|_| ()),
65 "s=" => Ok(UAString::from(v).into()),
66 "g=" => Guid::from_str(v).map(|v| v.into()).map_err(|_| ()),
67 "b=" => ByteString::from_base64(v).map(|v| v.into()).ok_or(()),
68 _ => Err(()),
69 }
70 }
71 }
72}
73
74impl From<i32> for Identifier {
75 fn from(v: i32) -> Self {
76 Identifier::Numeric(v as u32)
77 }
78}
79
80impl From<u32> for Identifier {
81 fn from(v: u32) -> Self {
82 Identifier::Numeric(v)
83 }
84}
85
86impl<'a> From<&'a str> for Identifier {
87 fn from(v: &'a str) -> Self {
88 Identifier::from(UAString::from(v))
89 }
90}
91
92impl From<&String> for Identifier {
93 fn from(v: &String) -> Self {
94 Identifier::from(UAString::from(v))
95 }
96}
97
98impl From<String> for Identifier {
99 fn from(v: String) -> Self {
100 Identifier::from(UAString::from(v))
101 }
102}
103
104impl From<UAString> for Identifier {
105 fn from(v: UAString) -> Self {
106 Identifier::String(v)
107 }
108}
109
110impl From<Guid> for Identifier {
111 fn from(v: Guid) -> Self {
112 Identifier::Guid(v)
113 }
114}
115
116impl From<ByteString> for Identifier {
117 fn from(v: ByteString) -> Self {
118 Identifier::ByteString(v)
119 }
120}
121
122#[derive(Debug)]
123pub struct NodeIdError;
125
126impl fmt::Display for NodeIdError {
127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128 write!(f, "NodeIdError")
129 }
130}
131
132impl std::error::Error for NodeIdError {}
133
134#[derive(PartialEq, Eq, Clone, Debug, Hash)]
136pub struct NodeId {
137 pub namespace: u16,
139 pub identifier: Identifier,
141}
142
143impl fmt::Display for NodeId {
144 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
145 if self.namespace != 0 {
146 write!(f, "ns={};{}", self.namespace, self.identifier)
147 } else {
148 write!(f, "{}", self.identifier)
149 }
150 }
151}
152
153impl UaNullable for NodeId {
154 fn is_ua_null(&self) -> bool {
155 self.is_null()
156 }
157}
158
159#[cfg(feature = "json")]
180mod json {
181 use std::io::{Read, Write};
182 use std::str::FromStr;
183
184 use tracing::warn;
185
186 use crate::{json::*, ByteString, Error, Guid};
187
188 use super::{Identifier, NodeId, UAString};
189 enum RawIdentifier {
190 String(String),
191 Integer(u32),
192 }
193
194 impl JsonEncodable for NodeId {
195 fn encode(
196 &self,
197 stream: &mut JsonStreamWriter<&mut dyn Write>,
198 ctx: &crate::json::Context<'_>,
199 ) -> super::EncodingResult<()> {
200 stream.begin_object()?;
201 match &self.identifier {
202 super::Identifier::Numeric(n) => {
203 stream.name("Id")?;
204 stream.number_value(*n)?;
205 }
206 super::Identifier::String(uastring) => {
207 stream.name("IdType")?;
208 stream.number_value(1)?;
209 stream.name("Id")?;
210 JsonEncodable::encode(uastring, stream, ctx)?;
211 }
212 super::Identifier::Guid(guid) => {
213 stream.name("IdType")?;
214 stream.number_value(2)?;
215 stream.name("Id")?;
216 JsonEncodable::encode(guid, stream, ctx)?;
217 }
218 super::Identifier::ByteString(byte_string) => {
219 stream.name("IdType")?;
220 stream.number_value(3)?;
221 stream.name("Id")?;
222 JsonEncodable::encode(byte_string, stream, ctx)?;
223 }
224 }
225 if self.namespace != 0 {
226 stream.name("Namespace")?;
227 stream.number_value(self.namespace)?;
228 }
229 stream.end_object()?;
230 Ok(())
231 }
232 }
233
234 impl JsonDecodable for NodeId {
235 fn decode(
236 stream: &mut JsonStreamReader<&mut dyn Read>,
237 _ctx: &Context<'_>,
238 ) -> super::EncodingResult<Self> {
239 match stream.peek()? {
240 ValueType::Null => {
241 stream.next_null()?;
242 return Ok(Self::null());
243 }
244 _ => stream.begin_object()?,
245 }
246
247 let mut id_type: Option<u16> = None;
248 let mut namespace: Option<u16> = None;
249 let mut value: Option<RawIdentifier> = None;
250
251 while stream.has_next()? {
252 match stream.next_name()? {
253 "IdType" => {
254 id_type = Some(stream.next_number()??);
255 }
256 "Namespace" => {
257 namespace = Some(stream.next_number()??);
258 }
259 "Id" => match stream.peek()? {
260 ValueType::Null => {
261 stream.next_null()?;
262 value = Some(RawIdentifier::Integer(0));
263 }
264 ValueType::Number => {
265 value = Some(RawIdentifier::Integer(stream.next_number()??));
266 }
267 _ => {
268 value = Some(RawIdentifier::String(stream.next_string()?));
269 }
270 },
271 _ => stream.skip_value()?,
272 }
273 }
274
275 let identifier = match id_type {
276 Some(1) => {
277 let Some(RawIdentifier::String(s)) = value else {
278 return Err(Error::decoding("Invalid NodeId, empty identifier"));
279 };
280 let s = UAString::from(s);
281 if s.is_null() || s.is_empty() {
282 return Err(Error::decoding("Invalid NodeId, empty identifier"));
283 }
284 Identifier::String(s)
285 }
286 Some(2) => {
287 let Some(RawIdentifier::String(s)) = value else {
288 return Err(Error::decoding("Invalid NodeId, empty identifier"));
289 };
290 if s.is_empty() {
291 return Err(Error::decoding("Invalid NodeId, empty identifier"));
292 }
293 let s = Guid::from_str(&s).map_err(|_| {
294 warn!("Unable to decode GUID identifier");
295 Error::decoding("Unable to decode GUID identifier")
296 })?;
297 Identifier::Guid(s)
298 }
299 Some(3) => {
300 let Some(RawIdentifier::String(s)) = value else {
301 return Err(Error::decoding("Invalid NodeId, empty identifier"));
302 };
303 if s.is_empty() {
304 return Err(Error::decoding("Invalid NodeId, empty identifier"));
305 }
306 let s: ByteString = ByteString::from_base64(&s)
307 .ok_or_else(|| Error::decoding("Unable to decode bytestring identifier"))?;
308 Identifier::ByteString(s)
309 }
310 None | Some(0) => {
311 let Some(RawIdentifier::Integer(s)) = value else {
312 return Err(Error::decoding("Invalid NodeId, empty identifier"));
313 };
314 Identifier::Numeric(s)
315 }
316 Some(r) => {
317 return Err(Error::decoding(format!(
318 "Failed to deserialize NodeId, got unexpected IdType {r}"
319 )));
320 }
321 };
322
323 stream.end_object()?;
324 Ok(Self {
325 namespace: namespace.unwrap_or_default(),
326 identifier,
327 })
328 }
329 }
330}
331
332#[cfg(feature = "xml")]
333mod xml {
334 use crate::xml::*;
335 use std::{
336 io::{Read, Write},
337 str::FromStr,
338 };
339
340 use super::NodeId;
341
342 impl XmlType for NodeId {
343 const TAG: &'static str = "NodeId";
344 }
345
346 impl XmlEncodable for NodeId {
347 fn encode(
348 &self,
349 writer: &mut XmlStreamWriter<&mut dyn Write>,
350 ctx: &crate::xml::Context<'_>,
351 ) -> Result<(), Error> {
352 let namespace_index = ctx.resolve_namespace_index_inverse(self.namespace)?;
353
354 let self_str = if namespace_index > 0 {
355 format!("ns={};{}", namespace_index, self.identifier)
356 } else {
357 self.identifier.to_string()
358 };
359 let val = ctx.resolve_alias_inverse(&self_str);
360 writer.encode_child("Identifier", val, ctx)
361 }
362 }
363
364 impl XmlDecodable for NodeId {
365 fn decode(
366 read: &mut XmlStreamReader<&mut dyn Read>,
367 context: &Context<'_>,
368 ) -> Result<Self, Error>
369 where
370 Self: Sized,
371 {
372 let val: Option<String> = read.decode_single_child("Identifier", context)?;
373 let Some(val) = val else {
374 return Ok(NodeId::null());
375 };
376
377 let val_str = context.resolve_alias(&val);
378 let mut id = NodeId::from_str(val_str)
379 .map_err(|e| Error::new(e, format!("Invalid node ID: {val_str}")))?;
380 id.namespace = context.resolve_namespace_index(id.namespace)?;
381 Ok(id)
382 }
383 }
384}
385
386impl BinaryEncodable for NodeId {
387 fn byte_len(&self, ctx: &crate::Context<'_>) -> usize {
388 let size: usize = match self.identifier {
390 Identifier::Numeric(value) => {
391 if self.namespace == 0 && value <= 255 {
392 2
393 } else if self.namespace <= 255 && value <= 65535 {
394 4
395 } else {
396 7
397 }
398 }
399 Identifier::String(ref value) => 3 + value.byte_len(ctx),
400 Identifier::Guid(ref value) => 3 + value.byte_len(ctx),
401 Identifier::ByteString(ref value) => 3 + value.byte_len(ctx),
402 };
403 size
404 }
405
406 fn encode<S: Write + ?Sized>(
407 &self,
408 stream: &mut S,
409 ctx: &crate::Context<'_>,
410 ) -> EncodingResult<()> {
411 match &self.identifier {
413 Identifier::Numeric(value) => {
414 if self.namespace == 0 && *value <= 255 {
415 write_u8(stream, 0x0)?;
417 write_u8(stream, *value as u8)
418 } else if self.namespace <= 255 && *value <= 65535 {
419 write_u8(stream, 0x1)?;
421 write_u8(stream, self.namespace as u8)?;
422 write_u16(stream, *value as u16)
423 } else {
424 write_u8(stream, 0x2)?;
426 write_u16(stream, self.namespace)?;
427 write_u32(stream, *value)
428 }
429 }
430 Identifier::String(value) => {
431 write_u8(stream, 0x3)?;
432 write_u16(stream, self.namespace)?;
433 value.encode(stream, ctx)
434 }
435 Identifier::Guid(value) => {
436 write_u8(stream, 0x4)?;
437 write_u16(stream, self.namespace)?;
438 value.encode(stream, ctx)
439 }
440 Identifier::ByteString(value) => {
441 write_u8(stream, 0x5)?;
442 write_u16(stream, self.namespace)?;
443 value.encode(stream, ctx)
444 }
445 }
446 }
447}
448
449impl BinaryDecodable for NodeId {
450 fn decode<S: Read + ?Sized>(stream: &mut S, ctx: &crate::Context<'_>) -> EncodingResult<Self> {
451 let identifier = read_u8(stream)?;
452 let node_id = match identifier {
453 0x0 => {
454 let namespace = 0;
455 let value = read_u8(stream)?;
456 NodeId::new(namespace, u32::from(value))
457 }
458 0x1 => {
459 let namespace = read_u8(stream)?;
460 let value = read_u16(stream)?;
461 NodeId::new(u16::from(namespace), u32::from(value))
462 }
463 0x2 => {
464 let namespace = read_u16(stream)?;
465 let value = read_u32(stream)?;
466 NodeId::new(namespace, value)
467 }
468 0x3 => {
469 let namespace = read_u16(stream)?;
470 let value = UAString::decode(stream, ctx)?;
471 NodeId::new(namespace, value)
472 }
473 0x4 => {
474 let namespace = read_u16(stream)?;
475 let value = Guid::decode(stream, ctx)?;
476 NodeId::new(namespace, value)
477 }
478 0x5 => {
479 let namespace = read_u16(stream)?;
480 let value = ByteString::decode(stream, ctx)?;
481 NodeId::new(namespace, value)
482 }
483 _ => {
484 return Err(Error::decoding(format!(
485 "Unrecognized node id type {identifier}"
486 )));
487 }
488 };
489 Ok(node_id)
490 }
491}
492
493impl FromStr for NodeId {
494 type Err = StatusCode;
495 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
496 use regex::Regex;
497
498 static RE: LazyLock<Regex> =
511 LazyLock::new(|| Regex::new(r"^(ns=(?P<ns>[0-9]+);)?(?P<t>[isgb]=.+)$").unwrap());
512
513 let captures = RE.captures(s).ok_or(StatusCode::BadNodeIdInvalid)?;
514
515 let namespace = if let Some(ns) = captures.name("ns") {
517 ns.as_str()
518 .parse::<u16>()
519 .map_err(|_| StatusCode::BadNodeIdInvalid)?
520 } else {
521 0
522 };
523
524 let t = captures.name("t").unwrap();
526 Identifier::from_str(t.as_str())
527 .map(|t| NodeId::new(namespace, t))
528 .map_err(|_| StatusCode::BadNodeIdInvalid)
529 }
530}
531
532impl<'a> From<&'a str> for NodeId {
533 fn from(value: &'a str) -> Self {
534 (0u16, value).into()
535 }
536}
537
538impl From<UAString> for NodeId {
539 fn from(value: UAString) -> Self {
540 (0u16, value).into()
541 }
542}
543
544impl From<u32> for NodeId {
545 fn from(value: u32) -> Self {
546 (0, value).into()
547 }
548}
549
550impl From<Guid> for NodeId {
551 fn from(value: Guid) -> Self {
552 (0, value).into()
553 }
554}
555
556impl From<ByteString> for NodeId {
557 fn from(value: ByteString) -> Self {
558 (0, value).into()
559 }
560}
561
562impl From<&NodeId> for NodeId {
563 fn from(v: &NodeId) -> Self {
564 v.clone()
565 }
566}
567
568impl From<NodeId> for String {
569 fn from(value: NodeId) -> Self {
570 value.to_string()
571 }
572}
573
574impl<'a> From<(u16, &'a str)> for NodeId {
575 fn from(v: (u16, &'a str)) -> Self {
576 Self::new(v.0, UAString::from(v.1))
577 }
578}
579
580impl From<(u16, UAString)> for NodeId {
581 fn from(v: (u16, UAString)) -> Self {
582 Self::new(v.0, v.1)
583 }
584}
585
586impl From<(u16, u32)> for NodeId {
587 fn from(v: (u16, u32)) -> Self {
588 Self::new(v.0, v.1)
589 }
590}
591
592impl From<(u16, Guid)> for NodeId {
593 fn from(v: (u16, Guid)) -> Self {
594 Self::new(v.0, v.1)
595 }
596}
597
598impl From<(u16, ByteString)> for NodeId {
599 fn from(v: (u16, ByteString)) -> Self {
600 Self::new(v.0, v.1)
601 }
602}
603
604impl PartialEq<(u16, &str)> for NodeId {
606 fn eq(&self, other: &(u16, &str)) -> bool {
607 self.namespace == other.0
608 && match &self.identifier {
609 Identifier::String(s) => s.as_ref() == other.1,
610 _ => false,
611 }
612 }
613}
614
615impl PartialEq<(u16, &[u8; 16])> for NodeId {
616 fn eq(&self, other: &(u16, &[u8; 16])) -> bool {
617 self.namespace == other.0
618 && match &self.identifier {
619 Identifier::Guid(s) => s.as_bytes() == other.1,
620 _ => false,
621 }
622 }
623}
624
625impl PartialEq<(u16, &[u8])> for NodeId {
626 fn eq(&self, other: &(u16, &[u8])) -> bool {
627 self.namespace == other.0
628 && match &self.identifier {
629 Identifier::ByteString(s) => {
630 s.value.as_ref().is_some_and(|v| v.as_slice() == other.1)
631 }
632 _ => false,
633 }
634 }
635}
636
637impl PartialEq<(u16, u32)> for NodeId {
638 fn eq(&self, other: &(u16, u32)) -> bool {
639 self.namespace == other.0
640 && match &self.identifier {
641 Identifier::Numeric(s) => s == &other.1,
642 _ => false,
643 }
644 }
645}
646
647impl PartialEq<ObjectId> for NodeId {
648 fn eq(&self, other: &ObjectId) -> bool {
649 *self == (0u16, *other as u32)
650 }
651}
652
653impl PartialEq<ObjectTypeId> for NodeId {
654 fn eq(&self, other: &ObjectTypeId) -> bool {
655 *self == (0u16, *other as u32)
656 }
657}
658
659impl PartialEq<ReferenceTypeId> for NodeId {
660 fn eq(&self, other: &ReferenceTypeId) -> bool {
661 *self == (0u16, *other as u32)
662 }
663}
664
665impl PartialEq<VariableId> for NodeId {
666 fn eq(&self, other: &VariableId) -> bool {
667 *self == (0u16, *other as u32)
668 }
669}
670
671impl PartialEq<VariableTypeId> for NodeId {
672 fn eq(&self, other: &VariableTypeId) -> bool {
673 *self == (0u16, *other as u32)
674 }
675}
676
677impl PartialEq<DataTypeId> for NodeId {
678 fn eq(&self, other: &DataTypeId) -> bool {
679 *self == (0u16, *other as u32)
680 }
681}
682
683static NEXT_NODE_ID_NUMERIC: AtomicUsize = AtomicUsize::new(1);
684
685impl Default for NodeId {
686 fn default() -> Self {
687 NodeId::null()
688 }
689}
690
691impl NodeId {
692 pub fn new<T>(namespace: u16, value: T) -> NodeId
695 where
696 T: 'static + Into<Identifier>,
697 {
698 NodeId {
699 namespace,
700 identifier: value.into(),
701 }
702 }
703
704 pub fn root_folder_id() -> NodeId {
706 ObjectId::RootFolder.into()
707 }
708
709 pub fn objects_folder_id() -> NodeId {
711 ObjectId::ObjectsFolder.into()
712 }
713
714 pub fn types_folder_id() -> NodeId {
716 ObjectId::TypesFolder.into()
717 }
718
719 pub fn views_folder_id() -> NodeId {
721 ObjectId::ViewsFolder.into()
722 }
723
724 pub fn is_null(&self) -> bool {
726 self.namespace == 0 && self.identifier == Identifier::Numeric(0)
727 }
728
729 pub fn null() -> NodeId {
731 NodeId::new(0, 0u32)
732 }
733
734 pub fn next_numeric(namespace: u16) -> NodeId {
736 NodeId::new(
737 namespace,
738 NEXT_NODE_ID_NUMERIC.fetch_add(1, Ordering::SeqCst) as u32,
739 )
740 }
741
742 pub fn as_object_id(&self) -> std::result::Result<ObjectId, NodeIdError> {
744 match self.identifier {
745 Identifier::Numeric(id) if self.namespace == 0 => {
746 ObjectId::try_from(id).map_err(|_| NodeIdError)
747 }
748 _ => Err(NodeIdError),
749 }
750 }
751
752 pub fn as_variable_id(&self) -> std::result::Result<VariableId, NodeIdError> {
754 match self.identifier {
755 Identifier::Numeric(id) if self.namespace == 0 => {
756 VariableId::try_from(id).map_err(|_| NodeIdError)
757 }
758 _ => Err(NodeIdError),
759 }
760 }
761
762 pub fn as_reference_type_id(&self) -> std::result::Result<ReferenceTypeId, NodeIdError> {
764 if self.is_null() {
765 Err(NodeIdError)
766 } else {
767 match self.identifier {
768 Identifier::Numeric(id) if self.namespace == 0 => {
769 ReferenceTypeId::try_from(id).map_err(|_| NodeIdError)
770 }
771 _ => Err(NodeIdError),
772 }
773 }
774 }
775
776 pub fn as_data_type_id(&self) -> std::result::Result<DataTypeId, NodeIdError> {
778 match self.identifier {
779 Identifier::Numeric(id) if self.namespace == 0 => {
780 DataTypeId::try_from(id).map_err(|_| NodeIdError)
781 }
782 _ => Err(NodeIdError),
783 }
784 }
785
786 pub fn as_method_id(&self) -> std::result::Result<MethodId, NodeIdError> {
788 match self.identifier {
789 Identifier::Numeric(id) if self.namespace == 0 => {
790 MethodId::try_from(id).map_err(|_| NodeIdError)
791 }
792 _ => Err(NodeIdError),
793 }
794 }
795
796 pub fn is_numeric(&self) -> bool {
798 matches!(self.identifier, Identifier::Numeric(_))
799 }
800
801 pub fn is_string(&self) -> bool {
803 matches!(self.identifier, Identifier::String(_))
804 }
805
806 pub fn is_guid(&self) -> bool {
808 matches!(self.identifier, Identifier::Guid(_))
809 }
810
811 pub fn is_byte_string(&self) -> bool {
813 matches!(self.identifier, Identifier::ByteString(_))
814 }
815
816 pub fn as_u32(&self) -> Option<u32> {
818 match &self.identifier {
819 Identifier::Numeric(i) => Some(*i),
820 _ => None,
821 }
822 }
823}