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

OISF / suricata / 22550237931

01 Mar 2026 06:56PM UTC coverage: 64.812% (-8.9%) from 73.687%
22550237931

Pull #14920

github

web-flow
Merge e05854a6d into 90823fa90
Pull Request #14920: draft: rust based configuration file parser and loader - v4

561 of 789 new or added lines in 4 files covered. (71.1%)

23225 existing lines in 498 files now uncovered.

131605 of 203055 relevant lines covered (64.81%)

2328818.84 hits per line

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

71.74
/rust/src/detect/float.rs
1
/* Copyright (C) 2025 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17

18
use nom8::{
19
    branch::alt,
20
    bytes::complete::{is_a, tag, tag_no_case, take_while},
21
    character::complete::{char, digit1},
22
    combinator::{all_consuming, map, map_opt, opt, recognize, value, verify},
23
    error::{make_error, ErrorKind},
24
    Err, IResult, Parser,
25
};
26

27
use num::traits::float::FloatCore;
28
use num::traits::{FromPrimitive, ToPrimitive};
29
use num::Bounded;
30

31
use std::ffi::CStr;
32

33
#[derive(PartialEq, Eq, Clone, Debug)]
34
#[repr(u8)]
35
pub enum DetectFloatMode {
36
    DetectFloatModeEqual,
37
    DetectFloatModeLt,
38
    DetectFloatModeLte,
39
    DetectFloatModeGt,
40
    DetectFloatModeGte,
41
    DetectFloatModeRange,
42
    DetectFloatModeNe,
43
    DetectFloatModeNegRg,
44
}
45

46
#[derive(Debug, PartialEq)]
47
#[repr(C)]
48
pub struct DetectFloatData<T> {
49
    pub arg1: T,
50
    pub arg2: T,
51
    pub mode: DetectFloatMode,
52
}
53

54
impl<T: Default> Default for DetectFloatData<T> {
55
    fn default() -> Self {
211✔
56
        Self {
211✔
57
            arg1: T::default(),
211✔
58
            arg2: T::default(),
211✔
59
            mode: DetectFloatMode::DetectFloatModeEqual,
211✔
60
        }
211✔
61
    }
211✔
62
}
63

64
pub trait DetectFloatType:
65
    FromPrimitive + ToPrimitive + std::str::FromStr + Bounded + PartialOrd + FloatCore + Sized
66
{
67
    fn from_str(s: &str) -> Option<Self>;
68
}
69

70
impl<T> DetectFloatType for T
71
where
72
    T: FromPrimitive + ToPrimitive + std::str::FromStr + Bounded + PartialOrd + FloatCore,
73
{
74
    fn from_str(s: &str) -> Option<Self> {
312✔
75
        s.parse().ok()
312✔
76
    }
312✔
77
}
78

79
pub fn parse_float_value<T: DetectFloatType>(input: &str) -> IResult<&str, T> {
417✔
80
    alt((
417✔
81
        // Handle special cases first
417✔
82
        map(tag_no_case("NaN"), |_| {
417✔
83
            <T as DetectFloatType>::from_str("NaN").unwrap()
4✔
84
        }),
417✔
85
        map(tag_no_case("+inf"), |_| {
417✔
86
            <T as DetectFloatType>::from_str("inf").unwrap()
3✔
87
        }),
417✔
88
        map(tag_no_case("inf"), |_| {
417✔
89
            <T as DetectFloatType>::from_str("inf").unwrap()
17✔
90
        }),
417✔
91
        map(tag_no_case("-inf"), |_| {
417✔
92
            <T as DetectFloatType>::from_str("-inf").unwrap()
7✔
93
        }),
417✔
94
        // Handle numeric parsing, including scientific notation
417✔
95
        map_opt(
417✔
96
            recognize((
417✔
97
                opt(alt((tag("+"), tag("-")))), // Handle optional signs
417✔
98
                alt((digit1, recognize((tag("."), digit1)))), // Handle integers & `.5`
417✔
99
                opt((tag("."), digit1)), // Handle decimals like `5.`
417✔
100
                opt((
417✔
101
                    tag_no_case("e"),
417✔
102
                    opt(alt((tag("+"), tag("-")))),
417✔
103
                    digit1,
417✔
104
                )), // Handle `1e10`, `-1e-5`
417✔
105
            )),
417✔
106
            |float_str: &str| <T as DetectFloatType>::from_str(float_str),
417✔
107
        ),
417✔
108
    )).parse(input)
417✔
109
}
417✔
110
fn detect_parse_float_start_equal<T: DetectFloatType>(
135✔
111
    i: &str,
135✔
112
) -> IResult<&str, DetectFloatData<T>> {
135✔
113
    let (i, _) = opt(tag("=")).parse(i)?;
135✔
114
    let (i, _) = opt(is_a(" ")).parse(i)?;
135✔
115
    let (i, arg1) = parse_float_value::<T>(i)?;
135✔
116
    Ok((
89✔
117
        i,
89✔
118
        DetectFloatData {
89✔
119
            arg1,
89✔
120
            arg2: <T as FloatCore>::min_value(),
89✔
121
            mode: DetectFloatMode::DetectFloatModeEqual,
89✔
122
        },
89✔
123
    ))
89✔
124
}
135✔
125

126
pub fn detect_parse_float_start_interval<T: DetectFloatType>(
183✔
127
    i: &str,
183✔
128
) -> IResult<&str, DetectFloatData<T>> {
183✔
129
    let (i, neg) = opt(char('!')).parse(i)?;
183✔
130
    let (i, arg1) = parse_float_value::<T>(i)?;
183✔
131
    let (i, _) = opt(is_a(" ")).parse(i)?;
136✔
132
    let (i, _) = alt((tag("-"), tag("<>"))).parse(i)?;
136✔
133
    let (i, _) = opt(is_a(" ")).parse(i)?;
65✔
134
    let (i, arg2) = verify(parse_float_value::<T>, |x| {
65✔
135
        *x > arg1 && *x - arg1 > <T as FloatCore>::epsilon()
59✔
136
    }).parse(i)?;
65✔
137
    let mode = if neg.is_some() {
48✔
138
        DetectFloatMode::DetectFloatModeNegRg
1✔
139
    } else {
140
        DetectFloatMode::DetectFloatModeRange
47✔
141
    };
142
    Ok((i, DetectFloatData { arg1, arg2, mode }))
48✔
143
}
183✔
144

145
fn detect_parse_float_mode(i: &str) -> IResult<&str, DetectFloatMode> {
46✔
146
    let (i, mode) = alt((
46✔
147
        value(DetectFloatMode::DetectFloatModeGte, tag(">=")),
46✔
148
        value(DetectFloatMode::DetectFloatModeLte, tag("<=")),
46✔
149
        value(DetectFloatMode::DetectFloatModeGt, tag(">")),
46✔
150
        value(DetectFloatMode::DetectFloatModeLt, tag("<")),
46✔
151
        value(DetectFloatMode::DetectFloatModeNe, tag("!=")),
46✔
152
        value(DetectFloatMode::DetectFloatModeEqual, tag("=")),
46✔
153
    )).parse(i)?;
46✔
154
    Ok((i, mode))
34✔
155
}
46✔
156

157
fn detect_parse_float_start_symbol<T: DetectFloatType>(
46✔
158
    i: &str,
46✔
159
) -> IResult<&str, DetectFloatData<T>> {
46✔
160
    let (i, mode) = detect_parse_float_mode(i)?;
46✔
161
    let (i, _) = opt(is_a(" ")).parse(i)?;
34✔
162
    let (i, arg1) = parse_float_value::<T>(i)?;
34✔
163

164
    match mode {
25✔
165
        DetectFloatMode::DetectFloatModeNe => {}
2✔
166
        DetectFloatMode::DetectFloatModeLt => {
167
            if arg1 == <T as FloatCore>::min_value() {
7✔
168
                return Err(Err::Error(make_error(i, ErrorKind::Verify)));
×
169
            }
7✔
170
        }
171
        DetectFloatMode::DetectFloatModeLte => {
172
            if arg1 == <T as FloatCore>::max_value() {
3✔
173
                return Err(Err::Error(make_error(i, ErrorKind::Verify)));
×
174
            }
3✔
175
        }
176
        DetectFloatMode::DetectFloatModeGt => {
177
            if arg1 == <T as FloatCore>::max_value() {
7✔
178
                return Err(Err::Error(make_error(i, ErrorKind::Verify)));
×
179
            }
7✔
180
        }
181
        DetectFloatMode::DetectFloatModeGte => {
182
            if arg1 == <T as FloatCore>::min_value() {
6✔
183
                return Err(Err::Error(make_error(i, ErrorKind::Verify)));
×
184
            }
6✔
185
        }
186
        _ => {
187
            return Err(Err::Error(make_error(i, ErrorKind::MapOpt)));
×
188
        }
189
    }
190

191
    Ok((
25✔
192
        i,
25✔
193
        DetectFloatData {
25✔
194
            arg1,
25✔
195
            arg2: <T as FloatCore>::min_value(),
25✔
196
            mode,
25✔
197
        },
25✔
198
    ))
25✔
199
}
46✔
200

201
pub fn detect_match_float<T: DetectFloatType>(x: &DetectFloatData<T>, val: T) -> bool {
135✔
202
    match x.mode {
135✔
203
        DetectFloatMode::DetectFloatModeEqual => val == x.arg1,
1✔
204
        DetectFloatMode::DetectFloatModeNe => val != x.arg1,
1✔
205
        DetectFloatMode::DetectFloatModeLt => val < x.arg1,
1✔
UNCOV
206
        DetectFloatMode::DetectFloatModeLte => val <= x.arg1,
×
207
        DetectFloatMode::DetectFloatModeGt => val > x.arg1,
2✔
208
        DetectFloatMode::DetectFloatModeGte => val >= x.arg1,
128✔
209
        DetectFloatMode::DetectFloatModeRange => val > x.arg1 && val < x.arg2,
1✔
210
        DetectFloatMode::DetectFloatModeNegRg => val <= x.arg1 || val >= x.arg2,
1✔
211
    }
212
}
135✔
213

214
pub fn detect_parse_float<T: DetectFloatType>(i: &str) -> IResult<&str, DetectFloatData<T>> {
183✔
215
    let (i, float) = detect_parse_float_notending(i)?;
183✔
216
    let (i, _) = all_consuming(take_while(|c| c == ' ')).parse(i)?;
282✔
217
    Ok((i, float))
106✔
218
}
183✔
219

220
fn detect_parse_float_notending<T: DetectFloatType>(i: &str) -> IResult<&str, DetectFloatData<T>> {
183✔
221
    let (i, _) = opt(is_a(" ")).parse(i)?;
183✔
222
    let (i, float) = alt((
183✔
223
        detect_parse_float_start_interval,
183✔
224
        detect_parse_float_start_equal,
183✔
225
        detect_parse_float_start_symbol,
183✔
226
    )).parse(i)?;
183✔
227
    Ok((i, float))
162✔
228
}
183✔
229

230
#[no_mangle]
UNCOV
231
pub unsafe extern "C" fn SCDetectF64Parse(
×
UNCOV
232
    ustr: *const std::os::raw::c_char,
×
UNCOV
233
) -> *mut DetectFloatData<f64> {
×
UNCOV
234
    let ft_name: &CStr = CStr::from_ptr(ustr); //unsafe
×
UNCOV
235
    if let Ok(s) = ft_name.to_str() {
×
UNCOV
236
        if let Ok((_, ctx)) = detect_parse_float::<f64>(s) {
×
UNCOV
237
            let boxed = Box::new(ctx);
×
UNCOV
238
            return Box::into_raw(boxed) as *mut _;
×
239
        }
×
240
    }
×
241
    return std::ptr::null_mut();
×
UNCOV
242
}
×
243

244
#[no_mangle]
UNCOV
245
pub unsafe extern "C" fn SCDetectF64Match(
×
UNCOV
246
    arg: f64, ctx: &DetectFloatData<f64>,
×
UNCOV
247
) -> std::os::raw::c_int {
×
UNCOV
248
    if detect_match_float::<f64>(ctx, arg) {
×
UNCOV
249
        return 1;
×
250
    }
×
251
    return 0;
×
UNCOV
252
}
×
253

254
#[no_mangle]
255
pub unsafe extern "C" fn SCDetectF64Free(ctx: &mut DetectFloatData<f64>) {
×
256
    // Just unbox...
×
257
    std::mem::drop(Box::from_raw(ctx));
×
258
}
×
259

260
#[no_mangle]
261
pub unsafe extern "C" fn SCDetectParseF64(
×
262
    ustr: *const std::os::raw::c_char,
×
263
) -> *mut DetectFloatData<f64> {
×
264
    let ft_name: &CStr = CStr::from_ptr(ustr);
×
265
    if let Ok(s) = ft_name.to_str() {
×
266
        if let Ok((_, ctx)) = detect_parse_float::<f64>(s) {
×
267
            let boxed = Box::new(ctx);
×
268
            return Box::into_raw(boxed);
×
269
        }
×
270
    }
×
271
    std::ptr::null_mut()
×
272
}
×
273

274
#[no_mangle]
275
pub unsafe extern "C" fn SCDetectMatchF64(
×
276
    arg: f64, ctx: &DetectFloatData<f64>,
×
277
) -> std::os::raw::c_int {
×
278
    if detect_match_float(ctx, arg) {
×
279
        1
×
280
    } else {
281
        0
×
282
    }
283
}
×
284

285
#[no_mangle]
286
pub unsafe extern "C" fn SCDetectFreeF64(ctx: *mut DetectFloatData<f64>) {
×
287
    std::mem::drop(Box::from_raw(ctx));
×
288
}
×
289

290
#[cfg(test)]
291
mod tests {
292
    use super::*;
293
    use std::ffi::CString;
294

295
    #[test]
296
    fn test_parse_float_value() {
297
        assert!(parse_float_value::<f64>("NaN").is_ok());
298
        assert!(parse_float_value::<f64>("-inf").is_ok());
299
        assert!(parse_float_value::<f64>("inf").is_ok());
300
        assert!(parse_float_value::<f64>("+inf").is_ok());
301
        assert!(parse_float_value::<f64>("123.45").is_ok());
302
        assert!(parse_float_value::<f64>("-0.001").is_ok());
303
        assert!(parse_float_value::<f64>("1e10").is_ok());
304
        assert!(parse_float_value::<f64>("-1e-10").is_ok());
305
        assert!(parse_float_value::<f64>("0.5").is_ok());
306
        assert!(parse_float_value::<f64>("5.").is_ok());
307
        assert!(parse_float_value::<f64>("0+").is_ok());
308
        assert!(parse_float_value::<f64>("0-").is_ok());
309
        assert!(parse_float_value::<f32>("NaN").is_ok());
310
        assert!(parse_float_value::<f32>("-inf").is_ok());
311
        assert!(parse_float_value::<f32>("inf").is_ok());
312
        assert!(parse_float_value::<f32>("+inf").is_ok());
313
        assert!(parse_float_value::<f32>("123.45").is_ok());
314
        assert!(parse_float_value::<f32>("-0.001").is_ok());
315
        assert!(parse_float_value::<f32>("1e10").is_ok());
316
        assert!(parse_float_value::<f32>("-1e-10").is_ok());
317
        assert!(parse_float_value::<f32>("0.5").is_ok());
318
        assert!(parse_float_value::<f32>("5.").is_ok());
319
        assert!(parse_float_value::<f32>("0+").is_ok());
320
        assert!(parse_float_value::<f32>("0-").is_ok());
321

322
        assert!(parse_float_value::<f32>(".e10").is_err());
323
    }
324
    #[test]
325
    fn test_detect_parse_valid() {
326
        let _ = do_parse("1.0", 1.0, DetectFloatMode::DetectFloatModeEqual);
327
        let _ = do_parse(">1.0", 1.0, DetectFloatMode::DetectFloatModeGt);
328
        let _ = do_parse(">=1.0", 1.0, DetectFloatMode::DetectFloatModeGte);
329
        let _ = do_parse("<1.0", 1.0, DetectFloatMode::DetectFloatModeLt);
330
        let _ = do_parse("<=1.0", 1.0, DetectFloatMode::DetectFloatModeLte);
331
        let _ = do_parse("=1.0", 1.0, DetectFloatMode::DetectFloatModeEqual);
332
        let _ = do_parse("!=1.0", 1.0, DetectFloatMode::DetectFloatModeNe);
333
        let _ = do_parse_mult_args(
334
            "37.0-42.0",
335
            37.0,
336
            42.0,
337
            DetectFloatMode::DetectFloatModeRange,
338
        );
339
    }
340

341
    #[test]
342
    fn test_detect_parse_invalid() {
343
        assert!(detect_parse_float::<f64>("suricata").is_err());
344

345
        // range should be <lower-val> - <higher-val>
346
        assert!(detect_parse_float::<f64>("42-37").is_err());
347

348
        assert!(detect_parse_float::<f64>("< suricata").is_err());
349
        assert!(detect_parse_float::<f64>("<= suricata").is_err());
350
        assert!(detect_parse_float::<f64>("= suricata").is_err());
351
        assert!(detect_parse_float::<f64>("> suricata").is_err());
352
        assert!(detect_parse_float::<f64>(">= suricata").is_err());
353
        assert!(detect_parse_float::<f64>("! suricata").is_err());
354
        assert!(detect_parse_float::<f64>("!= suricata").is_err());
355
    }
356

357
    fn do_parse<T: DetectFloatType + std::fmt::Display>(
358
        val: &str, fval: T, mode: DetectFloatMode,
359
    ) -> DetectFloatData<T> {
360
        let str_val = format!("{:.3}", fval);
361
        let (_, val) = detect_parse_float::<T>(val).unwrap();
362
        let str_arg1 = format!("{:.3}", val.arg1);
363
        assert_eq!(str_arg1, str_val);
364
        assert_eq!(val.mode, mode);
365
        val
366
    }
367

368
    fn do_parse_mult_args<T: DetectFloatType + std::fmt::Display>(
369
        val: &str, fval1: T, fval2: T, mode: DetectFloatMode,
370
    ) -> DetectFloatData<T> {
371
        let str_val = format!("{:.3}", fval1);
372
        let (_, val) = detect_parse_float::<T>(val).unwrap();
373
        let str_arg = format!("{:.3}", val.arg1);
374
        assert_eq!(str_arg, str_val);
375
        let str_val = format!("{:.3}", fval2);
376
        let str_arg = format!("{:.3}", val.arg2);
377
        assert_eq!(str_arg, str_val);
378
        assert_eq!(val.mode, mode);
379
        val
380
    }
381

382
    #[test]
383
    fn test_detect_match_valid() {
384
        let val = do_parse("= 1.264", 1.264, DetectFloatMode::DetectFloatModeEqual);
385
        assert!(detect_match_float(&val, 1.264));
386

387
        let val = do_parse("> 1.0", 1.0, DetectFloatMode::DetectFloatModeGt);
388
        assert!(detect_match_float(&val, 1.1));
389
        assert!(!detect_match_float(&val, 1.0));
390

391
        let val = do_parse(">= 1.0", 1.0, DetectFloatMode::DetectFloatModeGte);
392
        assert!(detect_match_float(&val, 1.0));
393
        assert!(detect_match_float(&val, 1.5));
394
        assert!(!detect_match_float(&val, 0.5));
395

396
        let val = do_parse("<= 1.0", 1.0, DetectFloatMode::DetectFloatModeLte);
397
        assert!(detect_match_float(&val, 1.0));
398
        assert!(detect_match_float(&val, 0.5));
399
        assert!(!detect_match_float(&val, 1.5));
400

401
        let val = do_parse("< 1.0", 1.0, DetectFloatMode::DetectFloatModeLt);
402
        assert!(detect_match_float(&val, 0.9));
403
        assert!(!detect_match_float(&val, 1.0));
404

405
        let val = do_parse("= 1.0", 1.0, DetectFloatMode::DetectFloatModeEqual);
406
        assert!(detect_match_float(&val, 1.0));
407
        assert!(!detect_match_float(&val, 0.9));
408
        assert!(!detect_match_float(&val, 1.1));
409

410
        let val = do_parse("!= 1.0", 1.0, DetectFloatMode::DetectFloatModeNe);
411
        assert!(detect_match_float(&val, 0.9));
412
        assert!(detect_match_float(&val, 1.1));
413
        assert!(!detect_match_float(&val, 1.0));
414

415
        let val = do_parse_mult_args(
416
            "37.0-42.0",
417
            37.0,
418
            42.0,
419
            DetectFloatMode::DetectFloatModeRange,
420
        );
421
        assert!(detect_match_float(&val, 37.1));
422
        assert!(detect_match_float(&val, 41.9));
423
        assert!(!detect_match_float(&val, 35.0));
424
        assert!(!detect_match_float(&val, 43.0));
425

426
        let val = do_parse_mult_args(
427
            "!37.0-42.0",
428
            37.0,
429
            42.0,
430
            DetectFloatMode::DetectFloatModeNegRg,
431
        );
432
        assert!(detect_match_float(&val, 37.0));
433
        assert!(detect_match_float(&val, 42.0));
434
        assert!(detect_match_float(&val, 35.0));
435
        assert!(detect_match_float(&val, 43.0));
436
        assert!(!detect_match_float(&val, 37.1));
437
        assert!(!detect_match_float(&val, 41.9));
438
    }
439

440
    fn do_match_test(val: &str, arg1: f64, arg1_cmp: f64, arg2: f64, mode: DetectFloatMode) {
441
        let c_string = CString::new(val).expect("CString::new failed");
442
        unsafe {
443
            let val = SCDetectF64Parse(c_string.as_ptr());
444
            let str_arg_a = format!("{:.3}", (*val).arg1);
445
            let str_arg_b = format!("{:.3}", arg1);
446
            assert_eq!(str_arg_a, str_arg_b);
447
            let str_arg_a = format!("{:.3}", (*val).arg2);
448
            let str_arg_b = format!("{:.3}", arg2);
449
            assert_eq!(str_arg_a, str_arg_b);
450

451
            assert_eq!((*val).mode, mode);
452
            assert_eq!(1, SCDetectF64Match(arg1_cmp, &*val));
453
        }
454
    }
455

456
    fn do_match_test_arg1(val: &str, arg1: f64, arg1_cmp: f64, mode: DetectFloatMode) {
457
        do_match_test(val, arg1, arg1_cmp, FloatCore::min_value(), mode);
458
    }
459

460
    fn do_parse_test(val: &str, arg1: f64, arg2: f64, mode: DetectFloatMode) {
461
        let c_string = CString::new(val).expect("CString::new failed");
462
        unsafe {
463
            let val = SCDetectF64Parse(c_string.as_ptr());
464
            let str_arg_a = format!("{:.3}", (*val).arg1);
465
            let str_arg_b = format!("{:.3}", arg1);
466
            assert_eq!(str_arg_a, str_arg_b);
467
            let str_arg_a = format!("{:.3}", (*val).arg2);
468
            let str_arg_b = format!("{:.3}", arg2);
469
            assert_eq!(str_arg_a, str_arg_b);
470

471
            assert_eq!((*val).mode, mode);
472
        }
473
    }
474

475
    fn do_parse_test_arg1(val: &str, arg1: f64, mode: DetectFloatMode) {
476
        do_parse_test(val, arg1, FloatCore::min_value(), mode);
477
    }
478

479
    #[test]
480
    fn test_ffi_detect_match_valid() {
481
        do_match_test_arg1("1.0", 1.0, 1.0, DetectFloatMode::DetectFloatModeEqual);
482
        do_match_test_arg1("> 1.0", 1.0, 1.1, DetectFloatMode::DetectFloatModeGt);
483
        do_match_test_arg1(">= 1.0", 1.0, 1.0, DetectFloatMode::DetectFloatModeGte);
484
        do_match_test_arg1("<= 1.0", 1.0, 1.0, DetectFloatMode::DetectFloatModeLte);
485
        do_match_test_arg1("< 1.0", 1.0, 0.9, DetectFloatMode::DetectFloatModeLt);
486
        do_match_test_arg1("= 1.0", 1.0, 1.0, DetectFloatMode::DetectFloatModeEqual);
487
        do_match_test_arg1("!= 1.0", 1.0, 1.1, DetectFloatMode::DetectFloatModeNe);
488
        do_match_test(
489
            "37.0-42.0",
490
            37.0,
491
            37.1,
492
            42.0,
493
            DetectFloatMode::DetectFloatModeRange,
494
        );
495
        do_match_test(
496
            "37.0-42.0",
497
            37.0,
498
            41.9,
499
            42.0,
500
            DetectFloatMode::DetectFloatModeRange,
501
        );
502
        do_match_test_arg1(
503
            ">= 4.15",
504
            4.15,
505
            4.150007324019584,
506
            DetectFloatMode::DetectFloatModeGte,
507
        );
508
        do_match_test_arg1(
509
            "> 4.15",
510
            4.15,
511
            4.150007324019584,
512
            DetectFloatMode::DetectFloatModeGt,
513
        );
514
    }
515

516
    #[test]
517
    fn test_ffi_detect_parse_valid() {
518
        do_parse_test_arg1("1.0", 1.0, DetectFloatMode::DetectFloatModeEqual);
519
        do_parse_test_arg1("> 1.0", 1.0, DetectFloatMode::DetectFloatModeGt);
520
        do_parse_test_arg1(">= 1.0", 1.0, DetectFloatMode::DetectFloatModeGte);
521
        do_parse_test_arg1("<= 1.0", 1.0, DetectFloatMode::DetectFloatModeLte);
522
        do_parse_test_arg1("< 1.0", 1.0, DetectFloatMode::DetectFloatModeLt);
523
        do_parse_test_arg1("= 1.0", 1.0, DetectFloatMode::DetectFloatModeEqual);
524
        do_parse_test_arg1("!= 1.0", 1.0, DetectFloatMode::DetectFloatModeNe);
525
        do_parse_test(
526
            "37.0-42.0",
527
            37.0,
528
            42.0,
529
            DetectFloatMode::DetectFloatModeRange,
530
        );
531
    }
532
}
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