• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kaspar030 / laze / 23058614601

13 Mar 2026 03:44PM UTC coverage: 77.864% (-1.3%) from 79.193%
23058614601

push

github

web-flow
feat: more functions and env access for expression evaluation (#880)

39 of 113 new or added lines in 5 files covered. (34.51%)

5 existing lines in 1 file now uncovered.

3398 of 4364 relevant lines covered (77.86%)

96.19 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

7.79
/src/nested_env/eval_context.rs
1
use camino::Utf8PathBuf;
2
use evalexpr::{EvalexprError, EvalexprResult, Value};
3

4
use super::EnvMap;
5

6
pub struct EvalContext<'a, 'b: 'a> {
7
    inner: &'a EnvMap<'b>,
8
    values: bumpalo::Bump,
9
}
10

11
impl<'a, 'b: 'a> EvalContext<'a, 'b> {
12
    pub fn new(env: &'a EnvMap<'b>) -> Self {
540✔
13
        Self {
540✔
14
            inner: env,
540✔
15
            values: bumpalo::Bump::new(),
540✔
16
        }
540✔
17
    }
540✔
18
}
19

20
impl evalexpr::Context for EvalContext<'_, '_> {
NEW
21
    fn get_value(&self, identifier: &str) -> Option<&Value> {
×
NEW
22
        self.inner
×
NEW
23
            .get(identifier)
×
NEW
24
            .map(|s| &*self.values.alloc(Value::String(s.to_string())))
×
NEW
25
    }
×
26

NEW
27
    fn call_function(
×
NEW
28
        &self,
×
NEW
29
        identifier: &str,
×
NEW
30
        argument: &evalexpr::Value,
×
NEW
31
    ) -> evalexpr::EvalexprResult<evalexpr::Value> {
×
32
        // This match lists the custom functions available to laze evaluations.
NEW
33
        match identifier {
×
NEW
34
            "tr" => self.fn_tr(argument),
×
NEW
35
            "joinpath" => self.fn_joinpath(argument),
×
NEW
36
            "relroot" => self.fn_relroot(argument),
×
NEW
37
            _ => EvalexprResult::Err(evalexpr::EvalexprError::FunctionIdentifierNotFound(
×
NEW
38
                identifier.to_string(),
×
NEW
39
            )),
×
40
        }
NEW
41
    }
×
42

NEW
43
    fn are_builtin_functions_disabled(&self) -> bool {
×
NEW
44
        false
×
NEW
45
    }
×
46

NEW
47
    fn set_builtin_functions_disabled(&mut self, _disabled: bool) -> evalexpr::EvalexprResult<()> {
×
NEW
48
        EvalexprResult::Ok(())
×
NEW
49
    }
×
50
}
51

52
impl<'a, 'b: 'a> EvalContext<'a, 'b> {
NEW
53
    fn fn_relroot(&self, argument: &evalexpr::Value) -> Result<evalexpr::Value, EvalexprError> {
×
54
        use normalize_path::NormalizePath;
NEW
55
        let s = argument.as_string()?;
×
NEW
56
        let relroot = if let Some(relroot) = self.inner.get("relroot") {
×
NEW
57
            relroot
×
58
        } else {
NEW
59
            return EvalexprResult::Err(evalexpr::EvalexprError::VariableIdentifierNotFound(
×
NEW
60
                "relroot".into(),
×
NEW
61
            ));
×
62
        };
NEW
63
        let path = Utf8PathBuf::from(relroot).join(s);
×
NEW
64
        let path = path.as_std_path().normalize();
×
NEW
65
        EvalexprResult::Ok(evalexpr::Value::String(path.to_str().unwrap().into()))
×
NEW
66
    }
×
67

NEW
68
    fn fn_joinpath(&self, argument: &evalexpr::Value) -> Result<evalexpr::Value, EvalexprError> {
×
NEW
69
        let paths = argument.as_tuple()?;
×
NEW
70
        let mut result = Utf8PathBuf::new();
×
NEW
71
        for path in paths {
×
NEW
72
            result.push(path.as_string()?);
×
73
        }
NEW
74
        EvalexprResult::Ok(evalexpr::Value::String(result.into()))
×
NEW
75
    }
×
76

NEW
77
    fn fn_tr(&self, argument: &evalexpr::Value) -> Result<Value, EvalexprError> {
×
78
        // from Gemini Pro 3.1 (2026-03-13):
NEW
79
        fn tr_iterative(input: &str, from: &str, to: &str) -> String {
×
NEW
80
            input
×
NEW
81
                .chars()
×
NEW
82
                .map(|c| {
×
83
                    // Find the index of the current char in the 'from' set
NEW
84
                    if let Some(pos) = from.chars().position(|f| f == c) {
×
85
                        // If found, return the char at the same position in 'to'
86
                        // (Defaults to the original char if 'to' is shorter than 'from')
NEW
87
                        to.chars().nth(pos).unwrap_or(c)
×
88
                    } else {
89
                        // If not found, keep the original character
NEW
90
                        c
×
91
                    }
NEW
92
                })
×
NEW
93
                .collect()
×
NEW
94
        }
×
95

NEW
96
        let args = argument.as_tuple()?;
×
NEW
97
        if args.len() != 3 {
×
NEW
98
            return EvalexprResult::Err(evalexpr::EvalexprError::wrong_function_argument_amount(
×
NEW
99
                args.len(),
×
NEW
100
                3,
×
NEW
101
            ));
×
NEW
102
        }
×
103

NEW
104
        let input = args[0].to_string();
×
NEW
105
        let from = args[1].to_string();
×
NEW
106
        let to = args[2].to_string();
×
107

NEW
108
        if from.len() != to.len() {
×
NEW
109
            return EvalexprResult::Err(evalexpr::EvalexprError::CustomMessage(
×
NEW
110
                "from and to have different lengths".to_string(),
×
NEW
111
            ));
×
NEW
112
        }
×
113

NEW
114
        let result = tr_iterative(&input, &from, &to);
×
115

NEW
116
        EvalexprResult::Ok(Value::String(result))
×
NEW
117
    }
×
118
}
119

120
#[cfg(test)]
121
mod test {
122
    use crate::nested_env::{expand_eval, EnvMap, IfMissing};
123

124
    #[test]
125
    fn joinpath() {
126
        let vars = EnvMap::new();
127
        assert_eq!(
128
            expand_eval(r#"$(joinpath ("/foo", "bar"))"#, &vars, IfMissing::Error),
129
            Ok("/foo/bar".into())
130
        );
131
    }
132
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc