feat: better error reporting
This commit is contained in:
@@ -1,10 +1,10 @@
|
|||||||
use core::panic;
|
use std::{any::type_name, fs, path::PathBuf};
|
||||||
use std::{fs, path::PathBuf};
|
|
||||||
|
|
||||||
use crossterm::style::Color;
|
use crossterm::style::Color;
|
||||||
use ini::configparser::ini::Ini;
|
use ini::configparser::ini::Ini;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
eprintln_quit,
|
||||||
modes::debug,
|
modes::debug,
|
||||||
rendering::color::{generate_gradient, parse_hex_color, ComputableColor},
|
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 {
|
pub fn load_from_file(path: PathBuf, debug_mode: bool) -> Config {
|
||||||
let mut ini = Ini::new();
|
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 {
|
Config {
|
||||||
be_polite: ini.getbool("general", "polite").unwrap().unwrap(),
|
be_polite: get_ini_value(&ini, "general", "polite"),
|
||||||
fps: ini.getuint("general", "fps").unwrap().unwrap(),
|
fps: get_ini_value(&ini, "general", "fps"),
|
||||||
color: load_color(&ini, debug_mode),
|
color: load_color(&ini, debug_mode),
|
||||||
time_format: ini.get("format", "time").unwrap(),
|
time_format: get_ini_value(&ini, "format", "time"),
|
||||||
date_format: ini.get("format", "date").unwrap(),
|
date_format: get_ini_value(&ini, "format", "date"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_default_config(path: PathBuf) -> () {
|
pub fn write_default_config(path: PathBuf) -> () {
|
||||||
// Write default config file to target path
|
// 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::create_dir_all(parent);
|
||||||
let _ = fs::write(path, DEFAULT_CONFIG);
|
let _ = fs::write(path, DEFAULT_CONFIG);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_color(ini: &Ini, debug_mode: bool) -> ComputableColor {
|
fn get_ini_value<T: std::str::FromStr>(ini: &Ini, section: &str, key: &str) -> T {
|
||||||
let color_mode = ini.get("styling", "color_mode").unwrap();
|
if let Some(value) = ini.get(section, key) {
|
||||||
|
value.parse::<T>().unwrap_or_else(|_| {
|
||||||
match color_mode.as_str() {
|
eprintln_quit!(
|
||||||
"term" => {
|
"Invalid value at {}.{}: Expected {}, got '{}'",
|
||||||
let color = ini.getint("styling", "color_term").unwrap().unwrap();
|
section,
|
||||||
ComputableColor::from(load_term_color(color))
|
key,
|
||||||
}
|
type_name::<T>(),
|
||||||
"hex" => {
|
value
|
||||||
let color = ini.get("styling", "color_hex").unwrap();
|
)
|
||||||
ComputableColor::from(load_hex_color(&color))
|
})
|
||||||
}
|
} else {
|
||||||
"ansi" => {
|
eprintln_quit!("Missing required config key: {}.{}", section, key)
|
||||||
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 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 {
|
match value {
|
||||||
0 => Color::Black,
|
0 => Color::Black,
|
||||||
1 => Color::DarkRed,
|
1 => Color::DarkRed,
|
||||||
@@ -78,7 +101,7 @@ fn load_term_color(value: i64) -> Color {
|
|||||||
13 => Color::Magenta,
|
13 => Color::Magenta,
|
||||||
14 => Color::Cyan,
|
14 => Color::Cyan,
|
||||||
15 => Color::White,
|
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 {
|
fn load_ansi_color(value: u8) -> Color {
|
||||||
Color::AnsiValue(value.try_into().unwrap())
|
Color::AnsiValue(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_gradient(ini: &Ini, debug_mode: bool) -> ComputableColor {
|
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
|
// Generate gradient loop if needed
|
||||||
if !debug_mode && ini.getbool("gradient", "gradient_loop").unwrap().unwrap() {
|
if !debug_mode && get_ini_value(&ini, "gradient", "gradient_loop") {
|
||||||
let mut loop_keys = keys.clone();
|
for &key in keys.clone().iter().rev().skip(1) {
|
||||||
loop_keys.reverse();
|
keys.push(key);
|
||||||
for i in 1..loop_keys.len() {
|
|
||||||
keys.push(*loop_keys.get(i).unwrap());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,11 +142,7 @@ fn load_gradient(ini: &Ini, debug_mode: bool) -> ComputableColor {
|
|||||||
let steps: usize = if debug_mode {
|
let steps: usize = if debug_mode {
|
||||||
debug::DEBUG_COLOR_DISPLAY_SIZE * 2
|
debug::DEBUG_COLOR_DISPLAY_SIZE * 2
|
||||||
} else {
|
} else {
|
||||||
ini.getuint("gradient", "gradient_steps")
|
get_ini_value(&ini, "gradient", "gradient_steps")
|
||||||
.unwrap()
|
|
||||||
.unwrap()
|
|
||||||
.try_into()
|
|
||||||
.unwrap()
|
|
||||||
};
|
};
|
||||||
generate_gradient(keys, steps - 1)
|
generate_gradient(keys, steps - 1)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,10 @@ fn main() -> io::Result<()> {
|
|||||||
PathBuf::from(custom_config)
|
PathBuf::from(custom_config)
|
||||||
} else {
|
} else {
|
||||||
// Or default one, located in ~/.config/tlock
|
// 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() {
|
if !config_file.exists() {
|
||||||
write_default_config(config_file.clone());
|
write_default_config(config_file.clone());
|
||||||
default_generated = true;
|
default_generated = true;
|
||||||
@@ -128,7 +131,7 @@ fn main() -> io::Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Disale raw mode, leave the alternate screen and show the cursor back
|
// 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)?;
|
execute!(stdout, terminal::LeaveAlternateScreen, cursor::Show)?;
|
||||||
|
|
||||||
// Be polite
|
// Be polite
|
||||||
|
|||||||
@@ -10,11 +10,11 @@ use crossterm::{
|
|||||||
terminal::{self, ClearType},
|
terminal::{self, ClearType},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::utils;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::Config,
|
config::Config,
|
||||||
rendering::{self, symbols},
|
rendering::{self, symbols},
|
||||||
};
|
};
|
||||||
|
use crate::{eprintln_quit, utils};
|
||||||
|
|
||||||
struct Timer {
|
struct Timer {
|
||||||
duration: Duration,
|
duration: Duration,
|
||||||
@@ -74,7 +74,8 @@ impl Timer {
|
|||||||
pub fn main_loop(config: &mut Config, duration: &str) -> io::Result<()> {
|
pub fn main_loop(config: &mut Config, duration: &str) -> io::Result<()> {
|
||||||
let mut stdout = io::stdout();
|
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 timer = Timer::new(duration);
|
||||||
|
|
||||||
let mut quit = false;
|
let mut quit = false;
|
||||||
|
|||||||
@@ -86,9 +86,14 @@ pub fn parse_hex_color(value: &str) -> (u8, u8, u8) {
|
|||||||
panic!("ERROR: Invalid hex color: {}", value);
|
panic!("ERROR: Invalid hex color: {}", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
let r = u8::from_str_radix(&value[0..2], 16).unwrap();
|
let extract_component = |index: usize| {
|
||||||
let g = u8::from_str_radix(&value[2..4], 16).unwrap();
|
u8::from_str_radix(&value[index * 2..(index + 1) * 2], 16)
|
||||||
let b = u8::from_str_radix(&value[4..6], 16).unwrap();
|
.unwrap_or_else(|_| panic!("error: invalid hex color: {}", value))
|
||||||
|
};
|
||||||
|
|
||||||
(r, g, b)
|
(
|
||||||
|
extract_component(0),
|
||||||
|
extract_component(1),
|
||||||
|
extract_component(2),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
10
src/utils.rs
10
src/utils.rs
@@ -8,3 +8,13 @@ pub fn format_duration(duration: time::Duration) -> String {
|
|||||||
|
|
||||||
format!("{:02}:{:02}:{:02}", hours, minutes, seconds)
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user