1use std::fmt::Display;
8
9use crate::{DataTypeId, NodeId, NodeIdError, StatusCode};
10
11#[derive(Debug, Clone, Copy, PartialEq)]
13pub enum VariantTypeId<'a> {
14 Empty,
16 Scalar(VariantScalarTypeId),
18 Array(VariantScalarTypeId, Option<&'a [u32]>),
20}
21
22impl From<VariantScalarTypeId> for VariantTypeId<'_> {
23 fn from(value: VariantScalarTypeId) -> Self {
24 Self::Scalar(value)
25 }
26}
27
28impl<'a> From<(VariantScalarTypeId, &'a [u32])> for VariantTypeId<'a> {
29 fn from(value: (VariantScalarTypeId, &'a [u32])) -> Self {
30 Self::Array(value.0, Some(value.1))
31 }
32}
33
34#[derive(Debug, Clone, Copy, PartialEq)]
35#[repr(u32)]
36pub enum VariantScalarTypeId {
38 Boolean = 1,
40 SByte = 2,
42 Byte = 3,
44 Int16 = 4,
46 UInt16 = 5,
48 Int32 = 6,
50 UInt32 = 7,
52 Int64 = 8,
54 UInt64 = 9,
56 Float = 10,
58 Double = 11,
60 String = 12,
62 DateTime = 13,
64 Guid = 14,
66 ByteString = 15,
68 XmlElement = 16,
70 NodeId = 17,
72 ExpandedNodeId = 18,
74 StatusCode = 19,
76 QualifiedName = 20,
78 LocalizedText = 21,
80 ExtensionObject = 22,
82 DataValue = 23,
84 Variant = 24,
86 DiagnosticInfo = 25,
88}
89
90impl Display for VariantScalarTypeId {
91 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92 match self {
93 VariantScalarTypeId::Boolean => write!(f, "Boolean"),
94 VariantScalarTypeId::SByte => write!(f, "SByte"),
95 VariantScalarTypeId::Byte => write!(f, "Byte"),
96 VariantScalarTypeId::Int16 => write!(f, "Int16"),
97 VariantScalarTypeId::UInt16 => write!(f, "UInt16"),
98 VariantScalarTypeId::Int32 => write!(f, "Int32"),
99 VariantScalarTypeId::UInt32 => write!(f, "UInt32"),
100 VariantScalarTypeId::Int64 => write!(f, "Int64"),
101 VariantScalarTypeId::UInt64 => write!(f, "UInt64"),
102 VariantScalarTypeId::Float => write!(f, "Float"),
103 VariantScalarTypeId::Double => write!(f, "Double"),
104 VariantScalarTypeId::String => write!(f, "String"),
105 VariantScalarTypeId::DateTime => write!(f, "DateTime"),
106 VariantScalarTypeId::Guid => write!(f, "Guid"),
107 VariantScalarTypeId::ByteString => write!(f, "ByteString"),
108 VariantScalarTypeId::XmlElement => write!(f, "XmlElement"),
109 VariantScalarTypeId::NodeId => write!(f, "NodeId"),
110 VariantScalarTypeId::ExpandedNodeId => write!(f, "ExpandedNodeId"),
111 VariantScalarTypeId::StatusCode => write!(f, "StatusCode"),
112 VariantScalarTypeId::QualifiedName => write!(f, "QualifiedName"),
113 VariantScalarTypeId::LocalizedText => write!(f, "LocalizedText"),
114 VariantScalarTypeId::ExtensionObject => write!(f, "ExtensionObject"),
115 VariantScalarTypeId::DataValue => write!(f, "DataValue"),
116 VariantScalarTypeId::Variant => write!(f, "Variant"),
117 VariantScalarTypeId::DiagnosticInfo => write!(f, "DiagnosticInfo"),
118 }
119 }
120}
121
122impl TryFrom<u32> for VariantScalarTypeId {
123 type Error = StatusCode;
124 fn try_from(value: u32) -> Result<Self, Self::Error> {
125 Ok(match value {
126 1 => Self::Boolean,
127 2 => Self::SByte,
128 3 => Self::Byte,
129 4 => Self::Int16,
130 5 => Self::UInt16,
131 6 => Self::Int32,
132 7 => Self::UInt32,
133 8 => Self::Int64,
134 9 => Self::UInt64,
135 10 => Self::Float,
136 11 => Self::Double,
137 12 => Self::String,
138 13 => Self::DateTime,
139 14 => Self::Guid,
140 15 => Self::ByteString,
141 16 => Self::XmlElement,
142 17 => Self::NodeId,
143 18 => Self::ExpandedNodeId,
144 19 => Self::StatusCode,
145 20 => Self::QualifiedName,
146 21 => Self::LocalizedText,
147 22 => Self::ExtensionObject,
148 23 => Self::DataValue,
149 24 => Self::Variant,
150 25 => Self::DiagnosticInfo,
151 r => {
152 tracing::error!("Got unexpected vlaue for enum VariantScalarTypeId: {r}");
153 return Err(StatusCode::BadDecodingError);
154 }
155 })
156 }
157}
158
159impl TryFrom<&NodeId> for VariantScalarTypeId {
160 type Error = NodeIdError;
161 fn try_from(value: &NodeId) -> Result<Self, NodeIdError> {
162 let type_id = value.as_data_type_id()?;
163
164 Ok(match type_id {
165 DataTypeId::Boolean => Self::Boolean,
166 DataTypeId::Byte => Self::Byte,
167 DataTypeId::Int16 => Self::Int16,
168 DataTypeId::UInt16 => Self::UInt16,
169 DataTypeId::Int32 => Self::Int32,
170 DataTypeId::UInt32 => Self::UInt32,
171 DataTypeId::Int64 => Self::Int64,
172 DataTypeId::UInt64 => Self::UInt64,
173 DataTypeId::Float => Self::Float,
174 DataTypeId::Double => Self::Double,
175 DataTypeId::String => Self::String,
176 DataTypeId::DateTime => Self::DateTime,
177 DataTypeId::Guid => Self::Guid,
178 DataTypeId::ByteString => Self::ByteString,
179 DataTypeId::XmlElement => Self::XmlElement,
180 DataTypeId::NodeId => Self::NodeId,
181 DataTypeId::ExpandedNodeId => Self::ExpandedNodeId,
182 DataTypeId::StatusCode => Self::StatusCode,
183 DataTypeId::QualifiedName => Self::QualifiedName,
184 DataTypeId::LocalizedText => Self::LocalizedText,
185 DataTypeId::DataValue => Self::DataValue,
186 DataTypeId::BaseDataType => Self::Variant,
187 DataTypeId::DiagnosticInfo => Self::DiagnosticInfo,
188 _ => return Err(NodeIdError),
189 })
190 }
191}
192
193impl TryFrom<&NodeId> for VariantTypeId<'_> {
194 type Error = NodeIdError;
195 fn try_from(value: &NodeId) -> Result<Self, NodeIdError> {
196 Ok(Self::Scalar(VariantScalarTypeId::try_from(value)?))
197 }
198}
199
200impl VariantScalarTypeId {
201 pub fn encoding_mask(&self) -> u8 {
203 match self {
204 Self::Boolean => EncodingMask::BOOLEAN,
205 Self::SByte => EncodingMask::SBYTE,
206 Self::Byte => EncodingMask::BYTE,
207 Self::Int16 => EncodingMask::INT16,
208 Self::UInt16 => EncodingMask::UINT16,
209 Self::Int32 => EncodingMask::INT32,
210 Self::UInt32 => EncodingMask::UINT32,
211 Self::Int64 => EncodingMask::INT64,
212 Self::UInt64 => EncodingMask::UINT64,
213 Self::Float => EncodingMask::FLOAT,
214 Self::Double => EncodingMask::DOUBLE,
215 Self::String => EncodingMask::STRING,
216 Self::DateTime => EncodingMask::DATE_TIME,
217 Self::Guid => EncodingMask::GUID,
218 Self::StatusCode => EncodingMask::STATUS_CODE,
219 Self::ByteString => EncodingMask::BYTE_STRING,
220 Self::XmlElement => EncodingMask::XML_ELEMENT,
221 Self::QualifiedName => EncodingMask::QUALIFIED_NAME,
222 Self::LocalizedText => EncodingMask::LOCALIZED_TEXT,
223 Self::NodeId => EncodingMask::NODE_ID,
224 Self::ExpandedNodeId => EncodingMask::EXPANDED_NODE_ID,
225 Self::ExtensionObject => EncodingMask::EXTENSION_OBJECT,
226 Self::Variant => EncodingMask::VARIANT,
227 Self::DataValue => EncodingMask::DATA_VALUE,
228 Self::DiagnosticInfo => EncodingMask::DIAGNOSTIC_INFO,
229 }
230 }
231
232 pub fn from_encoding_mask(encoding_mask: u8) -> Option<Self> {
234 Some(match encoding_mask & !EncodingMask::ARRAY_MASK {
235 EncodingMask::BOOLEAN => Self::Boolean,
236 EncodingMask::SBYTE => Self::SByte,
237 EncodingMask::BYTE => Self::Byte,
238 EncodingMask::INT16 => Self::Int16,
239 EncodingMask::UINT16 => Self::UInt16,
240 EncodingMask::INT32 => Self::Int32,
241 EncodingMask::UINT32 => Self::UInt32,
242 EncodingMask::INT64 => Self::Int64,
243 EncodingMask::UINT64 => Self::UInt64,
244 EncodingMask::FLOAT => Self::Float,
245 EncodingMask::DOUBLE => Self::Double,
246 EncodingMask::STRING => Self::String,
247 EncodingMask::DATE_TIME => Self::DateTime,
248 EncodingMask::GUID => Self::Guid,
249 EncodingMask::STATUS_CODE => Self::StatusCode,
250 EncodingMask::BYTE_STRING => Self::ByteString,
251 EncodingMask::XML_ELEMENT => Self::XmlElement,
252 EncodingMask::QUALIFIED_NAME => Self::QualifiedName,
253 EncodingMask::LOCALIZED_TEXT => Self::LocalizedText,
254 EncodingMask::NODE_ID => Self::NodeId,
255 EncodingMask::EXPANDED_NODE_ID => Self::ExpandedNodeId,
256 EncodingMask::EXTENSION_OBJECT => Self::ExtensionObject,
257 EncodingMask::VARIANT => Self::Variant,
258 EncodingMask::DATA_VALUE => Self::DataValue,
259 EncodingMask::DIAGNOSTIC_INFO => Self::DiagnosticInfo,
260 _ => {
261 return None;
262 }
263 })
264 }
265
266 pub fn is_numeric(&self) -> bool {
268 matches!(
269 self,
270 Self::SByte
271 | Self::Byte
272 | Self::Int16
273 | Self::UInt16
274 | Self::Int32
275 | Self::UInt32
276 | Self::Int64
277 | Self::UInt64
278 | Self::Float
279 | Self::Double
280 )
281 }
282
283 pub fn precedence(&self) -> u8 {
287 match self {
288 Self::Double => 1,
289 Self::Float => 2,
290 Self::Int64 => 3,
291 Self::UInt64 => 4,
292 Self::Int32 => 5,
293 Self::UInt32 => 6,
294 Self::StatusCode => 7,
295 Self::Int16 => 8,
296 Self::UInt16 => 9,
297 Self::SByte => 10,
298 Self::Byte => 11,
299 Self::Boolean => 12,
300 Self::Guid => 13,
301 Self::String => 14,
302 Self::ExpandedNodeId => 15,
303 Self::NodeId => 16,
304 Self::LocalizedText => 17,
305 Self::QualifiedName => 18,
306 _ => 100,
307 }
308 }
309}
310
311impl VariantTypeId<'_> {
312 pub fn encoding_mask(&self) -> u8 {
314 match self {
315 VariantTypeId::Empty => 0u8,
317 VariantTypeId::Scalar(s) => s.encoding_mask(),
319 VariantTypeId::Array(s, dims) => {
320 let mask = s.encoding_mask() | EncodingMask::ARRAY_VALUES_BIT;
321 if dims.is_some() {
322 mask | EncodingMask::ARRAY_DIMENSIONS_BIT
323 } else {
324 mask
325 }
326 }
327 }
328 }
329
330 pub fn precedence(&self) -> u8 {
332 match self {
333 Self::Scalar(s) => s.precedence(),
334 Self::Array(s, _) => s.precedence(),
335 Self::Empty => 100,
336 }
337 }
338}
339
340pub(crate) struct EncodingMask;
341
342impl EncodingMask {
343 pub(crate) const BOOLEAN: u8 = DataTypeId::Boolean as u8;
345 pub(crate) const SBYTE: u8 = DataTypeId::SByte as u8;
346 pub(crate) const BYTE: u8 = DataTypeId::Byte as u8;
347 pub(crate) const INT16: u8 = DataTypeId::Int16 as u8;
348 pub(crate) const UINT16: u8 = DataTypeId::UInt16 as u8;
349 pub(crate) const INT32: u8 = DataTypeId::Int32 as u8;
350 pub(crate) const UINT32: u8 = DataTypeId::UInt32 as u8;
351 pub(crate) const INT64: u8 = DataTypeId::Int64 as u8;
352 pub(crate) const UINT64: u8 = DataTypeId::UInt64 as u8;
353 pub(crate) const FLOAT: u8 = DataTypeId::Float as u8;
354 pub(crate) const DOUBLE: u8 = DataTypeId::Double as u8;
355 pub(crate) const STRING: u8 = DataTypeId::String as u8;
356 pub(crate) const DATE_TIME: u8 = DataTypeId::DateTime as u8;
357 pub(crate) const GUID: u8 = DataTypeId::Guid as u8;
358 pub(crate) const BYTE_STRING: u8 = DataTypeId::ByteString as u8;
359 pub(crate) const XML_ELEMENT: u8 = DataTypeId::XmlElement as u8;
360 pub(crate) const NODE_ID: u8 = DataTypeId::NodeId as u8;
361 pub(crate) const EXPANDED_NODE_ID: u8 = DataTypeId::ExpandedNodeId as u8;
362 pub(crate) const STATUS_CODE: u8 = DataTypeId::StatusCode as u8;
363 pub(crate) const QUALIFIED_NAME: u8 = DataTypeId::QualifiedName as u8;
364 pub(crate) const LOCALIZED_TEXT: u8 = DataTypeId::LocalizedText as u8;
365 pub(crate) const EXTENSION_OBJECT: u8 = 22; pub(crate) const DATA_VALUE: u8 = DataTypeId::DataValue as u8;
367 pub(crate) const VARIANT: u8 = 24;
368 pub(crate) const DIAGNOSTIC_INFO: u8 = DataTypeId::DiagnosticInfo as u8;
369 pub(crate) const ARRAY_DIMENSIONS_BIT: u8 = 1 << 6;
371 pub(crate) const ARRAY_VALUES_BIT: u8 = 1 << 7;
373
374 pub(crate) const ARRAY_MASK: u8 =
375 EncodingMask::ARRAY_DIMENSIONS_BIT | EncodingMask::ARRAY_VALUES_BIT;
376}