1use std::{
5 fmt,
6 io::{Read, Write},
7 str::FromStr,
8 sync::{
9 atomic::{AtomicU32, Ordering},
10 LazyLock,
11 },
12};
13
14mod id_ref;
15mod identifier;
16#[cfg(feature = "json")]
17mod json;
18#[cfg(feature = "xml")]
19mod xml;
20
21pub use id_ref::{IdentifierRef, IntoNodeIdRef, NodeIdRef};
22pub use identifier::Identifier;
23pub use identifier::{
24 IDENTIFIER_HASH_BYTE_STRING, IDENTIFIER_HASH_GUID, IDENTIFIER_HASH_NUMERIC,
25 IDENTIFIER_HASH_STRING,
26};
27
28use crate::{
29 read_u16, read_u32, read_u8, write_u16, write_u32, write_u8, BinaryDecodable, BinaryEncodable,
30 ByteString, DataTypeId, EncodingResult, Error, Guid, MethodId, ObjectId, ReferenceTypeId,
31 StatusCode, UAString, UaNullable, VariableId,
32};
33
34#[derive(Debug)]
35pub struct NodeIdError;
37
38impl fmt::Display for NodeIdError {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 write!(f, "NodeIdError")
41 }
42}
43
44impl std::error::Error for NodeIdError {}
45
46#[derive(PartialEq, Eq, Clone, Debug, Hash)]
48pub struct NodeId {
49 pub namespace: u16,
51 pub identifier: Identifier,
53}
54
55impl fmt::Display for NodeId {
56 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
57 if self.namespace != 0 {
58 write!(f, "ns={};{}", self.namespace, self.identifier)
59 } else {
60 write!(f, "{}", self.identifier)
61 }
62 }
63}
64
65impl UaNullable for NodeId {
66 fn is_ua_null(&self) -> bool {
67 self.is_null()
68 }
69}
70
71impl BinaryEncodable for NodeId {
72 fn byte_len(&self, ctx: &crate::Context<'_>) -> usize {
73 let size: usize = match self.identifier {
75 Identifier::Numeric(value) => {
76 if self.namespace == 0 && value <= 255 {
77 2
78 } else if self.namespace <= 255 && value <= 65535 {
79 4
80 } else {
81 7
82 }
83 }
84 Identifier::String(ref value) => 3 + value.byte_len(ctx),
85 Identifier::Guid(ref value) => 3 + value.byte_len(ctx),
86 Identifier::ByteString(ref value) => 3 + value.byte_len(ctx),
87 };
88 size
89 }
90
91 fn encode<S: Write + ?Sized>(
92 &self,
93 stream: &mut S,
94 ctx: &crate::Context<'_>,
95 ) -> EncodingResult<()> {
96 match &self.identifier {
98 Identifier::Numeric(value) => {
99 if self.namespace == 0 && *value <= 255 {
100 write_u8(stream, 0x0)?;
102 write_u8(stream, *value as u8)
103 } else if self.namespace <= 255 && *value <= 65535 {
104 write_u8(stream, 0x1)?;
106 write_u8(stream, self.namespace as u8)?;
107 write_u16(stream, *value as u16)
108 } else {
109 write_u8(stream, 0x2)?;
111 write_u16(stream, self.namespace)?;
112 write_u32(stream, *value)
113 }
114 }
115 Identifier::String(value) => {
116 write_u8(stream, 0x3)?;
117 write_u16(stream, self.namespace)?;
118 value.encode(stream, ctx)
119 }
120 Identifier::Guid(value) => {
121 write_u8(stream, 0x4)?;
122 write_u16(stream, self.namespace)?;
123 value.encode(stream, ctx)
124 }
125 Identifier::ByteString(value) => {
126 write_u8(stream, 0x5)?;
127 write_u16(stream, self.namespace)?;
128 value.encode(stream, ctx)
129 }
130 }
131 }
132}
133
134impl BinaryDecodable for NodeId {
135 fn decode<S: Read + ?Sized>(stream: &mut S, ctx: &crate::Context<'_>) -> EncodingResult<Self> {
136 let identifier = read_u8(stream)?;
137 let node_id = match identifier {
138 0x0 => {
139 let namespace = 0;
140 let value = read_u8(stream)?;
141 NodeId::new(namespace, u32::from(value))
142 }
143 0x1 => {
144 let namespace = read_u8(stream)?;
145 let value = read_u16(stream)?;
146 NodeId::new(u16::from(namespace), u32::from(value))
147 }
148 0x2 => {
149 let namespace = read_u16(stream)?;
150 let value = read_u32(stream)?;
151 NodeId::new(namespace, value)
152 }
153 0x3 => {
154 let namespace = read_u16(stream)?;
155 let value = UAString::decode(stream, ctx)?;
156 NodeId::new(namespace, value)
157 }
158 0x4 => {
159 let namespace = read_u16(stream)?;
160 let value = Guid::decode(stream, ctx)?;
161 NodeId::new(namespace, value)
162 }
163 0x5 => {
164 let namespace = read_u16(stream)?;
165 let value = ByteString::decode(stream, ctx)?;
166 NodeId::new(namespace, value)
167 }
168 _ => {
169 return Err(Error::decoding(format!(
170 "Unrecognized node id type {identifier}"
171 )));
172 }
173 };
174 Ok(node_id)
175 }
176}
177
178impl FromStr for NodeId {
179 type Err = StatusCode;
180 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
181 use regex::Regex;
182
183 static RE: LazyLock<Regex> =
196 LazyLock::new(|| Regex::new(r"^(ns=(?P<ns>[0-9]+);)?(?P<t>[isgb]=.+)$").unwrap());
197
198 let captures = RE.captures(s).ok_or(StatusCode::BadNodeIdInvalid)?;
199
200 let namespace = if let Some(ns) = captures.name("ns") {
202 ns.as_str()
203 .parse::<u16>()
204 .map_err(|_| StatusCode::BadNodeIdInvalid)?
205 } else {
206 0
207 };
208
209 let t = captures.name("t").unwrap();
211 Identifier::from_str(t.as_str())
212 .map(|t| NodeId::new(namespace, t))
213 .map_err(|_| StatusCode::BadNodeIdInvalid)
214 }
215}
216
217impl<'a> From<&'a str> for NodeId {
218 fn from(value: &'a str) -> Self {
219 (0u16, value).into()
220 }
221}
222
223impl From<UAString> for NodeId {
224 fn from(value: UAString) -> Self {
225 (0u16, value).into()
226 }
227}
228
229impl From<u32> for NodeId {
230 fn from(value: u32) -> Self {
231 (0, value).into()
232 }
233}
234
235impl From<Guid> for NodeId {
236 fn from(value: Guid) -> Self {
237 (0, value).into()
238 }
239}
240
241impl From<ByteString> for NodeId {
242 fn from(value: ByteString) -> Self {
243 (0, value).into()
244 }
245}
246
247impl From<&NodeId> for NodeId {
248 fn from(v: &NodeId) -> Self {
249 v.clone()
250 }
251}
252
253impl From<NodeId> for String {
254 fn from(value: NodeId) -> Self {
255 value.to_string()
256 }
257}
258
259impl<'a> From<(u16, &'a str)> for NodeId {
260 fn from(v: (u16, &'a str)) -> Self {
261 Self::new(v.0, UAString::from(v.1))
262 }
263}
264
265impl From<(u16, UAString)> for NodeId {
266 fn from(v: (u16, UAString)) -> Self {
267 Self::new(v.0, v.1)
268 }
269}
270
271impl From<(u16, u32)> for NodeId {
272 fn from(v: (u16, u32)) -> Self {
273 Self::new(v.0, v.1)
274 }
275}
276
277impl From<(u16, Guid)> for NodeId {
278 fn from(v: (u16, Guid)) -> Self {
279 Self::new(v.0, v.1)
280 }
281}
282
283impl From<(u16, ByteString)> for NodeId {
284 fn from(v: (u16, ByteString)) -> Self {
285 Self::new(v.0, v.1)
286 }
287}
288
289static NEXT_NODE_ID_NUMERIC: AtomicU32 = AtomicU32::new(1);
290
291impl Default for NodeId {
292 fn default() -> Self {
293 NodeId::null()
294 }
295}
296
297impl NodeId {
298 pub fn new<T>(namespace: u16, value: T) -> NodeId
301 where
302 T: Into<Identifier>,
303 {
304 NodeId {
305 namespace,
306 identifier: value.into(),
307 }
308 }
309
310 pub fn root_folder_id() -> NodeId {
312 ObjectId::RootFolder.into()
313 }
314
315 pub fn objects_folder_id() -> NodeId {
317 ObjectId::ObjectsFolder.into()
318 }
319
320 pub fn types_folder_id() -> NodeId {
322 ObjectId::TypesFolder.into()
323 }
324
325 pub fn views_folder_id() -> NodeId {
327 ObjectId::ViewsFolder.into()
328 }
329
330 pub fn is_null(&self) -> bool {
332 self.namespace == 0 && self.identifier == Identifier::Numeric(0)
333 }
334
335 pub fn null() -> NodeId {
337 NodeId::new(0, 0u32)
338 }
339
340 pub fn next_numeric(namespace: u16) -> NodeId {
342 NodeId::new(
343 namespace,
344 NEXT_NODE_ID_NUMERIC.fetch_add(1, Ordering::SeqCst),
345 )
346 }
347
348 pub fn as_object_id(&self) -> std::result::Result<ObjectId, NodeIdError> {
350 match self.identifier {
351 Identifier::Numeric(id) if self.namespace == 0 => {
352 ObjectId::try_from(id).map_err(|_| NodeIdError)
353 }
354 _ => Err(NodeIdError),
355 }
356 }
357
358 pub fn as_variable_id(&self) -> std::result::Result<VariableId, NodeIdError> {
360 match self.identifier {
361 Identifier::Numeric(id) if self.namespace == 0 => {
362 VariableId::try_from(id).map_err(|_| NodeIdError)
363 }
364 _ => Err(NodeIdError),
365 }
366 }
367
368 pub fn as_reference_type_id(&self) -> std::result::Result<ReferenceTypeId, NodeIdError> {
370 if self.is_null() {
371 Err(NodeIdError)
372 } else {
373 match self.identifier {
374 Identifier::Numeric(id) if self.namespace == 0 => {
375 ReferenceTypeId::try_from(id).map_err(|_| NodeIdError)
376 }
377 _ => Err(NodeIdError),
378 }
379 }
380 }
381
382 pub fn as_data_type_id(&self) -> std::result::Result<DataTypeId, NodeIdError> {
384 match self.identifier {
385 Identifier::Numeric(id) if self.namespace == 0 => {
386 DataTypeId::try_from(id).map_err(|_| NodeIdError)
387 }
388 _ => Err(NodeIdError),
389 }
390 }
391
392 pub fn as_method_id(&self) -> std::result::Result<MethodId, NodeIdError> {
394 match self.identifier {
395 Identifier::Numeric(id) if self.namespace == 0 => {
396 MethodId::try_from(id).map_err(|_| NodeIdError)
397 }
398 _ => Err(NodeIdError),
399 }
400 }
401
402 pub fn is_numeric(&self) -> bool {
404 matches!(self.identifier, Identifier::Numeric(_))
405 }
406
407 pub fn is_string(&self) -> bool {
409 matches!(self.identifier, Identifier::String(_))
410 }
411
412 pub fn is_guid(&self) -> bool {
414 matches!(self.identifier, Identifier::Guid(_))
415 }
416
417 pub fn is_byte_string(&self) -> bool {
419 matches!(self.identifier, Identifier::ByteString(_))
420 }
421
422 pub fn as_u32(&self) -> Option<u32> {
424 match &self.identifier {
425 Identifier::Numeric(i) => Some(*i),
426 _ => None,
427 }
428 }
429}