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)]
142/// Description of a generic or primitive data type.
143pub struct GenericTypeInfo {
144    /// True if the type is abstract.
145    pub is_abstract: bool,
146}
147
148impl GenericTypeInfo {
149    /// Creates a new `GenericTypeInfo`.
150    pub fn new(is_abstract: bool) -> Self {
151        Self { is_abstract }
152    }
153}
154
155#[derive(Debug)]
156/// Structure describing a data type on the server.
157pub enum TypeInfo {
158    /// Description of an enum data type.
159    Enum(Arc<EnumTypeInfo>),
160    /// Description of a structure data type.
161    Struct(Arc<StructTypeInfo>),
162    /// Description of a primitive data type.
163    Primitive(Arc<GenericTypeInfo>),
164}
165
166#[derive(Debug)]
167/// Reference to a `TypeInfo`.
168pub enum TypeInfoRef<'a> {
169    /// Description of an enum data type.
170    Enum(&'a Arc<EnumTypeInfo>),
171    /// Description of a structure data type.
172    Struct(&'a Arc<StructTypeInfo>),
173    /// Description of a primitive data type.
174    Primitive(&'a Arc<GenericTypeInfo>),
175}
176
177impl From<StructTypeInfo> for TypeInfo {
178    fn from(value: StructTypeInfo) -> Self {
179        Self::Struct(Arc::new(value))
180    }
181}
182
183impl From<EnumTypeInfo> for TypeInfo {
184    fn from(value: EnumTypeInfo) -> Self {
185        Self::Enum(Arc::new(value))
186    }
187}
188
189impl From<GenericTypeInfo> for TypeInfo {
190    fn from(value: GenericTypeInfo) -> Self {
191        Self::Primitive(Arc::new(value))
192    }
193}
194
195#[derive(Debug)]
196/// Map from child to parent node ID.
197pub struct ParentIds {
198    parent_ids: HashMap<NodeId, NodeId>,
199}
200
201impl Default for ParentIds {
202    fn default() -> Self {
203        Self::new()
204    }
205}
206
207/// Variant of a data type.
208pub enum DataTypeVariant {
209    /// Data type is an enumeration.
210    Enumeration,
211    /// Data type is a structure.
212    Structure,
213    /// Data type is some primitive.
214    Primitive,
215}
216
217impl ParentIds {
218    /// Create a new empty parent ID map.
219    pub fn new() -> Self {
220        Self {
221            parent_ids: HashMap::new(),
222        }
223    }
224
225    /// Add a child, parent type pair.
226    pub fn add_type(&mut self, node_id: NodeId, parent_id: NodeId) {
227        self.parent_ids.insert(node_id, parent_id);
228    }
229
230    /// Get the data type variant, essentially checking if it needs special treatment,
231    /// by traversing up the hierarchy until we hit a known type.
232    pub fn get_data_type_variant(&self, id: &NodeId) -> Option<DataTypeVariant> {
233        if let Ok(t) = id.as_data_type_id() {
234            match t {
235                crate::DataTypeId::Boolean
236                | crate::DataTypeId::SByte
237                | crate::DataTypeId::Byte
238                | crate::DataTypeId::Int16
239                | crate::DataTypeId::UInt16
240                | crate::DataTypeId::Int32
241                | crate::DataTypeId::UInt32
242                | crate::DataTypeId::Int64
243                | crate::DataTypeId::UInt64
244                | crate::DataTypeId::Float
245                | crate::DataTypeId::Double
246                | crate::DataTypeId::String
247                | crate::DataTypeId::DateTime
248                | crate::DataTypeId::Guid
249                | crate::DataTypeId::ByteString
250                | crate::DataTypeId::XmlElement
251                | crate::DataTypeId::NodeId
252                | crate::DataTypeId::ExpandedNodeId
253                | crate::DataTypeId::StatusCode
254                | crate::DataTypeId::QualifiedName
255                | crate::DataTypeId::LocalizedText
256                | crate::DataTypeId::DataValue
257                | crate::DataTypeId::DiagnosticInfo
258                | crate::DataTypeId::BaseDataType => return Some(DataTypeVariant::Primitive),
259                crate::DataTypeId::Structure | crate::DataTypeId::Decimal => {
260                    return Some(DataTypeVariant::Structure)
261                }
262                crate::DataTypeId::Enumeration => return Some(DataTypeVariant::Enumeration),
263                _ => (),
264            }
265        }
266
267        let parent = self.parent_ids.get(id)?;
268        self.get_data_type_variant(parent)
269    }
270
271    /// Get the variant type for the given `id` by recursively traversing up
272    /// the hierarchy until we hit a known type.
273    pub fn get_builtin_type(&self, id: &NodeId) -> Option<VariantScalarTypeId> {
274        if let Ok(t) = id.as_data_type_id() {
275            match t {
276                crate::DataTypeId::Boolean => return Some(VariantScalarTypeId::Boolean),
277                crate::DataTypeId::SByte => return Some(VariantScalarTypeId::SByte),
278                crate::DataTypeId::Byte => return Some(VariantScalarTypeId::Byte),
279                crate::DataTypeId::Int16 => return Some(VariantScalarTypeId::Int16),
280                crate::DataTypeId::UInt16 => return Some(VariantScalarTypeId::UInt16),
281                crate::DataTypeId::Int32 => return Some(VariantScalarTypeId::Int32),
282                crate::DataTypeId::UInt32 => return Some(VariantScalarTypeId::UInt32),
283                crate::DataTypeId::Int64 => return Some(VariantScalarTypeId::Int64),
284                crate::DataTypeId::UInt64 => return Some(VariantScalarTypeId::UInt64),
285                crate::DataTypeId::Float => return Some(VariantScalarTypeId::Float),
286                crate::DataTypeId::Double => return Some(VariantScalarTypeId::Double),
287                crate::DataTypeId::String => return Some(VariantScalarTypeId::String),
288                crate::DataTypeId::DateTime => return Some(VariantScalarTypeId::DateTime),
289                crate::DataTypeId::Guid => return Some(VariantScalarTypeId::Guid),
290                crate::DataTypeId::ByteString => return Some(VariantScalarTypeId::ByteString),
291                crate::DataTypeId::XmlElement => return Some(VariantScalarTypeId::XmlElement),
292                crate::DataTypeId::NodeId => return Some(VariantScalarTypeId::NodeId),
293                crate::DataTypeId::ExpandedNodeId => {
294                    return Some(VariantScalarTypeId::ExpandedNodeId)
295                }
296                crate::DataTypeId::StatusCode => return Some(VariantScalarTypeId::StatusCode),
297                crate::DataTypeId::QualifiedName => {
298                    return Some(VariantScalarTypeId::QualifiedName)
299                }
300                crate::DataTypeId::LocalizedText => {
301                    return Some(VariantScalarTypeId::LocalizedText)
302                }
303                // ExtensionObject in this context just means "Structure", which is what
304                // the base type in the type hierarchy is.
305                crate::DataTypeId::Structure | crate::DataTypeId::Decimal => {
306                    return Some(VariantScalarTypeId::ExtensionObject)
307                }
308                crate::DataTypeId::DataValue => return Some(VariantScalarTypeId::DataValue),
309                crate::DataTypeId::DiagnosticInfo => {
310                    return Some(VariantScalarTypeId::DiagnosticInfo)
311                }
312                crate::DataTypeId::Enumeration => return Some(VariantScalarTypeId::Int32),
313                // Not sure if this is actually correct, it's the only thing that really makes sense.
314                crate::DataTypeId::BaseDataType => return Some(VariantScalarTypeId::Variant),
315                _ => (),
316            }
317        }
318        let parent = self.parent_ids.get(id)?;
319        self.get_builtin_type(parent)
320    }
321}
322
323impl TypeInfo {
324    /// Build a TypeInfo from the data type definition of the data type.
325    pub fn from_type_definition(
326        value: DataTypeDefinition,
327        name: String,
328        encoding_ids: Option<EncodingIds>,
329        is_abstract: bool,
330        node_id: &NodeId,
331        parent_ids: &ParentIds,
332    ) -> Result<Self, String> {
333        match value {
334            DataTypeDefinition::Structure(d) => {
335                let Some(encoding_ids) = encoding_ids else {
336                    return Err("Missing encoding IDs for structured type".to_owned());
337                };
338                let mut fields =
339                    Vec::with_capacity(d.fields.as_ref().map(|f| f.len()).unwrap_or_default());
340                let mut fields_by_name = HashMap::with_capacity(fields.len());
341                for (idx, v) in d.fields.into_iter().flatten().enumerate() {
342                    let Some(builtin) = parent_ids.get_builtin_type(&v.data_type) else {
343                        return Err(format!(
344                            "Failed to resolve type id {node_id} to scalar type"
345                        ));
346                    };
347                    let f = ParsedStructureField::from_field(v, builtin)?;
348                    fields_by_name.insert(f.name.clone(), idx);
349                    fields.push(f);
350                }
351
352                Ok(Self::Struct(Arc::new(StructTypeInfo {
353                    structure_type: d.structure_type,
354                    fields,
355                    encoding_ids,
356                    is_abstract,
357                    node_id: node_id.clone(),
358                    index_by_name: fields_by_name,
359                    name,
360                })))
361            }
362            DataTypeDefinition::Enum(d) => Ok(Self::Enum(Arc::new(EnumTypeInfo {
363                variants: d
364                    .fields
365                    .into_iter()
366                    .flatten()
367                    .map(|v| (v.value, v))
368                    .collect(),
369            }))),
370        }
371    }
372}
373
374#[derive(Debug)]
375/// Data type tree, used for loading custom types at runtime.
376pub struct DataTypeTree {
377    struct_types: HashMap<NodeId, Arc<StructTypeInfo>>,
378    enum_types: HashMap<NodeId, Arc<EnumTypeInfo>>,
379    other_types: HashMap<NodeId, Arc<GenericTypeInfo>>,
380    parent_ids: ParentIds,
381    encoding_to_data_type: HashMap<NodeId, NodeId>,
382}
383
384impl DataTypeTree {
385    /// Create a new data type tree with the given parent IDs.
386    ///
387    /// Parent IDs should be populated before starting to populate the type tree.
388    pub fn new(parent_ids: ParentIds) -> Self {
389        Self {
390            struct_types: HashMap::new(),
391            enum_types: HashMap::new(),
392            other_types: HashMap::new(),
393            parent_ids,
394            encoding_to_data_type: HashMap::new(),
395        }
396    }
397
398    /// Add a type to the tree.
399    pub fn add_type(&mut self, id: NodeId, info: impl Into<TypeInfo>) {
400        let info = info.into();
401        match info {
402            TypeInfo::Enum(arc) => {
403                self.enum_types.insert(id.clone(), arc);
404            }
405            TypeInfo::Struct(arc) => {
406                self.encoding_to_data_type
407                    .insert(arc.encoding_ids.binary_id.clone(), id.clone());
408                self.encoding_to_data_type
409                    .insert(arc.encoding_ids.json_id.clone(), id.clone());
410                self.encoding_to_data_type
411                    .insert(arc.encoding_ids.xml_id.clone(), id.clone());
412                self.struct_types.insert(id.clone(), arc);
413            }
414            TypeInfo::Primitive(arc) => {
415                self.other_types.insert(id.clone(), arc);
416            }
417        }
418    }
419
420    /// Get a type from the tree.
421    pub fn get_type<'a>(&'a self, id: &NodeId) -> Option<TypeInfoRef<'a>> {
422        if let Some(d) = self.struct_types.get(id) {
423            Some(TypeInfoRef::Struct(d))
424        } else if let Some(d) = self.enum_types.get(id) {
425            Some(TypeInfoRef::Enum(d))
426        } else {
427            self.other_types.get(id).map(TypeInfoRef::Primitive)
428        }
429    }
430
431    /// Get a struct type from the tree.
432    pub fn get_struct_type(&self, id: &NodeId) -> Option<&Arc<StructTypeInfo>> {
433        self.struct_types.get(id)
434    }
435
436    /// Get a mutable reference to the parent ID map.
437    pub fn parent_ids_mut(&mut self) -> &mut ParentIds {
438        &mut self.parent_ids
439    }
440
441    /// Get a reference to the parent ID map.
442    pub fn parent_ids(&self) -> &ParentIds {
443        &self.parent_ids
444    }
445
446    /// Get the inner map from encoding to data type ID.
447    pub fn encoding_to_data_type(&self) -> &HashMap<NodeId, NodeId> {
448        &self.encoding_to_data_type
449    }
450}