From bb44f85e42b242ab6ca59a1a8f43b2b0205b8726 Mon Sep 17 00:00:00 2001 From: Lucien Cartier-Tilet Date: Thu, 9 Dec 2021 06:02:04 +0100 Subject: [PATCH] It seems to somewhat work on my machine --- Cargo.lock | 35 +++++++++-- Cargo.toml | 9 ++- src/args.rs | 9 ++- src/cli.rs | 155 ++++++++++++++++++++++++++++++++++++++++++++++++- src/main.rs | 4 ++ src/sysinfo.rs | 36 +++++++----- 6 files changed, 222 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e297979..7da07bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + [[package]] name = "atty" version = "0.2.14" @@ -140,6 +149,12 @@ dependencies = [ "libc", ] +[[package]] +name = "humansize" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02296996cb8796d7c6e3bc2d9211b7802812d36999a51bb754123ead7d37d026" + [[package]] name = "indexmap" version = "1.7.0" @@ -217,6 +232,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "output_vt100" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9" +dependencies = [ + "winapi", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -254,7 +278,10 @@ dependencies = [ name = "pumo-system-info" version = "0.1.0" dependencies = [ + "ansi_term", "clap", + "humansize", + "output_vt100", "serde", "serde_json", "sysinfo", @@ -308,18 +335,18 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.130" +version = "1.0.131" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" +checksum = "b4ad69dfbd3e45369132cc64e6748c2d65cdfb001a2b1c232d128b4ad60561c1" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.130" +version = "1.0.131" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" +checksum = "b710a83c4e0dff6a3d511946b95274ad9ca9e5d3ae497b63fda866ac955358d2" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 482d7e2..b63184f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,10 @@ publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -sysinfo = "0.21" +sysinfo = "0.21.2" clap = { version = "3.0.0-rc.0", default-features = false, features = ["std", "derive", "color"] } -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" +serde = { version = "1.0.131", features = ["derive"] } +serde_json = "1.0.72" +output_vt100 = "0.1.2" +ansi_term = "0.12.1" +humansize = "1.1.1" diff --git a/src/args.rs b/src/args.rs index 0b3483b..174e5a5 100644 --- a/src/args.rs +++ b/src/args.rs @@ -24,7 +24,7 @@ conditions; see the GPL-3.0 license or newer #[derive(Parser, Debug)] #[clap(name="Pumo System Info",about,version,author,after_long_help=AFTER_HELP)] pub struct Args { - #[clap(short, long)] + #[clap(short, long, help="JSON output")] pub json: bool, #[clap(short, long, default_value_t = 75)] @@ -33,6 +33,9 @@ pub struct Args { #[clap(short, long, default_value_t = 90)] pub critical_level: u8, - #[clap(short = 'W', long, default_value_t = 72)] - pub width: u8, + #[clap(short = 'W', long, default_value_t = 72, help="Width of the console output")] + pub width: usize, + + #[clap(short, long, help="Display seconds in uptime")] + pub seconds: bool, } diff --git a/src/cli.rs b/src/cli.rs index 4d6fef2..4939cc3 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -14,8 +14,159 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -mod sysinfo; -pub fn print_cli() { +use crate::sysinfo::{Machine, Memory}; +use crate::args::Args; +fn get_uptime(mut uptime: u64, seconds: bool) -> String { + let mut uptime_str = String::new(); + + let minute_length = 60_u64; + let hour_length = minute_length * 60; + let day_length = hour_length * 24; + + let days = uptime / day_length; + uptime %= day_length; + if days > 0 { + uptime_str.push_str(format!("{} days ", days).as_str()); + } + + let hours = uptime / hour_length; + uptime %= hour_length; + if days > 0 || hours > 0 { + uptime_str.push_str(format!("{} hours ", hours).as_str()); + } + + let minutes = uptime / minute_length; + uptime_str.push_str(format!("{} minutes", minutes).as_str()); + + if seconds { + uptime %= minute_length; + uptime_str.push_str(format!(" {} seconds", uptime).as_str()); + } + + uptime_str +} + +fn get_name_max_width(disks: &[Memory]) -> usize { + disks + .iter() + .max_by(|x, y| x.name.len().cmp(&y.name.len())) + .unwrap() + .name + .len() +} + +fn print_ramp( + memory: &Memory, + left_margin: usize, + total_width: usize, +) -> String { + use ansi_term::{Color, Style}; + use humansize::{FileSize, file_size_opts as foptions}; + + let humansize_option = foptions::FileSizeOpts { + decimal_places: 1, + decimal_zeroes: 1, + space: false, + ..foptions::CONVENTIONAL + }; + + let ramp_length = total_width - left_margin - 28; + let full = (ramp_length as f32 * memory.percentage()) as usize; + let empty = ramp_length - full; + + let percentage = memory.percentage() * 100_f32; + let warning = percentage >= 75_f32; + let critical = percentage >= 90_f32; + + let fg = if critical { + Color::Red + } else if warning { + Color::Yellow + } else { + Color::Green + }; + + + let mut ramp = String::from("["); + + for _ in 0..full { + ramp.push_str(Style::new().fg(fg).paint("=").to_string().as_str()); + } + for _ in 0..empty { + ramp.push('='); + } + + ramp.push_str( + format!( + "] {:>6} / {:>6} ", + memory + .used() + .file_size(&humansize_option) + .unwrap() + .replace("B", ""), + memory + .size + .file_size(&humansize_option) + .unwrap() + .replace("B", "") + ) + .as_str(), + ); + let percentage_str = Style::new().fg(fg).paint(format!("{:3.0}%", percentage)).to_string(); + ramp.push_str(format!("({}%)", percentage_str).as_str()); + + ramp +} + +pub fn print_cli(machine: Machine, args: Args) { + let width = args.width; + + let mut separator = String::from(""); + for _ in 0..width { + separator.push('='); + } + + let left_max_width = "Hostname".len().max(get_name_max_width(&machine.disks)); + let uptime = get_uptime(machine.uptime, args.seconds); + let right_width = machine.kernel.len().max(uptime.len()) + 3; + + println!("{}", separator); + println!("{0:.<1$}: {2}{3:>4$}.: {5}", + "OS", + left_max_width, + machine.os, + "Kernel", + width as usize - (left_max_width + 2 + machine.os.len()) - right_width, + machine.kernel + ); + println!("{0:.<1$}: {2}{3:>4$}.: {5}", + "Hostname", + left_max_width, + machine.hostname, + "Uptime", + width as usize - (left_max_width + 2 + machine.hostname.len()) - right_width, + uptime + ); + + println!("{0:.<1$}: {2}", + "Mem", + left_max_width, + print_ramp(&machine.ram, left_max_width, width) + ); + println!("{0:.<1$}: {2}", + "Swap", + left_max_width, + print_ramp(&machine.swap, left_max_width, width) + ); + + for disk in machine.disks { + println!("{0:.<1$}: {2}", + disk.name, + left_max_width, + print_ramp(&disk, left_max_width, width) + ); + } + println!("{}", separator); } diff --git a/src/main.rs b/src/main.rs index 089d100..d861ba9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,14 +16,18 @@ mod sysinfo; mod args; +mod cli; use clap::Parser; fn main() { + output_vt100::init(); + let args = args::Args::parse(); let machine = sysinfo::Machine::new(); if args.json { println!("{}", serde_json::to_string(&machine).unwrap()); } else { + cli::print_cli(machine, args); } } diff --git a/src/sysinfo.rs b/src/sysinfo.rs index ca1250f..d4ef344 100644 --- a/src/sysinfo.rs +++ b/src/sysinfo.rs @@ -18,27 +18,35 @@ use serde::Serialize; use sysinfo::{System, SystemExt, DiskExt}; #[derive(Debug, Serialize)] -struct Memory { - name: String, - size: u64, - available: u64 +pub struct Memory { + pub name: String, + pub size: u64, + pub available: u64 } impl Memory { pub fn new(name: String, size: u64, available: u64) -> Self { Self { name, size, available } } + + pub fn used(&self) -> u64 { + self.size - self.available + } + + pub fn percentage(&self) -> f32 { + self.used() as f32 / self.size as f32 + } } #[derive(Debug, Serialize)] pub struct Machine { - os: String, - kernel: String, - hostname: String, - uptime: u64, - ram: Memory, - swap: Memory, - disks: Vec, + pub os: String, + pub kernel: String, + pub hostname: String, + pub uptime: u64, + pub ram: Memory, + pub swap: Memory, + pub disks: Vec, } impl Machine { @@ -50,14 +58,14 @@ impl Machine { .iter() .map(|x| { Memory::new( - x.name().to_str().unwrap().to_string(), + x.mount_point().to_str().unwrap().to_string(), x.total_space(), x.available_space(), ) }) .collect::>(); - let ram = Memory::new("RAM".into(), sys.available_memory(), sys.available_memory()); - let swap = Memory::new("Swap".into(), sys.total_swap(), sys.free_swap()); + let ram = Memory::new("RAM".into(), sys.total_memory() * 1000, sys.available_memory() * 1000); + let swap = Memory::new("Swap".into(), sys.total_swap() * 1000, sys.free_swap() * 1000); Self { os: sys.name().unwrap(), kernel: sys.kernel_version().unwrap(),