vrl/datadog/filter/
filter.rs

1use std::fmt::Debug;
2
3use super::{Matcher, Run};
4use crate::datadog::search::{Comparison, ComparisonValue, Field};
5use crate::path::PathParseError;
6use dyn_clone::{DynClone, clone_trait_object};
7
8/// A `Filter` is a generic type that contains methods that are invoked by the `build_filter`
9/// function. Each method returns a heap-allocated `Matcher<V>` (typically a closure) containing
10/// logic to determine whether the value matches the filter. A filter is intended to be side-effect
11/// free and idempotent, and so only receives an immutable reference to self.
12pub trait Filter<V: Debug + Send + Sync + Clone + 'static>: DynClone {
13    /// Determine whether a field value exists.
14    ///
15    /// # Errors
16    ///
17    /// Will return `Err` if the query contains an invalid path.
18    fn exists(&self, field: Field) -> Result<Box<dyn Matcher<V>>, PathParseError>;
19
20    /// Determine whether a field value equals `to_match`.
21    ///
22    /// # Errors
23    ///
24    /// Will return `Err` if the query contains an invalid path.
25    fn equals(&self, field: Field, to_match: &str) -> Result<Box<dyn Matcher<V>>, PathParseError>;
26
27    /// Determine whether a value starts with a prefix.
28    ///
29    /// # Errors
30    /// Will return `Err` if the query contains an invalid path.
31    fn prefix(&self, field: Field, prefix: &str) -> Result<Box<dyn Matcher<V>>, PathParseError>;
32
33    /// Determine whether a value matches a wildcard.
34    ///
35    /// # Errors
36    ///
37    /// Will return `Err` if the query contains an invalid path.
38    fn wildcard(&self, field: Field, wildcard: &str)
39    -> Result<Box<dyn Matcher<V>>, PathParseError>;
40
41    /// Compare a field value against `comparison_value`, using one of the `comparator` operators.
42    ///
43    /// # Errors
44    ///
45    /// Will return `Err` if the query contains an invalid path.
46    fn compare(
47        &self,
48        field: Field,
49        comparator: Comparison,
50        comparison_value: ComparisonValue,
51    ) -> Result<Box<dyn Matcher<V>>, PathParseError>;
52
53    /// Determine whether a field value falls within a range. By default, this will use
54    /// `self.compare` on both the lower and upper bound.
55    ///
56    /// # Errors
57    ///
58    /// Will return `Err` if the query contains an invalid path.
59    fn range(
60        &self,
61        field: Field,
62        lower: ComparisonValue,
63        lower_inclusive: bool,
64        upper: ComparisonValue,
65        upper_inclusive: bool,
66    ) -> Result<Box<dyn Matcher<V>>, PathParseError> {
67        match (&lower, &upper) {
68            // If both bounds are wildcards, just check that the field exists to catch the
69            // special case for "tags".
70            (ComparisonValue::Unbounded, ComparisonValue::Unbounded) => self.exists(field),
71            // Unbounded lower.
72            (ComparisonValue::Unbounded, _) => {
73                let op = if upper_inclusive {
74                    Comparison::Lte
75                } else {
76                    Comparison::Lt
77                };
78
79                self.compare(field, op, upper)
80            }
81            // Unbounded upper.
82            (_, ComparisonValue::Unbounded) => {
83                let op = if lower_inclusive {
84                    Comparison::Gte
85                } else {
86                    Comparison::Gt
87                };
88
89                self.compare(field, op, lower)
90            }
91            // Definitive range.
92            _ => {
93                let lower_op = if lower_inclusive {
94                    Comparison::Gte
95                } else {
96                    Comparison::Gt
97                };
98
99                let upper_op = if upper_inclusive {
100                    Comparison::Lte
101                } else {
102                    Comparison::Lt
103                };
104
105                let lower_func = self.compare(field.clone(), lower_op, lower)?;
106                let upper_func = self.compare(field, upper_op, upper)?;
107
108                Ok(Run::boxed(move |value| {
109                    lower_func.run(value) && upper_func.run(value)
110                }))
111            }
112        }
113    }
114}
115
116clone_trait_object!(<V>Filter<V>);