vrl/compiler/expression/
query.rs

1use crate::compiler::{
2    Context, Expression,
3    expression::{Container, Resolved, Variable},
4    parser::ast::Ident,
5    state::ExternalEnv,
6    state::{TypeInfo, TypeState},
7    type_def::Details,
8};
9use crate::path::{OwnedTargetPath, OwnedValuePath, PathPrefix};
10use crate::value::Value;
11use std::fmt;
12
13#[derive(Clone, PartialEq)]
14pub struct Query {
15    target: Target,
16    path: OwnedValuePath,
17}
18
19impl Query {
20    // TODO:
21    // - error when trying to index into object
22    // - error when trying to path into array
23    #[must_use]
24    pub fn new(target: Target, path: OwnedValuePath) -> Self {
25        Query { target, path }
26    }
27
28    #[must_use]
29    pub fn path(&self) -> &OwnedValuePath {
30        &self.path
31    }
32
33    #[must_use]
34    pub fn target(&self) -> &Target {
35        &self.target
36    }
37
38    #[must_use]
39    pub fn is_external(&self) -> bool {
40        matches!(self.target, Target::External(_))
41    }
42
43    #[must_use]
44    pub fn external_path(&self) -> Option<OwnedTargetPath> {
45        match self.target {
46            Target::External(prefix) => Some(OwnedTargetPath {
47                prefix,
48                path: self.path.clone(),
49            }),
50            _ => None,
51        }
52    }
53
54    #[must_use]
55    pub fn as_variable(&self) -> Option<&Variable> {
56        match &self.target {
57            Target::Internal(variable) => Some(variable),
58            _ => None,
59        }
60    }
61
62    #[must_use]
63    pub fn variable_ident(&self) -> Option<&Ident> {
64        match &self.target {
65            Target::Internal(v) => Some(v.ident()),
66            _ => None,
67        }
68    }
69
70    #[must_use]
71    pub fn expression_target(&self) -> Option<&dyn Expression> {
72        match &self.target {
73            Target::FunctionCall(expr) => Some(expr),
74            Target::Container(expr) => Some(expr),
75            _ => None,
76        }
77    }
78
79    // Only "external" paths are supported. Non external paths are ignored
80    // see: https://github.com/vectordotdev/vector/issues/11246
81    pub fn delete_type_def(&self, external: &mut ExternalEnv, compact: bool) {
82        if let Some(target_path) = self.external_path() {
83            match target_path.prefix {
84                PathPrefix::Event => {
85                    let mut type_def = external.target().type_def.clone();
86                    type_def.remove(&target_path.path, compact);
87                    external.update_target(Details {
88                        type_def,
89                        value: None,
90                    });
91                }
92                PathPrefix::Metadata => {
93                    let mut kind = external.metadata_kind().clone();
94                    kind.remove(&target_path.path, compact);
95                    external.update_metadata(kind);
96                }
97            }
98        }
99    }
100}
101
102impl Expression for Query {
103    fn resolve(&self, ctx: &mut Context) -> Resolved {
104        use Target::{Container, External, FunctionCall, Internal};
105
106        let value = match &self.target {
107            External(prefix) => {
108                let path = OwnedTargetPath {
109                    prefix: *prefix,
110                    path: self.path.clone(),
111                };
112                return Ok(ctx
113                    .target()
114                    .target_get(&path)
115                    .ok()
116                    .flatten()
117                    .cloned()
118                    .unwrap_or(Value::Null));
119            }
120            Internal(variable) => variable.resolve(ctx)?,
121            FunctionCall(call) => call.resolve(ctx)?,
122            Container(container) => container.resolve(ctx)?,
123        };
124
125        Ok(value.get(&self.path).cloned().unwrap_or(Value::Null))
126    }
127
128    fn resolve_constant(&self, state: &TypeState) -> Option<Value> {
129        match self.target {
130            Target::Internal(ref variable) => variable
131                .resolve_constant(state)
132                .and_then(|v| v.get(self.path()).cloned()),
133            _ => None,
134        }
135    }
136
137    fn type_info(&self, state: &TypeState) -> TypeInfo {
138        use Target::{Container, External, FunctionCall, Internal};
139
140        match &self.target {
141            External(prefix) => {
142                let result = state.external.kind(*prefix).at_path(&self.path).into();
143                TypeInfo::new(state, result)
144            }
145            Internal(variable) => {
146                let result = variable.type_def(state).at_path(&self.path);
147                TypeInfo::new(state, result)
148            }
149            FunctionCall(call) => call
150                .type_info(state)
151                .map_result(|result| result.at_path(&self.path)),
152            Container(container) => container
153                .type_info(state)
154                .map_result(|result| result.at_path(&self.path)),
155        }
156    }
157}
158
159impl fmt::Display for Query {
160    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
161        match self.target {
162            Target::Internal(_)
163                if !self.path.is_root() && !self.path.segments.first().unwrap().is_index() =>
164            {
165                write!(f, "{}.{}", self.target, self.path)
166            }
167            _ => write!(f, "{}{}", self.target, self.path),
168        }
169    }
170}
171
172impl fmt::Debug for Query {
173    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174        write!(f, "Query({:?}, {:?})", self.target, self.path)
175    }
176}
177
178#[derive(Clone, PartialEq)]
179pub enum Target {
180    Internal(Variable),
181    External(PathPrefix),
182    FunctionCall(crate::compiler::expression::FunctionCall),
183    Container(Container),
184}
185
186impl fmt::Display for Target {
187    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188        use Target::{Container, External, FunctionCall, Internal};
189
190        match self {
191            Internal(v) => v.fmt(f),
192            External(prefix) => match prefix {
193                PathPrefix::Event => write!(f, "."),
194                PathPrefix::Metadata => write!(f, "%"),
195            },
196            FunctionCall(v) => v.fmt(f),
197            Container(v) => v.fmt(f),
198        }
199    }
200}
201
202impl fmt::Debug for Target {
203    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
204        use Target::{Container, External, FunctionCall, Internal};
205
206        match self {
207            Internal(v) => write!(f, "Internal({v:?})"),
208            External(prefix) => match prefix {
209                PathPrefix::Event => f.write_str("External(Event)"),
210                PathPrefix::Metadata => f.write_str("External(Metadata)"),
211            },
212            FunctionCall(v) => v.fmt(f),
213            Container(v) => v.fmt(f),
214        }
215    }
216}
217
218#[cfg(test)]
219mod tests {
220    use super::*;
221
222    #[test]
223    fn test_type_def() {
224        let query = Query {
225            target: Target::External(PathPrefix::Event),
226            path: OwnedValuePath::root(),
227        };
228
229        let state = TypeState::default();
230        let type_def = query.type_info(&state).result;
231
232        assert!(type_def.is_infallible());
233        assert!(type_def.is_object());
234
235        let object = type_def.as_object().unwrap();
236
237        assert!(object.is_any());
238    }
239}