vrl/stdlib/
exists.rs

1use crate::compiler::prelude::*;
2
3#[derive(Clone, Copy, Debug)]
4pub struct Exists;
5
6impl Function for Exists {
7    fn identifier(&self) -> &'static str {
8        "exists"
9    }
10
11    fn usage(&self) -> &'static str {
12        indoc! {"
13            Checks whether the `path` exists for the target.
14
15            This function distinguishes between a missing path
16            and a path with a `null` value. A regular path lookup,
17            such as `.foo`, cannot distinguish between the two cases
18            since it always returns `null` if the path doesn't exist.
19        "}
20    }
21
22    fn category(&self) -> &'static str {
23        Category::Path.as_ref()
24    }
25
26    fn return_kind(&self) -> u16 {
27        kind::BOOLEAN
28    }
29    fn parameters(&self) -> &'static [Parameter] {
30        const PARAMETERS: &[Parameter] = &[Parameter::required(
31            "field",
32            kind::ANY,
33            "The path of the field to check.",
34        )];
35        PARAMETERS
36    }
37
38    fn examples(&self) -> &'static [Example] {
39        &[
40            example! {
41                title: "Exists (field)",
42                source: indoc! {r#"
43                    . = { "field": 1 }
44                    exists(.field)
45                "#},
46                result: Ok("true"),
47            },
48            example! {
49                title: "Exists (array element)",
50                source: indoc! {r#"
51                    . = { "array": [1, 2, 3] }
52                    exists(.array[2])
53                "#},
54                result: Ok("true"),
55            },
56            example! {
57                title: "Does not exist (field)",
58                source: r#"exists({ "foo": "bar"}.baz)"#,
59                result: Ok("false"),
60            },
61        ]
62    }
63
64    fn compile(
65        &self,
66        _state: &state::TypeState,
67        _ctx: &mut FunctionCompileContext,
68        arguments: ArgumentList,
69    ) -> Compiled {
70        let query = arguments.required_query("field")?;
71
72        Ok(ExistsFn { query }.as_expr())
73    }
74}
75
76#[derive(Clone, Debug)]
77pub(crate) struct ExistsFn {
78    query: expression::Query,
79}
80
81fn exists(query: &expression::Query, ctx: &mut Context) -> Resolved {
82    let path = query.path();
83
84    if let Some(target_path) = query.external_path() {
85        return Ok(ctx
86            .target_mut()
87            .target_get(&target_path)
88            .ok()
89            .flatten()
90            .is_some()
91            .into());
92    }
93
94    if let Some(ident) = query.variable_ident() {
95        return match ctx.state().variable(ident) {
96            Some(value) => Ok(value.get(path).is_some().into()),
97            None => Ok(false.into()),
98        };
99    }
100
101    if let Some(expr) = query.expression_target() {
102        let value = expr.resolve(ctx)?;
103
104        return Ok(value.get(path).is_some().into());
105    }
106
107    Ok(false.into())
108}
109
110impl FunctionExpression for ExistsFn {
111    fn resolve(&self, ctx: &mut Context) -> Resolved {
112        exists(&self.query, ctx)
113    }
114
115    fn type_def(&self, _: &state::TypeState) -> TypeDef {
116        TypeDef::boolean().infallible()
117    }
118}