opcua_types/
array.rs

1//! The [`Array`] type, used to contain OPC-UA arrays, which are potentially
2//! multi-dimensional, but stored as a single vector of Variants.
3
4use thiserror::Error;
5use tracing::error;
6
7use crate::variant::*;
8
9/// An array is a vector of values with an optional number of dimensions.
10/// It is expected that the multi-dimensional array is valid, or it might not be encoded or decoded
11/// properly. The dimensions should match the number of values, or the array is invalid.
12#[derive(Debug, Clone, PartialEq)]
13pub struct Array {
14    /// Type of elements in the array
15    pub value_type: VariantScalarTypeId,
16
17    /// Values are stored sequentially
18    pub values: Vec<Variant>,
19
20    /// Multi dimension array which can contain any scalar type, all the same type. Nested
21    /// arrays are rejected. Higher rank dimensions are serialized first. For example an array
22    /// with dimensions `[2,2,2]` is written in this order - `[0,0,0]`, `[0,0,1]`, `[0,1,0]`, `[0,1,1]`,
23    /// `[1,0,0]`, `[1,0,1]`, `[1,1,0]`, `[1,1,1]`.
24    pub dimensions: Option<Vec<u32>>,
25}
26
27#[derive(Debug, Error)]
28/// Error returned when creating arrays.
29pub enum ArrayError {
30    #[error("Variant array do not match outer type")]
31    /// Variant array does not match outer type.
32    ContentMismatch,
33    /// Variant array dimensions multiplied together does not equal the actual array length.
34    #[error("Variant array dimensions multiplied together do not equal the actual array length")]
35    InvalidDimensions,
36}
37
38impl Array {
39    /// Constructs a single dimension array from the supplied values
40    pub fn new<V>(value_type: VariantScalarTypeId, values: V) -> Result<Array, ArrayError>
41    where
42        V: Into<Vec<Variant>>,
43    {
44        let values = values.into();
45        Self::validate_array_type_to_values(value_type, &values)?;
46        Ok(Array {
47            value_type,
48            values,
49            dimensions: None,
50        })
51    }
52
53    /// Constructs a multi-dimensional array from the supplied values. The values are still provided
54    /// and held as a single dimension array but a separate dimensions parameter indicates how the
55    /// values are accessed.
56    pub fn new_multi<V, D>(
57        value_type: VariantScalarTypeId,
58        values: V,
59        dimensions: D,
60    ) -> Result<Array, ArrayError>
61    where
62        V: Into<Vec<Variant>>,
63        D: Into<Vec<u32>>,
64    {
65        let values = values.into();
66        let dimensions: Vec<_> = dimensions.into();
67
68        if !Self::validate_dimensions(values.len(), &dimensions) {
69            return Err(ArrayError::InvalidDimensions);
70        }
71
72        Self::validate_array_type_to_values(value_type, &values)?;
73        Ok(Array {
74            value_type,
75            values,
76            dimensions: Some(dimensions),
77        })
78    }
79
80    /// This is a runtime check to ensure the type of the array also matches the types of the variants in the array.
81    fn validate_array_type_to_values(
82        value_type: VariantScalarTypeId,
83        values: &[Variant],
84    ) -> Result<(), ArrayError> {
85        if !values_are_of_type(values, value_type) {
86            // If the values exist, then validate them to the type
87            Err(ArrayError::ContentMismatch)
88        } else {
89            Ok(())
90        }
91    }
92
93    /// Whether this is a valid array.
94    pub fn is_valid(&self) -> bool {
95        self.is_valid_dimensions() && Self::array_is_valid(&self.values)
96    }
97
98    /// Encoding mask.
99    pub fn encoding_mask(&self) -> u8 {
100        let mut encoding_mask = self.value_type.encoding_mask();
101        encoding_mask |= EncodingMask::ARRAY_VALUES_BIT;
102        if self.dimensions.is_some() {
103            encoding_mask |= EncodingMask::ARRAY_DIMENSIONS_BIT;
104        }
105        encoding_mask
106    }
107
108    /// Tests that the variants in the slice all have the same variant type
109    fn array_is_valid(values: &[Variant]) -> bool {
110        if values.is_empty() {
111            true
112        } else {
113            let expected_type_id = values[0].type_id();
114            match expected_type_id {
115                VariantTypeId::Array(_, _) => {
116                    // Nested arrays are explicitly NOT allowed
117                    error!("Variant array contains nested array {:?}", expected_type_id);
118                    false
119                }
120                VariantTypeId::Empty => {
121                    error!("Variant array contains null values");
122                    false
123                }
124                VariantTypeId::Scalar(s) => {
125                    if values.len() > 1 {
126                        values_are_of_type(&values[1..], s)
127                    } else {
128                        true
129                    }
130                }
131            }
132        }
133    }
134
135    fn validate_dimensions(values_len: usize, dimensions: &[u32]) -> bool {
136        let len = dimensions
137            .iter()
138            .map(|d| *d as usize)
139            .reduce(|a, b| a * b)
140            .unwrap_or(0);
141        len == values_len
142    }
143
144    fn is_valid_dimensions(&self) -> bool {
145        if let Some(ref dimensions) = self.dimensions {
146            Self::validate_dimensions(self.values.len(), dimensions)
147        } else {
148            true
149        }
150    }
151}
152
153/// Check that all elements in the slice of arrays are the same type.
154pub fn values_are_of_type(values: &[Variant], expected_type: VariantScalarTypeId) -> bool {
155    // Ensure all remaining elements are the same type as the first element
156    let found_unexpected = values.iter().any(|v| match v.type_id() {
157        VariantTypeId::Array(_, _) => true,
158        VariantTypeId::Scalar(s) => s != expected_type,
159        VariantTypeId::Empty => true,
160    });
161    if found_unexpected {
162        error!(
163            "Variant array's type is expected to be {:?} but found other types in it",
164            expected_type
165        );
166    };
167    !found_unexpected
168}