Skip to main content

opcua_types/custom/
type_tree.rs

1use std::{collections::HashMap, sync::Arc};
2
3use crate::{
4    DataTypeDefinition, EnumField, Error, NodeId, StatusCode, StructureField, StructureType,
5    Variant, VariantScalarTypeId, VariantTypeId,
6};
7
8#[derive(Debug)]
9/// Parsed type information about an enum variant.
10pub struct EnumTypeInfo {
11    /// Enum fields.
12    pub variants: HashMap<i64, EnumField>,
13}
14
15#[derive(Debug)]
16/// Parsed type information about a struct field.
17pub struct ParsedStructureField {
18    /// Field name.
19    pub name: String,
20    /// Field data type ID.
21    pub type_id: NodeId,
22    /// Field value rank.
23    pub value_rank: i32,
24    /// Field array dimensions.
25    pub array_dimensions: Option<Vec<u32>>,
26    /// Whether this field is optional.
27    pub is_optional: bool,
28    /// Variant type used to store this field.
29    pub scalar_type: VariantScalarTypeId,
30}
31
32impl ParsedStructureField {
33    /// Parse this from a structure field.
34    pub fn from_field(f: StructureField, scalar_type: VariantScalarTypeId) -> Result<Self, String> {
35        if f.name.is_empty() || f.name.is_null() {
36            return Err("Field has null name".to_owned());
37        }
38        Ok(Self {
39            name: f.name.as_ref().to_owned(),
40            type_id: f.data_type,
41            value_rank: f.value_rank,
42            array_dimensions: f.array_dimensions,
43            is_optional: f.is_optional,
44            scalar_type,
45        })
46    }
47
48    /// Validate that `value` could be this field.
49    pub fn validate(&self, value: &Variant) -> Result<(), Error> {
50        let ty = match value.type_id() {
51            VariantTypeId::Empty => {
52                if !self.is_optional {
53                    return Err(Error::new(
54                        StatusCode::BadInvalidArgument,
55                        format!("Got null value for non-nullable field {}", self.name),
56                    ));
57                } else {
58                    return Ok(());
59                }
60            }
61            VariantTypeId::Scalar(ty) => ty,
62            VariantTypeId::Array(ty, dims) => {
63                let rank = dims.map(|d| d.len()).unwrap_or(1);
64                if rank as i32 != self.value_rank {
65                    return Err(Error::new(
66                        StatusCode::BadInvalidArgument,
67                    format!("Invalid dimensions, array dimensions {:?} length must match field value rank {}",
68                        dims, self.value_rank)));
69                }
70                ty
71            }
72        };
73        if ty != self.scalar_type {
74            return Err(Error::new(
75                StatusCode::BadInvalidArgument,
76                format!(
77                    "Invalid type for field {}. Got {}, expected {}",
78                    self.name, ty, self.scalar_type
79                ),
80            ));
81        }
82        Ok(())
83    }
84}
85
86#[derive(Debug)]
87/// Parsed info about a structure type.
88pub struct StructTypeInfo {
89    /// Structure variant. Structure, StructureWithOptionalFields, or Union.
90    pub structure_type: StructureType,
91    /// List of structure fields. The order is significant.
92    pub fields: Vec<ParsedStructureField>,
93    /// Field index by name.
94    pub index_by_name: HashMap<String, usize>,
95    /// Collection of encoding IDs.
96    pub encoding_ids: EncodingIds,
97    /// Whether this type is abstract and cannot be instantiated.
98    pub is_abstract: bool,
99    /// Structure node ID.
100    pub node_id: NodeId,
101    /// Structure name.
102    pub name: String,
103}
104
105impl StructTypeInfo {
106    /// Get a field by index.
107    pub fn get_field(&self, idx: usize) -> Option<&ParsedStructureField> {
108        self.fields.get(idx)
109    }
110
111    /// Get a field by name.
112    pub fn get_field_by_name(&self, idx: &str) -> Option<&ParsedStructureField> {
113        self.index_by_name
114            .get(idx)
115            .and_then(|i| self.fields.get(*i))
116    }
117
118    /// Return whether this struct is supported by the current version of the library.
119    /// Types that are not supported will panic on encoding, and be skipped when decoding.
120    ///
121    /// Currently this is only structures and unions with subtyped values.
122    pub fn is_supported(&self) -> bool {
123        !matches!(
124            self.structure_type,
125            StructureType::StructureWithSubtypedValues | StructureType::UnionWithSubtypedValues
126        )
127    }
128}
129
130#[derive(Debug, Default)]
131/// Encoding IDs for a structure type.
132pub struct EncodingIds {
133    /// Binary encoding ID.
134    pub binary_id: NodeId,
135    /// Json encoding ID.
136    pub json_id: NodeId,
137    /// XML encoding ID.
138    pub xml_id: NodeId,
139}
140
141#[derive(Debug)]
142pub struct GenericTypeInfo {
143    pub is_abstract: bool,
144}
145
146impl GenericTypeInfo {
147    pub fn new(is_abstract: bool) -> Self {
148        Self { is_abstract }
149    }
150}
151
152#[derive(Debug)]
153/// Structure describing a data type on the server.
154pub enum TypeInfo {
155    /// Description of an enum data type.
156    Enum(Arc<EnumTypeInfo>),
157    /// Description of a structure data type.
158    Struct(Arc<StructTypeInfo>),
159    /// Description of a primitive data type.
160    Primitive(Arc<GenericTypeInfo>),
161}
162
163#[derive(Debug)]
164/// Reference to a `TypeInfo`.
165pub enum TypeInfoRef<'a> {
166    /// Description of an enum data type.
167    Enum(&'a Arc<EnumTypeInfo>),
168    /// Description of a structure data type.
169    Struct(&'a Arc<StructTypeInfo>),
170    /// Description of a primitive data type.
171    Primitive(&'a Arc<GenericTypeInfo>),
172}
173
174impl From<StructTypeInfo> for TypeInfo {
175    fn from(value: StructTypeInfo) -> Self {
176        Self::Struct(Arc::new(value))
177    }
178}
179
180impl From<EnumTypeInfo> for TypeInfo {
181    fn from(value: EnumTypeInfo) -> Self {
182        Self::Enum(Arc::new(value))
183    }
184}
185
186impl From<GenericTypeInfo> for TypeInfo {
187    fn from(value: GenericTypeInfo) -> Self {
188        Self::Primitive(Arc::new(value))
189    }
190}
191
192#[derive(Debug)]
193/// Map from child to parent node ID.
194pub struct ParentIds {
195    parent_ids: HashMap<NodeId, NodeId>,
196}
197
198impl Default for ParentIds {
199    fn default() -> Self {
200        Self::new()
201    }
202}
203
204/// Variant of a data type.
205pub enum DataTypeVariant {
206    /// Data type is an enumeration.
207    Enumeration,
208    /// Data type is a structure.
209    Structure,
210    /// Data type is some primitive.
211    Primitive,
212}
213
214impl ParentIds {
215    /// Create a new empty parent ID map.
216    pub fn new() -> Self {
217        Self {
218            parent_ids: HashMap::new(),
219        }
220    }
221
222    /// Add a child, parent type pair.
223    pub fn add_type(&mut self, node_id: NodeId, parent_id: NodeId) {
224        self.parent_ids.insert(node_id, parent_id);
225    }
226
227    /// Get the data type variant, essentially checking if it needs special treatment,
228    /// by traversing up the hierarchy until we hit a known type.
229    pub fn get_data_type_variant(&self, id: &NodeId) -> Option<DataTypeVariant> {
230        if let Ok(t) = id.as_data_type_id() {
231            match t {
232                crate::DataTypeId::Boolean
233                | crate::DataTypeId::SByte
234                | crate::DataTypeId::Byte
235                | crate::DataTypeId::Int16
236                | crate::DataTypeId::UInt16
237                | crate::DataTypeId::Int32
238                | crate::DataTypeId::UInt32
239                | crate::DataTypeId::Int64
240                | crate::DataTypeId::UInt64
241                | crate::DataTypeId::Float
242                | crate::DataTypeId::Double
243                | crate::DataTypeId::String
244                | crate::DataTypeId::DateTime
245                | crate::DataTypeId::Guid
246                | crate::DataTypeId::ByteString
247                | crate::DataTypeId::XmlElement
248                | crate::DataTypeId::NodeId
249                | crate::DataTypeId::ExpandedNodeId
250                | crate::DataTypeId::StatusCode
251                | crate::DataTypeId::QualifiedName
252                | crate::DataTypeId::LocalizedText
253                | crate::DataTypeId::DataValue
254                | crate::DataTypeId::DiagnosticInfo
255                | crate::DataTypeId::BaseDataType => return Some(DataTypeVariant::Primitive),
256                crate::DataTypeId::Structure | crate::DataTypeId::Decimal => {
257                    return Some(DataTypeVariant::Structure)
258                }
259                crate::DataTypeId::Enumeration => return Some(DataTypeVariant::Enumeration),
260                _ => (),
261            }
262        }
263
264        let parent = self.parent_ids.get(id)?;
265        self.get_data_type_variant(parent)
266    }
267
268    /// Get the variant type for the given `id` by recursively traversing up
269    /// the hierarchy until we hit a known type.
270    pub fn get_builtin_type(&self, id: &NodeId) -> Option<VariantScalarTypeId> {
271        if let Ok(t) = id.as_data_type_id() {
272            match t {
273                crate::DataTypeId::Boolean => return Some(VariantScalarTypeId::Boolean),
274                crate::DataTypeId::SByte => return Some(VariantScalarTypeId::SByte),
275                crate::DataTypeId::Byte => return Some(VariantScalarTypeId::Byte),
276                crate::DataTypeId::Int16 => return Some(VariantScalarTypeId::Int16),
277                crate::DataTypeId::UInt16 => return Some(VariantScalarTypeId::UInt16),
278                crate::DataTypeId::Int32 => return Some(VariantScalarTypeId::Int32),
279                crate::DataTypeId::UInt32 => return Some(VariantScalarTypeId::UInt32),
280                crate::DataTypeId::Int64 => return Some(VariantScalarTypeId::Int64),
281                crate::DataTypeId::UInt64 => return Some(VariantScalarTypeId::UInt64),
282                crate::DataTypeId::Float => return Some(VariantScalarTypeId::Float),
283                crate::DataTypeId::Double => return Some(VariantScalarTypeId::Double),
284                crate::DataTypeId::String => return Some(VariantScalarTypeId::String),
285                crate::DataTypeId::DateTime => return Some(VariantScalarTypeId::DateTime),
286                crate::DataTypeId::Guid => return Some(VariantScalarTypeId::Guid),
287                crate::DataTypeId::ByteString => return Some(VariantScalarTypeId::ByteString),
288                crate::DataTypeId::XmlElement => return Some(VariantScalarTypeId::XmlElement),
289                crate::DataTypeId::NodeId => return Some(VariantScalarTypeId::NodeId),
290                crate::DataTypeId::ExpandedNodeId => {
291                    return Some(VariantScalarTypeId::ExpandedNodeId)
292                }
293                crate::DataTypeId::StatusCode => return Some(VariantScalarTypeId::StatusCode),
294                crate::DataTypeId::QualifiedName => {
295                    return Some(VariantScalarTypeId::QualifiedName)
296                }
297                crate::DataTypeId::LocalizedText => {
298                    return Some(VariantScalarTypeId::LocalizedText)
299                }
300                // ExtensionObject in this context just means "Structure", which is what
301                // the base type in the type hierarchy is.
302                crate::DataTypeId::Structure | crate::DataTypeId::Decimal => {
303                    return Some(VariantScalarTypeId::ExtensionObject)
304                }
305                crate::DataTypeId::DataValue => return Some(VariantScalarTypeId::DataValue),
306                crate::DataTypeId::DiagnosticInfo => {
307                    return Some(VariantScalarTypeId::DiagnosticInfo)
308                }
309                crate::DataTypeId::Enumeration => return Some(VariantScalarTypeId::Int32),
310                // Not sure if this is actually correct, it's the only thing that really makes sense.
311                crate::DataTypeId::BaseDataType => return Some(VariantScalarTypeId::Variant),
312                _ => (),
313            }
314        }
315        let parent = self.parent_ids.get(id)?;
316        self.get_builtin_type(parent)
317    }
318}
319
320impl TypeInfo {
321    /// Build a TypeInfo from the data type definition of the data type.
322    pub fn from_type_definition(
323        value: DataTypeDefinition,
324        name: String,
325        encoding_ids: Option<EncodingIds>,
326        is_abstract: bool,
327        node_id: &NodeId,
328        parent_ids: &ParentIds,
329    ) -> Result<Self, String> {
330        match value {
331            DataTypeDefinition::Structure(d) => {
332                let Some(encoding_ids) = encoding_ids else {
333                    return Err("Missing encoding IDs for structured type".to_owned());
334                };
335                let mut fields =
336                    Vec::with_capacity(d.fields.as_ref().map(|f| f.len()).unwrap_or_default());
337                let mut fields_by_name = HashMap::with_capacity(fields.len());
338                for (idx, v) in d.fields.into_iter().flatten().enumerate() {
339                    let Some(builtin) = parent_ids.get_builtin_type(&v.data_type) else {
340                        return Err(format!(
341                            "Failed to resolve type id {node_id} to scalar type"
342                        ));
343                    };
344                    let f = ParsedStructureField::from_field(v, builtin)?;
345                    fields_by_name.insert(f.name.clone(), idx);
346                    fields.push(f);
347                }
348
349                Ok(Self::Struct(Arc::new(StructTypeInfo {
350                    structure_type: d.structure_type,
351                    fields,
352                    encoding_ids,
353                    is_abstract,
354                    node_id: node_id.clone(),
355                    index_by_name: fields_by_name,
356                    name,
357                })))
358            }
359            DataTypeDefinition::Enum(d) => Ok(Self::Enum(Arc::new(EnumTypeInfo {
360                variants: d
361                    .fields
362                    .into_iter()
363                    .flatten()
364                    .map(|v| (v.value, v))
365                    .collect(),
366            }))),
367        }
368    }
369}
370
371#[derive(Debug)]
372/// Data type tree, used for loading custom types at runtime.
373pub struct DataTypeTree {
374    struct_types: HashMap<NodeId, Arc<StructTypeInfo>>,
375    enum_types: HashMap<NodeId, Arc<EnumTypeInfo>>,
376    other_types: HashMap<NodeId, Arc<GenericTypeInfo>>,
377    parent_ids: ParentIds,
378    encoding_to_data_type: HashMap<NodeId, NodeId>,
379}
380
381impl DataTypeTree {
382    /// Create a new data type tree with the given parent IDs.
383    ///
384    /// Parent IDs should be populated before starting to populate the type tree.
385    pub fn new(parent_ids: ParentIds) -> Self {
386        Self {
387            struct_types: HashMap::new(),
388            enum_types: HashMap::new(),
389            other_types: HashMap::new(),
390            parent_ids,
391            encoding_to_data_type: HashMap::new(),
392        }
393    }
394
395    /// Add a type to the tree.
396    pub fn add_type(&mut self, id: NodeId, info: impl Into<TypeInfo>) {
397        let info = info.into();
398        match info {
399            TypeInfo::Enum(arc) => {
400                self.enum_types.insert(id.clone(), arc);
401            }
402            TypeInfo::Struct(arc) => {
403                self.encoding_to_data_type
404                    .insert(arc.encoding_ids.binary_id.clone(), id.clone());
405                self.encoding_to_data_type
406                    .insert(arc.encoding_ids.json_id.clone(), id.clone());
407                self.encoding_to_data_type
408                    .insert(arc.encoding_ids.xml_id.clone(), id.clone());
409                self.struct_types.insert(id.clone(), arc);
410            }
411            TypeInfo::Primitive(arc) => {
412                self.other_types.insert(id.clone(), arc);
413            }
414        }
415    }
416
417    /// Get a type from the tree.
418    pub fn get_type<'a>(&'a self, id: &NodeId) -> Option<TypeInfoRef<'a>> {
419        if let Some(d) = self.struct_types.get(id) {
420            Some(TypeInfoRef::Struct(d))
421        } else if let Some(d) = self.enum_types.get(id) {
422            Some(TypeInfoRef::Enum(d))
423        } else {
424            self.other_types.get(id).map(TypeInfoRef::Primitive)
425        }
426    }
427
428    /// Get a struct type from the tree.
429    pub fn get_struct_type(&self, id: &NodeId) -> Option<&Arc<StructTypeInfo>> {
430        self.struct_types.get(id)
431    }
432
433    /// Get a mutable reference to the parent ID map.
434    pub fn parent_ids_mut(&mut self) -> &mut ParentIds {
435        &mut self.parent_ids
436    }
437
438    /// Get a reference to the parent ID map.
439    pub fn parent_ids(&self) -> &ParentIds {
440        &self.parent_ids
441    }
442
443    /// Get the inner map from encoding to data type ID.
444    pub fn encoding_to_data_type(&self) -> &HashMap<NodeId, NodeId> {
445        &self.encoding_to_data_type
446    }
447}