1use anyhow::{bail, Result};
2use std::collections::HashMap;
3use std::fs::{read_to_string, File};
4use std::io::{self, BufRead};
5use std::ops::Sub;
6use std::path::Path;
7use std::str::FromStr;
8
9#[derive(Default, Debug, Clone)]
10pub struct Snmp {
11 hm: HashMap<(String, String), isize>,
12}
13
14impl FromStr for Snmp {
15 type Err = anyhow::Error;
16 fn from_str(content: &str) -> Result<Self> {
17 let mut snmp = Snmp::default();
18
19 let lines = content.split('\n').collect::<Vec<&str>>();
20
21 for i in 0..lines.len() / 2 {
22 let line1 = lines[i * 2];
23 let line2 = lines[i * 2 + 1];
24
25 let mut iter1 = line1.split_whitespace();
26 let mut iter2 = line2.split_whitespace();
27
28 let prefix;
29 if let Some(x) = iter1.next() {
30 prefix = x.to_string();
31 } else {
32 bail!("failed to parse: prefix not found")
33 }
34 iter2.next();
35 loop {
36 let k;
37 let v: isize;
38 if let Some(x) = iter1.next() {
39 k = x;
40 } else {
41 break;
42 }
43
44 if let Some(x) = iter2.next() {
45 v = x.parse()?;
46 } else {
47 bail!("failed to parse: number of item is not match.")
48 }
49
50 snmp.insert((prefix.clone(), k.to_string()), v);
51 }
52 }
53
54 Ok(snmp)
55 }
56}
57
58impl Sub for Snmp {
59 type Output = Self;
60
61 fn sub(self, other: Self) -> Self {
62 let mut snmp = Snmp::default();
63
64 for (k, v) in self.hm.iter() {
65 assert_eq!(other.hm.contains_key(k), true);
66 snmp.insert(k.clone(), v - other.hm[k]);
67 }
68
69 snmp
70 }
71}
72
73impl Snmp {
74 pub fn from_file<P>(path: P) -> Result<Snmp>
75 where
76 P: AsRef<Path>,
77 {
78 let string = read_to_string(path)?;
79 Snmp::from_str(&string)
80 }
81
82 pub fn insert(&mut self, k: (String, String), v: isize) {
83 self.hm.insert(k, v);
84 }
85
86 pub fn lookup(&self, k: &(String, String)) -> Option<&isize> {
87 self.hm.get(k)
88 }
89
90 pub fn show_non_zero(&self) {
91 for (k, v) in &self.hm {
92 if *v != 0 {
93 print!("{}{}: {} ", k.0, k.1, v);
94 }
95 }
96 println!();
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103 #[test]
104 fn test_snmp_from_file() {
105 let snmp = Snmp::from_file("/proc/net/snmp");
106 assert_eq!(snmp.is_ok(), true);
107 }
108
109 #[test]
110 fn test_snmp_ops_sub() {
111 let snmp1 = Snmp::from_file("/proc/net/snmp").unwrap();
112 let snmp2 = Snmp::from_file("/proc/net/snmp").unwrap();
113 let delta = snmp1 - snmp2;
114 }
115
116 #[test]
117 fn test_snmp_show_non_zero() {
118 let snmp1 = Snmp::from_file("/proc/net/snmp").unwrap();
119 let snmp2 = Snmp::from_file("/proc/net/snmp").unwrap();
120 let delta = snmp1 - snmp2;
121 delta.show_non_zero();
122 }
123}