opcua_types/
operand.rs

1// OPCUA for Rust
2// SPDX-License-Identifier: MPL-2.0
3// Copyright (C) 2017-2024 Adam Lock
4
5//! Implementation of content filters.
6//!
7//! These are used as part of the `Query` service, and for events.
8
9use std::convert::TryFrom;
10
11use crate::{
12    attribute::AttributeId, match_extension_object_owned, status_code::StatusCode,
13    AttributeOperand, ContentFilter, ContentFilterElement, DataTypeId, ElementOperand,
14    ExtensionObject, FilterOperator, LiteralOperand, MethodId, NodeId, NumericRange, ObjectId,
15    ObjectTypeId, QualifiedName, ReferenceTypeId, SimpleAttributeOperand, VariableId,
16    VariableTypeId, Variant,
17};
18
19#[derive(PartialEq)]
20/// Type of operand.
21pub enum OperandType {
22    /// Operand pointing at another filter element.
23    ElementOperand,
24    /// Operand resolving to a literal value.
25    LiteralOperand,
26    /// Operand resolving to an attribute of some node.
27    AttributeOperand,
28    /// Operand resolving to an attribute of some type.
29    SimpleAttributeOperand,
30}
31
32#[derive(Debug, Clone)]
33/// A filter operand.
34pub enum Operand {
35    /// Operand pointing at another filter element.
36    ElementOperand(ElementOperand),
37    /// Operand resolving to a literal value.
38    LiteralOperand(LiteralOperand),
39    /// Operand resolving to an attribute of some node.
40    AttributeOperand(AttributeOperand),
41    /// Operand resolving to an attribute of some type.
42    SimpleAttributeOperand(SimpleAttributeOperand),
43}
44
45impl From<i8> for LiteralOperand {
46    fn from(v: i8) -> Self {
47        Self::from(Variant::from(v))
48    }
49}
50
51impl From<u8> for LiteralOperand {
52    fn from(v: u8) -> Self {
53        Self::from(Variant::from(v))
54    }
55}
56
57impl From<i16> for LiteralOperand {
58    fn from(v: i16) -> Self {
59        Self::from(Variant::from(v))
60    }
61}
62
63impl From<u16> for LiteralOperand {
64    fn from(v: u16) -> Self {
65        Self::from(Variant::from(v))
66    }
67}
68
69impl From<i32> for LiteralOperand {
70    fn from(v: i32) -> Self {
71        Self::from(Variant::from(v))
72    }
73}
74
75impl From<u32> for LiteralOperand {
76    fn from(v: u32) -> Self {
77        Self::from(Variant::from(v))
78    }
79}
80
81impl From<f32> for LiteralOperand {
82    fn from(v: f32) -> Self {
83        Self::from(Variant::from(v))
84    }
85}
86
87impl From<f64> for LiteralOperand {
88    fn from(v: f64) -> Self {
89        Self::from(Variant::from(v))
90    }
91}
92
93impl From<bool> for LiteralOperand {
94    fn from(v: bool) -> Self {
95        Self::from(Variant::from(v))
96    }
97}
98
99impl From<&str> for LiteralOperand {
100    fn from(v: &str) -> Self {
101        Self::from(Variant::from(v))
102    }
103}
104
105impl From<NodeId> for LiteralOperand {
106    fn from(v: NodeId) -> Self {
107        Self::from(Variant::from(v))
108    }
109}
110
111impl From<ObjectId> for LiteralOperand {
112    fn from(v: ObjectId) -> Self {
113        Self::from(Variant::NodeId(Box::new(v.into())))
114    }
115}
116
117impl From<VariableId> for LiteralOperand {
118    fn from(v: VariableId) -> Self {
119        Self::from(Variant::NodeId(Box::new(v.into())))
120    }
121}
122
123impl From<MethodId> for LiteralOperand {
124    fn from(v: MethodId) -> Self {
125        Self::from(Variant::NodeId(Box::new(v.into())))
126    }
127}
128
129impl From<DataTypeId> for LiteralOperand {
130    fn from(v: DataTypeId) -> Self {
131        Self::from(Variant::NodeId(Box::new(v.into())))
132    }
133}
134
135impl From<ObjectTypeId> for LiteralOperand {
136    fn from(v: ObjectTypeId) -> Self {
137        Self::from(Variant::NodeId(Box::new(v.into())))
138    }
139}
140
141impl From<VariableTypeId> for LiteralOperand {
142    fn from(v: VariableTypeId) -> Self {
143        Self::from(Variant::NodeId(Box::new(v.into())))
144    }
145}
146
147impl From<ReferenceTypeId> for LiteralOperand {
148    fn from(v: ReferenceTypeId) -> Self {
149        Self::from(Variant::NodeId(Box::new(v.into())))
150    }
151}
152
153impl From<()> for LiteralOperand {
154    fn from(_v: ()) -> Self {
155        Self::from(Variant::from(()))
156    }
157}
158
159impl From<Variant> for LiteralOperand {
160    fn from(v: Variant) -> Self {
161        LiteralOperand { value: v }
162    }
163}
164
165impl TryFrom<ExtensionObject> for Operand {
166    type Error = StatusCode;
167
168    fn try_from(v: ExtensionObject) -> Result<Self, Self::Error> {
169        let operand = match_extension_object_owned!(v,
170            v: ElementOperand => Self::ElementOperand(v),
171            v: LiteralOperand => Self::LiteralOperand(v),
172            v: AttributeOperand => Self::AttributeOperand(v),
173            v: SimpleAttributeOperand => Self::SimpleAttributeOperand(v),
174            _ => return Err(StatusCode::BadFilterOperandInvalid)
175        );
176
177        Ok(operand)
178    }
179}
180
181impl From<Operand> for ExtensionObject {
182    fn from(v: Operand) -> Self {
183        match v {
184            Operand::ElementOperand(op) => ExtensionObject::from_message(op),
185            Operand::LiteralOperand(op) => ExtensionObject::from_message(op),
186            Operand::AttributeOperand(op) => ExtensionObject::from_message(op),
187            Operand::SimpleAttributeOperand(op) => ExtensionObject::from_message(op),
188        }
189    }
190}
191
192impl From<&Operand> for ExtensionObject {
193    fn from(v: &Operand) -> Self {
194        Self::from(v.clone())
195    }
196}
197
198impl From<(FilterOperator, Vec<Operand>)> for ContentFilterElement {
199    fn from(v: (FilterOperator, Vec<Operand>)) -> ContentFilterElement {
200        ContentFilterElement {
201            filter_operator: v.0,
202            filter_operands: Some(v.1.iter().map(|op| op.into()).collect()),
203        }
204    }
205}
206
207impl From<ElementOperand> for Operand {
208    fn from(v: ElementOperand) -> Operand {
209        Operand::ElementOperand(v)
210    }
211}
212
213impl From<LiteralOperand> for Operand {
214    fn from(v: LiteralOperand) -> Self {
215        Operand::LiteralOperand(v)
216    }
217}
218
219impl From<SimpleAttributeOperand> for Operand {
220    fn from(v: SimpleAttributeOperand) -> Self {
221        Operand::SimpleAttributeOperand(v)
222    }
223}
224
225impl Operand {
226    /// Create an element operand with the given index.
227    pub fn element(index: u32) -> Operand {
228        ElementOperand { index }.into()
229    }
230
231    /// Create a literal operand with the given type.
232    pub fn literal<T>(literal: T) -> Operand
233    where
234        T: Into<LiteralOperand>,
235    {
236        Operand::LiteralOperand(literal.into())
237    }
238
239    /// Creates a simple attribute operand. The browse path is the browse name using / as a separator.
240    pub fn simple_attribute<T>(
241        type_definition_id: T,
242        browse_path: &str,
243        attribute_id: AttributeId,
244        index_range: NumericRange,
245    ) -> Operand
246    where
247        T: Into<NodeId>,
248    {
249        SimpleAttributeOperand::new(type_definition_id, browse_path, attribute_id, index_range)
250            .into()
251    }
252
253    /// Get the operand type.
254    pub fn operand_type(&self) -> OperandType {
255        match self {
256            Operand::ElementOperand(_) => OperandType::ElementOperand,
257            Operand::LiteralOperand(_) => OperandType::LiteralOperand,
258            Operand::AttributeOperand(_) => OperandType::AttributeOperand,
259            Operand::SimpleAttributeOperand(_) => OperandType::SimpleAttributeOperand,
260        }
261    }
262
263    /// Return `true` if the operand is an element operand.
264    pub fn is_element(&self) -> bool {
265        self.operand_type() == OperandType::ElementOperand
266    }
267
268    /// Return `true` if the operand is a literal operand.
269    pub fn is_literal(&self) -> bool {
270        self.operand_type() == OperandType::LiteralOperand
271    }
272
273    /// Return `true` if the operand is an attribute operand.
274    pub fn is_attribute(&self) -> bool {
275        self.operand_type() == OperandType::AttributeOperand
276    }
277
278    /// Return `true` if the operand is a simple attribute operand.
279    pub fn is_simple_attribute(&self) -> bool {
280        self.operand_type() == OperandType::SimpleAttributeOperand
281    }
282}
283
284/// This is a convenience for building [`ContentFilter`] using operands as building blocks
285/// This builder does not check to see that the content filter is valid, i.e. if you
286/// reference an element by index that doesn't exist, or introduce a loop then you will
287/// not get an error until you feed it to a server and the server rejects it or breaks.
288///
289/// The builder takes generic types to make it easier to work with. Operands are converted to
290/// extension objects.
291#[derive(Debug, Default)]
292pub struct ContentFilterBuilder {
293    elements: Vec<ContentFilterElement>,
294}
295
296impl ContentFilterBuilder {
297    /// Create a new empty content filter builder.
298    pub fn new() -> Self {
299        Self::default()
300    }
301
302    fn add_element(
303        mut self,
304        filter_operator: FilterOperator,
305        filter_operands: Vec<Operand>,
306    ) -> Self {
307        let filter_operands = filter_operands.iter().map(ExtensionObject::from).collect();
308        self.elements.push(ContentFilterElement {
309            filter_operator,
310            filter_operands: Some(filter_operands),
311        });
312        self
313    }
314
315    /// Add an equality operand.
316    pub fn eq<T, S>(self, o1: T, o2: S) -> Self
317    where
318        T: Into<Operand>,
319        S: Into<Operand>,
320    {
321        self.add_element(FilterOperator::Equals, vec![o1.into(), o2.into()])
322    }
323
324    /// Add an `is_null` operand.
325    pub fn is_null<T>(self, o1: T) -> Self
326    where
327        T: Into<Operand>,
328    {
329        self.add_element(FilterOperator::IsNull, vec![o1.into()])
330    }
331
332    /// Add a greater than operand.
333    pub fn gt<T, S>(self, o1: T, o2: S) -> Self
334    where
335        T: Into<Operand>,
336        S: Into<Operand>,
337    {
338        self.add_element(FilterOperator::GreaterThan, vec![o1.into(), o2.into()])
339    }
340
341    /// Add a less than operand.
342    pub fn lt<T, S>(self, o1: T, o2: S) -> Self
343    where
344        T: Into<Operand>,
345        S: Into<Operand>,
346    {
347        self.add_element(FilterOperator::LessThan, vec![o1.into(), o2.into()])
348    }
349
350    /// Add a greater than or equal operand.
351    pub fn gte<T, S>(self, o1: T, o2: S) -> Self
352    where
353        T: Into<Operand>,
354        S: Into<Operand>,
355    {
356        self.add_element(
357            FilterOperator::GreaterThanOrEqual,
358            vec![o1.into(), o2.into()],
359        )
360    }
361
362    /// Add a less than or equal operand.
363    pub fn lte<T, S>(self, o1: T, o2: S) -> Self
364    where
365        T: Into<Operand>,
366        S: Into<Operand>,
367    {
368        self.add_element(FilterOperator::LessThanOrEqual, vec![o1.into(), o2.into()])
369    }
370
371    /// Add a "like" operand.
372    pub fn like<T, S>(self, o1: T, o2: S) -> Self
373    where
374        T: Into<Operand>,
375        S: Into<Operand>,
376    {
377        self.add_element(FilterOperator::Like, vec![o1.into(), o2.into()])
378    }
379
380    /// Add a "not" operand.
381    pub fn not<T>(self, o1: T) -> Self
382    where
383        T: Into<Operand>,
384    {
385        self.add_element(FilterOperator::Not, vec![o1.into()])
386    }
387
388    /// Add a "between" operand.
389    pub fn between<T, S, U>(self, o1: T, o2: S, o3: U) -> Self
390    where
391        T: Into<Operand>,
392        S: Into<Operand>,
393        U: Into<Operand>,
394    {
395        self.add_element(
396            FilterOperator::Between,
397            vec![o1.into(), o2.into(), o3.into()],
398        )
399    }
400
401    /// Add an "in list" operand.
402    pub fn in_list<T, S>(self, o1: T, list_items: Vec<S>) -> Self
403    where
404        T: Into<Operand>,
405        S: Into<Operand>,
406    {
407        // Make a list from the operand and then the items
408        let mut filter_operands = Vec::with_capacity(list_items.len() + 1);
409        filter_operands.push(o1.into());
410        list_items.into_iter().for_each(|list_item| {
411            filter_operands.push(list_item.into());
412        });
413        self.add_element(FilterOperator::InList, filter_operands)
414    }
415
416    /// Add an "and" operand.
417    pub fn and<T, S>(self, o1: T, o2: S) -> Self
418    where
419        T: Into<Operand>,
420        S: Into<Operand>,
421    {
422        self.add_element(FilterOperator::And, vec![o1.into(), o2.into()])
423    }
424
425    /// Add an "or" operand.
426    pub fn or<T, S>(self, o1: T, o2: S) -> Self
427    where
428        T: Into<Operand>,
429        S: Into<Operand>,
430    {
431        self.add_element(FilterOperator::Or, vec![o1.into(), o2.into()])
432    }
433
434    /// Add a "cast" operand.
435    pub fn cast<T, S>(self, o1: T, o2: S) -> Self
436    where
437        T: Into<Operand>,
438        S: Into<Operand>,
439    {
440        self.add_element(FilterOperator::Cast, vec![o1.into(), o2.into()])
441    }
442
443    /// Add a "bitwise and" operand.
444    pub fn bitwise_and<T, S>(self, o1: T, o2: S) -> Self
445    where
446        T: Into<Operand>,
447        S: Into<Operand>,
448    {
449        self.add_element(FilterOperator::BitwiseAnd, vec![o1.into(), o2.into()])
450    }
451
452    /// Add a "bitwise or" operand.
453    pub fn bitwise_or<T, S>(self, o1: T, o2: S) -> Self
454    where
455        T: Into<Operand>,
456        S: Into<Operand>,
457    {
458        self.add_element(FilterOperator::BitwiseOr, vec![o1.into(), o2.into()])
459    }
460
461    /// Add an "of type" operand. `type_id` must resolve to a node ID.
462    pub fn of_type<T>(self, type_id: T) -> Self
463    where
464        T: Into<Operand>,
465    {
466        self.add_element(FilterOperator::OfType, vec![type_id.into()])
467    }
468
469    /// Build a content filter.
470    pub fn build(self) -> ContentFilter {
471        ContentFilter {
472            elements: Some(self.elements),
473        }
474    }
475}
476
477impl SimpleAttributeOperand {
478    /// Create a new simple attribute operand.
479    pub fn new<T>(
480        type_definition_id: T,
481        browse_path: &str,
482        attribute_id: AttributeId,
483        index_range: NumericRange,
484    ) -> Self
485    where
486        T: Into<NodeId>,
487    {
488        // An improbable string to replace escaped forward slashes.
489        const ESCAPE_PATTERN: &str = "###!!!###@@@$$$$";
490        // Any escaped forward slashes will be replaced temporarily to allow split to work.
491        let browse_path = browse_path.replace(r"\/", ESCAPE_PATTERN);
492        // If we had a regex with look around support then we could split a pattern such as `r"(?<!\\)/"` where it
493        // matches only if the pattern `/` isn't preceded by a backslash. Unfortunately the regex crate doesn't offer
494        // this so an escaped forward slash is replaced with an improbable string instead.
495        let browse_path = browse_path
496            .split('/')
497            .map(|s| QualifiedName::new(0, s.replace(ESCAPE_PATTERN, "/")))
498            .collect();
499        SimpleAttributeOperand {
500            type_definition_id: type_definition_id.into(),
501            browse_path: Some(browse_path),
502            attribute_id: attribute_id as u32,
503            index_range,
504        }
505    }
506
507    /// Create a new simple attribute operand targeting the `Value` attribute with
508    /// no index range.
509    pub fn new_value<T>(type_definition_id: T, browse_path: &str) -> Self
510    where
511        T: Into<NodeId>,
512    {
513        Self::new(
514            type_definition_id,
515            browse_path,
516            AttributeId::Value,
517            NumericRange::None,
518        )
519    }
520}