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

ISibboI / evalexpr / 18781572147

24 Oct 2025 01:40PM UTC coverage: 91.395% (-0.4%) from 91.817%
18781572147

Pull #186

github

ISibboI
Fix new lints.
Pull Request #186: Fix new lints

1317 of 1441 relevant lines covered (91.39%)

9.09 hits per line

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

89.29
/src/function/builtin.rs
1
#[cfg(feature = "regex")]
2
use regex::Regex;
3

4
use crate::{
5
    value::numeric_types::{EvalexprFloat, EvalexprInt, EvalexprNumericTypes},
6
    EvalexprError, Function, Value, ValueType,
7
};
8

9
macro_rules! simple_math {
10
    ($func:ident) => {
11
        Some(Function::new(|argument: &Value<NumericTypes>| {
44✔
12
            let num = argument.as_number()?;
22✔
13
            Ok(Value::Float(num.$func()))
44✔
14
        }))
15
    };
16
    ($func:ident, 2) => {
17
        Some(Function::new(|argument: &Value<NumericTypes>| {
8✔
18
            let tuple = argument.as_fixed_len_tuple(2)?;
4✔
19
            let (a, b) = (tuple[0].as_number()?, tuple[1].as_number()?);
8✔
20
            Ok(Value::Float(a.$func(&b)))
8✔
21
        }))
22
    };
23
}
24

25
fn float_is<NumericTypes: EvalexprNumericTypes>(
1✔
26
    func: fn(&NumericTypes::Float) -> bool,
27
) -> Option<Function<NumericTypes>> {
28
    Some(Function::new(move |argument: &Value<NumericTypes>| {
2✔
29
        Ok(func(&argument.as_number()?).into())
2✔
30
    }))
31
}
32

33
macro_rules! int_function {
34
    ($func:ident) => {
35
        Some(Function::new(|argument| {
2✔
36
            let int: NumericTypes::Int = argument.as_int()?;
1✔
37
            Ok(Value::Int(int.$func()))
2✔
38
        }))
39
    };
40
    ($func:ident, 2) => {
41
        Some(Function::new(|argument| {
10✔
42
            let tuple = argument.as_fixed_len_tuple(2)?;
5✔
43
            let (a, b): (NumericTypes::Int, NumericTypes::Int) =
5✔
44
                (tuple[0].as_int()?, tuple[1].as_int()?);
10✔
45
            Ok(Value::Int(a.$func(&b)))
10✔
46
        }))
47
    };
48
}
49

50
pub fn builtin_function<NumericTypes: EvalexprNumericTypes>(
5✔
51
    identifier: &str,
52
) -> Option<Function<NumericTypes>> {
53
    match identifier {
×
54
        // Log
55
        "math::ln" => simple_math!(ln),
5✔
56
        "math::log" => simple_math!(log, 2),
5✔
57
        "math::log2" => simple_math!(log2),
5✔
58
        "math::log10" => simple_math!(log10),
5✔
59
        // Exp
60
        "math::exp" => simple_math!(exp),
5✔
61
        "math::exp2" => simple_math!(exp2),
5✔
62
        // Pow
63
        "math::pow" => simple_math!(pow, 2),
5✔
64
        // Cos
65
        "math::cos" => simple_math!(cos),
5✔
66
        "math::acos" => simple_math!(acos),
5✔
67
        "math::cosh" => simple_math!(cosh),
5✔
68
        "math::acosh" => simple_math!(acosh),
5✔
69
        // Sin
70
        "math::sin" => simple_math!(sin),
5✔
71
        "math::asin" => simple_math!(asin),
5✔
72
        "math::sinh" => simple_math!(sinh),
5✔
73
        "math::asinh" => simple_math!(asinh),
5✔
74
        // Tan
75
        "math::tan" => simple_math!(tan),
5✔
76
        "math::atan" => simple_math!(atan),
5✔
77
        "math::tanh" => simple_math!(tanh),
5✔
78
        "math::atanh" => simple_math!(atanh),
5✔
79
        "math::atan2" => simple_math!(atan2, 2),
5✔
80
        // Root
81
        "math::sqrt" => simple_math!(sqrt),
5✔
82
        "math::cbrt" => simple_math!(cbrt),
5✔
83
        // Hypotenuse
84
        "math::hypot" => simple_math!(hypot, 2),
5✔
85
        // Rounding
86
        "floor" => simple_math!(floor),
5✔
87
        "round" => simple_math!(round),
5✔
88
        "ceil" => simple_math!(ceil),
5✔
89
        // Float special values
90
        "math::is_nan" => float_is(NumericTypes::Float::is_nan),
6✔
91
        "math::is_finite" => float_is(NumericTypes::Float::is_finite),
6✔
92
        "math::is_infinite" => float_is(NumericTypes::Float::is_infinite),
6✔
93
        "math::is_normal" => float_is(NumericTypes::Float::is_normal),
6✔
94
        // Absolute value
95
        "math::abs" => Some(Function::new(|argument| match argument {
8✔
96
            Value::Float(num) => Ok(Value::Float(
2✔
97
                <NumericTypes as EvalexprNumericTypes>::Float::abs(num),
1✔
98
            )),
99
            Value::Int(num) => Ok(Value::Int(
2✔
100
                <NumericTypes as EvalexprNumericTypes>::Int::abs(num)?,
1✔
101
            )),
102
            _ => Err(EvalexprError::expected_number(argument.clone())),
×
103
        })),
104
        // Other
105
        "typeof" => Some(Function::new(move |argument| {
7✔
106
            Ok(match argument {
3✔
107
                Value::String(_) => "string",
1✔
108
                Value::Float(_) => "float",
1✔
109
                Value::Int(_) => "int",
1✔
110
                Value::Boolean(_) => "boolean",
1✔
111
                Value::Tuple(_) => "tuple",
1✔
112
                Value::Empty => "empty",
1✔
113
            }
114
            .into())
1✔
115
        })),
116
        "min" => Some(Function::new(|argument| {
9✔
117
            let arguments = argument.as_tuple()?;
2✔
118
            let mut min_int = NumericTypes::Int::MAX;
2✔
119
            let mut min_float = NumericTypes::Float::MAX;
2✔
120
            debug_assert!(min_float.is_infinite());
4✔
121

122
            for argument in arguments {
6✔
123
                if let Value::Float(float) = argument {
3✔
124
                    min_float = min_float.min(&float);
2✔
125
                } else if let Value::Int(int) = argument {
4✔
126
                    min_int = min_int.min(int);
4✔
127
                } else {
128
                    return Err(EvalexprError::expected_number(argument));
×
129
                }
130
            }
131

132
            if (NumericTypes::int_as_float(&min_int)) < min_float {
4✔
133
                Ok(Value::Int(min_int))
2✔
134
            } else {
135
                Ok(Value::Float(min_float))
×
136
            }
137
        })),
138
        "max" => Some(Function::new(|argument| {
8✔
139
            let arguments = argument.as_tuple()?;
2✔
140
            let mut max_int = NumericTypes::Int::MIN;
2✔
141
            let mut max_float = NumericTypes::Float::MIN;
2✔
142
            debug_assert!(max_float.is_infinite());
4✔
143

144
            for argument in arguments {
6✔
145
                if let Value::Float(float) = argument {
3✔
146
                    max_float = max_float.max(&float);
2✔
147
                } else if let Value::Int(int) = argument {
4✔
148
                    max_int = max_int.max(int);
4✔
149
                } else {
150
                    return Err(EvalexprError::expected_number(argument));
×
151
                }
152
            }
153

154
            if (NumericTypes::int_as_float(&max_int)) > max_float {
4✔
155
                Ok(Value::Int(max_int))
2✔
156
            } else {
157
                Ok(Value::Float(max_float))
1✔
158
            }
159
        })),
160
        "if" => Some(Function::new(|argument| {
5✔
161
            let mut arguments = argument.as_fixed_len_tuple(3)?;
1✔
162
            let result_index = if arguments[0].as_boolean()? { 1 } else { 2 };
2✔
163
            Ok(arguments.swap_remove(result_index))
1✔
164
        })),
165
        "contains" => Some(Function::new(move |argument| {
5✔
166
            let arguments = argument.as_fixed_len_tuple(2)?;
1✔
167
            if let (Value::Tuple(a), b) = (&arguments[0].clone(), &arguments[1].clone()) {
4✔
168
                if let Value::String(_) | Value::Int(_) | Value::Float(_) | Value::Boolean(_) = b {
2✔
169
                    Ok(a.contains(b).into())
2✔
170
                } else {
171
                    Err(EvalexprError::type_error(
2✔
172
                        b.clone(),
2✔
173
                        vec![
2✔
174
                            ValueType::String,
×
175
                            ValueType::Int,
×
176
                            ValueType::Float,
×
177
                            ValueType::Boolean,
×
178
                        ],
179
                    ))
180
                }
181
            } else {
182
                Err(EvalexprError::expected_tuple(arguments[0].clone()))
2✔
183
            }
184
        })),
185
        "contains_any" => Some(Function::new(move |argument| {
5✔
186
            let arguments = argument.as_fixed_len_tuple(2)?;
1✔
187
            if let (Value::Tuple(a), b) = (&arguments[0].clone(), &arguments[1].clone()) {
4✔
188
                if let Value::Tuple(b) = b {
3✔
189
                    let mut contains = false;
1✔
190
                    for value in b {
2✔
191
                        if let Value::String(_)
1✔
192
                        | Value::Int(_)
×
193
                        | Value::Float(_)
×
194
                        | Value::Boolean(_) = value
1✔
195
                        {
196
                            if a.contains(value) {
2✔
197
                                contains = true;
1✔
198
                            }
199
                        } else {
200
                            return Err(EvalexprError::type_error(
2✔
201
                                value.clone(),
2✔
202
                                vec![
2✔
203
                                    ValueType::String,
×
204
                                    ValueType::Int,
×
205
                                    ValueType::Float,
×
206
                                    ValueType::Boolean,
×
207
                                ],
208
                            ));
209
                        }
210
                    }
211
                    Ok(contains.into())
1✔
212
                } else {
213
                    Err(EvalexprError::expected_tuple(b.clone()))
2✔
214
                }
215
            } else {
216
                Err(EvalexprError::expected_tuple(arguments[0].clone()))
2✔
217
            }
218
        })),
219
        "len" => Some(Function::new(|argument| {
5✔
220
            if let Ok(subject) = argument.as_string() {
2✔
221
                Ok(Value::Int(NumericTypes::Int::from_usize(subject.len())?))
2✔
222
            } else if let Ok(subject) = argument.as_tuple() {
4✔
223
                Ok(Value::Int(NumericTypes::Int::from_usize(subject.len())?))
2✔
224
            } else {
225
                Err(EvalexprError::type_error(
×
226
                    argument.clone(),
×
227
                    vec![ValueType::String, ValueType::Tuple],
×
228
                ))
229
            }
230
        })),
231
        // String functions
232
        #[cfg(feature = "regex")]
233
        "str::regex_matches" => Some(Function::new(|argument| {
5✔
234
            let arguments = argument.as_fixed_len_tuple(2)?;
1✔
235

236
            let subject = arguments[0].as_string()?;
2✔
237
            let re_str = arguments[1].as_string()?;
2✔
238
            match Regex::new(&re_str) {
2✔
239
                Ok(re) => Ok(Value::Boolean(re.is_match(&subject))),
1✔
240
                Err(err) => Err(EvalexprError::invalid_regex(
3✔
241
                    re_str.to_string(),
2✔
242
                    format!("{}", err),
2✔
243
                )),
244
            }
245
        })),
246
        #[cfg(feature = "regex")]
247
        "str::regex_replace" => Some(Function::new(|argument| {
5✔
248
            let arguments = argument.as_fixed_len_tuple(3)?;
1✔
249

250
            let subject = arguments[0].as_string()?;
2✔
251
            let re_str = arguments[1].as_string()?;
2✔
252
            let repl = arguments[2].as_string()?;
2✔
253
            match Regex::new(&re_str) {
2✔
254
                Ok(re) => Ok(Value::String(
2✔
255
                    re.replace_all(&subject, repl.as_str()).to_string(),
2✔
256
                )),
257
                Err(err) => Err(EvalexprError::invalid_regex(
×
258
                    re_str.to_string(),
×
259
                    format!("{}", err),
×
260
                )),
261
            }
262
        })),
263
        "str::to_lowercase" => Some(Function::new(|argument| {
4✔
264
            let subject = argument.as_string()?;
1✔
265
            Ok(Value::from(subject.to_lowercase()))
2✔
266
        })),
267
        "str::to_uppercase" => Some(Function::new(|argument| {
4✔
268
            let subject = argument.as_string()?;
1✔
269
            Ok(Value::from(subject.to_uppercase()))
2✔
270
        })),
271
        "str::trim" => Some(Function::new(|argument| {
4✔
272
            let subject = argument.as_string()?;
1✔
273
            Ok(Value::from(subject.trim()))
2✔
274
        })),
275
        "str::from" => Some(Function::new(|argument| {
4✔
276
            Ok(Value::String(argument.str_from()))
1✔
277
        })),
278
        "str::substring" => Some(Function::new(|argument| {
4✔
279
            let args = argument.as_ranged_len_tuple(2..=3)?;
1✔
280
            let subject = args[0].as_string()?;
2✔
281
            let start: NumericTypes::Int = args[1].as_int()?;
2✔
282
            let start = start
3✔
283
                .into_usize()
1✔
284
                .map_err(|_| EvalexprError::OutOfBoundsAccess)?;
4✔
285
            let end = if let Some(end) = args.get(2) {
1✔
286
                let end: NumericTypes::Int = end.as_int()?;
2✔
287
                end.into_usize()
3✔
288
                    .map_err(|_| EvalexprError::OutOfBoundsAccess)?
4✔
289
            } else {
290
                subject.len()
2✔
291
            };
292
            if start > end || end > subject.len() {
3✔
293
                return Err(EvalexprError::OutOfBoundsAccess);
1✔
294
            }
295
            Ok(Value::from(&subject[start..end]))
1✔
296
        })),
297
        #[cfg(feature = "rand")]
298
        "random" => Some(Function::new(|argument| {
4✔
299
            argument.as_empty()?;
1✔
300
            Ok(Value::Float(NumericTypes::Float::random()?))
1✔
301
        })),
302
        // Bitwise operators
303
        "bitand" => int_function!(bitand, 2),
1✔
304
        "bitor" => int_function!(bitor, 2),
1✔
305
        "bitxor" => int_function!(bitxor, 2),
1✔
306
        "bitnot" => int_function!(bitnot),
1✔
307
        "shl" => int_function!(bit_shift_left, 2),
1✔
308
        "shr" => int_function!(bit_shift_right, 2),
1✔
309
        _ => None,
1✔
310
    }
311
}
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