• 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

83.05
/src/nested_env/expr.rs
1
use std::borrow::Cow;
2

3
use evalexpr::{Context, EmptyContextWithBuiltinFunctions, EvalexprError};
4

5
pub trait Eval {
6
    #[expect(unused)]
7
    fn eval(&self) -> Result<String, EvalexprError>;
8
    fn eval_with_context(&self, context: impl Context) -> Result<String, EvalexprError>;
9
}
10

11
impl Eval for String {
UNCOV
12
    fn eval(&self) -> Result<Self, EvalexprError> {
×
UNCOV
13
        Ok(eval(self)?.into())
×
UNCOV
14
    }
×
15
    fn eval_with_context(&self, context: impl Context) -> Result<Self, EvalexprError> {
540✔
16
        Ok(eval_with_context(self, &context)?.into())
540✔
17
    }
540✔
18
}
19

UNCOV
20
pub fn eval(input: &str) -> Result<Cow<'_, str>, EvalexprError> {
×
UNCOV
21
    if input.contains("$(") {
×
NEW
22
        eval_recursive(input, false, &EmptyContextWithBuiltinFunctions)
×
23
    } else {
NEW
24
        Ok(input.into())
×
25
    }
NEW
26
}
×
27

28
pub fn eval_with_context<'a>(
540✔
29
    input: &'a str,
540✔
30
    context: &impl Context,
540✔
31
) -> Result<Cow<'a, str>, EvalexprError> {
540✔
32
    if input.contains("$(") {
540✔
33
        eval_recursive(input, false, context)
6✔
34
    } else {
35
        Ok(input.into())
534✔
36
    }
37
}
540✔
38

39
fn eval_recursive<'a>(
8✔
40
    input: &'a str,
8✔
41
    is_eval: bool,
8✔
42
    context: &impl Context,
8✔
43
) -> Result<Cow<'a, str>, EvalexprError> {
8✔
44
    let mut result = String::new();
8✔
45
    let mut start = 0;
8✔
46
    let mut level = 0;
8✔
47
    let mut input_changed = false;
8✔
48

49
    for (i, character) in input.char_indices() {
526✔
50
        if character == '$'
526✔
51
            && i + 1 < input.len()
50✔
52
            && input[i + 1..i + 2] == *"("
50✔
53
            && (i == 0 || (input[i - 1..i] != *"$"))
8✔
54
        {
55
            if level == 0 {
2✔
56
                start = i + 1;
2✔
57
            }
2✔
58
        } else if character == '(' && start > 0 {
524✔
59
            level += 1;
2✔
60
        } else if character == ')' && level > 0 && start > 0 {
522✔
61
            level -= 1;
2✔
62
            if level == 0 {
2✔
63
                input_changed = true;
2✔
64
                result.push_str(&eval_recursive(&input[start + 1..i], true, context)?);
2✔
65
                start = 0;
1✔
66
            }
×
67
        } else if level == 0 {
520✔
68
            result.push(character);
515✔
69
        }
515✔
70
    }
71

72
    if is_eval {
7✔
73
        let expr = match evalexpr::eval_with_context(&result, context)? {
2✔
74
            evalexpr::Value::String(string) => string,
×
75
            other => other.to_string(),
1✔
76
        };
77
        input_changed = true;
1✔
78
        result = expr;
1✔
79
    }
5✔
80

81
    if input_changed {
6✔
82
        Ok(result.into())
2✔
83
    } else {
84
        Ok(input.into())
4✔
85
    }
86
}
8✔
87

88
#[cfg(test)]
89
mod tests {
90
    use super::*;
91

92
    #[test]
93
    fn basic() {
94
        let result = eval("foo $(1+$(1+1)) after_foo");
95
        assert_eq!(result.unwrap(), "foo 3 after_foo");
96
    }
97
    #[test]
98
    fn nested_braces() {
99
        let result = eval("$((0))");
100
        assert_eq!(result.unwrap(), "0");
101
    }
102
    #[test]
103
    fn basic_eval_max() {
104
        let result = eval("$(max(1,2,3,4))");
105
        assert_eq!(result.unwrap(), "4");
106
    }
107
    #[test]
108
    fn basic_eval_add() {
109
        let result = eval("$(str::to_uppercase \"foobar\")");
110
        assert_eq!(result.unwrap(), "FOOBAR");
111
    }
112
    #[test]
113
    fn unchanged() {
114
        let result = eval("just some text");
115
        assert_eq!(result.unwrap(), Cow::Borrowed("just some text"));
116
    }
117
    #[test]
118
    fn escaped_dollar() {
119
        let literal = "just some $$(foo) text";
120
        let result = eval(literal);
121
        assert_eq!(result.unwrap(), Cow::Borrowed(literal));
122
    }
123
    #[test]
124
    fn escaped_dollar_with_another() {
125
        let literal = "$(1) just some $$(1) text";
126
        let result = eval(literal);
127
        assert_eq!(result.unwrap(), "1 just some $$(1) text");
128
    }
129
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc