diff --git a/experimental/Cargo.toml b/experimental/Cargo.toml index 759e3cd49..6dd617cd1 100644 --- a/experimental/Cargo.toml +++ b/experimental/Cargo.toml @@ -16,3 +16,4 @@ rasn-snmp = "0.26.2" regex = "1.11.1" serde = { version = "1.0.219", features = ["derive"] } serde_json = "1.0.140" +snafu = "0.8.5" diff --git a/experimental/src/compute/threshold.rs b/experimental/src/compute/threshold.rs index 2d55822aa..d7edd2eff 100644 --- a/experimental/src/compute/threshold.rs +++ b/experimental/src/compute/threshold.rs @@ -1,6 +1,6 @@ -use std::f64::INFINITY; - +use generic::error::Error; use log::{debug, error, info, trace, warn}; +use std::f64::INFINITY; pub struct Threshold { start: f64, @@ -8,12 +8,12 @@ pub struct Threshold { } impl Threshold { - pub fn parse(expr: &str) -> Threshold { + pub fn parse(expr: &str) -> Result { // https://nagios-plugins.org/doc/guidelines.html#THRESHOLDFORMAT let mut start: usize = 0; let mut in_number = false; - let mut start_value = -INFINITY; - let mut end_value = INFINITY; + let mut current = 0; + let mut value = [-INFINITY, INFINITY]; let mut in_range = false; for (idx, c) in expr.char_indices() { if in_number { @@ -22,7 +22,7 @@ impl Threshold { '.' | '-' | '+' | 'e' | 'E' => continue, _ => { in_number = false; - start_value = match expr[start..idx].parse() { + value[current] = match expr[start..idx].parse() { Ok(x) => x, Err(err) => { error!("parse error: {}", err); @@ -40,15 +40,19 @@ impl Threshold { in_number = true; start = idx; } + '~' => { + value[0] = -INFINITY; + } ':' => { in_range = true; + current = 1; } _ => break, } } } if in_number { - start_value = match expr[start..].parse() { + value[current] = match expr[start..].parse() { Ok(x) => x, Err(err) => { error!("parse error: {}", err); @@ -59,15 +63,24 @@ impl Threshold { /* We have noticed a ':' character, so the threshold is a range */ if in_range { - return Threshold { - start: start_value, - end: INFINITY, - }; + if value[0] > value[1] { + return Err(Error::BadThresholdRange { + start: value[0], + end: value[1], + }); + } + return Ok(Threshold { + start: value[0], + end: value[1], + }); } else { - return Threshold { + if value[0] <= 0_f64 { + return Err(Error::NegativeSimpleThreshold { value: value[0] }); + } + return Ok(Threshold { start: 0_f64, - end: start_value, - }; + end: value[0], + }); } } @@ -86,20 +99,88 @@ mod Test { fn test_parse_value() { let expr = "1.2"; let threshold = Threshold::parse(expr); - assert_eq!(threshold.start, 0_f64); - assert_eq!(threshold.end, 1.2_f64); - assert!(threshold.in_alert(2_f64)); - assert!(threshold.in_alert(-1_f64)); + match threshold { + Ok(threshold) => { + assert_eq!(threshold.start, 0_f64); + assert_eq!(threshold.end, 1.2_f64); + assert!(threshold.in_alert(2_f64)); + assert!(threshold.in_alert(-1_f64)); + } + Err(err) => { + panic!("We should not have this error here: {}", err); + } + } } #[test] fn test_parse_val_colon() { let expr = "10:"; let threshold = Threshold::parse(expr); - assert_eq!(threshold.start, 10_f64); - assert_eq!(threshold.end, INFINITY); - assert!(!threshold.in_alert(10_f64)); - assert!(!threshold.in_alert(11_f64)); - assert!(threshold.in_alert(9_f64)); + match threshold { + Ok(threshold) => { + assert_eq!(threshold.start, 10_f64); + assert_eq!(threshold.end, INFINITY); + assert!(!threshold.in_alert(10_f64)); + assert!(!threshold.in_alert(11_f64)); + assert!(threshold.in_alert(9_f64)); + } + Err(err) => { + panic!("We should not have this error here: {}", err); + } + } + } + + #[test] + fn test_parse_tilda_val() { + let expr = "~:10"; + let threshold = Threshold::parse(expr); + match threshold { + Ok(threshold) => { + assert_eq!(threshold.start, -INFINITY); + assert_eq!(threshold.end, 10_f64); + assert!(!threshold.in_alert(10_f64)); + assert!(threshold.in_alert(11_f64)); + assert!(!threshold.in_alert(9_f64)); + } + Err(err) => { + panic!("We should not have this error here: {}", err); + } + } + } + + #[test] + fn test_parse_val_val() { + let expr = "10:20"; + let threshold = Threshold::parse(expr); + match threshold { + Ok(threshold) => { + assert_eq!(threshold.start, 10_f64); + assert_eq!(threshold.end, 20_f64); + assert!(!threshold.in_alert(10_f64)); + assert!(!threshold.in_alert(11_f64)); + assert!(threshold.in_alert(9_f64)); + assert!(threshold.in_alert(21_f64)); + } + Err(err) => { + panic!("We should not have this error here: {}", err); + } + } + } + + #[test] + fn test_parse_bad_val_val() { + let expr = "30:20"; + let threshold = Threshold::parse(expr); + match threshold { + Ok(_) => { + panic!("The thrshold '{}' should not be valid", expr); + } + Err(err) => { + assert_eq!( + err.to_string(), + "The start value 30 must be less than the end value 20" + ); + } + } } } diff --git a/experimental/src/generic/error.rs b/experimental/src/generic/error.rs new file mode 100644 index 000000000..785551251 --- /dev/null +++ b/experimental/src/generic/error.rs @@ -0,0 +1,35 @@ +use snafu::prelude::*; +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.", + value, + value + ))] + NegativeSimpleThreshold { value: f64 }, + + #[snafu(display("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()))] + ReadConfiguration { source: io::Error, path: PathBuf }, + + #[snafu(display("Unable to write result to {}", path.display()))] + WriteResult { source: io::Error, path: PathBuf }, +} + +type Result = std::result::Result; + +fn process_data() -> Result<()> { + let path = "config.toml"; + let configuration = fs::read_to_string(path).context(ReadConfigurationSnafu { path })?; + let path = unpack_config(&configuration); + fs::write(&path, b"My complex calculation").context(WriteResultSnafu { path })?; + Ok(()) +} + +fn unpack_config(data: &str) -> &str { + "/some/path/that/does/not/exist" +} diff --git a/experimental/src/generic/mod.rs b/experimental/src/generic/mod.rs index 63147ae22..238a7e475 100644 --- a/experimental/src/generic/mod.rs +++ b/experimental/src/generic/mod.rs @@ -1,6 +1,8 @@ extern crate serde; extern crate serde_json; +pub mod error; + use compute::{ast::ExprResult, threshold::Threshold, Compute, Parser}; use log::{debug, trace}; use serde::Deserialize; diff --git a/experimental/src/main.rs b/experimental/src/main.rs index 3403d93b4..8af0633b5 100644 --- a/experimental/src/main.rs +++ b/experimental/src/main.rs @@ -8,6 +8,7 @@ extern crate rasn_snmp; extern crate regex; extern crate serde; extern crate serde_json; +extern crate snafu; mod compute; mod generic; @@ -18,7 +19,6 @@ use lalrpop_util::lalrpop_mod; use lexopt::Arg; use log::{debug, trace}; use serde_json::Result; -use std::collections::HashMap; use std::fs; lalrpop_mod!(grammar);