initial commit
This commit is contained in:
commit
f7069473ff
|
@ -0,0 +1 @@
|
|||
/target
|
|
@ -0,0 +1,100 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
|
||||
|
||||
[[package]]
|
||||
name = "mini-internal"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "926fbf1cd8695183e4712d1c77cdf2c5f7916f9f73ba103dca78ff2e6755ab47"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniserde"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0767e42acf28c5e08fee73cb657bcb23432c3769cbdf3881a8cb69d8df5020df"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"mini-internal",
|
||||
"ryu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb37d2df5df740e582f28f8560cf425f52bb267d872fe58358eadb554909f07a"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c9613b5a66ab9ba26415184cfc41156594925a9cf3a2057e57f31ff145f6568"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.130"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.130"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "vnstat_parse"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"miniserde",
|
||||
"serde",
|
||||
]
|
|
@ -0,0 +1,21 @@
|
|||
[package]
|
||||
name = "vnstat_parse"
|
||||
version = "0.1.0"
|
||||
authors = ["Andrew Reid <glyph@mycelial.technology"]
|
||||
edition = "2018"
|
||||
description = "Parse output from `vnstat --oneline` command"
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0.130", features = ["derive"], optional = true }
|
||||
miniserde = { version = "0.1.16", optional = true }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
# Provide `Serialize` and `Deserialize` traits for the `Vnstat` struct using `miniserde`.
|
||||
miniserde_support = ["miniserde"]
|
||||
|
||||
# Provide `Serialize` and `Deserialize` traits for the `Vnstat` struct using `serde`.
|
||||
serde_support = ["serde"]
|
|
@ -0,0 +1,20 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2021 Andrew 'glyph' Reid
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,62 @@
|
|||
# `vnstat` parse
|
||||
|
||||
A Rust library to parse `oneline` data output from [vnstat](https://humdi.net/vnstat/) ("a network and traffic monitor for Linux and BSD").
|
||||
|
||||
All fields are parsed, with the exception of the API version information.
|
||||
Dates are parsed to `String`, data values are parsed to `f32` and data units
|
||||
are parsed to `String`.
|
||||
|
||||
Here is the summary of the `--oneline` option from the `vnstat man` page:
|
||||
|
||||
> Show traffic summary for selected interface using one line with a parsable
|
||||
format. The output contains 15 fields with ; used as field delimiter. The
|
||||
1st field contains the API version information of the output that will only
|
||||
be changed in future versions if the field content or structure changes.
|
||||
The following fields in order 2) interface name, 3) timestamp for today, 4)
|
||||
rx for today, 5) tx for today, 6) total for today, 7) average traffic rate
|
||||
for today, 8) timestamp for current month, 9) rx for current month, 10) tx
|
||||
for current month, 11) total for current month, 12) average traffic rate
|
||||
for current month, 13) all time total rx, 14) all time total tx, 15) all
|
||||
time total traffic. An optional mode parameter can be used to force all
|
||||
fields to output in bytes without the unit itself shown.
|
||||
|
||||
**Example output**
|
||||
|
||||
```bash
|
||||
vnstat eno1 --oneline
|
||||
1;eno1;2021-11-29;6.02 GiB;0.99 GiB;7.00 GiB;738.84 kbit/s;2021-11;6.02 GiB;0.99 GiB;7.00 GiB;24.06 kbit/s;6.02 GiB;0.99 GiB;7.00 GiB
|
||||
```
|
||||
|
||||
## Library Usage
|
||||
|
||||
```rust
|
||||
use vnstat_parse::{Error, Vnstat};
|
||||
|
||||
fn main() -> Result<(), Error> {
|
||||
let vnstat_data = Vnstat::get("eno1")?;
|
||||
|
||||
println!("{:?}", vnstat_data);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
**Example output**
|
||||
|
||||
```bash
|
||||
Vnstat { iface: "eno1", today: "2021-11-29", day_rx: 6.02, day_rx_unit: "GiB", day_tx: 0.99, day_tx_unit: "GiB", day_total: 7.0, day_total_unit: "GiB", day_avg_rate: 738.84, day_avg_rate_unit: "kbit/s", month: "2021-11", month_rx: 6.02, month_rx_unit: "GiB", month_tx: 0.99, month_tx_unit: "GiB", month_total: 7.0, month_total_unit: "GiB", month_avg_rate: 24.06, month_avg_rate_unit: "kbit/s", all_time_rx: 6.02, all_time_rx_unit: "GiB", all_time_tx: 0.99, all_time_tx_unit: "GiB", all_time_total: 7.0, all_time_total_unit: "GiB" }
|
||||
```
|
||||
|
||||
## Optional Features
|
||||
|
||||
`Serialize` and `Deserialize` can be optionally derived for the `Vnstat` `struct` using either [miniserde](https://crates.io/crates/miniserde) or [serde](https://crates.io/crates/serde). These features are disabled by default to offer a zero dependency parser. `miniserde` offers a lightweight option when compared with `serde` (one less dependency and shorter compile times).
|
||||
|
||||
Specify the desired feature in your `Cargo.toml` manifest:
|
||||
|
||||
```toml
|
||||
vnstat_parse = { version = "0.1", features = ["miniserde"] }
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT.
|
|
@ -0,0 +1,15 @@
|
|||
use vnstat_parse::{Error, Vnstat};
|
||||
|
||||
fn main() -> Result<(), Error> {
|
||||
// supply the name of the desired interface
|
||||
let vnstat_data = Vnstat::get("eno1")?;
|
||||
|
||||
println!("{:?}", vnstat_data);
|
||||
|
||||
println!(
|
||||
"Total network traffic for today: {} {}",
|
||||
vnstat_data.day_total, vnstat_data.day_total_unit
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,257 @@
|
|||
#![warn(missing_docs)]
|
||||
|
||||
//! A library to parse oneline data output from `vnstat`.
|
||||
//!
|
||||
//! All fields are parsed, with the exception of the API version information.
|
||||
//! Dates are parsed to `String`, data values are parsed to `f32` and data units
|
||||
//! are parsed to `String`.
|
||||
//!
|
||||
//! Here is the summary of the `--oneline` option from the `vnstat man` page:
|
||||
//!
|
||||
//! > Show traffic summary for selected interface using one line with a parsable
|
||||
//! format. The output contains 15 fields with ; used as field delimiter. The
|
||||
//! 1st field contains the API version information of the output that will only
|
||||
//! be changed in future versions if the field content or structure changes.
|
||||
//! The following fields in order 2) interface name, 3) timestamp for today, 4)
|
||||
//! rx for today, 5) tx for today, 6) total for today, 7) average traffic rate
|
||||
//! for today, 8) timestamp for current month, 9) rx for current month, 10) tx
|
||||
//! for current month, 11) total for current month, 12) average traffic rate
|
||||
//! for current month, 13) all time total rx, 14) all time total tx, 15) all
|
||||
//! time total traffic. An optional mode parameter can be used to force all
|
||||
//! fields to output in bytes without the unit itself shown.
|
||||
//!
|
||||
//! Example output:
|
||||
//!
|
||||
//! `1;eno1;2021-11-29;6.02 GiB;0.99 GiB;7.00 GiB;738.84 kbit/s;2021-11;6.02 GiB;0.99 GiB;7.00 GiB;24.06 kbit/s;6.02 GiB;0.99 GiB;7.00 GiB`
|
||||
//!
|
||||
//! ## Library Usage
|
||||
//!
|
||||
//! ```rust
|
||||
//! use vnstat_parse::{Error, Vnstat};
|
||||
//!
|
||||
//! fn main() -> Result<(), Error> {
|
||||
//! let vnstat_data = Vnstat::get("eno1")?;
|
||||
//!
|
||||
//! println!("{:?}", vnstat_data);
|
||||
//!
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! **Example output**
|
||||
//!
|
||||
//! ```bash
|
||||
//! Vnstat { iface: "eno1", today: "2021-11-29", day_rx: 6.02, day_rx_unit: "GiB", day_tx: 0.99, day_tx_unit: "GiB", day_total: 7.0, day_total_unit: "GiB", day_avg_rate: 738.84, day_avg_rate_unit: "kbit/s", month: "2021-11", month_rx: 6.02, month_rx_unit: "GiB", month_tx: 0.99, month_tx_unit: "GiB", month_total: 7.0, month_total_unit: "GiB", month_avg_rate: 24.06, month_avg_rate_unit: "kbit/s", all_time_rx: 6.02, all_time_rx_unit: "GiB", all_time_tx: 0.99, all_time_tx_unit: "GiB", all_time_total: 7.0, all_time_total_unit: "GiB" }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Optional Features
|
||||
//!
|
||||
//! `Serialize` and `Deserialize` can be optionally derived for the `Vnstat`
|
||||
//! `struct` using either [miniserde](https://crates.io/crates/miniserde) or
|
||||
//! [serde](https://crates.io/crates/serde). These features are disabled by
|
||||
//! default to offer a zero dependency parser. `miniserde` offers a lightweight
|
||||
//! option when compared with `serde` (one less dependency and shorter compile
|
||||
//! times).
|
||||
//!
|
||||
//! Specify the desired feature in your `Cargo.toml` manifest:
|
||||
//!
|
||||
//! ```toml
|
||||
//! vnstat_parse = { version = "0.1", features = ["miniserde"] }
|
||||
//! ```
|
||||
|
||||
use std::{
|
||||
io::Error as IoError, num::ParseFloatError, process::Command, result::Result, str::FromStr,
|
||||
};
|
||||
|
||||
#[cfg(feature = "miniserde_support")]
|
||||
use miniserde::{Deserialize, Serialize};
|
||||
|
||||
#[cfg(feature = "serde_support")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Custom error type encapsulating all possible errors for this library.
|
||||
/// `From` implementations are provided for external error types.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Failed to find ' ' (space) while parsing traffic data.
|
||||
Find,
|
||||
/// IO error.
|
||||
Io(IoError),
|
||||
/// Failed to parse a string slice to `f32`.
|
||||
ParseFloat(ParseFloatError),
|
||||
/// Stderr output from `vnstat` command.
|
||||
StdErr(String),
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match *self {
|
||||
Error::Find => None,
|
||||
Error::Io(ref err) => Some(err),
|
||||
Error::ParseFloat(ref err) => Some(err),
|
||||
Error::StdErr(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match *self {
|
||||
Error::Find => write!(
|
||||
f,
|
||||
"Find error: failed to find ' ' character (space) while parsing traffic data and unit"
|
||||
),
|
||||
Error::Io(_) => write!(f, "IO error: failed to execute `vnstat` command"),
|
||||
Error::ParseFloat(_) => write!(f, "Parse error: failed to parse float from string"),
|
||||
Error::StdErr(ref err) => write!(f, "`vnstat` error: {}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IoError> for Error {
|
||||
fn from(err: IoError) -> Self {
|
||||
Error::Io(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParseFloatError> for Error {
|
||||
fn from(err: ParseFloatError) -> Self {
|
||||
Error::ParseFloat(err)
|
||||
}
|
||||
}
|
||||
|
||||
/// Parsed network usage data for a single interface (sourced from the `vnstat` database).
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "miniserde_support", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
|
||||
pub struct Vnstat {
|
||||
/// Network interface.
|
||||
pub iface: String,
|
||||
/// Timestamp for today (yyyy-mm-dd).
|
||||
pub today: String,
|
||||
/// Received data total for today.
|
||||
pub day_rx: f32,
|
||||
/// Unit of received data total for today.
|
||||
pub day_rx_unit: String,
|
||||
/// Transmitted data total for today.
|
||||
pub day_tx: f32,
|
||||
/// Unit of transmitted data total for today.
|
||||
pub day_tx_unit: String,
|
||||
/// Combined data total for today.
|
||||
pub day_total: f32,
|
||||
/// Unit of combined data total for today.
|
||||
pub day_total_unit: String,
|
||||
/// Average traffic rate for today.
|
||||
pub day_avg_rate: f32,
|
||||
/// Unit of average traffic rate for today.
|
||||
pub day_avg_rate_unit: String,
|
||||
/// Timestamp for current month (yyyy-mm).
|
||||
pub month: String,
|
||||
/// Received data for the current month.
|
||||
pub month_rx: f32,
|
||||
/// Unit of received data for the current month.
|
||||
pub month_rx_unit: String,
|
||||
/// Transmitted data for the current month.
|
||||
pub month_tx: f32,
|
||||
/// Unit of transmitted data for the current month.
|
||||
pub month_tx_unit: String,
|
||||
/// Combined data total for the current month.
|
||||
pub month_total: f32,
|
||||
/// Unit of combined data total for the current month.
|
||||
pub month_total_unit: String,
|
||||
/// Average traffic rate for the current month.
|
||||
pub month_avg_rate: f32,
|
||||
/// Unit of average traffic rate for the current month.
|
||||
pub month_avg_rate_unit: String,
|
||||
/// Received data for all time.
|
||||
pub all_time_rx: f32,
|
||||
/// Unit of received data for all time.
|
||||
pub all_time_rx_unit: String,
|
||||
/// Transmitted data for all time.
|
||||
pub all_time_tx: f32,
|
||||
/// Unit of transmitted data for all time.
|
||||
pub all_time_tx_unit: String,
|
||||
/// Combined data total for all time.
|
||||
pub all_time_total: f32,
|
||||
/// Unit of combined data total for all time.
|
||||
pub all_time_total_unit: String,
|
||||
}
|
||||
|
||||
impl Vnstat {
|
||||
/// Call `vnstat <iface> --online` with the given interface. If the command executes
|
||||
/// succesfully, parse the `stdout` data. Otherwise, return `stderr`.
|
||||
pub fn get(iface: &str) -> Result<Vnstat, Error> {
|
||||
let vnstat_output = Command::new("vnstat")
|
||||
.arg(&iface)
|
||||
.arg("--oneline")
|
||||
.output()?;
|
||||
|
||||
match vnstat_output.status.success() {
|
||||
true => {
|
||||
let raw_data = String::from_utf8_lossy(&vnstat_output.stdout);
|
||||
parse_vnstat_data(&raw_data)
|
||||
}
|
||||
false => Err(Error::StdErr(
|
||||
String::from_utf8_lossy(&vnstat_output.stderr).to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse the `stdout` data from the `vnstat <iface> --oneline` command.
|
||||
pub fn parse_vnstat_data(raw_data: &str) -> Result<Vnstat, Error> {
|
||||
let data_vec: Vec<&str> = raw_data.trim().split(';').collect();
|
||||
|
||||
// find the position of the space (' ') in each element (set to `0` if none is found)
|
||||
let space_vec: Vec<usize> = data_vec
|
||||
.iter()
|
||||
.map(|element| element.find(' ').unwrap_or(0))
|
||||
.collect();
|
||||
|
||||
// day data
|
||||
let (day_rx, day_rx_unit) = data_vec[3].split_at(space_vec[3]);
|
||||
let (day_tx, day_tx_unit) = data_vec[4].split_at(space_vec[4]);
|
||||
let (day_total, day_total_unit) = data_vec[5].split_at(space_vec[5]);
|
||||
let (day_avg_rate, day_avg_rate_unit) = data_vec[6].split_at(space_vec[6]);
|
||||
|
||||
// month data
|
||||
let (month_rx, month_rx_unit) = data_vec[8].split_at(space_vec[8]);
|
||||
let (month_tx, month_tx_unit) = data_vec[9].split_at(space_vec[9]);
|
||||
let (month_total, month_total_unit) = data_vec[10].split_at(space_vec[10]);
|
||||
let (month_avg_rate, month_avg_rate_unit) = data_vec[11].split_at(space_vec[11]);
|
||||
|
||||
// all time data
|
||||
let (all_time_rx, all_time_rx_unit) = data_vec[12].split_at(space_vec[12]);
|
||||
let (all_time_tx, all_time_tx_unit) = data_vec[13].split_at(space_vec[13]);
|
||||
let (all_time_total, all_time_total_unit) = data_vec[14].split_at(space_vec[14]);
|
||||
|
||||
let parsed_data = Vnstat {
|
||||
iface: data_vec[1].to_string(),
|
||||
today: data_vec[2].to_string(),
|
||||
day_rx: f32::from_str(day_rx)?,
|
||||
day_rx_unit: day_rx_unit.trim().to_string(),
|
||||
day_tx: f32::from_str(day_tx)?,
|
||||
day_tx_unit: day_tx_unit.trim().to_string(),
|
||||
day_total: f32::from_str(day_total)?,
|
||||
day_total_unit: day_total_unit.trim().to_string(),
|
||||
day_avg_rate: f32::from_str(day_avg_rate)?,
|
||||
day_avg_rate_unit: day_avg_rate_unit.trim().to_string(),
|
||||
month: data_vec[7].to_string(),
|
||||
month_rx: f32::from_str(month_rx)?,
|
||||
month_rx_unit: month_rx_unit.trim().to_string(),
|
||||
month_tx: f32::from_str(month_tx)?,
|
||||
month_tx_unit: month_tx_unit.trim().to_string(),
|
||||
month_total: f32::from_str(month_total)?,
|
||||
month_total_unit: month_total_unit.trim().to_string(),
|
||||
month_avg_rate: f32::from_str(month_avg_rate)?,
|
||||
month_avg_rate_unit: month_avg_rate_unit.trim().to_string(),
|
||||
all_time_rx: f32::from_str(all_time_rx)?,
|
||||
all_time_rx_unit: all_time_rx_unit.trim().to_string(),
|
||||
all_time_tx: f32::from_str(all_time_tx)?,
|
||||
all_time_tx_unit: all_time_tx_unit.trim().to_string(),
|
||||
all_time_total: f32::from_str(all_time_total)?,
|
||||
all_time_total_unit: all_time_total_unit.trim().to_string(),
|
||||
};
|
||||
|
||||
Ok(parsed_data)
|
||||
}
|
Loading…
Reference in New Issue