Skip to main content

opcua/types/
node_id.rs

1// OPCUA for Rust
2// SPDX-License-Identifier: MPL-2.0
3// Copyright (C) 2017-2022 Adam Lock
4
5//! Contains the implementation of `NodeId`.
6
7use std::{
8    self,
9    convert::TryFrom,
10    fmt,
11    io::{Read, Write},
12    str::FromStr,
13    sync::atomic::{AtomicUsize, Ordering},
14    u16, u32,
15};
16
17use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
18use serde_json::{self, json};
19
20use crate::types::{
21    byte_string::ByteString,
22    encoding::*,
23    guid::Guid,
24    node_ids::{ObjectId, ReferenceTypeId},
25    status_codes::StatusCode,
26    string::*,
27};
28
29/// The kind of identifier, numeric, string, guid or byte
30#[derive(Eq, PartialEq, Clone, Debug, Hash)]
31pub enum Identifier {
32    Numeric(u32),
33    String(UAString),
34    Guid(Guid),
35    ByteString(ByteString),
36}
37
38impl fmt::Display for Identifier {
39    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
40        match self {
41            Identifier::Numeric(v) => write!(f, "i={}", *v),
42            Identifier::String(v) => write!(f, "s={}", v),
43            Identifier::Guid(v) => write!(f, "g={:?}", v),
44            Identifier::ByteString(v) => write!(f, "b={}", v.as_base64()),
45        }
46    }
47}
48
49impl FromStr for Identifier {
50    type Err = ();
51
52    fn from_str(s: &str) -> Result<Self, Self::Err> {
53        if s.len() < 2 {
54            Err(())
55        } else {
56            let k = &s[..2];
57            let v = &s[2..];
58            match k {
59                "i=" => v.parse::<u32>().map(|v| v.into()).map_err(|_| ()),
60                "s=" => Ok(UAString::from(v).into()),
61                "g=" => Guid::from_str(v).map(|v| v.into()).map_err(|_| ()),
62                "b=" => ByteString::from_base64(v).map(|v| v.into()).ok_or(()),
63                _ => Err(()),
64            }
65        }
66    }
67}
68
69impl From<i32> for Identifier {
70    fn from(v: i32) -> Self {
71        Identifier::Numeric(v as u32)
72    }
73}
74
75impl From<u32> for Identifier {
76    fn from(v: u32) -> Self {
77        Identifier::Numeric(v as u32)
78    }
79}
80
81impl<'a> From<&'a str> for Identifier {
82    fn from(v: &'a str) -> Self {
83        Identifier::from(UAString::from(v))
84    }
85}
86
87impl From<&String> for Identifier {
88    fn from(v: &String) -> Self {
89        Identifier::from(UAString::from(v))
90    }
91}
92
93impl From<String> for Identifier {
94    fn from(v: String) -> Self {
95        Identifier::from(UAString::from(v))
96    }
97}
98
99impl From<UAString> for Identifier {
100    fn from(v: UAString) -> Self {
101        Identifier::String(v)
102    }
103}
104
105impl From<Guid> for Identifier {
106    fn from(v: Guid) -> Self {
107        Identifier::Guid(v)
108    }
109}
110
111impl From<ByteString> for Identifier {
112    fn from(v: ByteString) -> Self {
113        Identifier::ByteString(v)
114    }
115}
116
117#[derive(Debug)]
118pub struct NodeIdError;
119
120impl fmt::Display for NodeIdError {
121    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122        write!(f, "NodeIdError")
123    }
124}
125
126impl std::error::Error for NodeIdError {}
127
128/// An identifier for a node in the address space of an OPC UA Server.
129#[derive(PartialEq, Eq, Clone, Debug, Hash)]
130pub struct NodeId {
131    /// The index for a namespace
132    pub namespace: u16,
133    /// The identifier for the node in the address space
134    pub identifier: Identifier,
135}
136
137impl fmt::Display for NodeId {
138    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
139        if self.namespace != 0 {
140            write!(f, "ns={};{}", self.namespace, self.identifier)
141        } else {
142            write!(f, "{}", self.identifier)
143        }
144    }
145}
146
147// JSON serialization schema as per spec:
148//
149// "Type"
150//      The IdentifierType encoded as a JSON number.
151//      Allowed values are:
152//            0 - UInt32 Identifier encoded as a JSON number.
153//            1 - A String Identifier encoded as a JSON string.
154//            2 - A Guid Identifier encoded as described in 5.4.2.7.
155//            3 - A ByteString Identifier encoded as described in 5.4.2.8.
156//      This field is omitted for UInt32 identifiers.
157// "Id"
158//      The Identifier.
159//      The value of the id field specifies the encoding of this field.
160// "Namespace"
161//      The NamespaceIndex for the NodeId.
162//      The field is encoded as a JSON number for the reversible encoding.
163//      The field is omitted if the NamespaceIndex equals 0.
164//      For the non-reversible encoding, the field is the NamespaceUri associated with the NamespaceIndex, encoded as a JSON string.
165//      A NamespaceIndex of 1 is always encoded as a JSON number.
166
167#[derive(Serialize, Deserialize)]
168struct JsonNodeId {
169    #[serde(skip_serializing_if = "Option::is_none")]
170    #[serde(rename = "Type")]
171    id_type: Option<u32>,
172    #[serde(rename = "Id")]
173    id: serde_json::Value,
174    #[serde(skip_serializing_if = "Option::is_none")]
175    #[serde(rename = "Namespace")]
176    namespace: Option<serde_json::Value>,
177}
178
179impl Serialize for NodeId {
180    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
181    where
182        S: Serializer,
183    {
184        let (id_type, id) = match &self.identifier {
185            Identifier::Numeric(id) => (None, json!(id)),
186            Identifier::String(id) => (Some(1), json!(id.as_ref())),
187            Identifier::Guid(id) => (Some(2), json!(id.to_string())),
188            Identifier::ByteString(id) => (Some(3), json!(id.as_base64())),
189        };
190        // Omit namespace if it is 0
191        let namespace = if self.namespace == 0 {
192            None
193        } else {
194            Some(json!(self.namespace))
195        };
196
197        let json = JsonNodeId {
198            id_type,
199            id,
200            namespace,
201        };
202        json.serialize(serializer)
203    }
204}
205
206impl<'de> Deserialize<'de> for NodeId {
207    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
208    where
209        D: Deserializer<'de>,
210    {
211        let v = JsonNodeId::deserialize(deserializer)?;
212        // Only namespace index is supported. Spec says namespace uri can go there too, but not for this code it wonn't.
213        let namespace = if let Some(namespace) = v.namespace {
214            let namespace = namespace
215                .as_u64()
216                .ok_or_else(|| de::Error::custom("Expected numeric namespace index"))?;
217            if namespace > u16::MAX as u64 {
218                return Err(de::Error::custom("Numeric namespace index is out of range"));
219            }
220            namespace as u16
221        } else {
222            0
223        };
224        // Validate and extract
225        let id_type = v.id_type.unwrap_or(0);
226        match id_type {
227            0 => {
228                // Numeric
229                let v =
230                    v.id.as_u64()
231                        .ok_or_else(|| de::Error::custom("Expected Numeric identifier"))?;
232                Ok(NodeId::new(namespace, v as u32))
233            }
234            1 => {
235                // String
236                let v =
237                    v.id.as_str()
238                        .ok_or_else(|| de::Error::custom("Expected String identifier"))?;
239                if v.is_empty() {
240                    Err(de::Error::custom("String identifier is empty"))
241                } else {
242                    Ok(NodeId::new(namespace, String::from(v)))
243                }
244            }
245            2 => {
246                // Guid
247                let v =
248                    v.id.as_str()
249                        .ok_or_else(|| de::Error::custom("Expected Guid identifier"))?;
250                if v.is_empty() {
251                    Err(de::Error::custom("Guid identifier is empty"))
252                } else {
253                    let v = Guid::from_str(v)
254                        .map_err(|_| de::Error::custom("Error parsing Guid identifier"))?;
255                    Ok(NodeId::new(namespace, v))
256                }
257            }
258            3 => {
259                // Bytestring
260                let v =
261                    v.id.as_str()
262                        .ok_or_else(|| de::Error::custom("Expected ByteString identifier"))?;
263                if v.is_empty() {
264                    Err(de::Error::custom("ByteString identifier is empty"))
265                } else {
266                    let v = ByteString::from_base64(v)
267                        .ok_or_else(|| de::Error::custom("Error parsing ByteString identifier"))?;
268                    Ok(NodeId::new(namespace, v))
269                }
270            }
271            _ => Err(de::Error::custom("Invalid IdType")),
272        }
273    }
274}
275
276impl BinaryEncoder<NodeId> for NodeId {
277    fn byte_len(&self) -> usize {
278        // Type determines the byte code
279        let size: usize = match self.identifier {
280            Identifier::Numeric(value) => {
281                if self.namespace == 0 && value <= 255 {
282                    2
283                } else if self.namespace <= 255 && value <= 65535 {
284                    4
285                } else {
286                    7
287                }
288            }
289            Identifier::String(ref value) => 3 + value.byte_len(),
290            Identifier::Guid(ref value) => 3 + value.byte_len(),
291            Identifier::ByteString(ref value) => 3 + value.byte_len(),
292        };
293        size
294    }
295
296    fn encode<S: Write>(&self, stream: &mut S) -> EncodingResult<usize> {
297        let mut size: usize = 0;
298        // Type determines the byte code
299        match &self.identifier {
300            Identifier::Numeric(value) => {
301                if self.namespace == 0 && *value <= 255 {
302                    // node id fits into 2 bytes when the namespace is 0 and the value <= 255
303                    size += write_u8(stream, 0x0)?;
304                    size += write_u8(stream, *value as u8)?;
305                } else if self.namespace <= 255 && *value <= 65535 {
306                    // node id fits into 4 bytes when namespace <= 255 and value <= 65535
307                    size += write_u8(stream, 0x1)?;
308                    size += write_u8(stream, self.namespace as u8)?;
309                    size += write_u16(stream, *value as u16)?;
310                } else {
311                    // full node id
312                    size += write_u8(stream, 0x2)?;
313                    size += write_u16(stream, self.namespace)?;
314                    size += write_u32(stream, *value)?;
315                }
316            }
317            Identifier::String(value) => {
318                size += write_u8(stream, 0x3)?;
319                size += write_u16(stream, self.namespace)?;
320                size += value.encode(stream)?;
321            }
322            Identifier::Guid(value) => {
323                size += write_u8(stream, 0x4)?;
324                size += write_u16(stream, self.namespace)?;
325                size += value.encode(stream)?;
326            }
327            Identifier::ByteString(value) => {
328                size += write_u8(stream, 0x5)?;
329                size += write_u16(stream, self.namespace)?;
330                size += value.encode(stream)?;
331            }
332        }
333        assert_eq!(size, self.byte_len());
334        Ok(size)
335    }
336
337    fn decode<S: Read>(stream: &mut S, decoding_options: &DecodingOptions) -> EncodingResult<Self> {
338        let identifier = read_u8(stream)?;
339        let node_id = match identifier {
340            0x0 => {
341                let namespace = 0;
342                let value = read_u8(stream)?;
343                NodeId::new(namespace, u32::from(value))
344            }
345            0x1 => {
346                let namespace = read_u8(stream)?;
347                let value = read_u16(stream)?;
348                NodeId::new(u16::from(namespace), u32::from(value))
349            }
350            0x2 => {
351                let namespace = read_u16(stream)?;
352                let value = read_u32(stream)?;
353                NodeId::new(namespace, value)
354            }
355            0x3 => {
356                let namespace = read_u16(stream)?;
357                let value = UAString::decode(stream, decoding_options)?;
358                NodeId::new(namespace, value)
359            }
360            0x4 => {
361                let namespace = read_u16(stream)?;
362                let value = Guid::decode(stream, decoding_options)?;
363                NodeId::new(namespace, value)
364            }
365            0x5 => {
366                let namespace = read_u16(stream)?;
367                let value = ByteString::decode(stream, decoding_options)?;
368                NodeId::new(namespace, value)
369            }
370            _ => {
371                error!("Unrecognized node id type {}", identifier);
372                return Err(StatusCode::BadDecodingError);
373            }
374        };
375        Ok(node_id)
376    }
377}
378
379impl FromStr for NodeId {
380    type Err = StatusCode;
381    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
382        use regex::Regex;
383
384        // Parses a node from a string using the format specified in 5.3.1.10 part 6
385        //
386        // ns=<namespaceindex>;<type>=<value>
387        //
388        // Where type:
389        //   i = NUMERIC
390        //   s = STRING
391        //   g = GUID
392        //   b = OPAQUE (ByteString)
393        //
394        // If namespace == 0, the ns=0; will be omitted
395
396        lazy_static! {
397            // Contains capture groups "ns" and "t" for namespace and type respectively
398            static ref RE: Regex = Regex::new(r"^(ns=(?P<ns>[0-9]+);)?(?P<t>[isgb]=.+)$").unwrap();
399        }
400
401        let captures = RE.captures(s).ok_or(StatusCode::BadNodeIdInvalid)?;
402
403        // Check namespace (optional)
404        let namespace = if let Some(ns) = captures.name("ns") {
405            ns.as_str()
406                .parse::<u16>()
407                .map_err(|_| StatusCode::BadNodeIdInvalid)?
408        } else {
409            0
410        };
411
412        // Type identifier
413        let t = captures.name("t").unwrap();
414        Identifier::from_str(t.as_str())
415            .map(|t| NodeId::new(namespace, t))
416            .map_err(|_| StatusCode::BadNodeIdInvalid)
417    }
418}
419
420impl From<&NodeId> for NodeId {
421    fn from(v: &NodeId) -> Self {
422        v.clone()
423    }
424}
425
426impl Into<String> for NodeId {
427    fn into(self) -> String {
428        self.to_string()
429    }
430}
431
432impl<'a> From<(u16, &'a str)> for NodeId {
433    fn from(v: (u16, &'a str)) -> Self {
434        Self::new(v.0, UAString::from(v.1))
435    }
436}
437
438impl From<(u16, UAString)> for NodeId {
439    fn from(v: (u16, UAString)) -> Self {
440        Self::new(v.0, v.1)
441    }
442}
443
444impl From<(u16, u32)> for NodeId {
445    fn from(v: (u16, u32)) -> Self {
446        Self::new(v.0, v.1)
447    }
448}
449
450impl From<(u16, Guid)> for NodeId {
451    fn from(v: (u16, Guid)) -> Self {
452        Self::new(v.0, v.1)
453    }
454}
455
456impl From<(u16, ByteString)> for NodeId {
457    fn from(v: (u16, ByteString)) -> Self {
458        Self::new(v.0, v.1)
459    }
460}
461
462static NEXT_NODE_ID_NUMERIC: AtomicUsize = AtomicUsize::new(0);
463
464impl Default for NodeId {
465    fn default() -> Self {
466        NodeId::null()
467    }
468}
469
470impl NodeId {
471    // Constructs a new NodeId from anything that can be turned into Identifier
472    // u32, Guid, ByteString or String
473    pub fn new<T>(namespace: u16, value: T) -> NodeId
474    where
475        T: 'static + Into<Identifier>,
476    {
477        NodeId {
478            namespace,
479            identifier: value.into(),
480        }
481    }
482
483    /// Returns the node id for the root folder.
484    pub fn root_folder_id() -> NodeId {
485        ObjectId::RootFolder.into()
486    }
487
488    /// Returns the node id for the objects folder.
489    pub fn objects_folder_id() -> NodeId {
490        ObjectId::ObjectsFolder.into()
491    }
492
493    /// Returns the node id for the types folder.
494    pub fn types_folder_id() -> NodeId {
495        ObjectId::TypesFolder.into()
496    }
497
498    /// Returns the node id for the views folder.
499    pub fn views_folder_id() -> NodeId {
500        ObjectId::ViewsFolder.into()
501    }
502
503    /// Test if the node id is null, i.e. 0 namespace and 0 identifier
504    pub fn is_null(&self) -> bool {
505        self.namespace == 0 && self.identifier == Identifier::Numeric(0)
506    }
507
508    /// Returns a null node id
509    pub fn null() -> NodeId {
510        NodeId::new(0, 0u32)
511    }
512
513    // Creates a numeric node id with an id incrementing up from 1000
514    pub fn next_numeric(namespace: u16) -> NodeId {
515        NodeId::new(
516            namespace,
517            NEXT_NODE_ID_NUMERIC.fetch_add(1, Ordering::SeqCst) as u32,
518        )
519    }
520
521    /// Extracts an ObjectId from a node id, providing the node id holds an object id
522    pub fn as_object_id(&self) -> std::result::Result<ObjectId, NodeIdError> {
523        match self.identifier {
524            Identifier::Numeric(id) if self.namespace == 0 => {
525                ObjectId::try_from(id).map_err(|_| NodeIdError)
526            }
527            _ => Err(NodeIdError),
528        }
529    }
530
531    pub fn as_reference_type_id(&self) -> std::result::Result<ReferenceTypeId, NodeIdError> {
532        // TODO this function should not exist - filter code should work with non ns 0 reference
533        // types
534        if self.is_null() {
535            Err(NodeIdError)
536        } else {
537            match self.identifier {
538                Identifier::Numeric(id) if self.namespace == 0 => {
539                    ReferenceTypeId::try_from(id).map_err(|_| NodeIdError)
540                }
541                _ => Err(NodeIdError),
542            }
543        }
544    }
545
546    /// Test if the node id is numeric
547    pub fn is_numeric(&self) -> bool {
548        matches!(self.identifier, Identifier::Numeric(_))
549    }
550
551    /// Test if the node id is a string
552    pub fn is_string(&self) -> bool {
553        matches!(self.identifier, Identifier::String(_))
554    }
555
556    /// Test if the node id is a guid
557    pub fn is_guid(&self) -> bool {
558        matches!(self.identifier, Identifier::Guid(_))
559    }
560
561    /// Test if the node id us a byte string
562    pub fn is_byte_string(&self) -> bool {
563        matches!(self.identifier, Identifier::ByteString(_))
564    }
565}