vrl/compiler/
state.rs

1use crate::path::PathPrefix;
2use crate::value::{Kind, Value};
3use std::collections::{HashMap, hash_map::Entry};
4
5use super::{TypeDef, parser::ast::Ident, type_def::Details, value::Collection};
6
7#[derive(Debug, Clone)]
8pub struct TypeInfo {
9    pub state: TypeState,
10    pub result: TypeDef,
11}
12
13impl TypeInfo {
14    #[must_use]
15    pub fn new(state: impl Into<TypeState>, result: TypeDef) -> Self {
16        Self {
17            state: state.into(),
18            result,
19        }
20    }
21
22    #[must_use]
23    pub fn map_result(self, f: impl FnOnce(TypeDef) -> TypeDef) -> Self {
24        Self {
25            state: self.state,
26            result: f(self.result),
27        }
28    }
29}
30
31impl From<&TypeState> for TypeState {
32    fn from(state: &TypeState) -> Self {
33        state.clone()
34    }
35}
36
37#[allow(clippy::module_name_repetitions)]
38#[derive(Debug, Clone, Default)]
39pub struct TypeState {
40    pub local: LocalEnv,
41    pub external: ExternalEnv,
42}
43
44impl TypeState {
45    #[must_use]
46    pub fn merge(self, other: Self) -> Self {
47        Self {
48            local: self.local.merge(other.local),
49            external: self.external.merge(other.external),
50        }
51    }
52}
53
54/// Local environment, limited to a given scope.
55#[derive(Debug, Default, Clone, PartialEq)]
56pub struct LocalEnv {
57    pub(crate) bindings: HashMap<Ident, Details>,
58}
59
60impl LocalEnv {
61    pub(crate) fn variable_idents(&self) -> impl Iterator<Item = &Ident> + '_ {
62        self.bindings.keys()
63    }
64
65    pub(crate) fn variable(&self, ident: &Ident) -> Option<&Details> {
66        self.bindings.get(ident)
67    }
68
69    pub(crate) fn insert_variable(&mut self, ident: Ident, details: Details) {
70        self.bindings.insert(ident, details);
71    }
72
73    pub(crate) fn remove_variable(&mut self, ident: &Ident) -> Option<Details> {
74        self.bindings.remove(ident)
75    }
76
77    /// Any state the child scope modified that was part of the parent is copied to the parent scope
78    pub(crate) fn apply_child_scope(mut self, child: Self) -> Self {
79        for (ident, child_details) in child.bindings {
80            if let Some(self_details) = self.bindings.get_mut(&ident) {
81                *self_details = child_details;
82            }
83        }
84
85        self
86    }
87
88    /// Merges two local envs together. This is useful in cases such as if statements
89    /// where different `LocalEnv`'s can be created, and the result is decided at runtime.
90    /// The compile-time type must be the union of the options.
91    pub(crate) fn merge(mut self, other: Self) -> Self {
92        for (ident, other_details) in other.bindings {
93            if let Some(self_details) = self.bindings.get_mut(&ident) {
94                *self_details = self_details.clone().merge(other_details);
95            } else {
96                self.bindings.insert(ident, other_details);
97            }
98        }
99        self
100    }
101}
102
103/// A lexical scope within the program.
104#[derive(Debug, Clone)]
105pub struct ExternalEnv {
106    /// The external target of the program.
107    target: Details,
108
109    /// The type of metadata
110    metadata: Kind,
111}
112
113impl Default for ExternalEnv {
114    fn default() -> Self {
115        Self::new_with_kind(
116            Kind::object(Collection::any()),
117            Kind::object(Collection::any()),
118        )
119    }
120}
121
122impl ExternalEnv {
123    #[must_use]
124    pub fn merge(self, other: Self) -> Self {
125        Self {
126            target: self.target.merge(other.target),
127            metadata: self.metadata.union(other.metadata),
128        }
129    }
130
131    /// Creates a new external environment that starts with an initial given
132    /// [`Kind`].
133    #[must_use]
134    pub fn new_with_kind(target: Kind, metadata: Kind) -> Self {
135        Self {
136            target: Details {
137                type_def: target.into(),
138                value: None,
139            },
140            metadata,
141        }
142    }
143
144    pub(crate) fn target(&self) -> &Details {
145        &self.target
146    }
147
148    pub fn target_kind(&self) -> &Kind {
149        self.target().type_def.kind()
150    }
151
152    pub fn kind(&self, prefix: PathPrefix) -> Kind {
153        match prefix {
154            PathPrefix::Event => self.target_kind(),
155            PathPrefix::Metadata => self.metadata_kind(),
156        }
157        .clone()
158    }
159
160    pub fn metadata_kind(&self) -> &Kind {
161        &self.metadata
162    }
163
164    pub(crate) fn update_target(&mut self, details: Details) {
165        self.target = details;
166    }
167
168    pub fn update_metadata(&mut self, kind: Kind) {
169        self.metadata = kind;
170    }
171}
172
173/// The state used at runtime to track changes as they happen.
174#[allow(clippy::module_name_repetitions)]
175#[derive(Debug, Default)]
176pub struct RuntimeState {
177    /// The [`Value`] stored in each variable.
178    variables: HashMap<Ident, Value>,
179}
180
181impl RuntimeState {
182    #[must_use]
183    pub fn is_empty(&self) -> bool {
184        self.variables.is_empty()
185    }
186
187    pub fn clear(&mut self) {
188        self.variables.clear();
189    }
190
191    #[must_use]
192    pub fn variable(&self, ident: &Ident) -> Option<&Value> {
193        self.variables.get(ident)
194    }
195
196    pub fn variable_mut(&mut self, ident: &Ident) -> Option<&mut Value> {
197        self.variables.get_mut(ident)
198    }
199
200    pub(crate) fn insert_variable(&mut self, ident: Ident, value: Value) {
201        self.variables.insert(ident, value);
202    }
203
204    pub(crate) fn remove_variable(&mut self, ident: &Ident) {
205        self.variables.remove(ident);
206    }
207
208    pub(crate) fn swap_variable(&mut self, ident: Ident, value: Value) -> Option<Value> {
209        match self.variables.entry(ident) {
210            Entry::Occupied(mut v) => Some(std::mem::replace(v.get_mut(), value)),
211            Entry::Vacant(v) => {
212                v.insert(value);
213                None
214            }
215        }
216    }
217}