vrl/compiler/
type_def.rs

1//! `TypeDefs`
2//!
3//! The type definitions for typedefs record the various possible type definitions for the state
4//! that can be passed through a VRL program.
5//!
6//! `TypeDef` contains a `KindInfo`.
7//!
8//! `KindInfo` can be:
9//! `Unknown` - We don't know what type this is.
10//! `Known` - A set of the possible known `TypeKind`s. There can be multiple possible types for a
11//! path in scenarios such as `if .thing { .x = "hello" } else { .x = 42 }`. In that example after
12//! that statement is run, `.x` could contain either an string or an integer, we won't know until
13//! runtime exactly which.
14//!
15//! `TypeKind` is a concrete type for a path, `Bytes` (string), `Integer`, `Float`, `Boolean`,
16//! `Timestamp`, `Regex`, `Null` or `Array` or `Object`.
17//!
18//! `Array` is a Map of `Index` -> `KindInfo`.
19//! `Index` can be a specific index into that array, or `Any` which represents any index found within
20//! that array.
21//!
22//! `Object` is a Map of `Field` -> `KindInfo`.
23//! `Field` can be a specific field name of the object, or `Any` which represents any element found
24//! within that object.
25
26use std::ops::{Deref, DerefMut};
27
28use crate::path::ValuePath;
29use crate::value::{
30    Kind, Value,
31    kind::{Collection, Field, Index, merge},
32};
33
34#[derive(Debug, Clone, Eq, PartialEq)]
35pub enum Fallibility {
36    CannotFail,
37    MightFail,
38    AlwaysFails,
39}
40
41impl Fallibility {
42    #[must_use]
43    /// Merges two [`Fallibility`] values using the following rules:
44    ///
45    /// - Merging with [`Fallibility::AlwaysFails`] always results in [`Fallibility::AlwaysFails`].
46    /// - Merging [`Fallibility::MightFail`] with any variant results in [`Fallibility::MightFail`].
47    /// - Merging two [`Fallibility::CannotFail`] values results in [`Fallibility::CannotFail`].
48    ///
49    /// This is useful for combining the fallibility of sub-expressions.
50    pub fn merge(left: &Self, right: &Self) -> Self {
51        use Fallibility::{AlwaysFails, CannotFail, MightFail};
52
53        match (left, right) {
54            (AlwaysFails, _) | (_, AlwaysFails) => AlwaysFails,
55            (MightFail, _) | (_, MightFail) => MightFail,
56            _ => CannotFail,
57        }
58    }
59}
60
61#[derive(Default, Debug, Clone, Eq, PartialEq)]
62pub enum Purity {
63    /// Used for functions that are idempotent and have no side effects.
64    /// The vast majority of VRL expressions (and functions) are pure.
65    #[default]
66    Pure,
67    /// Used for impure functions.
68    Impure,
69}
70
71impl Purity {
72    #[must_use]
73    /// Merges two [`Purity`] values. There is only one rule, [`Purity::Impure`] trumps [`Purity::Pure`].
74    fn merge(left: &Self, right: &Self) -> Self {
75        use Purity::{Impure, Pure};
76
77        match (left, right) {
78            (Pure, Pure) => Pure,
79            _ => Impure,
80        }
81    }
82}
83
84/// Properties for a given expression that express the expected outcome of the
85/// expression.
86#[derive(Debug, Clone, Eq, PartialEq)]
87pub struct TypeDef {
88    /// If the function *might* fail, the fallibility must not be [`Fallibility::CannotFail`].
89    ///
90    /// If the function *might* succeed, the fallibility must not be [`Fallibility::AlwaysFails`].
91    ///
92    /// Prefer [`Fallibility::AlwaysFails`] over [`Fallibility::MightFail`] whenever possible. If not possible,
93    /// choose [`Fallibility::MightFail`].
94    ///
95    /// Some expressions are infallible e.g. the [`Literal`][crate::expression::Literal] expression, or any
96    // custom function designed to be infallible.
97    fallibility: Fallibility,
98
99    /// The [`Kind`][value::Kind]s this definition represents.
100    kind: Kind,
101
102    /// A function is [`Purity::Pure`] if it is idempotent and has no side effects.
103    /// Otherwise, it is [`Purity::Impure`].
104    purity: Purity,
105
106    /// The union of [`Kind`][value::Kind]s that can be returned from a nested expression.
107    returns: Kind,
108}
109
110impl Deref for TypeDef {
111    type Target = Kind;
112
113    fn deref(&self) -> &Self::Target {
114        &self.kind
115    }
116}
117
118impl DerefMut for TypeDef {
119    fn deref_mut(&mut self) -> &mut Self::Target {
120        &mut self.kind
121    }
122}
123
124impl TypeDef {
125    #[must_use]
126    pub fn kind(&self) -> &Kind {
127        &self.kind
128    }
129
130    #[must_use]
131    pub fn kind_mut(&mut self) -> &mut Kind {
132        &mut self.kind
133    }
134
135    #[must_use]
136    pub fn returns(&self) -> &Kind {
137        &self.returns
138    }
139
140    #[must_use]
141    pub fn returns_mut(&mut self) -> &mut Kind {
142        &mut self.returns
143    }
144
145    #[must_use]
146    pub fn at_path<'a>(&self, path: impl ValuePath<'a>) -> TypeDef {
147        Self {
148            fallibility: self.fallibility.clone(),
149            kind: self.kind.at_path(path),
150            purity: self.purity.clone(),
151            returns: self.returns.clone(),
152        }
153    }
154
155    #[inline]
156    #[must_use]
157    pub fn fallible(mut self) -> Self {
158        self.fallibility = Fallibility::MightFail;
159        self
160    }
161
162    #[inline]
163    #[must_use]
164    pub fn infallible(mut self) -> Self {
165        self.fallibility = Fallibility::CannotFail;
166        self
167    }
168
169    #[inline]
170    #[must_use]
171    pub fn always_fails(mut self) -> Self {
172        self.fallibility = Fallibility::AlwaysFails;
173        self
174    }
175
176    #[inline]
177    #[must_use]
178    /// Provided for backwards compatibility. Prefer `with_fallibility` for new code.
179    pub fn maybe_fallible(mut self, might_fail: bool) -> Self {
180        if might_fail {
181            self.fallibility = Fallibility::MightFail;
182        } else {
183            self.fallibility = Fallibility::CannotFail;
184        }
185        self
186    }
187
188    #[inline]
189    #[must_use]
190    pub fn with_fallibility(mut self, fallibility: Fallibility) -> Self {
191        self.fallibility = fallibility;
192        self
193    }
194
195    #[inline]
196    #[must_use]
197    pub fn pure(mut self) -> Self {
198        self.purity = Purity::Pure;
199        self
200    }
201
202    #[inline]
203    #[must_use]
204    pub fn impure(mut self) -> Self {
205        self.purity = Purity::Impure;
206        self
207    }
208
209    #[inline]
210    #[must_use]
211    pub fn any() -> Self {
212        Kind::any().into()
213    }
214
215    #[inline]
216    #[must_use]
217    pub fn bytes() -> Self {
218        Kind::bytes().into()
219    }
220
221    #[inline]
222    #[must_use]
223    pub fn or_bytes(mut self) -> Self {
224        self.kind.add_bytes();
225        self
226    }
227
228    #[inline]
229    #[must_use]
230    pub fn integer() -> Self {
231        Kind::integer().into()
232    }
233
234    #[inline]
235    #[must_use]
236    pub fn or_integer(mut self) -> Self {
237        self.kind.add_integer();
238        self
239    }
240
241    #[inline]
242    #[must_use]
243    pub fn float() -> Self {
244        Kind::float().into()
245    }
246
247    #[inline]
248    #[must_use]
249    pub fn or_float(mut self) -> Self {
250        self.kind.add_float();
251        self
252    }
253
254    #[inline]
255    #[must_use]
256    pub fn boolean() -> Self {
257        Kind::boolean().into()
258    }
259
260    #[inline]
261    #[must_use]
262    pub fn or_boolean(mut self) -> Self {
263        self.kind.add_boolean();
264        self
265    }
266
267    #[inline]
268    #[must_use]
269    pub fn timestamp() -> Self {
270        Kind::timestamp().into()
271    }
272
273    #[inline]
274    #[must_use]
275    pub fn or_timestamp(mut self) -> Self {
276        self.kind.add_timestamp();
277        self
278    }
279
280    #[inline]
281    #[must_use]
282    pub fn regex() -> Self {
283        Kind::regex().into()
284    }
285
286    #[inline]
287    #[must_use]
288    pub fn or_regex(mut self) -> Self {
289        self.kind.add_regex();
290        self
291    }
292
293    #[inline]
294    #[must_use]
295    pub fn null() -> Self {
296        Kind::null().into()
297    }
298
299    #[inline]
300    #[must_use]
301    pub fn or_null(mut self) -> Self {
302        self.kind.add_null();
303        self
304    }
305
306    #[inline]
307    #[must_use]
308    pub fn undefined() -> Self {
309        Kind::undefined().into()
310    }
311
312    #[inline]
313    #[must_use]
314    pub fn or_undefined(mut self) -> Self {
315        self.kind.add_undefined();
316        self
317    }
318
319    #[inline]
320    #[must_use]
321    pub fn never() -> Self {
322        Kind::never().into()
323    }
324
325    #[inline]
326    #[must_use]
327    pub fn add_null(mut self) -> Self {
328        self.kind.add_null();
329        self
330    }
331
332    #[inline]
333    pub fn array(collection: impl Into<Collection<Index>>) -> Self {
334        Kind::array(collection).into()
335    }
336
337    #[inline]
338    #[must_use]
339    pub fn or_array(mut self, collection: impl Into<Collection<Index>>) -> Self {
340        self.kind.add_array(collection);
341        self
342    }
343
344    /// Convert the [`TypeDef`]s [`Kind`] to an array.
345    ///
346    /// If `Kind` already has the array state, all other states are removed. If it does not yet
347    /// have an array, then equally all existing states are removed, and an "any" array state is
348    /// added.
349    ///
350    /// `TypeDef`s fallibility is kept unmodified.
351    #[inline]
352    #[must_use]
353    pub fn restrict_array(self) -> Self {
354        let fallible = self.fallibility;
355        let collection = match self.kind.into_array() {
356            Some(array) => array,
357            None => Collection::any(),
358        };
359
360        Self {
361            fallibility: fallible,
362            kind: Kind::array(collection),
363            purity: self.purity.clone(),
364            returns: self.returns.clone(),
365        }
366    }
367
368    #[inline]
369    pub fn object(collection: impl Into<Collection<Field>>) -> Self {
370        Kind::object(collection).into()
371    }
372
373    #[inline]
374    #[must_use]
375    pub fn or_object(mut self, collection: impl Into<Collection<Field>>) -> Self {
376        self.kind.add_object(collection);
377        self
378    }
379
380    /// Convert the [`TypeDef`]s [`Kind`] to an object.
381    ///
382    /// If `Kind` already has the object state, all other states are removed. If it does not yet
383    /// have an object, then equally all existing states are removed, and an "any" object state is
384    /// added.
385    ///
386    /// `TypeDef`s fallibility is kept unmodified.
387    #[inline]
388    #[must_use]
389    pub fn restrict_object(self) -> Self {
390        let fallible = self.fallibility;
391        let collection = match self.kind.into_object() {
392            Some(object) => object,
393            None => Collection::any(),
394        };
395
396        Self {
397            fallibility: fallible,
398            kind: Kind::object(collection),
399            purity: self.purity.clone(),
400            returns: self.returns.clone(),
401        }
402    }
403
404    #[inline]
405    #[must_use]
406    pub fn with_kind(mut self, kind: Kind) -> Self {
407        self.kind = kind;
408        self
409    }
410
411    #[inline]
412    #[must_use]
413    pub fn with_returns(mut self, returns: Kind) -> Self {
414        self.returns = returns;
415        self
416    }
417
418    /// VRL has an interesting property where accessing an undefined value "upgrades"
419    /// it to a "null" value.
420    /// This should be used in places those implicit upgrades can occur.
421    // see: https://github.com/vectordotdev/vector/issues/13594
422    #[must_use]
423    pub fn upgrade_undefined(mut self) -> Self {
424        self.kind = self.kind.upgrade_undefined();
425        self
426    }
427
428    /// Collects any subtypes that can contain multiple indexed types (array, object) and collects
429    /// them into a single type for all indexes.
430    ///
431    /// Used for functions that cant determine which indexes of a collection have been used in the
432    /// result.
433    #[must_use]
434    pub fn collect_subtypes(mut self) -> Self {
435        if let Some(object) = self.kind.as_object_mut() {
436            object.set_unknown(Kind::undefined());
437            object.anonymize();
438        }
439        if let Some(array) = self.kind.as_array_mut() {
440            array.set_unknown(Kind::undefined());
441            array.anonymize();
442        }
443
444        self
445    }
446
447    // -------------------------------------------------------------------------
448
449    #[must_use]
450    pub fn is_fallible(&self) -> bool {
451        self.fallibility == Fallibility::MightFail || self.fallibility == Fallibility::AlwaysFails
452    }
453
454    #[must_use]
455    pub fn is_infallible(&self) -> bool {
456        !self.is_fallible()
457    }
458
459    #[must_use]
460    pub fn is_pure(&self) -> bool {
461        self.purity == Purity::Pure
462    }
463
464    #[must_use]
465    pub fn is_impure(&self) -> bool {
466        self.purity == Purity::Impure
467    }
468
469    /// Set the type definition to be fallible if its kind is not contained
470    /// within the provided kind.
471    #[must_use]
472    pub fn fallible_unless(mut self, kind: impl Into<Kind>) -> Self {
473        let kind = kind.into();
474        if kind.is_superset(&self.kind).is_err() {
475            self.fallibility = Fallibility::MightFail;
476        }
477
478        self
479    }
480
481    #[must_use]
482    pub fn union(mut self, other: Self) -> Self {
483        self.fallibility = Fallibility::merge(&self.fallibility, &other.fallibility);
484        self.kind = self.kind.union(other.kind);
485        self.purity = Purity::merge(&self.purity, &other.purity);
486        self.returns = self.returns.union(other.returns);
487        self
488    }
489
490    // deprecated
491    pub fn merge(&mut self, other: Self, strategy: merge::Strategy) {
492        self.fallibility = Fallibility::merge(&self.fallibility, &other.fallibility);
493        self.kind.merge(other.kind, strategy);
494        self.purity = Purity::merge(&self.purity, &other.purity);
495        self.returns = self.returns.union(other.returns);
496    }
497
498    #[must_use]
499    pub fn with_type_inserted<'a>(self, path: impl ValuePath<'a>, other: Self) -> Self {
500        let mut kind = self.kind;
501        kind.insert(path, other.kind);
502        Self {
503            fallibility: Fallibility::merge(&self.fallibility, &other.fallibility),
504            kind,
505            purity: Purity::merge(&self.purity, &other.purity),
506            returns: self.returns.clone(),
507        }
508    }
509
510    #[must_use]
511    // deprecated
512    pub fn merge_overwrite(mut self, other: Self) -> Self {
513        self.merge(
514            other,
515            merge::Strategy {
516                collisions: merge::CollisionStrategy::Overwrite,
517            },
518        );
519        self
520    }
521}
522
523impl From<Kind> for TypeDef {
524    fn from(kind: Kind) -> Self {
525        Self {
526            fallibility: Fallibility::CannotFail,
527            kind,
528            purity: Purity::Pure,
529            returns: Kind::never(),
530        }
531    }
532}
533
534impl From<TypeDef> for Kind {
535    fn from(type_def: TypeDef) -> Self {
536        type_def.kind
537    }
538}
539
540#[derive(Debug, Clone, PartialEq)]
541pub(crate) struct Details {
542    pub(crate) type_def: TypeDef,
543    pub(crate) value: Option<Value>,
544}
545
546impl Details {
547    /// Returns the union of 2 possible states
548    pub(crate) fn merge(self, other: Self) -> Self {
549        Self {
550            type_def: self.type_def.union(other.type_def),
551            value: if self.value == other.value {
552                self.value
553            } else {
554                None
555            },
556        }
557    }
558}
559
560#[cfg(test)]
561mod test {
562    use super::Fallibility::*;
563    use super::Purity::*;
564    use super::*;
565
566    #[test]
567    fn merge_details_same_literal() {
568        let a = Details {
569            type_def: TypeDef::integer(),
570            value: Some(Value::from(5)),
571        };
572        let b = Details {
573            type_def: TypeDef::float(),
574            value: Some(Value::from(5)),
575        };
576        assert_eq!(
577            a.merge(b),
578            Details {
579                type_def: TypeDef::integer().or_float(),
580                value: Some(Value::from(5)),
581            }
582        );
583    }
584
585    #[test]
586    fn merge_details_different_literal() {
587        let a = Details {
588            type_def: TypeDef::any(),
589            value: Some(Value::from(5)),
590        };
591        let b = Details {
592            type_def: TypeDef::object(Collection::empty()),
593            value: Some(Value::from(6)),
594        };
595        assert_eq!(
596            a.merge(b),
597            Details {
598                type_def: TypeDef::any(),
599                value: None,
600            }
601        );
602    }
603
604    #[test]
605    fn merge_fallibility_instances() {
606        assert_eq!(Fallibility::merge(&AlwaysFails, &MightFail), AlwaysFails);
607        assert_eq!(Fallibility::merge(&AlwaysFails, &CannotFail), AlwaysFails);
608        assert_eq!(
609            Fallibility::merge(&Fallibility::merge(&CannotFail, &MightFail), &AlwaysFails),
610            AlwaysFails
611        );
612
613        assert_eq!(Fallibility::merge(&MightFail, &MightFail), MightFail);
614        assert_eq!(Fallibility::merge(&CannotFail, &MightFail), MightFail);
615
616        assert_eq!(Fallibility::merge(&CannotFail, &CannotFail), CannotFail);
617    }
618
619    #[test]
620    fn merge_purity() {
621        assert_eq!(Purity::merge(&Pure, &Impure), Impure);
622        assert_eq!(Purity::merge(&Impure, &Pure), Impure);
623        assert_eq!(Purity::merge(&Impure, &Impure), Impure);
624        assert_eq!(Purity::merge(&Pure, &Pure), Pure);
625    }
626}