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

kaspar030 / laze / 23060053129

13 Mar 2026 04:19PM UTC coverage: 77.885%. Remained the same
23060053129

Pull #882

github

web-flow
Merge 8679c7d71 into 90282dbf4
Pull Request #882: fix: do not add quotation marks to `tr` arguments

0 of 3 new or added lines in 1 file covered. (0.0%)

3402 of 4368 relevant lines covered (77.88%)

98.24 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<'_, '_> {
21
    fn get_value(&self, identifier: &str) -> Option<&Value> {
×
22
        self.inner
×
23
            .get(identifier)
×
24
            .map(|s| &*self.values.alloc(Value::String(s.to_string())))
×
25
    }
×
26

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

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

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

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

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

77
    fn fn_tr(&self, argument: &evalexpr::Value) -> Result<Value, EvalexprError> {
×
78
        // from Gemini Pro 3.1 (2026-03-13):
79
        fn tr_iterative(input: &str, from: &str, to: &str) -> String {
×
80
            input
×
81
                .chars()
×
82
                .map(|c| {
×
83
                    // Find the index of the current char in the 'from' set
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')
87
                        to.chars().nth(pos).unwrap_or(c)
×
88
                    } else {
89
                        // If not found, keep the original character
90
                        c
×
91
                    }
92
                })
×
93
                .collect()
×
94
        }
×
95

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

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

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

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

116
        EvalexprResult::Ok(Value::String(result))
×
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