Skip to main content

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.to_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.to_bytes_le()))
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_le(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    /// Note that this uses Microsoft's per-field little endian encoding,
183    /// i.e. [Uuid::to_bytes_le].
184    pub fn to_bytes(&self) -> [u8; 16] {
185        self.uuid.to_bytes_le()
186    }
187
188    /// Creates a guid from a byte array.
189    pub fn from_bytes(bytes: [u8; 16]) -> Guid {
190        Guid {
191            uuid: Uuid::from_bytes_le(bytes),
192        }
193    }
194
195    /// Creates an UUID from a byte slice of exactly 16 bytes.
196    pub fn from_slice(bytes: &[u8]) -> Result<Guid, uuid::Error> {
197        Ok(Guid {
198            uuid: Uuid::from_slice_le(bytes)?,
199        })
200    }
201}
202
203impl PartialEq<[u8; 16]> for Guid {
204    fn eq(&self, other: &[u8; 16]) -> bool {
205        self.to_bytes() == *other
206    }
207}