From 96d405a3536884e9046972d5d413aa61b3bec2ec Mon Sep 17 00:00:00 2001 From: ClementTsang Date: Thu, 6 Feb 2020 23:58:33 -0500 Subject: [PATCH] Add config options --- src/constants.rs | 7 +++ src/main.rs | 111 ++++++++++++++++++++++++++++++++++++++------- src/utils/error.rs | 13 ++++++ 3 files changed, 115 insertions(+), 16 deletions(-) diff --git a/src/constants.rs b/src/constants.rs index b512f0a1..2dbb546d 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -4,3 +4,10 @@ pub const TICK_RATE_IN_MILLISECONDS: u64 = 200; // How fast the screen refreshes pub const DEFAULT_REFRESH_RATE_IN_MILLISECONDS: u128 = 1000; pub const MAX_KEY_TIMEOUT_IN_MILLISECONDS: u128 = 1000; pub const NUM_COLOURS: i32 = 256; + +// Config and flags +pub const DEFAULT_CONFIG_FILE_PATH: &str = "~/.config/btm/btm.conf"; + +pub const KELVIN: &str = "kelvin"; +pub const FAHRENHEIT: &str = "fahrenheit"; +pub const CELSIUS: &str = "celsius"; diff --git a/src/main.rs b/src/main.rs index 41addd79..fdf81072 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,8 @@ extern crate failure; #[macro_use] extern crate lazy_static; +use serde::Deserialize; + use crossterm::{ event::{ poll, read, DisableMouseCapture, EnableMouseCapture, Event as CEvent, KeyCode, @@ -38,7 +40,7 @@ mod constants; mod data_conversion; use app::data_harvester::{self, processes::ProcessSorting}; -use constants::TICK_RATE_IN_MILLISECONDS; +use constants::*; use data_conversion::*; use utils::error::{self, BottomError}; @@ -53,8 +55,22 @@ enum ResetEvent { Reset, } +#[derive(Deserialize)] +struct Config { + avg_cpu: Option, + dot_marker: Option, + temperature_type: Option, + rate: Option, + left_legend: Option, + current_usage: Option, + group_processes: Option, + case_sensitive: Option, + whole_word: Option, + regex: Option, +} + fn main() -> error::Result<()> { - // Parse command line options + //Parse command line options let matches = clap_app!(app => (name: crate_name!()) (version: crate_version!()) @@ -62,7 +78,6 @@ fn main() -> error::Result<()> { (about: crate_description!()) (@arg AVG_CPU: -a --avg_cpu "Enables showing the average CPU usage.") (@arg DOT_MARKER: -m --dot_marker "Use a dot marker instead of the default braille marker.") - (@arg DEBUG: -d --debug "Enables debug mode, which will output a log file.") (@group TEMPERATURE_TYPE => (@arg KELVIN : -k --kelvin "Sets the temperature type to Kelvin.") (@arg FAHRENHEIT : -f --fahrenheit "Sets the temperature type to Fahrenheit.") @@ -71,20 +86,39 @@ fn main() -> error::Result<()> { (@arg RATE_MILLIS: -r --rate +takes_value "Sets a refresh rate in milliseconds; the minimum is 250ms, defaults to 1000ms. Smaller values may take more resources.") (@arg LEFT_LEGEND: -l --left_legend "Puts external chart legends on the left side rather than the default right side.") (@arg USE_CURR_USAGE: -u --current_usage "Within Linux, sets a process' CPU usage to be based on the total current CPU usage, rather than assuming 100% usage.") - //(@arg CONFIG_LOCATION: -co --config +takes_value "Sets the location of the config file. Expects a config file in the JSON format.") + (@arg CONFIG_LOCATION: -C --config +takes_value "Sets the location of the config file. Expects a config file in the TOML format.") //(@arg BASIC_MODE: -b --basic "Sets bottom to basic mode, not showing graphs and only showing basic tables.") (@arg GROUP_PROCESSES: -g --group "Groups processes with the same name together on launch.") (@arg CASE_SENSITIVE: -S --case_sensitive "Match case when searching by default.") - (@arg WHOLE_WORD: -W --whole "Match whole word when searching by default.") + (@arg WHOLE_WORD: -W --whole_word "Match whole word when searching by default.") (@arg REGEX_DEFAULT: -R --regex "Use regex in searching by default.") ) .get_matches(); + if cfg!(debug_assertions) { + utils::logging::init_logger()?; + } + + let config_path = std::path::Path::new( + matches + .value_of("CONFIG_LOCATION") + .unwrap_or(DEFAULT_CONFIG_FILE_PATH), + ); + + let config_string = std::fs::read_to_string(config_path); + let config_toml: Config = if let Ok(config_str) = config_string { + toml::from_str(&config_str)? + } else { + toml::from_str("")? + }; + let update_rate_in_milliseconds: u128 = if matches.is_present("RATE_MILLIS") { matches .value_of("RATE_MILLIS") - .unwrap_or(&constants::DEFAULT_REFRESH_RATE_IN_MILLISECONDS.to_string()) + .unwrap_or(&DEFAULT_REFRESH_RATE_IN_MILLISECONDS.to_string()) .parse::()? + } else if let Some(rate) = config_toml.rate { + rate as u128 } else { constants::DEFAULT_REFRESH_RATE_IN_MILLISECONDS }; @@ -99,24 +133,53 @@ fn main() -> error::Result<()> { }); } - // Attempt to create debugging... - let enable_debugging = matches.is_present("DEBUG"); - if enable_debugging || cfg!(debug_assertions) { - utils::logging::init_logger()?; - } - // Set other settings let temperature_type = if matches.is_present("FAHRENHEIT") { data_harvester::temperature::TemperatureType::Fahrenheit } else if matches.is_present("KELVIN") { data_harvester::temperature::TemperatureType::Kelvin + } else if matches.is_present("CELSIUS") { + data_harvester::temperature::TemperatureType::Celsius + } else if let Some(temp_type) = config_toml.temperature_type { + // Give lowest priority to config. + match temp_type.as_str() { + constants::FAHRENHEIT => data_harvester::temperature::TemperatureType::Fahrenheit, + constants::KELVIN => data_harvester::temperature::TemperatureType::Kelvin, + constants::CELSIUS => data_harvester::temperature::TemperatureType::Celsius, + _ => data_harvester::temperature::TemperatureType::Celsius, + } } else { data_harvester::temperature::TemperatureType::Celsius }; - let show_average_cpu = matches.is_present("AVG_CPU"); - let use_dot = matches.is_present("DOT_MARKER"); - let left_legend = matches.is_present("LEFT_LEGEND"); - let use_current_cpu_total = matches.is_present("USE_CURR_USAGE"); + let show_average_cpu = if matches.is_present("AVG_CPU") { + true + } else if let Some(avg_cpu) = config_toml.avg_cpu { + avg_cpu + } else { + false + }; + let use_dot = if matches.is_present("DOT_MARKER") { + true + } else if let Some(dot_marker) = config_toml.dot_marker { + dot_marker + } else { + false + }; + let left_legend = if matches.is_present("LEFT_LEGEND") { + true + } else if let Some(left_legend) = config_toml.left_legend { + left_legend + } else { + false + }; + + let use_current_cpu_total = if matches.is_present("USE_CURR_USAGE") { + true + } else if let Some(current_usage) = config_toml.current_usage { + current_usage + } else { + false + }; // Create "app" struct, which will control most of the program and store settings/state let mut app = app::App::new( @@ -131,19 +194,35 @@ fn main() -> error::Result<()> { // Enable grouping immediately if set. if matches.is_present("GROUP_PROCESSES") { app.toggle_grouping(); + } else if let Some(grouping) = config_toml.group_processes { + if grouping { + app.toggle_grouping(); + } } // Set default search method if matches.is_present("CASE_SENSITIVE") { app.search_state.toggle_ignore_case(); + } else if let Some(case_sensitive) = config_toml.case_sensitive { + if case_sensitive { + app.search_state.toggle_ignore_case(); + } } if matches.is_present("WHOLE_WORD") { app.search_state.toggle_search_whole_word(); + } else if let Some(whole_word) = config_toml.whole_word { + if whole_word { + app.search_state.toggle_search_whole_word(); + } } if matches.is_present("REGEX_DEFAULT") { app.search_state.toggle_search_regex(); + } else if let Some(regex) = config_toml.regex { + if regex { + app.search_state.toggle_search_regex(); + } } // Set up up tui and crossterm diff --git a/src/utils/error.rs b/src/utils/error.rs index 145f6684..2383acf9 100644 --- a/src/utils/error.rs +++ b/src/utils/error.rs @@ -40,6 +40,11 @@ pub enum BottomError { /// The data provided is the error found. #[fail(display = "ERROR: Invalid fern error: {}", message)] FernError { message: String }, + /// An error to represent errors with fern + /// + /// The data provided is the error found. + #[fail(display = "ERROR: Invalid config file error: {}", message)] + ConfigError { message: String }, } impl From for BottomError { @@ -80,6 +85,14 @@ impl From for BottomError { } } +impl From for BottomError { + fn from(err: toml::de::Error) -> Self { + BottomError::ConfigError { + message: err.to_string(), + } + } +} + impl From for BottomError { fn from(err: fern::InitError) -> Self { BottomError::FernError {