opcua_types/
guid.rs

1// OPCUA for Rust
2// SPDX-License-Identifier: MPL-2.0
3// Copyright (C) 2017-2024 Adam Lock
4
5//! Contains the implementation of `Guid`.
6
7use std::{
8    fmt,
9    io::{Read, Write},
10    str::FromStr,
11};
12
13use uuid::Uuid;
14
15use crate::encoding::*;
16
17/// A Guid is a 16 byte Globally Unique Identifier.
18#[derive(Eq, PartialEq, Clone)]
19pub struct Guid {
20    uuid: Uuid,
21}
22
23// Explicit implementation of hash to avoid any issues
24// when implementing Equivalent elsewhere.
25impl std::hash::Hash for Guid {
26    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
27        self.as_bytes().hash(state);
28    }
29}
30
31impl From<Guid> for Uuid {
32    fn from(value: Guid) -> Self {
33        value.uuid
34    }
35}
36
37impl UaNullable for Guid {
38    fn is_ua_null(&self) -> bool {
39        self.uuid.is_nil()
40    }
41}
42
43#[cfg(feature = "json")]
44mod json {
45    use std::io::{Read, Write};
46    use std::str::FromStr;
47
48    use crate::{json::*, Error};
49
50    use super::Guid;
51
52    impl JsonEncodable for Guid {
53        fn encode(
54            &self,
55            stream: &mut JsonStreamWriter<&mut dyn Write>,
56            _ctx: &crate::json::Context<'_>,
57        ) -> super::EncodingResult<()> {
58            Ok(stream.string_value(&self.uuid.to_string())?)
59        }
60    }
61
62    impl JsonDecodable for Guid {
63        fn decode(
64            stream: &mut JsonStreamReader<&mut dyn Read>,
65            _ctx: &Context<'_>,
66        ) -> super::EncodingResult<Self> {
67            let s = stream.next_str()?;
68            let guid = Guid::from_str(s).map_err(Error::decoding)?;
69            Ok(guid)
70        }
71    }
72}
73
74#[cfg(feature = "xml")]
75mod xml {
76    use crate::xml::*;
77    use std::{
78        io::{Read, Write},
79        str::FromStr,
80    };
81
82    use super::Guid;
83
84    impl XmlType for Guid {
85        const TAG: &'static str = "Guid";
86    }
87
88    impl XmlEncodable for Guid {
89        fn encode(
90            &self,
91            writer: &mut XmlStreamWriter<&mut dyn Write>,
92            ctx: &crate::xml::Context<'_>,
93        ) -> EncodingResult<()> {
94            writer.encode_child("String", &self.to_string(), ctx)
95        }
96    }
97
98    impl XmlDecodable for Guid {
99        fn decode(
100            reader: &mut XmlStreamReader<&mut dyn Read>,
101            ctx: &crate::xml::Context<'_>,
102        ) -> EncodingResult<Self> {
103            let val: Option<String> = reader.decode_single_child("String", ctx)?;
104            let Some(val) = val else {
105                return Ok(Guid::null());
106            };
107            Guid::from_str(&val).map_err(Error::decoding)
108        }
109    }
110}
111
112impl fmt::Display for Guid {
113    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
114        write!(f, "{}", self.uuid)
115    }
116}
117
118impl fmt::Debug for Guid {
119    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120        write!(f, "{}", self.uuid.hyphenated())
121    }
122}
123
124impl BinaryEncodable for Guid {
125    fn byte_len(&self, _ctx: &crate::Context<'_>) -> usize {
126        16
127    }
128
129    fn encode<S: Write + ?Sized>(
130        &self,
131        stream: &mut S,
132        _ctx: &crate::Context<'_>,
133    ) -> EncodingResult<()> {
134        process_encode_io_result(stream.write_all(self.uuid.as_bytes()))
135    }
136}
137
138impl BinaryDecodable for Guid {
139    fn decode<S: Read + ?Sized>(stream: &mut S, _ctx: &crate::Context<'_>) -> EncodingResult<Self> {
140        let mut bytes = [0u8; 16];
141        process_decode_io_result(stream.read_exact(&mut bytes))?;
142        Ok(Guid {
143            uuid: Uuid::from_bytes(bytes),
144        })
145    }
146}
147
148impl FromStr for Guid {
149    type Err = <Uuid as FromStr>::Err;
150
151    fn from_str(s: &str) -> Result<Self, Self::Err> {
152        Uuid::from_str(s).map(|uuid| Guid { uuid })
153    }
154}
155
156impl From<Uuid> for Guid {
157    fn from(uuid: Uuid) -> Self {
158        Self { uuid }
159    }
160}
161
162impl Default for Guid {
163    fn default() -> Self {
164        Guid::null()
165    }
166}
167
168impl Guid {
169    /// Return a null guid, i.e. 00000000-0000-0000-0000-000000000000
170    pub fn null() -> Guid {
171        Guid { uuid: Uuid::nil() }
172    }
173
174    /// Creates a random Guid
175    pub fn new() -> Guid {
176        Guid {
177            uuid: Uuid::new_v4(),
178        }
179    }
180
181    /// Returns the bytes of the Guid
182    pub fn as_bytes(&self) -> &[u8; 16] {
183        self.uuid.as_bytes()
184    }
185
186    /// Creates a guid from a byte array.
187    pub fn from_bytes(bytes: [u8; 16]) -> Guid {
188        Guid {
189            uuid: Uuid::from_bytes(bytes),
190        }
191    }
192
193    /// Creates an UUID from a byte slice of exactly 16 bytes.
194    pub fn from_slice(bytes: &[u8]) -> Result<Guid, uuid::Error> {
195        Ok(Guid {
196            uuid: Uuid::from_slice(bytes)?,
197        })
198    }
199}
200
201impl AsRef<[u8; 16]> for Guid {
202    fn as_ref(&self) -> &[u8; 16] {
203        self.as_bytes()
204    }
205}
206
207impl PartialEq<[u8; 16]> for Guid {
208    fn eq(&self, other: &[u8; 16]) -> bool {
209        self.as_ref() == other
210    }
211}
212
213/// Reference to a Guid that can be created without allocating a
214/// Guid object. Used when comparing with NodeIds,
215/// to distinguish from the generic ByteString case.
216pub struct GuidRef<'a>(pub &'a [u8; 16]);
217
218impl PartialEq<Guid> for GuidRef<'_> {
219    fn eq(&self, other: &Guid) -> bool {
220        self.as_ref() == other.as_bytes()
221    }
222}
223
224impl PartialEq<GuidRef<'_>> for Guid {
225    fn eq(&self, other: &GuidRef<'_>) -> bool {
226        other == self
227    }
228}
229
230impl std::hash::Hash for GuidRef<'_> {
231    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
232        self.0.hash(state);
233    }
234}
235
236impl AsRef<[u8; 16]> for GuidRef<'_> {
237    fn as_ref(&self) -> &[u8; 16] {
238        self.0
239    }
240}