diff --git a/src/config.rs b/src/config.rs index 167e22b..fc8e84f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,10 +1,10 @@ -use core::panic; -use std::{fs, path::PathBuf}; +use std::{any::type_name, fs, path::PathBuf}; use crossterm::style::Color; use ini::configparser::ini::Ini; use crate::{ + eprintln_quit, modes::debug, rendering::color::{generate_gradient, parse_hex_color, ComputableColor}, }; @@ -21,46 +21,69 @@ const DEFAULT_CONFIG: &str = include_str!("default_config"); pub fn load_from_file(path: PathBuf, debug_mode: bool) -> Config { let mut ini = Ini::new(); - ini.load(&path.to_str().unwrap()).unwrap(); + ini.load( + &path + .to_str() + .unwrap_or_else(|| eprintln_quit!("Invalid configuration path")), + ) + .unwrap_or_else(|_| eprintln_quit!("Unable to parse configuration file")); Config { - be_polite: ini.getbool("general", "polite").unwrap().unwrap(), - fps: ini.getuint("general", "fps").unwrap().unwrap(), + be_polite: get_ini_value(&ini, "general", "polite"), + fps: get_ini_value(&ini, "general", "fps"), color: load_color(&ini, debug_mode), - time_format: ini.get("format", "time").unwrap(), - date_format: ini.get("format", "date").unwrap(), + time_format: get_ini_value(&ini, "format", "time"), + date_format: get_ini_value(&ini, "format", "date"), } } pub fn write_default_config(path: PathBuf) -> () { // Write default config file to target path - let parent = path.parent().unwrap(); + let parent = path + .parent() + .unwrap_or_else(|| eprintln_quit!("Invalid configuration path")); let _ = fs::create_dir_all(parent); let _ = fs::write(path, DEFAULT_CONFIG); } -fn load_color(ini: &Ini, debug_mode: bool) -> ComputableColor { - let color_mode = ini.get("styling", "color_mode").unwrap(); - - match color_mode.as_str() { - "term" => { - let color = ini.getint("styling", "color_term").unwrap().unwrap(); - ComputableColor::from(load_term_color(color)) - } - "hex" => { - let color = ini.get("styling", "color_hex").unwrap(); - ComputableColor::from(load_hex_color(&color)) - } - "ansi" => { - let color = ini.getint("styling", "color_ansi").unwrap().unwrap(); - ComputableColor::from(load_ansi_color(color)) - } - "gradient" => load_gradient(ini, debug_mode), - _ => panic!("ERROR: Invalid color mode: {}", color_mode), +fn get_ini_value(ini: &Ini, section: &str, key: &str) -> T { + if let Some(value) = ini.get(section, key) { + value.parse::().unwrap_or_else(|_| { + eprintln_quit!( + "Invalid value at {}.{}: Expected {}, got '{}'", + section, + key, + type_name::(), + value + ) + }) + } else { + eprintln_quit!("Missing required config key: {}.{}", section, key) } } -fn load_term_color(value: i64) -> Color { +fn load_color(ini: &Ini, debug_mode: bool) -> ComputableColor { + let color_mode: String = get_ini_value(&ini, "styling", "color_mode"); + + match color_mode.as_str() { + "term" => { + let color: u8 = get_ini_value(&ini, "styling", "color_term"); + ComputableColor::from(load_term_color(color)) + } + "hex" => { + let color: String = get_ini_value(&ini, "styling", "color_hex"); + ComputableColor::from(load_hex_color(&color)) + } + "ansi" => { + let color: u8 = get_ini_value(&ini, "styling", "color_ansi"); + ComputableColor::from(load_ansi_color(color)) + } + "gradient" => load_gradient(ini, debug_mode), + _ => eprintln_quit!("Invalid color mode: {}", color_mode), + } +} + +fn load_term_color(value: u8) -> Color { match value { 0 => Color::Black, 1 => Color::DarkRed, @@ -78,7 +101,7 @@ fn load_term_color(value: i64) -> Color { 13 => Color::Magenta, 14 => Color::Cyan, 15 => Color::White, - _ => panic!("ERROR: Invalid terminal color: {}", value), + _ => eprintln_quit!("Invalid terminal color: {}", value), } } @@ -91,8 +114,8 @@ fn load_hex_color(value: &str) -> Color { } } -fn load_ansi_color(value: i64) -> Color { - Color::AnsiValue(value.try_into().unwrap()) +fn load_ansi_color(value: u8) -> Color { + Color::AnsiValue(value) } fn load_gradient(ini: &Ini, debug_mode: bool) -> ComputableColor { @@ -109,11 +132,9 @@ fn load_gradient(ini: &Ini, debug_mode: bool) -> ComputableColor { } // Generate gradient loop if needed - if !debug_mode && ini.getbool("gradient", "gradient_loop").unwrap().unwrap() { - let mut loop_keys = keys.clone(); - loop_keys.reverse(); - for i in 1..loop_keys.len() { - keys.push(*loop_keys.get(i).unwrap()); + if !debug_mode && get_ini_value(&ini, "gradient", "gradient_loop") { + for &key in keys.clone().iter().rev().skip(1) { + keys.push(key); } } @@ -121,11 +142,7 @@ fn load_gradient(ini: &Ini, debug_mode: bool) -> ComputableColor { let steps: usize = if debug_mode { debug::DEBUG_COLOR_DISPLAY_SIZE * 2 } else { - ini.getuint("gradient", "gradient_steps") - .unwrap() - .unwrap() - .try_into() - .unwrap() + get_ini_value(&ini, "gradient", "gradient_steps") }; generate_gradient(keys, steps - 1) } diff --git a/src/main.rs b/src/main.rs index 258c2a4..7e6931f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -57,7 +57,10 @@ fn main() -> io::Result<()> { PathBuf::from(custom_config) } else { // Or default one, located in ~/.config/tlock - let config_file = config_dir().unwrap().join("tlock").join("config"); + let config_file = config_dir() + .unwrap_or_else(|| eprintln_quit!("Unble to get configuration directory")) + .join("tlock") + .join("config"); if !config_file.exists() { write_default_config(config_file.clone()); default_generated = true; @@ -128,7 +131,7 @@ fn main() -> io::Result<()> { } // Disale raw mode, leave the alternate screen and show the cursor back - let _ = terminal::disable_raw_mode().unwrap(); + let _ = terminal::disable_raw_mode()?; execute!(stdout, terminal::LeaveAlternateScreen, cursor::Show)?; // Be polite diff --git a/src/modes/timer.rs b/src/modes/timer.rs index 923ad0f..9c3ae74 100644 --- a/src/modes/timer.rs +++ b/src/modes/timer.rs @@ -10,11 +10,11 @@ use crossterm::{ terminal::{self, ClearType}, }; -use crate::utils; use crate::{ config::Config, rendering::{self, symbols}, }; +use crate::{eprintln_quit, utils}; struct Timer { duration: Duration, @@ -74,7 +74,8 @@ impl Timer { pub fn main_loop(config: &mut Config, duration: &str) -> io::Result<()> { let mut stdout = io::stdout(); - let duration = parse_duration::parse(duration).unwrap(); + let duration = parse_duration::parse(duration) + .unwrap_or_else(|_| eprintln_quit!("Invalid duration provided")); let mut timer = Timer::new(duration); let mut quit = false; diff --git a/src/rendering/color.rs b/src/rendering/color.rs index 6c85486..1dbaf67 100644 --- a/src/rendering/color.rs +++ b/src/rendering/color.rs @@ -86,9 +86,14 @@ pub fn parse_hex_color(value: &str) -> (u8, u8, u8) { panic!("ERROR: Invalid hex color: {}", value); } - let r = u8::from_str_radix(&value[0..2], 16).unwrap(); - let g = u8::from_str_radix(&value[2..4], 16).unwrap(); - let b = u8::from_str_radix(&value[4..6], 16).unwrap(); + let extract_component = |index: usize| { + u8::from_str_radix(&value[index * 2..(index + 1) * 2], 16) + .unwrap_or_else(|_| panic!("error: invalid hex color: {}", value)) + }; - (r, g, b) + ( + extract_component(0), + extract_component(1), + extract_component(2), + ) } diff --git a/src/utils.rs b/src/utils.rs index de6f054..0ed0eec 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -8,3 +8,13 @@ pub fn format_duration(duration: time::Duration) -> String { format!("{:02}:{:02}:{:02}", hours, minutes, seconds) } + +#[macro_export] +macro_rules! eprintln_quit { + ($($arg:tt)*) => ({ + use std::io::Write; + write!(&mut std::io::stderr(), "ERROR: ").unwrap(); + writeln!(&mut std::io::stderr(), $($arg)*).unwrap(); + std::process::exit(1) + }) +}