vrl/datadog/filter/
matcher.rs

1use std::{fmt, marker::PhantomData};
2
3use super::{BooleanType, Filter, QueryNode, Resolver};
4use crate::path::PathParseError;
5use dyn_clone::{DynClone, clone_trait_object};
6
7/// A `Matcher` is a type that contains a "run" method which returns true/false if value `V`
8/// matches a filter.
9pub trait Matcher<V>: DynClone + fmt::Debug + Send + Sync {
10    fn run(&self, value: &V) -> bool;
11}
12
13clone_trait_object!(<V>Matcher<V>);
14
15/// Implementing `Matcher` for bool allows a `Box::new(true|false)` convenience.
16impl<V> Matcher<V> for bool {
17    fn run(&self, _value: &V) -> bool {
18        *self
19    }
20}
21
22/// Container for holding a thread-safe function type that can receive a `V` value and
23/// return true/false for whether the value matches some internal expectation.
24#[derive(Clone)]
25pub struct Run<V, T>
26where
27    V: fmt::Debug + Send + Sync + Clone,
28    T: Fn(&V) -> bool + Send + Sync + Clone,
29{
30    func: T,
31    _phantom: PhantomData<V>, // Necessary to make generic over `V`.
32}
33
34impl<V, T> Run<V, T>
35where
36    V: fmt::Debug + Send + Sync + Clone,
37    T: Fn(&V) -> bool + Send + Sync + Clone,
38{
39    /// Convenience for allocating a `Self`, which is generally how a `Run` is instantiated.
40    pub fn boxed(func: T) -> Box<Self> {
41        Box::new(Self {
42            func,
43            _phantom: PhantomData,
44        })
45    }
46}
47
48impl<V, T> Matcher<V> for Run<V, T>
49where
50    V: fmt::Debug + Send + Sync + Clone,
51    T: Fn(&V) -> bool + Send + Sync + Clone,
52{
53    /// Invokes the internal `func`, returning true if a value matches.
54    fn run(&self, obj: &V) -> bool {
55        (self.func)(obj)
56    }
57}
58
59impl<V, T> fmt::Debug for Run<V, T>
60where
61    V: fmt::Debug + Send + Sync + Clone,
62    T: Fn(&V) -> bool + Send + Sync + Clone,
63{
64    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65        write!(f, "Datadog matcher fn")
66    }
67}
68
69/// Returns a closure that negates the value of the provided `Matcher`.
70fn not<V>(matcher: Box<dyn Matcher<V>>) -> Box<dyn Matcher<V>>
71where
72    V: fmt::Debug + Send + Sync + Clone + 'static,
73{
74    Run::boxed(move |value| !matcher.run(value))
75}
76
77/// Returns a closure that returns true if any of the vector of `Matcher<V>` return true.
78fn any<V>(matchers: Vec<Box<dyn Matcher<V>>>) -> Box<dyn Matcher<V>>
79where
80    V: fmt::Debug + Send + Sync + Clone + 'static,
81{
82    Run::boxed(move |value| matchers.iter().any(|func| func.run(value)))
83}
84
85/// Returns a closure that returns true if all of the vector of `Matcher<V>` return true.
86fn all<V>(matchers: Vec<Box<dyn Matcher<V>>>) -> Box<dyn Matcher<V>>
87where
88    V: fmt::Debug + Send + Sync + Clone + 'static,
89{
90    Run::boxed(move |value| matchers.iter().all(|func| func.run(value)))
91}
92
93/// Wrapper around `QueryNode::build_matcher` for backwards compatibility.
94///
95/// # Errors
96///
97/// Same as `QueryNode::build_matcher`.
98pub fn build_matcher<V, F>(
99    node: &QueryNode,
100    filter: &F,
101) -> Result<Box<dyn Matcher<V>>, PathParseError>
102where
103    V: fmt::Debug + Send + Sync + Clone + 'static,
104    F: Filter<V> + Resolver,
105{
106    node.build_matcher(filter)
107}
108
109impl QueryNode {
110    /// Build a filter by parsing a Datadog Search Syntax `QueryNode`, and invoking the appropriate
111    /// method on a `Filter` + `Resolver` implementation to determine the matching logic. Each method
112    /// returns a `Matcher<V>` which is intended to be invoked at runtime. `F` should implement both
113    /// `Fielder` + `Filter` in order to applying any required caching which may affect the operation
114    /// of a filter method. This function is intended to be used at boot-time and NOT in a hot path!
115    ///
116    /// # Errors
117    ///
118    /// Will return `Err` if the query contains an invalid path.
119    #[allow(clippy::module_name_repetitions)] // Renaming is a breaking change.
120    pub fn build_matcher<V, F>(&self, filter: &F) -> Result<Box<dyn Matcher<V>>, PathParseError>
121    where
122        V: fmt::Debug + Send + Sync + Clone + 'static,
123        F: Filter<V> + Resolver,
124    {
125        match self {
126            Self::MatchNoDocs => Ok(Box::new(false)),
127            Self::MatchAllDocs => Ok(Box::new(true)),
128            Self::AttributeExists { attr } => {
129                let matchers: Result<Vec<_>, _> = filter
130                    .build_fields(attr)
131                    .into_iter()
132                    .map(|field| filter.exists(field))
133                    .collect();
134
135                Ok(any(matchers?))
136            }
137            Self::AttributeMissing { attr } => {
138                let matchers: Result<Vec<_>, _> = filter
139                    .build_fields(attr)
140                    .into_iter()
141                    .map(|field| filter.exists(field).map(|matcher| not(matcher)))
142                    .collect();
143
144                Ok(all(matchers?))
145            }
146            Self::AttributeTerm { attr, value }
147            | Self::QuotedAttribute {
148                attr,
149                phrase: value,
150            } => {
151                let matchers: Result<Vec<_>, _> = filter
152                    .build_fields(attr)
153                    .into_iter()
154                    .map(|field| filter.equals(field, value))
155                    .collect();
156
157                Ok(any(matchers?))
158            }
159            Self::AttributePrefix { attr, prefix } => {
160                let matchers: Result<Vec<_>, _> = filter
161                    .build_fields(attr)
162                    .into_iter()
163                    .map(|field| filter.prefix(field, prefix))
164                    .collect();
165
166                Ok(any(matchers?))
167            }
168            Self::AttributeWildcard { attr, wildcard } => {
169                let matchers: Result<Vec<_>, _> = filter
170                    .build_fields(attr)
171                    .into_iter()
172                    .map(|field| filter.wildcard(field, wildcard))
173                    .collect();
174
175                Ok(any(matchers?))
176            }
177            Self::AttributeComparison {
178                attr,
179                comparator,
180                value,
181            } => {
182                let matchers: Result<Vec<_>, _> = filter
183                    .build_fields(attr)
184                    .into_iter()
185                    .map(|field| filter.compare(field, *comparator, value.clone()))
186                    .collect();
187
188                Ok(any(matchers?))
189            }
190            Self::AttributeRange {
191                attr,
192                lower,
193                lower_inclusive,
194                upper,
195                upper_inclusive,
196            } => {
197                let matchers: Result<Vec<_>, _> = filter
198                    .build_fields(attr)
199                    .into_iter()
200                    .map(|field| {
201                        filter.range(
202                            field,
203                            lower.clone(),
204                            *lower_inclusive,
205                            upper.clone(),
206                            *upper_inclusive,
207                        )
208                    })
209                    .collect();
210
211                Ok(any(matchers?))
212            }
213            Self::NegatedNode { node } => Ok(not(node.build_matcher(filter)?)),
214            Self::Boolean { oper, nodes } => {
215                let funcs: Result<Vec<_>, _> = nodes
216                    .iter()
217                    .map(|node| node.build_matcher(filter))
218                    .collect();
219
220                match oper {
221                    BooleanType::And => Ok(all(funcs?)),
222                    BooleanType::Or => Ok(any(funcs?)),
223                }
224            }
225        }
226    }
227}