From 6d9ed34dcb0d7a14142fdd6dbef3f5e070dd95eb Mon Sep 17 00:00:00 2001 From: ClementTsang Date: Sat, 14 Sep 2019 16:46:14 -0400 Subject: [PATCH] Added clap support for command line options, as well as tweaked some table placement. --- README.md | 1 + src/app.rs | 30 ++++++--------- src/app/data_collection/temperature.rs | 15 +++++++- src/canvas.rs | 53 ++++++++++++-------------- src/main.rs | 51 ++++++++++++++++++++----- 5 files changed, 91 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index 6e660f61..7810cbcc 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ Currently, I'm unable to test on MacOS, so I'm not sure how well this will work, * As mentioned, this project is very much inspired by both [gotop](https://github.com/cjbassi/gotop) and [gtop](https://github.com/aksakalli/gtop) . * This application was written with the following libraries: + * clap * [crossterm](https://github.com/TimonPost/crossterm) * [heim](https://github.com/heim-rs/heim) * [sysinfo](https://github.com/GuillaumeGomez/sysinfo) diff --git a/src/app.rs b/src/app.rs index 2ac16ad9..2a54739e 100644 --- a/src/app.rs +++ b/src/app.rs @@ -5,22 +5,14 @@ use std::collections::HashMap; use sysinfo::{System, SystemExt}; #[allow(dead_code)] -#[derive(Clone)] -pub enum TemperatureType { - Celsius, - Kelvin, - Fahrenheit, -} - -#[allow(dead_code)] -pub struct App<'a> { - title : &'a str, +pub struct App { pub should_quit : bool, pub process_sorting_type : processes::ProcessSorting, pub process_sorting_reverse : bool, pub to_be_resorted : bool, pub current_selected_process_position : u64, - pub temperature_type : TemperatureType, + pub temperature_type : data_collection::temperature::TemperatureType, + pub update_rate_in_milliseconds : u64, } fn set_if_valid(result : &Result, value_to_set : &mut T) { @@ -56,7 +48,7 @@ pub struct DataState { prev_pid_stats : HashMap, // TODO: Purge list? prev_idle : f64, prev_non_idle : f64, - temperature_type : TemperatureType, + temperature_type : data_collection::temperature::TemperatureType, } impl Default for DataState { @@ -69,7 +61,7 @@ impl Default for DataState { prev_pid_stats : HashMap::new(), prev_idle : 0_f64, prev_non_idle : 0_f64, - temperature_type : TemperatureType::Celsius, + temperature_type : data_collection::temperature::TemperatureType::Celsius, } } } @@ -79,7 +71,7 @@ impl DataState { self.stale_max_seconds = stale_max_seconds; } - pub fn set_temperature_type(&mut self, temperature_type : TemperatureType) { + pub fn set_temperature_type(&mut self, temperature_type : data_collection::temperature::TemperatureType) { self.temperature_type = temperature_type; } @@ -108,7 +100,7 @@ impl DataState { set_if_valid(&disks::get_disk_usage_list().await, &mut self.data.list_of_disks); push_if_valid(&disks::get_io_usage_list(false).await, &mut self.data.list_of_io); push_if_valid(&disks::get_io_usage_list(true).await, &mut self.data.list_of_physical_io); - set_if_valid(&temperature::get_temperature_data().await, &mut self.data.list_of_temperature_sensor); + set_if_valid(&temperature::get_temperature_data(&self.temperature_type).await, &mut self.data.list_of_temperature_sensor); if self.first_run { self.data = Data::default(); @@ -170,16 +162,16 @@ impl DataState { } } -impl<'a> App<'a> { - pub fn new(title : &str) -> App { +impl App { + pub fn new(temperature_type : data_collection::temperature::TemperatureType, update_rate_in_milliseconds : u64) -> App { App { - title, process_sorting_type : processes::ProcessSorting::CPU, // TODO: Change this based on input args... basically set this on app creation should_quit : false, process_sorting_reverse : true, to_be_resorted : false, current_selected_process_position : 0, - temperature_type : TemperatureType::Celsius, + temperature_type, + update_rate_in_milliseconds, } } diff --git a/src/app/data_collection/temperature.rs b/src/app/data_collection/temperature.rs index d1c49c1a..19be8871 100644 --- a/src/app/data_collection/temperature.rs +++ b/src/app/data_collection/temperature.rs @@ -6,7 +6,14 @@ pub struct TempData { pub temperature : f32, } -pub async fn get_temperature_data() -> Result, heim::Error> { +#[derive(Clone, Debug)] +pub enum TemperatureType { + Celsius, + Kelvin, + Fahrenheit, +} + +pub async fn get_temperature_data(temp_type : &TemperatureType) -> Result, heim::Error> { let mut temperature_vec : Vec = Vec::new(); let mut sensor_data = heim::sensors::temperatures(); @@ -14,7 +21,11 @@ pub async fn get_temperature_data() -> Result, heim::Error> { if let Ok(sensor) = sensor { temperature_vec.push(TempData { component_name : Box::from(sensor.unit()), - temperature : sensor.current().get::(), // TODO: Allow for toggling this! + temperature : match temp_type { + TemperatureType::Celsius => sensor.current().get::(), + TemperatureType::Kelvin => sensor.current().get::(), + TemperatureType::Fahrenheit => sensor.current().get::(), + }, }); } } diff --git a/src/canvas.rs b/src/canvas.rs index d4e42932..e7d6db05 100644 --- a/src/canvas.rs +++ b/src/canvas.rs @@ -74,11 +74,6 @@ pub fn draw_data(terminal : &mut Terminal, canvas_ .margin(0) .constraints([Constraint::Percentage(40), Constraint::Percentage(60)].as_ref()) .split(bottom_divided_chunk_1[0]); - let bottom_divided_chunk_1_2 = Layout::default() - .direction(Direction::Horizontal) - .margin(0) - .constraints([Constraint::Percentage(40), Constraint::Percentage(60)].as_ref()) - .split(bottom_divided_chunk_1[1]); // Set up blocks and their components @@ -133,18 +128,14 @@ pub fn draw_data(terminal : &mut Terminal, canvas_ Block::default().title("Network").borders(Borders::ALL).border_style(border_style).render(&mut f, middle_chunks[1]); // Temperature table - Table::new(["Sensor", "Temperature"].iter(), temperature_rows) - .block(Block::default().title("Temperatures").borders(Borders::ALL).border_style(border_style)) - .header_style(Style::default().fg(Color::LightBlue)) - .widths(&[15, 5]) - .render(&mut f, bottom_divided_chunk_1_1[0]); - - // Disk usage table - Table::new(["Disk", "Mount", "Used", "Total", "Free"].iter(), disk_rows) - .block(Block::default().title("Disk Usage").borders(Borders::ALL).border_style(border_style)) - .header_style(Style::default().fg(Color::LightBlue).modifier(Modifier::BOLD)) - .widths(&[15, 10, 5, 5, 5]) - .render(&mut f, bottom_divided_chunk_1_2[0]); + { + let width = f64::from(bottom_divided_chunk_1_1[0].width); + Table::new(["Sensor", "Temp"].iter(), temperature_rows) + .block(Block::default().title("Temperatures").borders(Borders::ALL).border_style(border_style)) + .header_style(Style::default().fg(Color::LightBlue)) + .widths(&[(width * 0.45) as u16, (width * 0.4) as u16]) + .render(&mut f, bottom_divided_chunk_1_1[0]); + } // Temp graph Block::default() @@ -153,19 +144,25 @@ pub fn draw_data(terminal : &mut Terminal, canvas_ .border_style(border_style) .render(&mut f, bottom_divided_chunk_1_1[1]); - // IO graph - Block::default() - .title("IO Usage") - .borders(Borders::ALL) - .border_style(border_style) - .render(&mut f, bottom_divided_chunk_1_2[1]); + // Disk usage table + { + let width = f64::from(bottom_divided_chunk_1[1].width); + Table::new(["Disk", "Mount", "Used", "Total", "Free"].iter(), disk_rows) + .block(Block::default().title("Disk Usage").borders(Borders::ALL).border_style(border_style)) + .header_style(Style::default().fg(Color::LightBlue).modifier(Modifier::BOLD)) + .widths(&[(width * 0.25) as u16, (width * 0.2) as u16, (width * 0.15) as u16, (width * 0.15) as u16, (width * 0.15) as u16]) + .render(&mut f, bottom_divided_chunk_1[1]); + } // Processes table - Table::new(["PID", "Name", "CPU%", "Mem%"].iter(), process_rows) - .block(Block::default().title("Processes").borders(Borders::ALL).border_style(border_style)) - .header_style(Style::default().fg(Color::LightBlue)) - .widths(&[5, 15, 10, 10]) - .render(&mut f, bottom_chunks[1]); + { + let width = f64::from(bottom_chunks[1].width); + Table::new(["PID", "Name", "CPU%", "Mem%"].iter(), process_rows) + .block(Block::default().title("Processes").borders(Borders::ALL).border_style(border_style)) + .header_style(Style::default().fg(Color::LightBlue)) + .widths(&[(width * 0.2) as u16, (width * 0.35) as u16, (width * 0.2) as u16, (width * 0.2) as u16]) + .render(&mut f, bottom_chunks[1]); + } })?; Ok(()) diff --git a/src/main.rs b/src/main.rs index bfb13768..ba1144c0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,25 +16,56 @@ mod canvas; #[macro_use] extern crate log; +#[macro_use] +extern crate clap; + enum Event { Input(I), Update(Box), } const STALE_MAX_MILLISECONDS : u64 = 60 * 1000; +const TICK_RATE_IN_MILLISECONDS : u64 = 250; #[tokio::main] async fn main() -> Result<(), io::Error> { + let _log = utils::logging::init_logger(); // TODO: Error handling + + let matches = clap_app!(app => + (name: "rustop") + (version: crate_version!()) + (author: "Clement Tsang ") + (about: "A graphical top clone.") + (@arg THEME: -t --theme +takes_value "Sets a colour theme.") + (@group TEMPERATURE_TYPE => + (@arg celsius : -c --celsius "Sets the temperature type to Celsius. This is the default option.") + (@arg fahrenheit : -f --fahrenheit "Sets the temperature type to Fahrenheit.") + (@arg kelvin : -k --kelvin "Sets the temperature type to Kelvin.") + + ) + (@arg RATE: -r --rate +takes_value "Sets a refresh rate in milliseconds, min is 250ms, defaults to 1000ms. Higher values may take more resources.") + ) + .after_help("Themes:") + .get_matches(); + let screen = AlternateScreen::to_alternate(true)?; let backend = CrosstermBackend::with_alternate_screen(screen)?; let mut terminal = Terminal::new(backend)?; - let tick_rate_in_milliseconds : u64 = 250; - let update_rate_in_milliseconds : u64 = 500; // TODO: Must set a check to prevent this from going into negatives! + let update_rate_in_milliseconds : u64 = matches.value_of("rate").unwrap_or("1000").parse::().unwrap_or(1000); + let temperature_type = if matches.is_present("fahrenheit") { + app::data_collection::temperature::TemperatureType::Fahrenheit + } + else if matches.is_present("kelvin") { + app::data_collection::temperature::TemperatureType::Kelvin + } + else { + app::data_collection::temperature::TemperatureType::Celsius + }; - let mut app = app::App::new("rustop"); + info!("Temperature type: {:?}", temperature_type); - let _log = utils::logging::init_logger(); + let mut app = app::App::new(temperature_type, if update_rate_in_milliseconds < 250 { 250 } else { update_rate_in_milliseconds }); terminal.hide_cursor()?; // Setup input handling @@ -77,7 +108,7 @@ async fn main() -> Result<(), io::Error> { let mut canvas_data = canvas::CanvasData::default(); loop { - if let Ok(recv) = rx.recv_timeout(Duration::from_millis(tick_rate_in_milliseconds)) { + if let Ok(recv) = rx.recv_timeout(Duration::from_millis(TICK_RATE_IN_MILLISECONDS)) { match recv { Event::Input(event) => { debug!("Input event fired!"); @@ -125,17 +156,17 @@ async fn main() -> Result<(), io::Error> { Ok(()) } -fn update_temp_row(app_data : &app::Data, temp_type : &app::TemperatureType) -> Vec> { +fn update_temp_row(app_data : &app::Data, temp_type : &app::data_collection::temperature::TemperatureType) -> Vec> { let mut sensor_vector : Vec> = Vec::new(); for sensor in &app_data.list_of_temperature_sensor { sensor_vector.push(vec![ sensor.component_name.to_string(), - sensor.temperature.to_string() + (sensor.temperature.ceil() as u64).to_string() + match temp_type { - app::TemperatureType::Celsius => "C", - app::TemperatureType::Kelvin => "K", - app::TemperatureType::Fahrenheit => "F", + app::data_collection::temperature::TemperatureType::Celsius => "C", + app::data_collection::temperature::TemperatureType::Kelvin => "K", + app::data_collection::temperature::TemperatureType::Fahrenheit => "F", }, ]); }