From 48302c0fec180ae7acefb5c510f30ef4eb5994ba Mon Sep 17 00:00:00 2001 From: David Boucher Date: Sat, 10 May 2025 16:28:02 +0200 Subject: [PATCH] enh(generic-snmp): threshold works almost. Some error cases are still to implement --- experimental/src/compute/threshold.rs | 138 +++++++++++++++++++++++--- experimental/src/generic/error.rs | 9 +- 2 files changed, 130 insertions(+), 17 deletions(-) diff --git a/experimental/src/compute/threshold.rs b/experimental/src/compute/threshold.rs index d7edd2eff..02f24d165 100644 --- a/experimental/src/compute/threshold.rs +++ b/experimental/src/compute/threshold.rs @@ -5,6 +5,7 @@ use std::f64::INFINITY; pub struct Threshold { start: f64, end: f64, + negation: bool, } impl Threshold { @@ -14,7 +15,8 @@ impl Threshold { let mut in_number = false; let mut current = 0; let mut value = [-INFINITY, INFINITY]; - let mut in_range = false; + let mut in_range = 0; + let mut negation = 0; for (idx, c) in expr.char_indices() { if in_number { match c { @@ -24,10 +26,7 @@ impl Threshold { in_number = false; value[current] = match expr[start..idx].parse() { Ok(x) => x, - Err(err) => { - error!("parse error: {}", err); - std::process::exit(3); - } + Err(_) => return Err(Error::BadThreshold), } } } @@ -35,7 +34,14 @@ impl Threshold { /* No else here, because we can continue the previous match */ if !in_number { match c { + '@' => { + negation += 1; + } ' ' => continue, + '-' => { + in_number = true; + start = idx; + } '0'..='9' => { in_number = true; start = idx; @@ -44,25 +50,25 @@ impl Threshold { value[0] = -INFINITY; } ':' => { - in_range = true; + in_range += 1; current = 1; } - _ => break, + _ => return Err(Error::BadThreshold), } } } + if negation > 1 { + return Err(Error::BadThreshold); + } if in_number { value[current] = match expr[start..].parse() { Ok(x) => x, - Err(err) => { - error!("parse error: {}", err); - std::process::exit(3); - } + Err(_) => return Err(Error::BadThreshold), } } /* We have noticed a ':' character, so the threshold is a range */ - if in_range { + if in_range == 1 { if value[0] > value[1] { return Err(Error::BadThresholdRange { start: value[0], @@ -72,7 +78,10 @@ impl Threshold { return Ok(Threshold { start: value[0], end: value[1], + negation: negation > 0, }); + } else if in_range > 1 { + return Err(Error::BadThreshold); } else { if value[0] <= 0_f64 { return Err(Error::NegativeSimpleThreshold { value: value[0] }); @@ -80,15 +89,24 @@ impl Threshold { return Ok(Threshold { start: 0_f64, end: value[0], + negation: negation > 0, }); } } fn in_alert(&self, value: f64) -> bool { if value < self.start || value > self.end { + if self.negation { + return false; + } else { return true; + } + } + if self.negation { + true + } else { + false } - false } } @@ -178,9 +196,101 @@ mod Test { Err(err) => { assert_eq!( err.to_string(), - "The start value 30 must be less than the end value 20" + "Threshold: The start value 30 must be less than the end value 20" ); } } } + + #[test] + fn test_bad_range() { + let expr = "10:20:30"; + let threshold = Threshold::parse(expr); + match threshold { + Ok(_) => { + panic!("The threshold '{}' should not be valid", expr); + } + Err(err) => { + assert_eq!( + err.to_string(), + "Threshold: Threshold not of the form '[@]start:end'" + ); + } + } + } + + #[test] + fn test_threshold_foobar() { + let expr = "foobar"; + let threshold = Threshold::parse(expr); + match threshold { + Ok(_) => { + panic!("The threshold '{}' should not be valid", expr); + } + Err(err) => { + assert_eq!(err.to_string(), + "Threshold: Threshold not of the form '[@]start:end'"); + } + } + } + + #[test] + fn test_threshold_12foo() { + let expr = "12foo"; + let threshold = Threshold::parse(expr); + match threshold { + Ok(_) => { + panic!("The threshold '{}' should not be valid", expr); + } + Err(err) => { + assert_eq!(err.to_string(), + "Threshold: Threshold not of the form '[@]start:end'"); + } + } + } + + #[test] + fn test_threshold_bad_number() { + let expr = "12e.1.e28"; + let threshold = Threshold::parse(expr); + match threshold { + Ok(_) => { + panic!("The threshold '{}' should not be valid", expr); + } + Err(err) => { + assert_eq!(err.to_string(), + "Threshold: Threshold not of the form '[@]start:end'"); + } + } + } + + #[test] + fn test_threshold_bad_number_and_range() { + let expr = "12e.1.e28:1.2.3.4"; + let threshold = Threshold::parse(expr); + match threshold { + Ok(_) => { + panic!("The threshold '{}' should not be valid", expr); + } + Err(err) => { + assert_eq!(err.to_string(), + "Threshold: Threshold not of the form '[@]start:end'"); + } + } + } + + #[test] + fn test_threshold_not_allowed_negative() { + let expr = "-12"; + let threshold = Threshold::parse(expr); + match threshold { + Ok(_) => { + panic!("The threshold '{}' should not be valid", expr); + } + Err(err) => { + assert_eq!(err.to_string(), + "Threshold: This syntax is a shortcut of '0:-12', so -12 must be greater than 0."); + } + } + } } diff --git a/experimental/src/generic/error.rs b/experimental/src/generic/error.rs index 785551251..15e873a41 100644 --- a/experimental/src/generic/error.rs +++ b/experimental/src/generic/error.rs @@ -4,18 +4,21 @@ use std::{fs, io, path::PathBuf}; #[derive(Debug, Snafu)] pub enum Error { #[snafu(display( - "This syntax is a shortcut of '0:{}', so {} must be greater than 0.", + "Threshold: This syntax is a shortcut of '0:{}', so {} must be greater than 0.", value, value ))] NegativeSimpleThreshold { value: f64 }, - #[snafu(display("The start value {} must be less than the end value {}", start, end))] + #[snafu(display("Threshold: The start value {} must be less than the end value {}", start, end))] BadThresholdRange { start: f64, end: f64 }, - #[snafu(display("Unable to read configuration from {}", path.display()))] + #[snafu(display("Threshold: Unable to read configuration from {}", path.display()))] ReadConfiguration { source: io::Error, path: PathBuf }, + #[snafu(display("Threshold: Threshold not of the form '[@]start:end'"))] + BadThreshold, + #[snafu(display("Unable to write result to {}", path.display()))] WriteResult { source: io::Error, path: PathBuf }, }