minijinja/vm/
closure_object.rs

1use std::collections::BTreeMap;
2use std::sync::{Arc, Mutex};
3
4use crate::value::{Enumerator, Object, Value};
5
6/// Closure cycle breaker utility.
7///
8/// The closure tracker is a crude way to forcefully break the cycles
9/// caused by closures on teardown of the state.  Whenever a closure is
10/// exposed to the engine it's also passed to the [`track_closure`] function.
11#[derive(Default)]
12pub(crate) struct ClosureTracker {
13    closures: Mutex<Vec<Arc<Closure>>>,
14}
15
16impl ClosureTracker {
17    /// This accepts a closure as value and registers it in the
18    /// tracker for cycle breaking.
19    pub(crate) fn track_closure(&self, closure: Arc<Closure>) {
20        self.closures.lock().unwrap().push(closure);
21    }
22}
23
24impl Drop for ClosureTracker {
25    fn drop(&mut self) {
26        for closure in self.closures.lock().unwrap().iter() {
27            closure.clear();
28        }
29    }
30}
31
32/// Utility to enclose values for macros.
33///
34/// See `closure` on the [`Frame`] for how it's used.
35#[derive(Debug, Default)]
36pub(crate) struct Closure {
37    values: Mutex<BTreeMap<Arc<str>, Value>>,
38}
39
40impl Closure {
41    /// Stores a value by key in the closure.
42    pub fn store(&self, key: &str, value: Value) {
43        self.values.lock().unwrap().insert(Arc::from(key), value);
44    }
45
46    /// Upset a value into the closure.
47    #[cfg(feature = "macros")]
48    pub fn store_if_missing<F: FnOnce() -> Value>(&self, key: &str, f: F) {
49        let mut values = self.values.lock().unwrap();
50        if !values.contains_key(key) {
51            values.insert(Arc::from(key), f());
52        }
53    }
54
55    /// Clears the closure.
56    ///
57    /// This is required to break cycles.
58    pub fn clear(&self) {
59        self.values.lock().unwrap().clear();
60    }
61}
62
63impl Object for Closure {
64    fn get_value(self: &Arc<Self>, key: &Value) -> Option<Value> {
65        self.values.lock().unwrap().get(key.as_str()?).cloned()
66    }
67
68    fn enumerate(self: &Arc<Self>) -> Enumerator {
69        let values = self.values.lock().unwrap();
70        let keys = values.keys().cloned().map(Value::from);
71        Enumerator::Values(keys.collect())
72    }
73}