enh(generic-snmp): bugs fixed and unit tests added

This commit is contained in:
David Boucher 2025-05-04 19:07:57 +02:00
parent 9ff58afa42
commit bc64e1e2ae
7 changed files with 613 additions and 156 deletions

View File

@ -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";
}

View File

@ -1,4 +1,4 @@
use log::{debug, info, trace}; use log::{debug, info, trace, warn};
use snmp::SnmpResult; use snmp::SnmpResult;
use std::str; use std::str;
@ -23,8 +23,10 @@ pub enum Func {
#[derive(Debug)] #[derive(Debug)]
pub enum ExprResult { pub enum ExprResult {
Vector(Vec<f64>), Vector(Vec<f64>),
Scalar(f64), Number(f64),
StrVector(Vec<String>), StrVector(Vec<String>),
Str(String),
Empty,
} }
impl std::ops::Add for ExprResult { impl std::ops::Add for ExprResult {
@ -32,7 +34,7 @@ impl std::ops::Add for ExprResult {
fn add(self, other: Self) -> Self::Output { fn add(self, other: Self) -> Self::Output {
match (self, other) { 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)) => { (ExprResult::Vector(a), ExprResult::Vector(b)) => {
let len_a = a.len(); let len_a = a.len();
let len_b = b.len(); let len_b = b.len();
@ -43,14 +45,18 @@ impl std::ops::Add for ExprResult {
} }
ExprResult::Vector(result) ExprResult::Vector(result)
} else { } else {
warn!(
"Trying to add to arrays of different lengths: {} and {}",
len_a, len_b
);
if 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() { for (idx, value) in b.iter().enumerate() {
result[idx] += value; result[idx] += value;
} }
ExprResult::Vector(result) ExprResult::Vector(result)
} else { } else {
let mut result = b.clone(); let mut result = b;
for (idx, value) in a.iter().enumerate() { for (idx, value) in a.iter().enumerate() {
result[idx] += value; result[idx] += value;
} }
@ -58,15 +64,15 @@ impl std::ops::Add for ExprResult {
} }
} }
} }
(ExprResult::Scalar(a), ExprResult::Vector(b)) => { (ExprResult::Number(a), ExprResult::Vector(b)) => {
let mut result = b.clone(); let mut result = b;
for value in result.iter_mut() { for value in result.iter_mut() {
*value += a; *value += a;
} }
ExprResult::Vector(result) ExprResult::Vector(result)
} }
(ExprResult::Vector(a), ExprResult::Scalar(b)) => { (ExprResult::Vector(a), ExprResult::Number(b)) => {
let mut result = a.clone(); let mut result = a;
for value in result.iter_mut() { for value in result.iter_mut() {
*value += b; *value += b;
} }
@ -82,7 +88,7 @@ impl std::ops::Sub for ExprResult {
fn sub(self, other: Self) -> Self::Output { fn sub(self, other: Self) -> Self::Output {
match (self, other) { 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)) => { (ExprResult::Vector(a), ExprResult::Vector(b)) => {
let len_a = a.len(); let len_a = a.len();
let len_b = b.len(); let len_b = b.len();
@ -93,30 +99,38 @@ impl std::ops::Sub for ExprResult {
} }
ExprResult::Vector(result) ExprResult::Vector(result)
} else { } else {
warn!(
"Trying to subtract arrays of different lengths: {} and {}",
len_a, len_b
);
if 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() { for (idx, value) in b.iter().enumerate() {
result[idx] -= value; result[idx] -= value;
} }
ExprResult::Vector(result) ExprResult::Vector(result)
} else { } else {
let mut result = b.clone(); let mut result = b;
for (idx, value) in a.iter().enumerate() { for (idx, value) in result.iter_mut().enumerate() {
result[idx] -= value; if idx < a.len() {
*value = a[idx] - *value;
} else {
*value = -*value;
}
} }
ExprResult::Vector(result) ExprResult::Vector(result)
} }
} }
} }
(ExprResult::Scalar(a), ExprResult::Vector(b)) => { (ExprResult::Number(a), ExprResult::Vector(b)) => {
let mut result = vec![0_f64; b.len()]; let mut result = vec![0_f64; b.len()];
for (idx, value) in result.iter_mut().enumerate() { for (idx, value) in result.iter_mut().enumerate() {
*value = a - b[idx]; *value = a - b[idx];
} }
ExprResult::Vector(result) ExprResult::Vector(result)
} }
(ExprResult::Vector(a), ExprResult::Scalar(b)) => { (ExprResult::Vector(a), ExprResult::Number(b)) => {
let mut result = a.clone(); let mut result = a;
for value in result.iter_mut() { for value in result.iter_mut() {
*value -= b; *value -= b;
} }
@ -132,7 +146,7 @@ impl std::ops::Mul for ExprResult {
fn mul(self, other: Self) -> Self::Output { fn mul(self, other: Self) -> Self::Output {
match (self, other) { 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)) => { (ExprResult::Vector(a), ExprResult::Vector(b)) => {
let len_a = a.len(); let len_a = a.len();
let len_b = b.len(); let len_b = b.len();
@ -143,14 +157,18 @@ impl std::ops::Mul for ExprResult {
} }
ExprResult::Vector(result) ExprResult::Vector(result)
} else { } else {
warn!(
"Trying to multiply arrays of different lengths: {} and {}",
len_a, len_b
);
if 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() { for (idx, value) in b.iter().enumerate() {
result[idx] *= value; result[idx] *= value;
} }
ExprResult::Vector(result) ExprResult::Vector(result)
} else { } else {
let mut result = b.clone(); let mut result = b;
for (idx, value) in a.iter().enumerate() { for (idx, value) in a.iter().enumerate() {
result[idx] *= value; 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(); let mut result = b.clone();
for value in result.iter_mut() { for value in result.iter_mut() {
*value *= a; *value *= a;
} }
ExprResult::Vector(result) ExprResult::Vector(result)
} }
(ExprResult::Vector(a), ExprResult::Scalar(b)) => { (ExprResult::Vector(a), ExprResult::Number(b)) => {
let mut result = a.clone(); let mut result = a.clone();
for value in result.iter_mut() { for value in result.iter_mut() {
*value *= b; *value *= b;
@ -182,7 +200,7 @@ impl std::ops::Div for ExprResult {
fn div(self, other: Self) -> Self::Output { fn div(self, other: Self) -> Self::Output {
match (self, other) { 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)) => { (ExprResult::Vector(a), ExprResult::Vector(b)) => {
let len_a = a.len(); let len_a = a.len();
let len_b = b.len(); let len_b = b.len();
@ -193,30 +211,38 @@ impl std::ops::Div for ExprResult {
} }
ExprResult::Vector(result) ExprResult::Vector(result)
} else { } else {
warn!(
"Trying to divide arrays of different lengths: {} and {}",
len_a, len_b
);
if 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() { for (idx, value) in b.iter().enumerate() {
result[idx] /= value; result[idx] /= value;
} }
ExprResult::Vector(result) ExprResult::Vector(result)
} else { } else {
let mut result = b.clone(); let mut result = b;
for (idx, value) in a.iter().enumerate() { for (idx, value) in result.iter_mut().enumerate() {
result[idx] /= value; if idx < a.len() {
*value = a[idx] / *value;
} else {
*value = 1_f64 / *value;
}
} }
ExprResult::Vector(result) ExprResult::Vector(result)
} }
} }
} }
(ExprResult::Scalar(a), ExprResult::Vector(b)) => { (ExprResult::Number(a), ExprResult::Vector(b)) => {
let mut result = vec![0_f64; b.len()]; let mut result = vec![0_f64; b.len()];
for (idx, value) in result.iter_mut().enumerate() { for (idx, value) in result.iter_mut().enumerate() {
*value = a / b[idx]; *value = a / b[idx];
} }
ExprResult::Vector(result) ExprResult::Vector(result)
} }
(ExprResult::Vector(a), ExprResult::Scalar(b)) => { (ExprResult::Vector(a), ExprResult::Number(b)) => {
let mut result = a.clone(); let mut result = a;
for value in result.iter_mut() { for value in result.iter_mut() {
*value /= b; *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> { impl<'input> Expr<'input> {
pub fn eval(&self, collect: &Vec<SnmpResult>) -> ExprResult { pub fn eval(&self, collect: &Vec<SnmpResult>) -> ExprResult {
match self { match self {
Expr::Number(n) => ExprResult::Scalar(*n), Expr::Number(n) => ExprResult::Number(*n),
Expr::Id(key) => { Expr::Id(key) => {
let k = str::from_utf8(key).unwrap(); let k = str::from_utf8(key).unwrap();
for result in collect { for result in collect {
@ -239,7 +315,7 @@ impl<'input> Expr<'input> {
ExprResult::Vector(n) => { ExprResult::Vector(n) => {
if n.len() == 1 { if n.len() == 1 {
info!("ID '{}' has value {}", k, n[0]); info!("ID '{}' has value {}", k, n[0]);
return ExprResult::Scalar(n[0]); return ExprResult::Number(n[0]);
} else { } else {
info!("ID '{}' has value {:?}", k, n); info!("ID '{}' has value {:?}", k, n);
return ExprResult::Vector(n.clone()); return ExprResult::Vector(n.clone());
@ -250,7 +326,7 @@ impl<'input> Expr<'input> {
None => continue, None => continue,
} }
} }
ExprResult::Scalar(0.0) ExprResult::Number(0.0)
} }
Expr::OpPlus(left, right) => left.eval(collect) + right.eval(collect), Expr::OpPlus(left, right) => left.eval(collect) + right.eval(collect),
Expr::OpMinus(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); let v = expr.eval(collect);
match func { match func {
Func::Average => match v { Func::Average => match v {
ExprResult::Scalar(n) => ExprResult::Scalar(n), ExprResult::Number(n) => ExprResult::Number(n),
ExprResult::Vector(v) => { ExprResult::Vector(v) => {
let sum = v.iter().sum::<f64>(); let sum = v.iter().sum::<f64>();
ExprResult::Scalar(sum / v.len() as f64) ExprResult::Number(sum / v.len() as f64)
} }
_ => panic!("Invalid operation"), _ => panic!("Invalid operation"),
}, },
Func::Min => match v { Func::Min => match v {
ExprResult::Scalar(n) => ExprResult::Scalar(n), ExprResult::Number(n) => ExprResult::Number(n),
ExprResult::Vector(v) => { ExprResult::Vector(v) => {
let min = v.iter().cloned().fold(f64::INFINITY, f64::min); let min = v.iter().cloned().fold(f64::INFINITY, f64::min);
ExprResult::Scalar(min) ExprResult::Number(min)
} }
_ => panic!("Invalid operation"), _ => panic!("Invalid operation"),
}, },
Func::Max => match v { Func::Max => match v {
ExprResult::Scalar(n) => ExprResult::Scalar(n), ExprResult::Number(n) => ExprResult::Number(n),
ExprResult::Vector(v) => { ExprResult::Vector(v) => {
let max = v.iter().cloned().fold(f64::NEG_INFINITY, f64::max); let max = v.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
ExprResult::Scalar(max) ExprResult::Number(max)
} }
_ => panic!("Invalid operation"), _ => panic!("Invalid operation"),
}, },

View File

@ -186,13 +186,13 @@ mod Test {
#[test] #[test]
fn test_lexer_num_op() { fn test_lexer_num_op() {
init(); init();
let input = "1+2*3"; let input = "1 +2*3";
let mut lexer = Lexer::new(input); let mut lexer = Lexer::new(input);
assert_eq!(lexer.next(), Some(Ok((0, Tok::Num(1_f64), 1)))); 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::OpPlus, 3))));
assert_eq!(lexer.next(), Some(Ok((2, Tok::Num(2_f64), 3)))); assert_eq!(lexer.next(), Some(Ok((3, Tok::Num(2_f64), 4))));
assert_eq!(lexer.next(), Some(Ok((3, Tok::OpStar, 4)))); assert_eq!(lexer.next(), Some(Ok((4, Tok::OpStar, 5))));
assert_eq!(lexer.next(), Some(Ok((4, Tok::Num(3_f64), 5)))); assert_eq!(lexer.next(), Some(Ok((5, Tok::Num(3_f64), 6))));
assert_eq!(lexer.next(), None); assert_eq!(lexer.next(), None);
} }
} }

View File

@ -4,7 +4,7 @@ pub mod lexer;
use self::ast::ExprResult; use self::ast::ExprResult;
use self::lexer::{LexicalError, Tok}; use self::lexer::{LexicalError, Tok};
use lalrpop_util::{lalrpop_mod, ParseError}; use lalrpop_util::{lalrpop_mod, ParseError};
use log::{trace, debug}; use log::{debug, trace};
use regex::Regex; use regex::Regex;
use serde::Deserialize; use serde::Deserialize;
use snmp::SnmpResult; use snmp::SnmpResult;
@ -64,28 +64,28 @@ impl<'a> Parser<'a> {
) -> Result<ExprResult, ParseError<usize, Tok<'a>, LexicalError>> { ) -> Result<ExprResult, ParseError<usize, Tok<'a>, LexicalError>> {
let re = Regex::new(r"\{[a-zA-Z_][a-zA-Z0-9_.]*\}").unwrap(); let re = Regex::new(r"\{[a-zA-Z_][a-zA-Z0-9_.]*\}").unwrap();
let mut suffix = expr; let mut suffix = expr;
let mut result: ExprResult = ExprResult::StrVector(vec![]); let mut result: ExprResult = ExprResult::Empty;
loop { loop {
let found = re.find(suffix); let found = re.find(suffix);
if let Some(m) = found { if let Some(m) = found {
let start = m.start(); let start = m.start();
let end = m.end(); let end = m.end();
debug!( if start > 0 {
"Identifier '{}' found in expr '{}'", result.join(&ExprResult::Str(suffix[0..start].to_string()));
&expr[start + 1..end - 1], }
expr
);
let prefix = &expr[0..start];
suffix = &expr[end..];
let mut result = vec![];
for snmp_result in self.collect { for snmp_result in self.collect {
if let Some(v) = snmp_result.items.get(&expr[start + 1..end - 1]) { if let Some(v) = snmp_result.items.get(&suffix[start + 1..end - 1]) {
result = join_str_expr(prefix, v); result.join(v);
break; break;
} }
} }
trace!("Result string {:?}", result); debug!(
"Evaluation as string of expression '{}' returns {:?}",
suffix, result
);
suffix = &suffix[end..];
} else { } else {
result.join(&ExprResult::Str(suffix.to_string()));
break; break;
} }
} }
@ -125,7 +125,7 @@ mod Test {
let snmp_result = vec![]; let snmp_result = vec![];
let res = res.unwrap().eval(&snmp_result); let res = res.unwrap().eval(&snmp_result);
match res { match res {
ExprResult::Scalar(n) => assert!(n == 123_f64), ExprResult::Number(n) => assert!(n == 123_f64),
_ => panic!("Expected a scalar value"), _ => panic!("Expected a scalar value"),
} }
let lexer = lexer::Lexer::new("123"); let lexer = lexer::Lexer::new("123");
@ -144,7 +144,7 @@ mod Test {
let snmp_result = vec![]; let snmp_result = vec![];
let res = res.unwrap().eval(&snmp_result); let res = res.unwrap().eval(&snmp_result);
match res { match res {
ExprResult::Scalar(n) => assert!(n == 3_f64), ExprResult::Number(n) => assert!(n == 3_f64),
_ => panic!("Expected a scalar value"), _ => panic!("Expected a scalar value"),
} }
@ -153,7 +153,7 @@ mod Test {
assert!(res.is_ok()); assert!(res.is_ok());
let res = res.unwrap().eval(&snmp_result); let res = res.unwrap().eval(&snmp_result);
match res { match res {
ExprResult::Scalar(n) => assert!(n == 0_f64), ExprResult::Number(n) => assert!(n == 0_f64),
_ => panic!("Expected a scalar value"), _ => panic!("Expected a scalar value"),
} }
@ -162,7 +162,7 @@ mod Test {
assert!(res.is_ok()); assert!(res.is_ok());
let res = res.unwrap().eval(&snmp_result); let res = res.unwrap().eval(&snmp_result);
match res { match res {
ExprResult::Scalar(n) => assert!(n == 2_f64), ExprResult::Number(n) => assert!(n == 2_f64),
_ => panic!("Expected a scalar value"), _ => panic!("Expected a scalar value"),
} }
@ -171,7 +171,7 @@ mod Test {
assert!(res.is_ok()); assert!(res.is_ok());
let res = res.unwrap().eval(&snmp_result); let res = res.unwrap().eval(&snmp_result);
match res { match res {
ExprResult::Scalar(n) => assert!(n == -4_f64), ExprResult::Number(n) => assert!(n == -4_f64),
_ => panic!("Expected a scalar value"), _ => panic!("Expected a scalar value"),
} }
@ -180,7 +180,7 @@ mod Test {
assert!(res.is_ok()); assert!(res.is_ok());
let res = res.unwrap().eval(&snmp_result); let res = res.unwrap().eval(&snmp_result);
match res { match res {
ExprResult::Scalar(n) => assert!(n == -8_f64), ExprResult::Number(n) => assert!(n == -8_f64),
_ => panic!("Expected a scalar value"), _ => panic!("Expected a scalar value"),
} }
} }
@ -194,7 +194,7 @@ mod Test {
let snmp_result = vec![]; let snmp_result = vec![];
let res = res.unwrap().eval(&snmp_result); let res = res.unwrap().eval(&snmp_result);
match res { match res {
ExprResult::Scalar(n) => assert!(n == 6_f64), ExprResult::Number(n) => assert!(n == 6_f64),
_ => panic!("Expected a scalar value"), _ => panic!("Expected a scalar value"),
} }
@ -203,7 +203,7 @@ mod Test {
assert!(res.is_ok()); assert!(res.is_ok());
let res = res.unwrap().eval(&snmp_result); let res = res.unwrap().eval(&snmp_result);
match res { match res {
ExprResult::Scalar(n) => assert!(n == 7_f64), ExprResult::Number(n) => assert!(n == 7_f64),
_ => panic!("Expected a scalar value"), _ => panic!("Expected a scalar value"),
} }
@ -212,7 +212,7 @@ mod Test {
assert!(res.is_ok()); assert!(res.is_ok());
let res = res.unwrap().eval(&snmp_result); let res = res.unwrap().eval(&snmp_result);
match res { match res {
ExprResult::Scalar(n) => assert!(n == 9_f64), ExprResult::Number(n) => assert!(n == 9_f64),
_ => panic!("Expected a scalar value"), _ => panic!("Expected a scalar value"),
} }
@ -221,7 +221,7 @@ mod Test {
assert!(res.is_ok()); assert!(res.is_ok());
let res = res.unwrap().eval(&snmp_result); let res = res.unwrap().eval(&snmp_result);
match res { match res {
ExprResult::Scalar(n) => assert!(n == 24_f64), ExprResult::Number(n) => assert!(n == 24_f64),
_ => panic!("Expected a scalar value"), _ => panic!("Expected a scalar value"),
} }
@ -230,7 +230,7 @@ mod Test {
assert!(res.is_ok()); assert!(res.is_ok());
let res = res.unwrap().eval(&snmp_result); let res = res.unwrap().eval(&snmp_result);
match res { match res {
ExprResult::Scalar(n) => assert!(n == 3_f64), ExprResult::Number(n) => assert!(n == 3_f64),
_ => panic!("Expected a scalar value"), _ => panic!("Expected a scalar value"),
} }
@ -246,7 +246,7 @@ mod Test {
let snmp_result = vec![]; let snmp_result = vec![];
let res = res.unwrap().eval(&snmp_result); let res = res.unwrap().eval(&snmp_result);
match res { match res {
ExprResult::Scalar(n) => assert!(n == 4_f64), ExprResult::Number(n) => assert!(n == 4_f64),
_ => panic!("Expected a scalar value"), _ => panic!("Expected a scalar value"),
} }
} }
@ -262,7 +262,7 @@ mod Test {
let snmp_result = vec![SnmpResult::new(items)]; let snmp_result = vec![SnmpResult::new(items)];
let res = res.unwrap().eval(&snmp_result); let res = res.unwrap().eval(&snmp_result);
match res { match res {
ExprResult::Scalar(n) => assert!(n == 2_f64), ExprResult::Number(n) => assert!(n == 2_f64),
_ => panic!("Expected a scalar value"), _ => panic!("Expected a scalar value"),
} }
@ -271,6 +271,370 @@ mod Test {
assert!(res.is_err()); 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] #[test]
fn two_identifiers() { fn two_identifiers() {
init(); init();
@ -284,7 +648,7 @@ mod Test {
let snmp_result = vec![SnmpResult::new(items)]; let snmp_result = vec![SnmpResult::new(items)];
let res = res.unwrap().eval(&snmp_result); let res = res.unwrap().eval(&snmp_result);
match res { match res {
ExprResult::Scalar(n) => assert!(n == 96.04125652657707_f64), ExprResult::Number(n) => assert!(n == 96.04125652657707_f64),
_ => panic!("Expected a scalar value"), _ => panic!("Expected a scalar value"),
} }
} }
@ -302,13 +666,13 @@ mod Test {
let snmp_result = vec![SnmpResult::new(items)]; let snmp_result = vec![SnmpResult::new(items)];
let res = res.unwrap().eval(&snmp_result); let res = res.unwrap().eval(&snmp_result);
match res { match res {
ExprResult::Scalar(n) => assert!(n == 2_f64), ExprResult::Number(n) => assert!(n == 2_f64),
_ => panic!("Expected a scalar value"), _ => panic!("Expected a scalar value"),
} }
} }
#[test] #[test]
fn identifier_str() { fn join_str_str_identifier() {
init(); init();
let items = HashMap::from([ let items = HashMap::from([
( (
@ -327,13 +691,50 @@ mod Test {
let res = res.unwrap(); let res = res.unwrap();
match res { match res {
ExprResult::StrVector(v) => { ExprResult::StrVector(v) => {
assert_eq!(v.len(), 4); assert_eq!(v.len(), 2);
assert_eq!(v[0], "free-onefoo"); assert_eq!(v[0], "free-onefoototal-onebar");
assert_eq!(v[1], "free-twofoo"); assert_eq!(v[1], "free-twofoototal-twobar");
assert_eq!(v[2], "total-onebar");
assert_eq!(v[3], "total-twobar");
} }
_ => panic!("Expected a string vector"), _ => 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"),
}
}
} }

View File

@ -158,17 +158,21 @@ impl Command {
match &value { match &value {
ExprResult::Vector(v) => { ExprResult::Vector(v) => {
for item in v { let prefix_str = match &metric.prefix {
let name = match &metric.prefix { Some(prefix) => parser.eval_str(prefix).unwrap(),
Some(prefix) => { None => ExprResult::Empty,
let name_resolved = parser.eval_str(prefix).unwrap(); };
format!("{:?}#{}", prefix, metric.name) for (i, item) in v.iter().enumerate() {
} let name = match &prefix_str {
None => { ExprResult::StrVector(v) => format!("{:?}#{}", v[i], metric.name),
ExprResult::Empty => {
let res = format!("{}#{}", idx, metric.name); let res = format!("{}#{}", idx, metric.name);
idx += 1; idx += 1;
res res
} }
_ => {
panic!("A label must be a string");
}
}; };
let m = Perfdata { let m = Perfdata {
name, name,
@ -180,7 +184,7 @@ impl Command {
metrics.push(m); metrics.push(m);
} }
} }
ExprResult::Scalar(s) => { ExprResult::Number(s) => {
let name = match &metric.prefix { let name = match &metric.prefix {
Some(prefix) => { Some(prefix) => {
format!("{:?}#{}", prefix, metric.name) format!("{:?}#{}", prefix, metric.name)
@ -214,7 +218,7 @@ impl Command {
let max = if let Some(max_expr) = metric.max_expr.as_ref() { let max = if let Some(max_expr) = metric.max_expr.as_ref() {
let res = parser.eval(&max_expr).unwrap(); let res = parser.eval(&max_expr).unwrap();
Some(match res { Some(match res {
ExprResult::Scalar(v) => v, ExprResult::Number(v) => v,
ExprResult::Vector(v) => { ExprResult::Vector(v) => {
assert!(v.len() == 1); assert!(v.len() == 1);
v[0] v[0]
@ -229,7 +233,7 @@ impl Command {
let min = if let Some(min_expr) = metric.min_expr.as_ref() { let min = if let Some(min_expr) = metric.min_expr.as_ref() {
let res = parser.eval(&min_expr).unwrap(); let res = parser.eval(&min_expr).unwrap();
Some(match res { Some(match res {
ExprResult::Scalar(v) => v, ExprResult::Number(v) => v,
ExprResult::Vector(v) => { ExprResult::Vector(v) => {
assert!(v.len() == 1); assert!(v.len() == 1);
v[0] v[0]
@ -265,7 +269,7 @@ impl Command {
metrics.push(m); metrics.push(m);
} }
} }
ExprResult::Scalar(s) => { ExprResult::Number(s) => {
let name = &metric.name; let name = &metric.name;
let m = Perfdata { let m = Perfdata {
name: name.to_string(), name: name.to_string(),

View File

@ -80,38 +80,36 @@ fn main() {
loop { loop {
let arg = parser.next(); let arg = parser.next();
match arg { match arg {
Ok(arg) => { Ok(arg) => match arg {
match arg { Some(arg) => match arg {
Some(arg) => match arg { Short('H') | Long("hostname") => {
Short('H') | Long("hostname") => { hostname = parser.value().unwrap().into_string().unwrap();
hostname = parser.value().unwrap().into_string().unwrap(); trace!("hostname: {:}", hostname);
trace!("hostname: {:}", hostname);
}
Short('p') | Long("port") => {
port = parser.value().unwrap().parse::<u16>().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;
} }
Short('p') | Long("port") => {
port = parser.value().unwrap().parse::<u16>().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) => { Err(err) => {
println!("err: {:?}", err); println!("err: {:?}", err);
std::process::exit(3); std::process::exit(3);

View File

@ -457,7 +457,8 @@ impl SnmpResult {
self.items self.items
.entry(key) .entry(key)
.and_modify(|e| match e { .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 { ExprResult::Vector(v) => v.push(match &typ {
ValueType::Float(f) => *f, ValueType::Float(f) => *f,
ValueType::None(()) => { ValueType::None(()) => {
@ -480,6 +481,9 @@ impl SnmpResult {
panic!("Value should be a string"); panic!("Value should be a string");
} }
}), }),
ExprResult::Empty => {
panic!("Value from SNMP query cannot be empty");
}
}) })
.or_insert(match typ { .or_insert(match typ {
ValueType::Float(f) => ExprResult::Vector(vec![f]), ValueType::Float(f) => ExprResult::Vector(vec![f]),
@ -569,7 +573,7 @@ impl SnmpResult {
self.items self.items
.entry(key) .entry(key)
.and_modify(|e| match e { .and_modify(|e| match e {
ExprResult::Scalar(_) => panic!("Should not arrive"), ExprResult::Number(_) => panic!("Should not arrive"),
ExprResult::Vector(v) => v.push(match &typ { ExprResult::Vector(v) => v.push(match &typ {
ValueType::Float(f) => *f, ValueType::Float(f) => *f,
ValueType::None(()) => { ValueType::None(()) => {
@ -580,6 +584,7 @@ impl SnmpResult {
} }
ValueType::Integer(i) => *i as f64, ValueType::Integer(i) => *i as f64,
}), }),
ExprResult::Str(_) => panic!("Should not arrive"),
ExprResult::StrVector(v) => v.push(match &typ { ExprResult::StrVector(v) => v.push(match &typ {
ValueType::Float(_) => { ValueType::Float(_) => {
panic!("Value should be a string"); panic!("Value should be a string");
@ -592,6 +597,9 @@ impl SnmpResult {
panic!("Value should be a string"); panic!("Value should be a string");
} }
}), }),
ExprResult::Empty => {
panic!("Value should not be empty");
}
}) })
.or_insert(match typ { .or_insert(match typ {
ValueType::Float(f) => ExprResult::Vector(vec![f]), ValueType::Float(f) => ExprResult::Vector(vec![f]),
@ -712,7 +720,7 @@ impl SnmpResult {
self.items self.items
.entry(key) .entry(key)
.and_modify(|e| match e { .and_modify(|e| match e {
ExprResult::Scalar(_) => panic!("Should not arrive"), ExprResult::Number(_) => panic!("Should not arrive"),
ExprResult::Vector(v) => v.push(match &typ { ExprResult::Vector(v) => v.push(match &typ {
ValueType::Float(f) => *f, ValueType::Float(f) => *f,
ValueType::None(()) => { ValueType::None(()) => {
@ -723,6 +731,7 @@ impl SnmpResult {
} }
ValueType::Integer(i) => *i as f64, ValueType::Integer(i) => *i as f64,
}), }),
ExprResult::Str(_) => panic!("Should not arrive"),
ExprResult::StrVector(v) => v.push(match &typ { ExprResult::StrVector(v) => v.push(match &typ {
ValueType::Float(_) => { ValueType::Float(_) => {
panic!("Value should be a string"); panic!("Value should be a string");
@ -735,6 +744,9 @@ impl SnmpResult {
panic!("Value should be a string"); panic!("Value should be a string");
} }
}), }),
ExprResult::Empty => {
panic!("Value should be a string");
}
}) })
.or_insert(match typ { .or_insert(match typ {
ValueType::Float(f) => ExprResult::Vector(vec![f]), ValueType::Float(f) => ExprResult::Vector(vec![f]),