From bc64e1e2ae373c614d09dd7a2634fa0a5e39ccc3 Mon Sep 17 00:00:00 2001 From: David Boucher Date: Sun, 4 May 2025 19:07:57 +0200 Subject: [PATCH] enh(generic-snmp): bugs fixed and unit tests added --- experimental/perl/with-ffi.pl | 34 --- experimental/src/compute/ast.rs | 154 +++++++--- experimental/src/compute/lexer.rs | 10 +- experimental/src/compute/mod.rs | 469 +++++++++++++++++++++++++++--- experimental/src/generic/mod.rs | 26 +- experimental/src/main.rs | 58 ++-- experimental/src/snmp/mod.rs | 18 +- 7 files changed, 613 insertions(+), 156 deletions(-) delete mode 100755 experimental/perl/with-ffi.pl diff --git a/experimental/perl/with-ffi.pl b/experimental/perl/with-ffi.pl deleted file mode 100755 index e1d5e4450..000000000 --- a/experimental/perl/with-ffi.pl +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/perl - -use strict; -use warnings; -use lib '/home/david/perl5/lib/perl5'; -use FFI::Platypus 2.00; -my $ffi = FFI::Platypus->new(api => 2, lang => => 'Rust'); - -$ffi->lib( - '../target/debug/libconn.so'); - -### Types ### -$ffi->type('object(SnmpResult)' => 'snmpresult_t'); -$ffi->type('object(SnmpVariable)' => 'snmpvariable_t'); - -### Global functions ### -$ffi->attach(snmp_get => ['string', 'string', 'string'] => 'snmpresult_t'); -$ffi->attach(snmp_walk => ['string', 'string'] => 'snmpresult_t'); - -$ffi->attach(snmpresult_variables_count => ['snmpresult_t'] => 'usize'); -$ffi->attach(snmpresult_get_variable => ['snmpresult_t', 'usize'] => 'snmpvariable_t'); -$ffi->attach( snmpresult_DESTROY => [ 'snmpresult_t' ] ); - -$ffi->attach(snmpvariable_get_name => ['snmpvariable_t'] => 'string'); -$ffi->attach(snmpvariable_get_value => ['snmpvariable_t'] => 'string'); -$ffi->attach( snmpvariable_DESTROY => [ 'snmpvariable_t' ] ); - -### Main program ### - -my $result = snmp_walk('127.0.0.1:161', '1.3.6.1.2.1.25.3.3.1.2'); -for (my $i = 0; $i < snmpresult_variables_count($result); $i++) { - my $variable = snmpresult_get_variable($result, $i); - print snmpvariable_get_name($variable) . " => " . snmpvariable_get_value($variable) . "\n"; -} diff --git a/experimental/src/compute/ast.rs b/experimental/src/compute/ast.rs index 7b3045ce3..c26c89317 100644 --- a/experimental/src/compute/ast.rs +++ b/experimental/src/compute/ast.rs @@ -1,4 +1,4 @@ -use log::{debug, info, trace}; +use log::{debug, info, trace, warn}; use snmp::SnmpResult; use std::str; @@ -23,8 +23,10 @@ pub enum Func { #[derive(Debug)] pub enum ExprResult { Vector(Vec), - Scalar(f64), + Number(f64), StrVector(Vec), + Str(String), + Empty, } impl std::ops::Add for ExprResult { @@ -32,7 +34,7 @@ impl std::ops::Add for ExprResult { fn add(self, other: Self) -> Self::Output { match (self, other) { - (ExprResult::Scalar(a), ExprResult::Scalar(b)) => ExprResult::Scalar(a + b), + (ExprResult::Number(a), ExprResult::Number(b)) => ExprResult::Number(a + b), (ExprResult::Vector(a), ExprResult::Vector(b)) => { let len_a = a.len(); let len_b = b.len(); @@ -43,14 +45,18 @@ impl std::ops::Add for ExprResult { } ExprResult::Vector(result) } else { + warn!( + "Trying to add to arrays of different lengths: {} and {}", + len_a, len_b + ); if len_a > len_b { - let mut result = a.clone(); + let mut result = a; for (idx, value) in b.iter().enumerate() { result[idx] += value; } ExprResult::Vector(result) } else { - let mut result = b.clone(); + let mut result = b; for (idx, value) in a.iter().enumerate() { result[idx] += value; } @@ -58,15 +64,15 @@ impl std::ops::Add for ExprResult { } } } - (ExprResult::Scalar(a), ExprResult::Vector(b)) => { - let mut result = b.clone(); + (ExprResult::Number(a), ExprResult::Vector(b)) => { + let mut result = b; for value in result.iter_mut() { *value += a; } ExprResult::Vector(result) } - (ExprResult::Vector(a), ExprResult::Scalar(b)) => { - let mut result = a.clone(); + (ExprResult::Vector(a), ExprResult::Number(b)) => { + let mut result = a; for value in result.iter_mut() { *value += b; } @@ -82,7 +88,7 @@ impl std::ops::Sub for ExprResult { fn sub(self, other: Self) -> Self::Output { match (self, other) { - (ExprResult::Scalar(a), ExprResult::Scalar(b)) => ExprResult::Scalar(a - b), + (ExprResult::Number(a), ExprResult::Number(b)) => ExprResult::Number(a - b), (ExprResult::Vector(a), ExprResult::Vector(b)) => { let len_a = a.len(); let len_b = b.len(); @@ -93,30 +99,38 @@ impl std::ops::Sub for ExprResult { } ExprResult::Vector(result) } else { + warn!( + "Trying to subtract arrays of different lengths: {} and {}", + len_a, len_b + ); if len_a > len_b { - let mut result = a.clone(); + let mut result = a; for (idx, value) in b.iter().enumerate() { result[idx] -= value; } ExprResult::Vector(result) } else { - let mut result = b.clone(); - for (idx, value) in a.iter().enumerate() { - result[idx] -= value; + let mut result = b; + for (idx, value) in result.iter_mut().enumerate() { + if idx < a.len() { + *value = a[idx] - *value; + } else { + *value = -*value; + } } ExprResult::Vector(result) } } } - (ExprResult::Scalar(a), ExprResult::Vector(b)) => { + (ExprResult::Number(a), ExprResult::Vector(b)) => { let mut result = vec![0_f64; b.len()]; for (idx, value) in result.iter_mut().enumerate() { *value = a - b[idx]; } ExprResult::Vector(result) } - (ExprResult::Vector(a), ExprResult::Scalar(b)) => { - let mut result = a.clone(); + (ExprResult::Vector(a), ExprResult::Number(b)) => { + let mut result = a; for value in result.iter_mut() { *value -= b; } @@ -132,7 +146,7 @@ impl std::ops::Mul for ExprResult { fn mul(self, other: Self) -> Self::Output { match (self, other) { - (ExprResult::Scalar(a), ExprResult::Scalar(b)) => ExprResult::Scalar(a * b), + (ExprResult::Number(a), ExprResult::Number(b)) => ExprResult::Number(a * b), (ExprResult::Vector(a), ExprResult::Vector(b)) => { let len_a = a.len(); let len_b = b.len(); @@ -143,14 +157,18 @@ impl std::ops::Mul for ExprResult { } ExprResult::Vector(result) } else { + warn!( + "Trying to multiply arrays of different lengths: {} and {}", + len_a, len_b + ); if len_a > len_b { - let mut result = a.clone(); + let mut result = a; for (idx, value) in b.iter().enumerate() { result[idx] *= value; } ExprResult::Vector(result) } else { - let mut result = b.clone(); + let mut result = b; for (idx, value) in a.iter().enumerate() { result[idx] *= value; } @@ -158,14 +176,14 @@ impl std::ops::Mul for ExprResult { } } } - (ExprResult::Scalar(a), ExprResult::Vector(b)) => { + (ExprResult::Number(a), ExprResult::Vector(b)) => { let mut result = b.clone(); for value in result.iter_mut() { *value *= a; } ExprResult::Vector(result) } - (ExprResult::Vector(a), ExprResult::Scalar(b)) => { + (ExprResult::Vector(a), ExprResult::Number(b)) => { let mut result = a.clone(); for value in result.iter_mut() { *value *= b; @@ -182,7 +200,7 @@ impl std::ops::Div for ExprResult { fn div(self, other: Self) -> Self::Output { match (self, other) { - (ExprResult::Scalar(a), ExprResult::Scalar(b)) => ExprResult::Scalar(a / b), + (ExprResult::Number(a), ExprResult::Number(b)) => ExprResult::Number(a / b), (ExprResult::Vector(a), ExprResult::Vector(b)) => { let len_a = a.len(); let len_b = b.len(); @@ -193,30 +211,38 @@ impl std::ops::Div for ExprResult { } ExprResult::Vector(result) } else { + warn!( + "Trying to divide arrays of different lengths: {} and {}", + len_a, len_b + ); if len_a > len_b { - let mut result = a.clone(); + let mut result = a; for (idx, value) in b.iter().enumerate() { result[idx] /= value; } ExprResult::Vector(result) } else { - let mut result = b.clone(); - for (idx, value) in a.iter().enumerate() { - result[idx] /= value; + let mut result = b; + for (idx, value) in result.iter_mut().enumerate() { + if idx < a.len() { + *value = a[idx] / *value; + } else { + *value = 1_f64 / *value; + } } ExprResult::Vector(result) } } } - (ExprResult::Scalar(a), ExprResult::Vector(b)) => { + (ExprResult::Number(a), ExprResult::Vector(b)) => { let mut result = vec![0_f64; b.len()]; for (idx, value) in result.iter_mut().enumerate() { *value = a / b[idx]; } ExprResult::Vector(result) } - (ExprResult::Vector(a), ExprResult::Scalar(b)) => { - let mut result = a.clone(); + (ExprResult::Vector(a), ExprResult::Number(b)) => { + let mut result = a; for value in result.iter_mut() { *value /= b; } @@ -227,10 +253,60 @@ impl std::ops::Div for ExprResult { } } +impl ExprResult { + pub fn join(&mut self, other: &ExprResult) { + match self { + ExprResult::Empty => match other { + ExprResult::StrVector(vv) => { + *self = ExprResult::StrVector(vv.clone()); + } + ExprResult::Str(s) => { + *self = ExprResult::Str(s.clone()); + } + _ => panic!("Unable to join objects others than strings"), + }, + ExprResult::StrVector(v) => match other { + ExprResult::StrVector(vv) => { + if v.len() != vv.len() { + warn!( + "Trying to join arrays of different lengths: {} and {}", + v.len(), + vv.len() + ); + if v.len() < vv.len() { + v.resize(vv.len(), "".to_string()); + } + } + for (key, value) in v.iter_mut().enumerate() { + value.push_str(vv.get(key).unwrap_or(&"".to_string())); + } + } + ExprResult::Str(s) => { + *v = v.iter().map(|a| format!("{}{}", a, s)).collect(); + } + _ => panic!("Unable to join objects others than strings"), + }, + ExprResult::Str(s) => match other { + ExprResult::StrVector(vv) => { + *self = + ExprResult::StrVector(vv.iter().map(|a| format!("{}{}", s, a)).collect()); + } + ExprResult::Str(ss) => { + *s = format!("{}{}", s, ss); + } + _ => panic!("Unable to join objects others than strings"), + }, + _ => { + panic!("Unable to join objects that are not strings"); + } + } + } +} + impl<'input> Expr<'input> { pub fn eval(&self, collect: &Vec) -> ExprResult { match self { - Expr::Number(n) => ExprResult::Scalar(*n), + Expr::Number(n) => ExprResult::Number(*n), Expr::Id(key) => { let k = str::from_utf8(key).unwrap(); for result in collect { @@ -239,7 +315,7 @@ impl<'input> Expr<'input> { ExprResult::Vector(n) => { if n.len() == 1 { info!("ID '{}' has value {}", k, n[0]); - return ExprResult::Scalar(n[0]); + return ExprResult::Number(n[0]); } else { info!("ID '{}' has value {:?}", k, n); return ExprResult::Vector(n.clone()); @@ -250,7 +326,7 @@ impl<'input> Expr<'input> { None => continue, } } - ExprResult::Scalar(0.0) + ExprResult::Number(0.0) } Expr::OpPlus(left, right) => left.eval(collect) + right.eval(collect), Expr::OpMinus(left, right) => left.eval(collect) - right.eval(collect), @@ -260,26 +336,26 @@ impl<'input> Expr<'input> { let v = expr.eval(collect); match func { Func::Average => match v { - ExprResult::Scalar(n) => ExprResult::Scalar(n), + ExprResult::Number(n) => ExprResult::Number(n), ExprResult::Vector(v) => { let sum = v.iter().sum::(); - ExprResult::Scalar(sum / v.len() as f64) + ExprResult::Number(sum / v.len() as f64) } _ => panic!("Invalid operation"), }, Func::Min => match v { - ExprResult::Scalar(n) => ExprResult::Scalar(n), + ExprResult::Number(n) => ExprResult::Number(n), ExprResult::Vector(v) => { let min = v.iter().cloned().fold(f64::INFINITY, f64::min); - ExprResult::Scalar(min) + ExprResult::Number(min) } _ => panic!("Invalid operation"), }, Func::Max => match v { - ExprResult::Scalar(n) => ExprResult::Scalar(n), + ExprResult::Number(n) => ExprResult::Number(n), ExprResult::Vector(v) => { let max = v.iter().cloned().fold(f64::NEG_INFINITY, f64::max); - ExprResult::Scalar(max) + ExprResult::Number(max) } _ => panic!("Invalid operation"), }, diff --git a/experimental/src/compute/lexer.rs b/experimental/src/compute/lexer.rs index 6e6876c8f..4585e138f 100644 --- a/experimental/src/compute/lexer.rs +++ b/experimental/src/compute/lexer.rs @@ -186,13 +186,13 @@ mod Test { #[test] fn test_lexer_num_op() { init(); - let input = "1+2*3"; + let input = "1 +2*3"; let mut lexer = Lexer::new(input); assert_eq!(lexer.next(), Some(Ok((0, Tok::Num(1_f64), 1)))); - assert_eq!(lexer.next(), Some(Ok((1, Tok::OpPlus, 2)))); - assert_eq!(lexer.next(), Some(Ok((2, Tok::Num(2_f64), 3)))); - assert_eq!(lexer.next(), Some(Ok((3, Tok::OpStar, 4)))); - assert_eq!(lexer.next(), Some(Ok((4, Tok::Num(3_f64), 5)))); + assert_eq!(lexer.next(), Some(Ok((2, Tok::OpPlus, 3)))); + assert_eq!(lexer.next(), Some(Ok((3, Tok::Num(2_f64), 4)))); + assert_eq!(lexer.next(), Some(Ok((4, Tok::OpStar, 5)))); + assert_eq!(lexer.next(), Some(Ok((5, Tok::Num(3_f64), 6)))); assert_eq!(lexer.next(), None); } } diff --git a/experimental/src/compute/mod.rs b/experimental/src/compute/mod.rs index c8814a03f..df9426f59 100644 --- a/experimental/src/compute/mod.rs +++ b/experimental/src/compute/mod.rs @@ -4,7 +4,7 @@ pub mod lexer; use self::ast::ExprResult; use self::lexer::{LexicalError, Tok}; use lalrpop_util::{lalrpop_mod, ParseError}; -use log::{trace, debug}; +use log::{debug, trace}; use regex::Regex; use serde::Deserialize; use snmp::SnmpResult; @@ -64,28 +64,28 @@ impl<'a> Parser<'a> { ) -> Result, LexicalError>> { let re = Regex::new(r"\{[a-zA-Z_][a-zA-Z0-9_.]*\}").unwrap(); let mut suffix = expr; - let mut result: ExprResult = ExprResult::StrVector(vec![]); + let mut result: ExprResult = ExprResult::Empty; loop { let found = re.find(suffix); if let Some(m) = found { let start = m.start(); let end = m.end(); - debug!( - "Identifier '{}' found in expr '{}'", - &expr[start + 1..end - 1], - expr - ); - let prefix = &expr[0..start]; - suffix = &expr[end..]; - let mut result = vec![]; + if start > 0 { + result.join(&ExprResult::Str(suffix[0..start].to_string())); + } for snmp_result in self.collect { - if let Some(v) = snmp_result.items.get(&expr[start + 1..end - 1]) { - result = join_str_expr(prefix, v); + if let Some(v) = snmp_result.items.get(&suffix[start + 1..end - 1]) { + result.join(v); break; } } - trace!("Result string {:?}", result); + debug!( + "Evaluation as string of expression '{}' returns {:?}", + suffix, result + ); + suffix = &suffix[end..]; } else { + result.join(&ExprResult::Str(suffix.to_string())); break; } } @@ -125,7 +125,7 @@ mod Test { let snmp_result = vec![]; let res = res.unwrap().eval(&snmp_result); match res { - ExprResult::Scalar(n) => assert!(n == 123_f64), + ExprResult::Number(n) => assert!(n == 123_f64), _ => panic!("Expected a scalar value"), } let lexer = lexer::Lexer::new("123"); @@ -144,7 +144,7 @@ mod Test { let snmp_result = vec![]; let res = res.unwrap().eval(&snmp_result); match res { - ExprResult::Scalar(n) => assert!(n == 3_f64), + ExprResult::Number(n) => assert!(n == 3_f64), _ => panic!("Expected a scalar value"), } @@ -153,7 +153,7 @@ mod Test { assert!(res.is_ok()); let res = res.unwrap().eval(&snmp_result); match res { - ExprResult::Scalar(n) => assert!(n == 0_f64), + ExprResult::Number(n) => assert!(n == 0_f64), _ => panic!("Expected a scalar value"), } @@ -162,7 +162,7 @@ mod Test { assert!(res.is_ok()); let res = res.unwrap().eval(&snmp_result); match res { - ExprResult::Scalar(n) => assert!(n == 2_f64), + ExprResult::Number(n) => assert!(n == 2_f64), _ => panic!("Expected a scalar value"), } @@ -171,7 +171,7 @@ mod Test { assert!(res.is_ok()); let res = res.unwrap().eval(&snmp_result); match res { - ExprResult::Scalar(n) => assert!(n == -4_f64), + ExprResult::Number(n) => assert!(n == -4_f64), _ => panic!("Expected a scalar value"), } @@ -180,7 +180,7 @@ mod Test { assert!(res.is_ok()); let res = res.unwrap().eval(&snmp_result); match res { - ExprResult::Scalar(n) => assert!(n == -8_f64), + ExprResult::Number(n) => assert!(n == -8_f64), _ => panic!("Expected a scalar value"), } } @@ -194,7 +194,7 @@ mod Test { let snmp_result = vec![]; let res = res.unwrap().eval(&snmp_result); match res { - ExprResult::Scalar(n) => assert!(n == 6_f64), + ExprResult::Number(n) => assert!(n == 6_f64), _ => panic!("Expected a scalar value"), } @@ -203,7 +203,7 @@ mod Test { assert!(res.is_ok()); let res = res.unwrap().eval(&snmp_result); match res { - ExprResult::Scalar(n) => assert!(n == 7_f64), + ExprResult::Number(n) => assert!(n == 7_f64), _ => panic!("Expected a scalar value"), } @@ -212,7 +212,7 @@ mod Test { assert!(res.is_ok()); let res = res.unwrap().eval(&snmp_result); match res { - ExprResult::Scalar(n) => assert!(n == 9_f64), + ExprResult::Number(n) => assert!(n == 9_f64), _ => panic!("Expected a scalar value"), } @@ -221,7 +221,7 @@ mod Test { assert!(res.is_ok()); let res = res.unwrap().eval(&snmp_result); match res { - ExprResult::Scalar(n) => assert!(n == 24_f64), + ExprResult::Number(n) => assert!(n == 24_f64), _ => panic!("Expected a scalar value"), } @@ -230,7 +230,7 @@ mod Test { assert!(res.is_ok()); let res = res.unwrap().eval(&snmp_result); match res { - ExprResult::Scalar(n) => assert!(n == 3_f64), + ExprResult::Number(n) => assert!(n == 3_f64), _ => panic!("Expected a scalar value"), } @@ -246,7 +246,7 @@ mod Test { let snmp_result = vec![]; let res = res.unwrap().eval(&snmp_result); match res { - ExprResult::Scalar(n) => assert!(n == 4_f64), + ExprResult::Number(n) => assert!(n == 4_f64), _ => panic!("Expected a scalar value"), } } @@ -262,7 +262,7 @@ mod Test { let snmp_result = vec![SnmpResult::new(items)]; let res = res.unwrap().eval(&snmp_result); match res { - ExprResult::Scalar(n) => assert!(n == 2_f64), + ExprResult::Number(n) => assert!(n == 2_f64), _ => panic!("Expected a scalar value"), } @@ -271,6 +271,370 @@ mod Test { assert!(res.is_err()); } + #[test] + fn vec_sum() { + init(); + let lexer = lexer::Lexer::new("{abc} + {def}"); + let res = grammar::ExprParser::new().parse(lexer); + assert!(res.is_ok()); + println!("{:?}", res); + let items = HashMap::from([ + ("abc".to_string(), ExprResult::Vector(vec![1_f64, 2_f64])), + ("def".to_string(), ExprResult::Vector(vec![3_f64, 4_f64])), + ( + "ghi".to_string(), + ExprResult::Vector(vec![5_f64, 6_f64, 7_f64]), + ), + ]); + let snmp_result = vec![SnmpResult::new(items)]; + let res = res.unwrap().eval(&snmp_result); + match res { + ExprResult::Vector(v) => assert!(v == vec![4_f64, 6_f64]), + _ => panic!("Expected a vector value"), + } + + let lexer = lexer::Lexer::new("abc + 1"); + let res = grammar::ExprParser::new().parse(lexer); + assert!(res.is_err()); + } + + #[test] + fn vec_sum_diff_len() { + let lexer = lexer::Lexer::new("{abc} + {def} + {ghi}"); + let res = grammar::ExprParser::new().parse(lexer); + assert!(res.is_ok()); + debug!("{:?}", res); + let items = HashMap::from([ + ( + "abc".to_string(), + ExprResult::Vector(vec![1_f64, 2_f64, 5_f64]), + ), + ("def".to_string(), ExprResult::Vector(vec![3_f64, 4_f64])), + ( + "ghi".to_string(), + ExprResult::Vector(vec![5_f64, 6_f64, 7_f64, 8_f64]), + ), + ]); + let snmp_result = vec![SnmpResult::new(items)]; + let res = res.unwrap().eval(&snmp_result); + debug!("{:?}", res); + match res { + ExprResult::Vector(v) => assert!(v == vec![9_f64, 12_f64, 12_f64, 8_f64]), + _ => panic!("Expected a vector value"), + } + } + + #[test] + fn vec_scalar_sum() { + let lexer = lexer::Lexer::new("{abc} + 5"); + let res = grammar::ExprParser::new().parse(lexer); + assert!(res.is_ok()); + debug!("{:?}", res); + let items = HashMap::from([( + "abc".to_string(), + ExprResult::Vector(vec![1_f64, 2_f64, 5_f64]), + )]); + let snmp_result = vec![SnmpResult::new(items)]; + let res = res.unwrap().eval(&snmp_result); + debug!("{:?}", res); + match res { + ExprResult::Vector(v) => assert!(v == vec![6_f64, 7_f64, 10_f64]), + _ => panic!("Expected a vector value"), + } + } + + #[test] + fn scalar_vec_sum() { + let lexer = lexer::Lexer::new("18 + {abc}"); + let res = grammar::ExprParser::new().parse(lexer); + assert!(res.is_ok()); + debug!("{:?}", res); + let items = HashMap::from([( + "abc".to_string(), + ExprResult::Vector(vec![1_f64, 2_f64, 5_f64]), + )]); + let snmp_result = vec![SnmpResult::new(items)]; + let res = res.unwrap().eval(&snmp_result); + debug!("{:?}", res); + match res { + ExprResult::Vector(v) => assert!(v == vec![19_f64, 20_f64, 23_f64]), + _ => panic!("Expected a vector value"), + } + } + + #[test] + fn vec_sub() { + init(); + let lexer = lexer::Lexer::new("{abc} - {def}"); + let res = grammar::ExprParser::new().parse(lexer); + assert!(res.is_ok()); + println!("{:?}", res); + let items = HashMap::from([ + ("abc".to_string(), ExprResult::Vector(vec![1_f64, 3_f64])), + ("def".to_string(), ExprResult::Vector(vec![3_f64, 4_f64])), + ( + "ghi".to_string(), + ExprResult::Vector(vec![5_f64, 6_f64, 7_f64]), + ), + ]); + let snmp_result = vec![SnmpResult::new(items)]; + let res = res.unwrap().eval(&snmp_result); + match res { + ExprResult::Vector(v) => assert!(v == vec![-2_f64, -1_f64]), + _ => panic!("Expected a vector value"), + } + + let lexer = lexer::Lexer::new("abc + 1"); + let res = grammar::ExprParser::new().parse(lexer); + assert!(res.is_err()); + } + + #[test] + fn vec_sub_diff_len() { + let lexer = lexer::Lexer::new("{abc} - {def} - {ghi}"); + let res = grammar::ExprParser::new().parse(lexer); + assert!(res.is_ok()); + debug!("{:?}", res); + let items = HashMap::from([ + ( + "abc".to_string(), + ExprResult::Vector(vec![1_f64, 2_f64, 5_f64]), + ), + ("def".to_string(), ExprResult::Vector(vec![3_f64, 4_f64])), + ( + "ghi".to_string(), + ExprResult::Vector(vec![5_f64, 6_f64, 7_f64, 8_f64]), + ), + ]); + let snmp_result = vec![SnmpResult::new(items)]; + let res = res.unwrap().eval(&snmp_result); + debug!("{:?}", res); + match res { + ExprResult::Vector(v) => assert!(v == vec![-7_f64, -8_f64, -2_f64, -8_f64]), + _ => panic!("Expected a vector value"), + } + } + + #[test] + fn scalar_vec_sub() { + let lexer = lexer::Lexer::new("18 - {abc}"); + let res = grammar::ExprParser::new().parse(lexer); + assert!(res.is_ok()); + debug!("{:?}", res); + let items = HashMap::from([( + "abc".to_string(), + ExprResult::Vector(vec![1_f64, 2_f64, 5_f64]), + )]); + let snmp_result = vec![SnmpResult::new(items)]; + let res = res.unwrap().eval(&snmp_result); + debug!("{:?}", res); + match res { + ExprResult::Vector(v) => assert!(v == vec![17_f64, 16_f64, 13_f64]), + _ => panic!("Expected a vector value"), + } + } + + #[test] + fn vec_scalar_sub() { + let lexer = lexer::Lexer::new("{abc} - 19.2"); + let res = grammar::ExprParser::new().parse(lexer); + assert!(res.is_ok()); + debug!("{:?}", res); + let items = HashMap::from([( + "abc".to_string(), + ExprResult::Vector(vec![1_f64, 2_f64, 5_f64]), + )]); + let snmp_result = vec![SnmpResult::new(items)]; + let res = res.unwrap().eval(&snmp_result); + debug!("{:?}", res); + match res { + ExprResult::Vector(v) => assert!(v == vec![-18.2_f64, -17.2_f64, -14.2_f64]), + _ => panic!("Expected a vector value"), + } + } + + #[test] + fn vec_mul() { + init(); + let lexer = lexer::Lexer::new("{abc} * {def}"); + let res = grammar::ExprParser::new().parse(lexer); + assert!(res.is_ok()); + println!("{:?}", res); + let items = HashMap::from([ + ("abc".to_string(), ExprResult::Vector(vec![1_f64, 3_f64])), + ("def".to_string(), ExprResult::Vector(vec![3_f64, 4_f64])), + ( + "ghi".to_string(), + ExprResult::Vector(vec![5_f64, 6_f64, 7_f64]), + ), + ]); + let snmp_result = vec![SnmpResult::new(items)]; + let res = res.unwrap().eval(&snmp_result); + match res { + ExprResult::Vector(v) => assert!(v == vec![3_f64, 12_f64]), + _ => panic!("Expected a vector value"), + } + + let lexer = lexer::Lexer::new("abc * 1"); + let res = grammar::ExprParser::new().parse(lexer); + assert!(res.is_err()); + } + + #[test] + fn vec_mul_diff_len() { + let lexer = lexer::Lexer::new("{abc} * {def} * {ghi}"); + let res = grammar::ExprParser::new().parse(lexer); + assert!(res.is_ok()); + debug!("{:?}", res); + let items = HashMap::from([ + ( + "abc".to_string(), + ExprResult::Vector(vec![1_f64, 2_f64, 5_f64]), + ), + ("def".to_string(), ExprResult::Vector(vec![3_f64, 4_f64])), + ( + "ghi".to_string(), + ExprResult::Vector(vec![5_f64, 6_f64, 7_f64, 8_f64]), + ), + ]); + let snmp_result = vec![SnmpResult::new(items)]; + let res = res.unwrap().eval(&snmp_result); + debug!("{:?}", res); + match res { + ExprResult::Vector(v) => assert!(v == vec![15_f64, 48_f64, 35_f64, 8_f64]), + _ => panic!("Expected a vector value"), + } + } + + #[test] + fn scalar_vec_mul() { + let lexer = lexer::Lexer::new("18 * {abc}"); + let res = grammar::ExprParser::new().parse(lexer); + assert!(res.is_ok()); + debug!("{:?}", res); + let items = HashMap::from([( + "abc".to_string(), + ExprResult::Vector(vec![1_f64, 2_f64, 5_f64]), + )]); + let snmp_result = vec![SnmpResult::new(items)]; + let res = res.unwrap().eval(&snmp_result); + debug!("{:?}", res); + match res { + ExprResult::Vector(v) => assert!(v == vec![18_f64, 36_f64, 90_f64]), + _ => panic!("Expected a vector value"), + } + } + + #[test] + fn vec_scalar_mul() { + let lexer = lexer::Lexer::new("{abc} * 4"); + let res = grammar::ExprParser::new().parse(lexer); + assert!(res.is_ok()); + debug!("{:?}", res); + let items = HashMap::from([( + "abc".to_string(), + ExprResult::Vector(vec![1_f64, 2_f64, 5_f64]), + )]); + let snmp_result = vec![SnmpResult::new(items)]; + let res = res.unwrap().eval(&snmp_result); + debug!("{:?}", res); + match res { + ExprResult::Vector(v) => assert!(v == vec![4_f64, 8_f64, 20_f64]), + _ => panic!("Expected a vector value"), + } + } + + #[test] + fn vec_div() { + init(); + let lexer = lexer::Lexer::new("{abc} / {def}"); + let res = grammar::ExprParser::new().parse(lexer); + assert!(res.is_ok()); + println!("{:?}", res); + let items = HashMap::from([ + ("abc".to_string(), ExprResult::Vector(vec![3_f64, 12_f64])), + ("def".to_string(), ExprResult::Vector(vec![1_f64, 3_f64])), + ( + "ghi".to_string(), + ExprResult::Vector(vec![5_f64, 6_f64, 7_f64]), + ), + ]); + let snmp_result = vec![SnmpResult::new(items)]; + let res = res.unwrap().eval(&snmp_result); + match res { + ExprResult::Vector(v) => assert!(v == vec![3_f64, 4_f64]), + _ => panic!("Expected a vector value"), + } + + let lexer = lexer::Lexer::new("abc * 1"); + let res = grammar::ExprParser::new().parse(lexer); + assert!(res.is_err()); + } + + #[test] + fn vec_div_diff_len() { + let lexer = lexer::Lexer::new("{abc} / {def} / {ghi}"); + let res = grammar::ExprParser::new().parse(lexer); + assert!(res.is_ok()); + debug!("{:?}", res); + let items = HashMap::from([ + ( + "abc".to_string(), + ExprResult::Vector(vec![12_f64, 22_f64, 15_f64]), + ), + ("def".to_string(), ExprResult::Vector(vec![3_f64, 11_f64])), + ( + "ghi".to_string(), + ExprResult::Vector(vec![2_f64, 2_f64, 3_f64, 8_f64]), + ), + ]); + let snmp_result = vec![SnmpResult::new(items)]; + let res = res.unwrap().eval(&snmp_result); + debug!("{:?}", res); + match res { + ExprResult::Vector(v) => assert!(v == vec![2_f64, 1_f64, 5_f64, 0.125_f64]), + _ => panic!("Expected a vector value"), + } + } + + #[test] + fn scalar_vec_div() { + let lexer = lexer::Lexer::new("18 / {abc}"); + let res = grammar::ExprParser::new().parse(lexer); + assert!(res.is_ok()); + debug!("{:?}", res); + let items = HashMap::from([( + "abc".to_string(), + ExprResult::Vector(vec![1_f64, 2_f64, 5_f64]), + )]); + let snmp_result = vec![SnmpResult::new(items)]; + let res = res.unwrap().eval(&snmp_result); + debug!("{:?}", res); + match res { + ExprResult::Vector(v) => assert!(v == vec![18_f64, 9_f64, 3.6_f64]), + _ => panic!("Expected a vector value"), + } + } + + #[test] + fn vec_scalar_div() { + let lexer = lexer::Lexer::new("{abc} / 4"); + let res = grammar::ExprParser::new().parse(lexer); + assert!(res.is_ok()); + debug!("{:?}", res); + let items = HashMap::from([( + "abc".to_string(), + ExprResult::Vector(vec![1_f64, 2_f64, 5_f64]), + )]); + let snmp_result = vec![SnmpResult::new(items)]; + let res = res.unwrap().eval(&snmp_result); + debug!("{:?}", res); + match res { + ExprResult::Vector(v) => assert!(v == vec![0.25_f64, 0.5_f64, 1.25_f64]), + _ => panic!("Expected a vector value"), + } + } + #[test] fn two_identifiers() { init(); @@ -284,7 +648,7 @@ mod Test { let snmp_result = vec![SnmpResult::new(items)]; let res = res.unwrap().eval(&snmp_result); match res { - ExprResult::Scalar(n) => assert!(n == 96.04125652657707_f64), + ExprResult::Number(n) => assert!(n == 96.04125652657707_f64), _ => panic!("Expected a scalar value"), } } @@ -302,13 +666,13 @@ mod Test { let snmp_result = vec![SnmpResult::new(items)]; let res = res.unwrap().eval(&snmp_result); match res { - ExprResult::Scalar(n) => assert!(n == 2_f64), + ExprResult::Number(n) => assert!(n == 2_f64), _ => panic!("Expected a scalar value"), } } #[test] - fn identifier_str() { + fn join_str_str_identifier() { init(); let items = HashMap::from([ ( @@ -327,13 +691,50 @@ mod Test { let res = res.unwrap(); match res { ExprResult::StrVector(v) => { - assert_eq!(v.len(), 4); - assert_eq!(v[0], "free-onefoo"); - assert_eq!(v[1], "free-twofoo"); - assert_eq!(v[2], "total-onebar"); - assert_eq!(v[3], "total-twobar"); + assert_eq!(v.len(), 2); + assert_eq!(v[0], "free-onefoototal-onebar"); + assert_eq!(v[1], "free-twofoototal-twobar"); } _ => panic!("Expected a string vector"), } } + + #[test] + fn join_str_identifier_str() { + init(); + let items = HashMap::from([ + ( + "free".to_string(), + ExprResult::StrVector(vec!["free-one".to_string(), "free-two".to_string()]), + ), + ( + "total".to_string(), + ExprResult::StrVector(vec!["total-one".to_string(), "total-two".to_string()]), + ), + ]); + let collect = vec![SnmpResult::new(items)]; + let parser = Parser::new(&collect); + let res = parser.eval_str("test{free}{total}foo{free}"); + assert!(res.is_ok()); + let res = res.unwrap(); + match res { + ExprResult::StrVector(v) => { + assert_eq!(v.len(), 2); + assert_eq!(v[0], "testfree-onetotal-onefoofree-one"); + assert_eq!(v[1], "testfree-twototal-twofoofree-two"); + } + _ => panic!("Expected a string vector"), + } + } + + #[test] + fn join_str_str_str() { + let mut a = ExprResult::Str("test".to_string()); + let b = ExprResult::Str("foobar".to_string()); + a.join(&b); + match a { + ExprResult::Str(s) => assert_eq!(s, "testfoobar".to_string()), + _ => panic!("Expected a string"), + } + } } diff --git a/experimental/src/generic/mod.rs b/experimental/src/generic/mod.rs index 613054f75..b80dbffb9 100644 --- a/experimental/src/generic/mod.rs +++ b/experimental/src/generic/mod.rs @@ -158,17 +158,21 @@ impl Command { match &value { ExprResult::Vector(v) => { - for item in v { - let name = match &metric.prefix { - Some(prefix) => { - let name_resolved = parser.eval_str(prefix).unwrap(); - format!("{:?}#{}", prefix, metric.name) - } - None => { + let prefix_str = match &metric.prefix { + Some(prefix) => parser.eval_str(prefix).unwrap(), + None => ExprResult::Empty, + }; + for (i, item) in v.iter().enumerate() { + let name = match &prefix_str { + ExprResult::StrVector(v) => format!("{:?}#{}", v[i], metric.name), + ExprResult::Empty => { let res = format!("{}#{}", idx, metric.name); idx += 1; res } + _ => { + panic!("A label must be a string"); + } }; let m = Perfdata { name, @@ -180,7 +184,7 @@ impl Command { metrics.push(m); } } - ExprResult::Scalar(s) => { + ExprResult::Number(s) => { let name = match &metric.prefix { Some(prefix) => { format!("{:?}#{}", prefix, metric.name) @@ -214,7 +218,7 @@ impl Command { let max = if let Some(max_expr) = metric.max_expr.as_ref() { let res = parser.eval(&max_expr).unwrap(); Some(match res { - ExprResult::Scalar(v) => v, + ExprResult::Number(v) => v, ExprResult::Vector(v) => { assert!(v.len() == 1); v[0] @@ -229,7 +233,7 @@ impl Command { let min = if let Some(min_expr) = metric.min_expr.as_ref() { let res = parser.eval(&min_expr).unwrap(); Some(match res { - ExprResult::Scalar(v) => v, + ExprResult::Number(v) => v, ExprResult::Vector(v) => { assert!(v.len() == 1); v[0] @@ -265,7 +269,7 @@ impl Command { metrics.push(m); } } - ExprResult::Scalar(s) => { + ExprResult::Number(s) => { let name = &metric.name; let m = Perfdata { name: name.to_string(), diff --git a/experimental/src/main.rs b/experimental/src/main.rs index 208ecb424..66f930f17 100644 --- a/experimental/src/main.rs +++ b/experimental/src/main.rs @@ -80,38 +80,36 @@ fn main() { loop { let arg = parser.next(); match arg { - Ok(arg) => { - match arg { - Some(arg) => match arg { - Short('H') | Long("hostname") => { - hostname = parser.value().unwrap().into_string().unwrap(); - trace!("hostname: {:}", hostname); - } - Short('p') | Long("port") => { - port = parser.value().unwrap().parse::().unwrap(); - trace!("port: {}", port); - } - Short('j') | Long("json") => { - json = Some(parser.value().unwrap().into_string().unwrap()); - trace!("json: {:?}", json); - } - Short('v') | Long("snmp-version") => { - snmp_version = parser.value().unwrap().into_string().unwrap(); - trace!("snmp_version: {}", snmp_version); - } - Short('c') | Long("snmp-community") => { - snmp_community = parser.value().unwrap().into_string().unwrap(); - trace!("snmp_community: {}", snmp_community); - } - _ => { - debug!("other unknown argument"); - } - }, - None => { - break; + Ok(arg) => match arg { + Some(arg) => match arg { + Short('H') | Long("hostname") => { + hostname = parser.value().unwrap().into_string().unwrap(); + trace!("hostname: {:}", hostname); } + Short('p') | Long("port") => { + port = parser.value().unwrap().parse::().unwrap(); + trace!("port: {}", port); + } + Short('j') | Long("json") => { + json = Some(parser.value().unwrap().into_string().unwrap()); + trace!("json: {:?}", json); + } + Short('v') | Long("snmp-version") => { + snmp_version = parser.value().unwrap().into_string().unwrap(); + trace!("snmp_version: {}", snmp_version); + } + Short('c') | Long("snmp-community") => { + snmp_community = parser.value().unwrap().into_string().unwrap(); + trace!("snmp_community: {}", snmp_community); + } + _ => { + debug!("other unknown argument"); + } + }, + None => { + break; } - } + }, Err(err) => { println!("err: {:?}", err); std::process::exit(3); diff --git a/experimental/src/snmp/mod.rs b/experimental/src/snmp/mod.rs index 48d016322..a53d03b5b 100644 --- a/experimental/src/snmp/mod.rs +++ b/experimental/src/snmp/mod.rs @@ -457,7 +457,8 @@ impl SnmpResult { self.items .entry(key) .and_modify(|e| match e { - ExprResult::Scalar(_) => panic!("Should not arrive"), + ExprResult::Number(_) => panic!("Should not arrive"), + ExprResult::Str(_) => panic!("Should not arrive"), ExprResult::Vector(v) => v.push(match &typ { ValueType::Float(f) => *f, ValueType::None(()) => { @@ -480,6 +481,9 @@ impl SnmpResult { panic!("Value should be a string"); } }), + ExprResult::Empty => { + panic!("Value from SNMP query cannot be empty"); + } }) .or_insert(match typ { ValueType::Float(f) => ExprResult::Vector(vec![f]), @@ -569,7 +573,7 @@ impl SnmpResult { self.items .entry(key) .and_modify(|e| match e { - ExprResult::Scalar(_) => panic!("Should not arrive"), + ExprResult::Number(_) => panic!("Should not arrive"), ExprResult::Vector(v) => v.push(match &typ { ValueType::Float(f) => *f, ValueType::None(()) => { @@ -580,6 +584,7 @@ impl SnmpResult { } ValueType::Integer(i) => *i as f64, }), + ExprResult::Str(_) => panic!("Should not arrive"), ExprResult::StrVector(v) => v.push(match &typ { ValueType::Float(_) => { panic!("Value should be a string"); @@ -592,6 +597,9 @@ impl SnmpResult { panic!("Value should be a string"); } }), + ExprResult::Empty => { + panic!("Value should not be empty"); + } }) .or_insert(match typ { ValueType::Float(f) => ExprResult::Vector(vec![f]), @@ -712,7 +720,7 @@ impl SnmpResult { self.items .entry(key) .and_modify(|e| match e { - ExprResult::Scalar(_) => panic!("Should not arrive"), + ExprResult::Number(_) => panic!("Should not arrive"), ExprResult::Vector(v) => v.push(match &typ { ValueType::Float(f) => *f, ValueType::None(()) => { @@ -723,6 +731,7 @@ impl SnmpResult { } ValueType::Integer(i) => *i as f64, }), + ExprResult::Str(_) => panic!("Should not arrive"), ExprResult::StrVector(v) => v.push(match &typ { ValueType::Float(_) => { panic!("Value should be a string"); @@ -735,6 +744,9 @@ impl SnmpResult { panic!("Value should be a string"); } }), + ExprResult::Empty => { + panic!("Value should be a string"); + } }) .or_insert(match typ { ValueType::Float(f) => ExprResult::Vector(vec![f]),