opcua_types/
namespaces.rs

1//! Utilities for working with namespaces.
2
3use hashbrown::HashMap;
4
5use crate::{errors::OpcUaError, ExpandedNodeId, NodeId, Variant};
6
7/// Utility for handling assignment of namespaces on server startup.
8#[derive(Debug, Default, Clone)]
9pub struct NamespaceMap {
10    known_namespaces: HashMap<String, u16>,
11}
12
13impl NamespaceMap {
14    /// Create a new namespace map containing only the base namespace.
15    pub fn new() -> Self {
16        let mut known_namespaces = HashMap::new();
17        known_namespaces.insert("http://opcfoundation.org/UA/".to_owned(), 0u16);
18
19        Self { known_namespaces }
20    }
21
22    /// Create a new namespace map from the given list of namespaces.
23    pub fn new_full(map: HashMap<String, u16>) -> Self {
24        Self {
25            known_namespaces: map,
26        }
27    }
28
29    /// Create a new namespace map from a vec of variant as we get when reading
30    /// the namespace array from the server
31    pub fn new_from_variant_array(array: &[Variant]) -> Result<Self, OpcUaError> {
32        let known_namespaces: HashMap<String, u16> = array
33            .iter()
34            .enumerate()
35            .map(|(idx, v)| {
36                if let Variant::String(s) = v {
37                    Ok((s.value().clone().unwrap_or(String::new()), idx as u16))
38                } else {
39                    Err(OpcUaError::UnexpectedVariantType {
40                        variant_id: v.scalar_type_id(),
41                        message: "Namespace array on server contains invalid data".to_string(),
42                    })
43                }
44            })
45            .collect::<Result<HashMap<_, _>, _>>()?;
46        Ok(Self { known_namespaces })
47    }
48
49    /// Add a new namespace, returning its index in the namespace map.
50    /// If the namespace is already added, its old index is returned.
51    pub fn add_namespace(&mut self, namespace: &str) -> u16 {
52        if let Some(ns) = self.known_namespaces.get(namespace) {
53            return *ns;
54        }
55        let max = self
56            .known_namespaces
57            .iter()
58            .map(|kv| *kv.1)
59            .max()
60            .unwrap_or_default();
61        self.known_namespaces.insert(namespace.to_owned(), max + 1);
62
63        max + 1
64    }
65
66    /// Return the inner namespace map.
67    pub fn known_namespaces(&self) -> &HashMap<String, u16> {
68        &self.known_namespaces
69    }
70
71    /// Get the index of the given namespace.
72    pub fn get_index(&self, ns: &str) -> Option<u16> {
73        self.known_namespaces.get(ns).copied()
74    }
75
76    /// Try to resolve an expanded node ID to a NodeId.
77    pub fn resolve_node_id<'b>(
78        &self,
79        id: &'b ExpandedNodeId,
80    ) -> Option<std::borrow::Cow<'b, NodeId>> {
81        id.try_resolve(self)
82    }
83}
84
85/// Utility handling namespaces when loading node sets.
86pub struct NodeSetNamespaceMapper<'a> {
87    namespaces: &'a mut NamespaceMap,
88    index_map: HashMap<u16, u16>,
89}
90
91#[derive(Debug)]
92/// Error returned when trying to get an index that is not initialized.
93pub struct UninitializedIndex(pub u16);
94
95impl<'a> NodeSetNamespaceMapper<'a> {
96    /// Create a new namespace mapper from the given namespace map.
97    pub fn new(namespaces: &'a mut NamespaceMap) -> Self {
98        Self {
99            namespaces,
100            index_map: HashMap::new(),
101        }
102    }
103
104    /// Add a namespace. `index_in_node_set` is the index in the NodeSet2 file being loaded.
105    pub fn add_namespace(&mut self, namespace: &str, index_in_node_set: u16) {
106        let index = self.namespaces.add_namespace(namespace);
107        self.index_map.insert(index_in_node_set, index);
108    }
109
110    /// Get the index of a namespace given its index in a NodeSet2 file.
111    pub fn get_index(&self, index_in_node_set: u16) -> Result<u16, UninitializedIndex> {
112        if index_in_node_set == 0 {
113            return Ok(0);
114        }
115        let Some(idx) = self.index_map.get(&index_in_node_set) else {
116            return Err(UninitializedIndex(index_in_node_set));
117        };
118        Ok(*idx)
119    }
120
121    /// Return the inner namespace map.
122    pub fn namespaces(&'a self) -> &'a NamespaceMap {
123        &*self.namespaces
124    }
125
126    /// Return the inner index map.
127    pub fn index_map(&self) -> &HashMap<u16, u16> {
128        &self.index_map
129    }
130}