mirror of
https://github.com/ClementTsang/bottom
synced 2024-11-10 14:44:18 +00:00
refactor: Update error messages w/ anyhow and thiserror (#216)
Refactoring and updating of error messages + tests to be more useful.
This commit is contained in:
parent
5ed573157c
commit
a4ddd649e1
17 changed files with 240 additions and 130 deletions
2
.cargo-husky/hooks/pre-push
Executable file
2
.cargo-husky/hooks/pre-push
Executable file
|
@ -0,0 +1,2 @@
|
|||
echo "Running pre-push hook: cargo +nightly clippy -- -D clippy::all"
|
||||
cargo +nightly clippy -- -D clippy::all
|
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
|
@ -38,6 +38,7 @@
|
|||
"curr",
|
||||
"czvf",
|
||||
"fpath",
|
||||
"fract",
|
||||
"gotop",
|
||||
"gtop",
|
||||
"haase",
|
||||
|
|
28
Cargo.lock
generated
28
Cargo.lock
generated
|
@ -27,6 +27,12 @@ dependencies = [
|
|||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b"
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
version = "0.4.6"
|
||||
|
@ -131,6 +137,7 @@ dependencies = [
|
|||
name = "bottom"
|
||||
version = "0.4.7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assert_cmd",
|
||||
"backtrace",
|
||||
"battery",
|
||||
|
@ -151,6 +158,7 @@ dependencies = [
|
|||
"regex",
|
||||
"serde",
|
||||
"sysinfo",
|
||||
"thiserror",
|
||||
"toml",
|
||||
"tui",
|
||||
"typed-builder",
|
||||
|
@ -1273,6 +1281,26 @@ dependencies = [
|
|||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.0.1"
|
||||
|
|
12
Cargo.toml
12
Cargo.toml
|
@ -24,14 +24,17 @@ lto = "fat"
|
|||
codegen-units = 1
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.32"
|
||||
battery = "0.7.6"
|
||||
crossterm = "0.17"
|
||||
chrono = "0.4.15"
|
||||
crossterm = "0.17"
|
||||
ctrlc = {version = "3.1", features = ["termination"]}
|
||||
clap = "2.33"
|
||||
dirs = "3.0.1"
|
||||
futures = "0.3.5"
|
||||
heim = "0.0.10"
|
||||
itertools = "0.9.0"
|
||||
libc = "0.2"
|
||||
regex = "1.3"
|
||||
sysinfo = "0.15.1"
|
||||
toml = "0.5.6"
|
||||
|
@ -41,8 +44,7 @@ backtrace = "0.3"
|
|||
serde = {version = "1.0", features = ["derive"] }
|
||||
unicode-segmentation = "1.6.0"
|
||||
unicode-width = "0.1"
|
||||
libc = "0.2"
|
||||
ctrlc = {version = "3.1", features = ["termination"]}
|
||||
thiserror = "1.0.20"
|
||||
tui = {version = "0.9.5", features = ["crossterm"], default-features = false }
|
||||
|
||||
# For debugging only...
|
||||
|
@ -82,5 +84,5 @@ output = "bottom_x86_64_installer.msi"
|
|||
|
||||
[dev-dependencies.cargo-husky]
|
||||
version = "1"
|
||||
default-features = false
|
||||
features = ["prepush-hook", "run-cargo-clippy"]
|
||||
default-features = false
|
||||
features = ["user-hooks"]
|
|
@ -509,7 +509,7 @@ Supported named colours are one of the following strings: `Reset, Black, Red, Gr
|
|||
| Cursor colour | The cursor's colour | `cursor_color="#ffffff"` |
|
||||
| Selected text colour | The colour of text that is selected | `scroll_entry_text_color="#ffffff"` |
|
||||
| Selected text background colour | The background colour of text that is selected | `scroll_entry_bg_color="#ffffff"` |
|
||||
| Battery bar colours | Colour used is based on percentage and no. of colours | `battery_colours=["green", "yellow", "red"]` |
|
||||
| Battery bar colours | Colour used is based on percentage and no. of colours | `battery_colors=["green", "yellow", "red"]` |
|
||||
|
||||
#### Layout
|
||||
|
||||
|
|
|
@ -253,11 +253,11 @@ fn read_proc<S: core::hash::BuildHasher>(
|
|||
.splitn(2, '(')
|
||||
.collect::<Vec<_>>()
|
||||
.last()
|
||||
.ok_or(BottomError::MinorError())?
|
||||
.ok_or(BottomError::MinorError)?
|
||||
.rsplitn(2, ')')
|
||||
.collect::<Vec<_>>()
|
||||
.last()
|
||||
.ok_or(BottomError::MinorError())?
|
||||
.ok_or(BottomError::MinorError)?
|
||||
.to_string();
|
||||
let command = {
|
||||
let cmd = read_path_contents(&pid_stat.proc_cmdline_path)?;
|
||||
|
@ -271,7 +271,7 @@ fn read_proc<S: core::hash::BuildHasher>(
|
|||
.split(')')
|
||||
.collect::<Vec<_>>()
|
||||
.last()
|
||||
.ok_or(BottomError::MinorError())?
|
||||
.ok_or(BottomError::MinorError)?
|
||||
.split_whitespace()
|
||||
.collect::<Vec<&str>>();
|
||||
let (process_state_char, process_state) = get_linux_process_state(&stat);
|
||||
|
|
|
@ -943,7 +943,25 @@ impl std::str::FromStr for BottomWidgetType {
|
|||
"empty" => Ok(BottomWidgetType::Empty),
|
||||
"battery" | "batt" => Ok(BottomWidgetType::Battery),
|
||||
_ => Err(BottomError::ConfigError(format!(
|
||||
"invalid widget type: {}", // FIXME: Make this more helpful, specify valid widget types (just go through the list)
|
||||
"\"{}\" is an invalid widget name.
|
||||
|
||||
Supported widget names:
|
||||
+--------------------------+
|
||||
| cpu |
|
||||
+--------------------------+
|
||||
| mem, memory |
|
||||
+--------------------------+
|
||||
| net, network |
|
||||
+--------------------------+
|
||||
| proc, process, processes |
|
||||
+--------------------------+
|
||||
| temp, temperature |
|
||||
+--------------------------+
|
||||
| disk |
|
||||
+--------------------------+
|
||||
| batt, battery |
|
||||
+--------------------------+
|
||||
",
|
||||
s
|
||||
))),
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
use bottom::{canvas, constants::*, data_conversion::*, options::*, utils::error, *};
|
||||
use bottom::{canvas, constants::*, data_conversion::*, options::*, *};
|
||||
|
||||
use std::{
|
||||
boxed::Box,
|
||||
|
@ -17,6 +17,7 @@ use std::{
|
|||
time::Duration,
|
||||
};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use crossterm::{
|
||||
event::EnableMouseCapture,
|
||||
execute,
|
||||
|
@ -24,18 +25,22 @@ use crossterm::{
|
|||
};
|
||||
use tui::{backend::CrosstermBackend, Terminal};
|
||||
|
||||
fn main() -> error::Result<()> {
|
||||
fn main() -> Result<()> {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
utils::logging::init_logger()?;
|
||||
}
|
||||
let matches = clap::get_matches();
|
||||
|
||||
let config: Config = create_config(matches.value_of("CONFIG_LOCATION"))?;
|
||||
let config_path = read_config(matches.value_of("CONFIG_LOCATION"))
|
||||
.context("Unable to access the given config file location.")?;
|
||||
let config: Config = create_or_get_config(&config_path)
|
||||
.context("Unable to properly parse or create the config file.")?;
|
||||
|
||||
// Get widget layout separately
|
||||
let (widget_layout, default_widget_id, default_widget_type_option) =
|
||||
get_widget_layout(&matches, &config)?;
|
||||
get_widget_layout(&matches, &config)
|
||||
.context("Found an issue while trying to build the widget layout.")?;
|
||||
|
||||
// Create "app" struct, which will control most of the program and store settings/state
|
||||
let mut app = build_app(
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use tui::style::{Color, Style};
|
||||
// use tui::style::Modifier;
|
||||
|
||||
use colour_utils::*;
|
||||
|
||||
|
@ -175,11 +174,10 @@ impl CanvasColours {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_battery_colours(&mut self, colours: &[String]) -> error::Result<()> {
|
||||
pub fn set_battery_colors(&mut self, colours: &[String]) -> error::Result<()> {
|
||||
if colours.is_empty() {
|
||||
Err(error::BottomError::ConfigError(
|
||||
"invalid colour config: battery colour list must have at least one colour!"
|
||||
.to_string(),
|
||||
"battery colour list must have at least one colour.".to_string(),
|
||||
))
|
||||
} else {
|
||||
let generated_colours: Result<Vec<_>, _> = colours
|
||||
|
|
|
@ -100,7 +100,7 @@ pub fn convert_hex_to_color(hex: &str) -> error::Result<Color> {
|
|||
fn hex_err(hex: &str) -> error::Result<u8> {
|
||||
Err(
|
||||
error::BottomError::ConfigError(format!(
|
||||
"invalid color hex: error when parsing hex value {}. It must be a valid 7 character hex string of the (ie: \"#112233\")."
|
||||
"\"{}\" is an invalid hex colour. It must be a valid 7 character hex string of the (ie: \"#112233\")."
|
||||
, hex))
|
||||
)
|
||||
}
|
||||
|
@ -124,7 +124,7 @@ pub fn convert_hex_to_color(hex: &str) -> error::Result<Color> {
|
|||
}
|
||||
|
||||
Err(error::BottomError::ConfigError(format!(
|
||||
"invalid color hex: value {} is not of valid length. It must be a 7 character string of the form \"#112233\".",
|
||||
"\"{}\" is an invalid hex colour. It must be a 7 character string of the form \"#112233\".",
|
||||
hex
|
||||
)))
|
||||
}
|
||||
|
@ -144,7 +144,7 @@ pub fn get_style_from_config(input_val: &str) -> error::Result<Style> {
|
|||
}
|
||||
} else {
|
||||
Err(error::BottomError::ConfigError(format!(
|
||||
"invalid color: value {} is not valid.",
|
||||
"value \"{}\" is not valid.",
|
||||
input_val
|
||||
)))
|
||||
}
|
||||
|
@ -161,7 +161,7 @@ pub fn get_colour_from_config(input_val: &str) -> error::Result<Color> {
|
|||
}
|
||||
} else {
|
||||
Err(error::BottomError::ConfigError(format!(
|
||||
"invalid color: value {} is not valid.",
|
||||
"value \"{}\" is not valid.",
|
||||
input_val
|
||||
)))
|
||||
}
|
||||
|
@ -175,7 +175,7 @@ fn convert_rgb_to_color(rgb_str: &str) -> error::Result<Color> {
|
|||
let rgb_list = rgb_str.split(',').collect::<Vec<&str>>();
|
||||
if rgb_list.len() != 3 {
|
||||
return Err(error::BottomError::ConfigError(format!(
|
||||
"invalid RGB color: value {} is not of valid length. It must be a comma separated value with 3 integers from 0 to 255 (ie: \"255, 0, 155\").",
|
||||
"value \"{}\" is an invalid RGB colour. It must be a comma separated value with 3 integers from 0 to 255 (ie: \"255, 0, 155\").",
|
||||
rgb_str
|
||||
)));
|
||||
}
|
||||
|
@ -194,7 +194,7 @@ fn convert_rgb_to_color(rgb_str: &str) -> error::Result<Color> {
|
|||
Ok(Color::Rgb(rgb[0], rgb[1], rgb[2]))
|
||||
} else {
|
||||
Err(error::BottomError::ConfigError(format!(
|
||||
"invalid RGB color: value {} contained invalid RGB values. It must be a comma separated value with 3 integers from 0 to 255 (ie: \"255, 0, 155\").",
|
||||
"value \"{}\" contained invalid RGB values. It must be a comma separated value with 3 integers from 0 to 255 (ie: \"255, 0, 155\").",
|
||||
rgb_str
|
||||
)))
|
||||
}
|
||||
|
@ -211,9 +211,23 @@ fn convert_name_to_color(color_name: &str) -> error::Result<Color> {
|
|||
}
|
||||
|
||||
Err(error::BottomError::ConfigError(format!(
|
||||
"invalid named color: value {} is not a supported named colour. The following are supported strings: \
|
||||
Reset, Black, Red, Green, Yellow, Blue, Magenta, Cyan, Gray, DarkGray, LightRed, LightGreen, \
|
||||
LightYellow, LightBlue, LightMagenta, LightCyan, White",
|
||||
"\"{}\" is an invalid named colour.
|
||||
|
||||
The following are supported strings:
|
||||
+--------+------------+--------------+
|
||||
| Reset | Magenta | LightYellow |
|
||||
+--------+------------+--------------+
|
||||
| Black | Cyan | LightBlue |
|
||||
+--------+------------+--------------+
|
||||
| Red | Gray | LightMagenta |
|
||||
+--------+------------+--------------+
|
||||
| Green | DarkGray | LightCyan |
|
||||
+--------+------------+--------------+
|
||||
| Yellow | LightRed | White |
|
||||
+--------+------------+--------------+
|
||||
| Blue | LightGreen | |
|
||||
+--------+------------+--------------+
|
||||
",
|
||||
color_name
|
||||
)))
|
||||
}
|
||||
|
|
|
@ -239,7 +239,7 @@ For example, suppose we have a layout that looks like:
|
|||
Setting '--default_widget_type Temp' will make the Temperature
|
||||
widget selected by default.
|
||||
|
||||
Supported widget types:
|
||||
Supported widget names:
|
||||
+--------------------------+
|
||||
| cpu |
|
||||
+--------------------------+
|
||||
|
|
95
src/lib.rs
95
src/lib.rs
|
@ -5,8 +5,10 @@ extern crate log;
|
|||
|
||||
use std::{
|
||||
boxed::Box,
|
||||
fs,
|
||||
io::{stdout, Write},
|
||||
panic::PanicInfo,
|
||||
path::PathBuf,
|
||||
thread,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
@ -18,6 +20,8 @@ use crossterm::{
|
|||
terminal::{disable_raw_mode, LeaveAlternateScreen},
|
||||
};
|
||||
|
||||
use anyhow::Context;
|
||||
|
||||
use app::{
|
||||
data_harvester::{self, processes::ProcessSorting},
|
||||
layout_manager::{UsedWidgets, WidgetDirection},
|
||||
|
@ -164,15 +168,14 @@ pub fn handle_key_event_or_break(
|
|||
false
|
||||
}
|
||||
|
||||
pub fn create_config(flag_config_location: Option<&str>) -> error::Result<Config> {
|
||||
use std::{ffi::OsString, fs};
|
||||
let config_path = if let Some(conf_loc) = flag_config_location {
|
||||
Some(OsString::from(conf_loc))
|
||||
pub fn read_config(config_location: Option<&str>) -> error::Result<Option<PathBuf>> {
|
||||
let config_path = if let Some(conf_loc) = config_location {
|
||||
Some(PathBuf::from(conf_loc))
|
||||
} else if cfg!(target_os = "windows") {
|
||||
if let Some(home_path) = dirs::config_dir() {
|
||||
let mut path = home_path;
|
||||
path.push(DEFAULT_CONFIG_FILE_PATH);
|
||||
Some(path.into_os_string())
|
||||
Some(path)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -182,13 +185,13 @@ pub fn create_config(flag_config_location: Option<&str>) -> error::Result<Config
|
|||
path.push(DEFAULT_CONFIG_FILE_PATH);
|
||||
if path.exists() {
|
||||
// If it already exists, use the old one.
|
||||
Some(path.into_os_string())
|
||||
Some(path)
|
||||
} else {
|
||||
// If it does not, use the new one!
|
||||
if let Some(config_path) = dirs::config_dir() {
|
||||
let mut path = config_path;
|
||||
path.push(DEFAULT_CONFIG_FILE_PATH);
|
||||
Some(path.into_os_string())
|
||||
Some(path)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -197,9 +200,11 @@ pub fn create_config(flag_config_location: Option<&str>) -> error::Result<Config
|
|||
None
|
||||
};
|
||||
|
||||
if let Some(config_path) = config_path {
|
||||
let path = std::path::Path::new(&config_path);
|
||||
Ok(config_path)
|
||||
}
|
||||
|
||||
pub fn create_or_get_config(config_path: &Option<PathBuf>) -> error::Result<Config> {
|
||||
if let Some(path) = config_path {
|
||||
if let Ok(config_string) = fs::read_to_string(path) {
|
||||
Ok(toml::from_str(config_string.as_str())?)
|
||||
} else {
|
||||
|
@ -229,48 +234,76 @@ pub fn try_drawing(
|
|||
|
||||
pub fn generate_config_colours(
|
||||
config: &Config, painter: &mut canvas::Painter,
|
||||
) -> error::Result<()> {
|
||||
) -> anyhow::Result<()> {
|
||||
if let Some(colours) = &config.colors {
|
||||
if let Some(border_color) = &colours.border_color {
|
||||
painter.colours.set_border_colour(border_color)?;
|
||||
painter
|
||||
.colours
|
||||
.set_border_colour(border_color)
|
||||
.context("Update 'border_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(highlighted_border_color) = &colours.highlighted_border_color {
|
||||
painter
|
||||
.colours
|
||||
.set_highlighted_border_colour(highlighted_border_color)?;
|
||||
.set_highlighted_border_colour(highlighted_border_color)
|
||||
.context("Update 'highlighted_border_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(text_color) = &colours.text_color {
|
||||
painter.colours.set_text_colour(text_color)?;
|
||||
painter
|
||||
.colours
|
||||
.set_text_colour(text_color)
|
||||
.context("Update 'text_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(avg_cpu_color) = &colours.avg_cpu_color {
|
||||
painter.colours.set_avg_cpu_colour(avg_cpu_color)?;
|
||||
painter
|
||||
.colours
|
||||
.set_avg_cpu_colour(avg_cpu_color)
|
||||
.context("Update 'avg_cpu_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(all_cpu_color) = &colours.all_cpu_color {
|
||||
painter.colours.set_all_cpu_colour(all_cpu_color)?;
|
||||
painter
|
||||
.colours
|
||||
.set_all_cpu_colour(all_cpu_color)
|
||||
.context("Update 'all_cpu_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(cpu_core_colors) = &colours.cpu_core_colors {
|
||||
painter.colours.set_cpu_colours(cpu_core_colors)?;
|
||||
painter
|
||||
.colours
|
||||
.set_cpu_colours(cpu_core_colors)
|
||||
.context("Update 'cpu_core_colors' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(ram_color) = &colours.ram_color {
|
||||
painter.colours.set_ram_colour(ram_color)?;
|
||||
painter
|
||||
.colours
|
||||
.set_ram_colour(ram_color)
|
||||
.context("Update 'ram_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(swap_color) = &colours.swap_color {
|
||||
painter.colours.set_swap_colour(swap_color)?;
|
||||
painter
|
||||
.colours
|
||||
.set_swap_colour(swap_color)
|
||||
.context("Update 'swap_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(rx_color) = &colours.rx_color {
|
||||
painter.colours.set_rx_colour(rx_color)?;
|
||||
painter
|
||||
.colours
|
||||
.set_rx_colour(rx_color)
|
||||
.context("Update 'rx_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(tx_color) = &colours.tx_color {
|
||||
painter.colours.set_tx_colour(tx_color)?;
|
||||
painter
|
||||
.colours
|
||||
.set_tx_colour(tx_color)
|
||||
.context("Update 'tx_color' in your config file..")?;
|
||||
}
|
||||
|
||||
// if let Some(rx_total_color) = &colours.rx_total_color {
|
||||
|
@ -284,33 +317,43 @@ pub fn generate_config_colours(
|
|||
if let Some(table_header_color) = &colours.table_header_color {
|
||||
painter
|
||||
.colours
|
||||
.set_table_header_colour(table_header_color)?;
|
||||
.set_table_header_colour(table_header_color)
|
||||
.context("Update 'table_header_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(scroll_entry_text_color) = &colours.selected_text_color {
|
||||
painter
|
||||
.colours
|
||||
.set_scroll_entry_text_color(scroll_entry_text_color)?;
|
||||
.set_scroll_entry_text_color(scroll_entry_text_color)
|
||||
.context("Update 'selected_text_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(scroll_entry_bg_color) = &colours.selected_bg_color {
|
||||
painter
|
||||
.colours
|
||||
.set_scroll_entry_bg_color(scroll_entry_bg_color)?;
|
||||
.set_scroll_entry_bg_color(scroll_entry_bg_color)
|
||||
.context("Update 'selected_bg_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(widget_title_color) = &colours.widget_title_color {
|
||||
painter
|
||||
.colours
|
||||
.set_widget_title_colour(widget_title_color)?;
|
||||
.set_widget_title_colour(widget_title_color)
|
||||
.context("Update 'widget_title_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(graph_color) = &colours.graph_color {
|
||||
painter.colours.set_graph_colour(graph_color)?;
|
||||
painter
|
||||
.colours
|
||||
.set_graph_colour(graph_color)
|
||||
.context("Update 'graph_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(battery_colors) = &colours.battery_colors {
|
||||
painter.colours.set_battery_colours(battery_colors)?;
|
||||
painter
|
||||
.colours
|
||||
.set_battery_colors(battery_colors)
|
||||
.context("Update 'battery_colors' in your config file.")?;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@ use layout_options::*;
|
|||
|
||||
pub mod layout_options;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
|
||||
#[derive(Default, Deserialize)]
|
||||
pub struct Config {
|
||||
pub flags: Option<ConfigFlags>,
|
||||
|
@ -70,10 +72,11 @@ pub struct ConfigColours {
|
|||
pub fn build_app(
|
||||
matches: &clap::ArgMatches<'static>, config: &Config, widget_layout: &BottomLayout,
|
||||
default_widget_id: u64, default_widget_type_option: &Option<BottomWidgetType>,
|
||||
) -> error::Result<App> {
|
||||
) -> Result<App> {
|
||||
use BottomWidgetType::*;
|
||||
let autohide_time = get_autohide_time(&matches, &config);
|
||||
let default_time_value = get_default_time_value(&matches, &config)?;
|
||||
let default_time_value = get_default_time_value(&matches, &config)
|
||||
.context("Update 'default_time_value' in your config file.")?;
|
||||
let use_basic_mode = get_use_basic_mode(&matches, &config);
|
||||
|
||||
// For processes
|
||||
|
@ -213,15 +216,18 @@ pub fn build_app(
|
|||
};
|
||||
|
||||
let app_config_fields = AppConfigFields {
|
||||
update_rate_in_milliseconds: get_update_rate_in_milliseconds(matches, config)?,
|
||||
temperature_type: get_temperature(matches, config)?,
|
||||
update_rate_in_milliseconds: get_update_rate_in_milliseconds(matches, config)
|
||||
.context("Update 'rate' in your config file.")?,
|
||||
temperature_type: get_temperature(matches, config)
|
||||
.context("Update 'temperature_type' in your config file.")?,
|
||||
show_average_cpu: get_show_average_cpu(matches, config),
|
||||
use_dot: get_use_dot(matches, config),
|
||||
left_legend: get_use_left_legend(matches, config),
|
||||
use_current_cpu_total: get_use_current_cpu_total(matches, config),
|
||||
use_basic_mode,
|
||||
default_time_value,
|
||||
time_interval: get_time_interval(matches, config)?,
|
||||
time_interval: get_time_interval(matches, config)
|
||||
.context("Update 'time_delta' in your config file.")?,
|
||||
hide_time: get_hide_time(matches, config),
|
||||
autohide_time,
|
||||
use_old_network_legend: get_use_old_network_legend(matches, config),
|
||||
|
@ -316,7 +322,7 @@ pub fn get_widget_layout(
|
|||
ret_bottom_layout
|
||||
} else {
|
||||
return Err(error::BottomError::ConfigError(
|
||||
"invalid layout config: please have at least one widget.".to_string(),
|
||||
"please have at least one widget under the '[[row]]' section.".to_string(),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
@ -340,12 +346,12 @@ fn get_update_rate_in_milliseconds(
|
|||
};
|
||||
|
||||
if update_rate_in_milliseconds < 250 {
|
||||
return Err(BottomError::InvalidArg(
|
||||
"Please set your update rate to be at least 250 milliseconds.".to_string(),
|
||||
return Err(BottomError::ConfigError(
|
||||
"set your update rate to be at least 250 milliseconds.".to_string(),
|
||||
));
|
||||
} else if update_rate_in_milliseconds as u128 > std::u64::MAX as u128 {
|
||||
return Err(BottomError::InvalidArg(
|
||||
"Please set your update rate to be at most unsigned INT_MAX.".to_string(),
|
||||
return Err(BottomError::ConfigError(
|
||||
"set your update rate to be at most unsigned INT_MAX.".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -368,11 +374,10 @@ fn get_temperature(
|
|||
"fahrenheit" | "f" => Ok(data_harvester::temperature::TemperatureType::Fahrenheit),
|
||||
"kelvin" | "k" => Ok(data_harvester::temperature::TemperatureType::Kelvin),
|
||||
"celsius" | "c" => Ok(data_harvester::temperature::TemperatureType::Celsius),
|
||||
_ => Err(BottomError::ConfigError(
|
||||
"invalid temperature type: please have the value be of the form \
|
||||
<kelvin|k|celsius|c|fahrenheit|f>"
|
||||
.to_string(),
|
||||
)),
|
||||
_ => Err(BottomError::ConfigError(format!(
|
||||
"\"{}\" is an invalid temperature type, use \"<kelvin|k|celsius|c|fahrenheit|f>\".",
|
||||
temp_type
|
||||
))),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -455,12 +460,12 @@ fn get_default_time_value(
|
|||
};
|
||||
|
||||
if default_time < 30000 {
|
||||
return Err(BottomError::InvalidArg(
|
||||
"Please set your default value to be at least 30000 milliseconds.".to_string(),
|
||||
return Err(BottomError::ConfigError(
|
||||
"set your default value to be at least 30000 milliseconds.".to_string(),
|
||||
));
|
||||
} else if default_time as u128 > STALE_MAX_MILLISECONDS as u128 {
|
||||
return Err(BottomError::InvalidArg(format!(
|
||||
"Please set your default value to be at most {} milliseconds.",
|
||||
return Err(BottomError::ConfigError(format!(
|
||||
"set your default value to be at most {} milliseconds.",
|
||||
STALE_MAX_MILLISECONDS
|
||||
)));
|
||||
}
|
||||
|
@ -482,12 +487,12 @@ fn get_time_interval(matches: &clap::ArgMatches<'static>, config: &Config) -> er
|
|||
};
|
||||
|
||||
if time_interval < 1000 {
|
||||
return Err(BottomError::InvalidArg(
|
||||
"Please set your time delta to be at least 1000 milliseconds.".to_string(),
|
||||
return Err(BottomError::ConfigError(
|
||||
"set your time delta to be at least 1000 milliseconds.".to_string(),
|
||||
));
|
||||
} else if time_interval > STALE_MAX_MILLISECONDS as u128 {
|
||||
return Err(BottomError::InvalidArg(format!(
|
||||
"Please set your time delta to be at most {} milliseconds.",
|
||||
return Err(BottomError::ConfigError(format!(
|
||||
"set your time delta to be at most {} milliseconds.",
|
||||
STALE_MAX_MILLISECONDS
|
||||
)));
|
||||
}
|
||||
|
@ -601,8 +606,8 @@ fn get_default_widget_and_count(
|
|||
};
|
||||
|
||||
if widget_count > std::u64::MAX as u128 {
|
||||
Err(BottomError::InvalidArg(
|
||||
"Please set your widget count to be at most unsigned INT_MAX.".to_string(),
|
||||
Err(BottomError::ConfigError(
|
||||
"set your widget count to be at most unsigned INT_MAX.".to_string(),
|
||||
))
|
||||
} else {
|
||||
Ok((widget_type, widget_count as u64))
|
||||
|
|
|
@ -1,60 +1,39 @@
|
|||
use std::{borrow::Cow, result};
|
||||
use thiserror::Error;
|
||||
|
||||
/// A type alias for handling errors related to Bottom.
|
||||
pub type Result<T> = result::Result<T, BottomError>;
|
||||
|
||||
/// An error that can occur while Bottom runs.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Error)]
|
||||
pub enum BottomError {
|
||||
/// An error when there is an IO exception.
|
||||
#[error("IO exception, {0}")]
|
||||
InvalidIO(String),
|
||||
/// An error when there is an invalid argument passed in.
|
||||
InvalidArg(String),
|
||||
/// An error when the heim library encounters a problem.
|
||||
#[error("Error caused by Heim, {0}")]
|
||||
InvalidHeim(String),
|
||||
/// An error when the Crossterm library encounters a problem.
|
||||
#[error("Error caused by Crossterm, {0}")]
|
||||
CrosstermError(String),
|
||||
/// An error to represent generic errors.
|
||||
#[error("Generic error, {0}")]
|
||||
GenericError(String),
|
||||
/// An error to represent errors with fern.
|
||||
#[error("Fern error, {0}")]
|
||||
FernError(String),
|
||||
/// An error to represent errors with the config.
|
||||
#[error("Configuration file error, {0}")]
|
||||
ConfigError(String),
|
||||
/// An error to represent errors with converting between data types.
|
||||
#[error("Conversion error, {0}")]
|
||||
ConversionError(String),
|
||||
/// An error to represent errors with querying.
|
||||
#[error("Query error, {0}")]
|
||||
QueryError(Cow<'static, str>),
|
||||
/// An error that just signifies something minor went wrong; no message.
|
||||
MinorError(),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for BottomError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match *self {
|
||||
BottomError::InvalidIO(ref message) => {
|
||||
write!(f, "encountered an IO exception: {}", message)
|
||||
}
|
||||
BottomError::InvalidArg(ref message) => write!(f, "Invalid argument: {}", message),
|
||||
BottomError::InvalidHeim(ref message) => write!(
|
||||
f,
|
||||
"invalid error during data collection due to heim: {}",
|
||||
message
|
||||
),
|
||||
BottomError::CrosstermError(ref message) => {
|
||||
write!(f, "invalid error due to Crossterm: {}", message)
|
||||
}
|
||||
BottomError::GenericError(ref message) => write!(f, "{}", message),
|
||||
BottomError::FernError(ref message) => write!(f, "Invalid fern error: {}", message),
|
||||
BottomError::ConfigError(ref message) => {
|
||||
write!(f, "invalid config file error: {}", message)
|
||||
}
|
||||
BottomError::ConversionError(ref message) => {
|
||||
write!(f, "unable to convert: {}", message)
|
||||
}
|
||||
BottomError::QueryError(ref message) => write!(f, "{}", message),
|
||||
BottomError::MinorError() => write!(f, "Minor error."),
|
||||
}
|
||||
}
|
||||
#[error("Minor error.")]
|
||||
MinorError,
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for BottomError {
|
||||
|
@ -77,7 +56,7 @@ impl From<crossterm::ErrorKind> for BottomError {
|
|||
|
||||
impl From<std::num::ParseIntError> for BottomError {
|
||||
fn from(err: std::num::ParseIntError) -> Self {
|
||||
BottomError::InvalidArg(err.to_string())
|
||||
BottomError::ConfigError(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ fn test_small_rate() -> Result<(), Box<dyn std::error::Error>> {
|
|||
.assert()
|
||||
.failure()
|
||||
.stderr(predicate::str::contains(
|
||||
"Please set your update rate to be at least 250 milliseconds.",
|
||||
"set your update rate to be at least 250 milliseconds.",
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ fn test_large_default_time() -> Result<(), Box<dyn std::error::Error>> {
|
|||
.assert()
|
||||
.failure()
|
||||
.stderr(predicate::str::contains(
|
||||
"Please set your default value to be at most",
|
||||
"set your default value to be at most",
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ fn test_small_default_time() -> Result<(), Box<dyn std::error::Error>> {
|
|||
.assert()
|
||||
.failure()
|
||||
.stderr(predicate::str::contains(
|
||||
"Please set your default value to be at least",
|
||||
"set your default value to be at least",
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ fn test_large_delta_time() -> Result<(), Box<dyn std::error::Error>> {
|
|||
.assert()
|
||||
.failure()
|
||||
.stderr(predicate::str::contains(
|
||||
"Please set your time delta to be at most",
|
||||
"set your time delta to be at most",
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ fn test_small_delta_time() -> Result<(), Box<dyn std::error::Error>> {
|
|||
.assert()
|
||||
.failure()
|
||||
.stderr(predicate::str::contains(
|
||||
"Please set your time delta to be at least",
|
||||
"set your time delta to be at least",
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ fn test_large_rate() -> Result<(), Box<dyn std::error::Error>> {
|
|||
.assert()
|
||||
.failure()
|
||||
.stderr(predicate::str::contains(
|
||||
"Please set your update rate to be at most unsigned INT_MAX.",
|
||||
"set your update rate to be at most unsigned INT_MAX.",
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ fn test_invalid_default_widget_1() -> Result<(), Box<dyn std::error::Error>> {
|
|||
.arg("fake_widget")
|
||||
.assert()
|
||||
.failure()
|
||||
.stderr(predicate::str::contains("invalid widget type"));
|
||||
.stderr(predicate::str::contains("invalid widget name"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -151,7 +151,7 @@ fn test_invalid_default_widget_2() -> Result<(), Box<dyn std::error::Error>> {
|
|||
.assert()
|
||||
.failure()
|
||||
.stderr(predicate::str::contains(
|
||||
"Please set your widget count to be at most unsigned INT_MAX",
|
||||
"set your widget count to be at most unsigned INT_MAX",
|
||||
));
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -26,7 +26,7 @@ fn test_empty_layout() -> Result<(), Box<dyn std::error::Error>> {
|
|||
.arg("./tests/invalid_configs/empty_layout.toml")
|
||||
.assert()
|
||||
.failure()
|
||||
.stderr(predicate::str::contains("invalid layout config"));
|
||||
.stderr(predicate::str::contains("at least one widget"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ fn test_invalid_layout_widget_type() -> Result<(), Box<dyn std::error::Error>> {
|
|||
.arg("./tests/invalid_configs/invalid_layout_widget_type.toml")
|
||||
.assert()
|
||||
.failure()
|
||||
.stderr(predicate::str::contains("invalid widget type"));
|
||||
.stderr(predicate::str::contains("invalid widget name"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@ fn test_invalid_colour_hex() -> Result<(), Box<dyn std::error::Error>> {
|
|||
.arg("./tests/invalid_configs/invalid_colour_hex.toml")
|
||||
.assert()
|
||||
.failure()
|
||||
.stderr(predicate::str::contains("invalid color hex"));
|
||||
.stderr(predicate::str::contains("invalid hex colour"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ fn test_invalid_colour_hex_2() -> Result<(), Box<dyn std::error::Error>> {
|
|||
.arg("./tests/invalid_configs/invalid_colour_hex_2.toml")
|
||||
.assert()
|
||||
.failure()
|
||||
.stderr(predicate::str::contains("invalid color hex"));
|
||||
.stderr(predicate::str::contains("invalid hex colour"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -87,7 +87,7 @@ fn test_invalid_colour_hex_3() -> Result<(), Box<dyn std::error::Error>> {
|
|||
.arg("./tests/invalid_configs/invalid_colour_hex_3.toml")
|
||||
.assert()
|
||||
.failure()
|
||||
.stderr(predicate::str::contains("invalid color hex"));
|
||||
.stderr(predicate::str::contains("invalid hex colour"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -98,7 +98,7 @@ fn test_invalid_colour_name() -> Result<(), Box<dyn std::error::Error>> {
|
|||
.arg("./tests/invalid_configs/invalid_colour_name.toml")
|
||||
.assert()
|
||||
.failure()
|
||||
.stderr(predicate::str::contains("invalid named color"));
|
||||
.stderr(predicate::str::contains("invalid named colour"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -109,7 +109,7 @@ fn test_invalid_colour_rgb() -> Result<(), Box<dyn std::error::Error>> {
|
|||
.arg("./tests/invalid_configs/invalid_colour_rgb.toml")
|
||||
.assert()
|
||||
.failure()
|
||||
.stderr(predicate::str::contains("invalid RGB color"));
|
||||
.stderr(predicate::str::contains("invalid RGB"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -120,7 +120,7 @@ fn test_invalid_colour_rgb_2() -> Result<(), Box<dyn std::error::Error>> {
|
|||
.arg("./tests/invalid_configs/invalid_colour_rgb_2.toml")
|
||||
.assert()
|
||||
.failure()
|
||||
.stderr(predicate::str::contains("invalid RGB color"));
|
||||
.stderr(predicate::str::contains("invalid RGB"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -131,6 +131,19 @@ fn test_invalid_colour_string() -> Result<(), Box<dyn std::error::Error>> {
|
|||
.arg("./tests/invalid_configs/invalid_colour_string.toml")
|
||||
.assert()
|
||||
.failure()
|
||||
.stderr(predicate::str::contains("invalid named color"));
|
||||
.stderr(predicate::str::contains("invalid named colour"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_battery() -> Result<(), Box<dyn std::error::Error>> {
|
||||
Command::new(get_binary_location())
|
||||
.arg("-C")
|
||||
.arg("./tests/invalid_configs/empty_battery.toml")
|
||||
.assert()
|
||||
.failure()
|
||||
.stderr(predicate::str::contains(
|
||||
"battery colour list must have at least one colour.",
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
|
2
tests/invalid_configs/empty_battery.toml
Normal file
2
tests/invalid_configs/empty_battery.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
[colors]
|
||||
battery_colors=[]
|
Loading…
Reference in a new issue