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

kaspar030 / laze / 19943820673

04 Dec 2025 09:02PM UTC coverage: 79.995% (-0.02%) from 80.01%
19943820673

push

github

web-flow
fix: don't escape expression string results (#822)

2 of 3 new or added lines in 1 file covered. (66.67%)

3367 of 4209 relevant lines covered (80.0%)

97.49 hits per line

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

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

3
use evalexpr::EvalexprError;
4

5
pub trait Eval {
6
    fn eval(&self) -> Result<String, EvalexprError>;
7
}
8

9
impl Eval for String {
10
    fn eval(&self) -> Result<Self, EvalexprError> {
540✔
11
        Ok(eval(self)?.into())
540✔
12
    }
540✔
13
}
14

15
pub fn eval(input: &str) -> Result<Cow<'_, str>, EvalexprError> {
540✔
16
    if input.contains("$(") {
540✔
17
        eval_recursive(input, false)
6✔
18
    } else {
19
        Ok(input.into())
534✔
20
    }
21
}
540✔
22

23
fn eval_recursive(input: &str, is_eval: bool) -> Result<Cow<'_, str>, EvalexprError> {
8✔
24
    let mut result = String::new();
8✔
25
    let mut start = 0;
8✔
26
    let mut level = 0;
8✔
27
    let mut input_changed = false;
8✔
28

29
    for (i, character) in input.char_indices() {
526✔
30
        if character == '$'
526✔
31
            && i + 1 < input.len()
50✔
32
            && input[i + 1..i + 2] == *"("
50✔
33
            && (i == 0 || (input[i - 1..i] != *"$"))
8✔
34
        {
35
            if level == 0 {
2✔
36
                start = i + 1;
2✔
37
            }
2✔
38
        } else if character == '(' && start > 0 {
524✔
39
            level += 1;
2✔
40
        } else if character == ')' && level > 0 && start > 0 {
522✔
41
            level -= 1;
2✔
42
            if level == 0 {
2✔
43
                input_changed = true;
2✔
44
                result.push_str(&eval_recursive(&input[start + 1..i], true)?);
2✔
45
                start = 0;
1✔
46
            }
×
47
        } else if level == 0 {
520✔
48
            result.push(character);
515✔
49
        }
515✔
50
    }
51

52
    if is_eval {
7✔
53
        let expr = match evalexpr::eval(&result)? {
2✔
NEW
54
            evalexpr::Value::String(string) => string,
×
55
            other => other.to_string(),
1✔
56
        };
57
        input_changed = true;
1✔
58
        result = expr;
1✔
59
    }
5✔
60

61
    if input_changed {
6✔
62
        Ok(result.into())
2✔
63
    } else {
64
        Ok(input.into())
4✔
65
    }
66
}
8✔
67

68
#[cfg(test)]
69
mod tests {
70
    use super::*;
71

72
    #[test]
73
    fn basic() {
74
        let result = eval("foo $(1+$(1+1)) after_foo");
75
        assert_eq!(result.unwrap(), "foo 3 after_foo");
76
    }
77
    #[test]
78
    fn nested_braces() {
79
        let result = eval("$((0))");
80
        assert_eq!(result.unwrap(), "0");
81
    }
82
    #[test]
83
    fn basic_eval_max() {
84
        let result = eval("$(max(1,2,3,4))");
85
        assert_eq!(result.unwrap(), "4");
86
    }
87
    #[test]
88
    fn basic_eval_add() {
89
        let result = eval("$(str::to_uppercase \"foobar\")");
90
        assert_eq!(result.unwrap(), "FOOBAR");
91
    }
92
    #[test]
93
    fn unchanged() {
94
        let result = eval("just some text");
95
        assert_eq!(result.unwrap(), Cow::Borrowed("just some text"));
96
    }
97
    #[test]
98
    fn escaped_dollar() {
99
        let literal = "just some $$(foo) text";
100
        let result = eval(literal);
101
        assert_eq!(result.unwrap(), Cow::Borrowed(literal));
102
    }
103
    #[test]
104
    fn escaped_dollar_with_another() {
105
        let literal = "$(1) just some $$(1) text";
106
        let result = eval(literal);
107
        assert_eq!(result.unwrap(), "1 just some $$(1) text");
108
    }
109
}
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

© 2025 Coveralls, Inc