From c115e7b45001e68168833c526d03d2ac931c6ec1 Mon Sep 17 00:00:00 2001 From: Pihkaal Date: Sun, 21 Jan 2024 13:18:54 +0100 Subject: [PATCH] refactor: restructure project --- src/config.rs | 6 +- src/main.rs | 229 ++------------------------------- src/modes/chrono.rs | 98 ++++++++++++++ src/modes/clock.rs | 77 +++++++++++ src/{ => modes}/debug.rs | 0 src/modes/mod.rs | 3 + src/{ => rendering}/color.rs | 0 src/rendering/mod.rs | 92 +++++++++++++ src/{ => rendering}/symbols.rs | 0 src/utils.rs | 10 ++ 10 files changed, 297 insertions(+), 218 deletions(-) create mode 100644 src/modes/chrono.rs create mode 100644 src/modes/clock.rs rename src/{ => modes}/debug.rs (100%) create mode 100644 src/modes/mod.rs rename src/{ => rendering}/color.rs (100%) create mode 100644 src/rendering/mod.rs rename src/{ => rendering}/symbols.rs (100%) create mode 100644 src/utils.rs diff --git a/src/config.rs b/src/config.rs index c373bcf..bc1ceb4 100644 --- a/src/config.rs +++ b/src/config.rs @@ -5,8 +5,10 @@ use crossterm::style::Color; use ini::configparser::ini::Ini; use crate::{ - color::{generate_gradient, parse_hex_color, ComputableColor}, - debug, get_app_mode, AppMode, + get_app_mode, + modes::debug, + rendering::color::{generate_gradient, parse_hex_color, ComputableColor}, + AppMode, }; pub struct Config { diff --git a/src/main.rs b/src/main.rs index 2097d49..afe38d4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,31 +1,22 @@ use core::panic; use std::{ - cmp::min, io::{self, Write}, path::PathBuf, sync::atomic::Ordering, - thread, - time::{self, Duration}, }; use atomic_enum::atomic_enum; -use chrono; use clap::{Parser, Subcommand}; -use config::{write_default_config, Config}; -use crossterm::{ - cursor, - event::{self, Event, KeyCode, KeyModifiers}, - execute, queue, - style::{self, Attribute, Color}, - terminal::{self, ClearType}, -}; -use debug::print_debug_infos; +use config::write_default_config; +use crossterm::{cursor, execute, terminal}; use dirs::config_dir; -mod color; +use crate::modes::debug; + mod config; -mod debug; -mod symbols; +mod modes; +mod rendering; +mod utils; #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] @@ -68,11 +59,6 @@ pub fn set_app_mode(mode: AppMode) { return APP_MODE.store(mode, Ordering::Relaxed); } -struct Lapse { - pub time: time::Duration, - pub delta: time::Duration, -} - fn main() -> io::Result<()> { let cli = Cli::parse(); @@ -129,7 +115,7 @@ fn main() -> io::Result<()> { match get_app_mode() { AppMode::Debug => { - print_debug_infos(&mut config)?; + debug::print_debug_infos(&mut config)?; return Ok(()); } _ => {} @@ -139,58 +125,11 @@ fn main() -> io::Result<()> { execute!(stdout, terminal::EnterAlternateScreen, cursor::Hide)?; let _ = terminal::enable_raw_mode()?; - // For the chronometer - let start_time = time::Instant::now(); - let mut lapses: Vec = vec![]; - - // Main loop - let mut quit = false; - while !quit { - // Handle events - while event::poll(Duration::ZERO)? { - match event::read()? { - Event::Key(e) => match e.code { - // Handle CTRL-C - KeyCode::Char('c') => { - if e.modifiers.contains(KeyModifiers::CONTROL) { - quit = true; - } - } - // Handle lapse - KeyCode::Char(' ') => { - if get_app_mode() == AppMode::Chrono { - let time = start_time.elapsed(); - let delta = if let Some(last_lap) = lapses.last() { - time::Duration::from_secs(time.as_secs() - last_lap.time.as_secs()) - } else { - time - }; - - lapses.push(Lapse { time, delta }); - } - } - _ => {} - }, - _ => {} - } - } - - // Clear frame - queue!(stdout, terminal::Clear(ClearType::All))?; - - // Render - match get_app_mode() { - AppMode::Clock => render_clock(&config)?, - AppMode::Chrono => render_chrono(&config, start_time, &lapses)?, - AppMode::Debug => unreachable!(), - }; - - config.color.update(); - - stdout.flush()?; - - thread::sleep(Duration::from_millis(1000 / config.fps)); - } + match get_app_mode() { + AppMode::Clock => modes::clock::main_loop(&mut config)?, + AppMode::Chrono => modes::chrono::main_loop(&mut config)?, + AppMode::Debug => unreachable!(), + }; // Disale raw mode, leave the alternate screen and show the cursor back let _ = terminal::disable_raw_mode().unwrap(); @@ -203,145 +142,3 @@ fn main() -> io::Result<()> { return Ok(()); } - -fn render_clock(config: &Config) -> io::Result<()> { - let color = config.color.get_value(); - - let date_time = chrono::Local::now(); - - // Display time - let time = date_time.time().format(&config.time_format).to_string(); - draw_time(&time, color)?; - - // Display date - let date = date_time - .date_naive() - .format(&config.date_format.to_owned()) - .to_string(); - - let (width, height) = terminal::size()?; - let x = width / 2 - (date.len() as u16) / 2; - let y = height / 2 + symbols::SYMBOL_HEIGHT as u16 / 2 + 2; - draw_text(&date, x, y, color)?; - - return Ok(()); -} - -fn render_chrono( - config: &Config, - start_time: time::Instant, - lapses: &Vec, -) -> io::Result<()> { - let color = config.color.get_value(); - - // Display time - let elapsed = format_duration(start_time.elapsed()); - draw_time(&elapsed, color)?; - - // Display lapses - let (width, height) = terminal::size()?; - let y = height / 2 + symbols::SYMBOL_HEIGHT as u16 / 2 + 2; - let max_items = min(10, height - y) as usize; - - for (i, lapse) in lapses.iter().rev().take(max_items).enumerate() { - let delta = format_duration(lapse.delta); - let time = format_duration(lapse.time); - - let lapse = format!("#0{} -- +{} -- {}", lapses.len() - i, delta, time); - let x = width / 2 - (lapse.len() as u16) / 2; - draw_text(&lapse, x, y + i as u16, color)?; - } - - return Ok(()); -} - -fn draw_time_width(time: &str) -> u16 { - if time.len() == 0 { - return 0; - } - - let mut w = 0; - for c in time.chars() { - w += if c == ':' { - symbols::SYMBOL_HEIGHT - } else { - symbols::SYMBOL_WIDTH + 1 - }; - } - - w -= if time.len() == 1 { 1 } else { 2 }; - return w.try_into().unwrap(); -} - -fn draw_time(time: &str, color: Color) -> io::Result<()> { - let (width, height) = terminal::size()?; - - let text_width = draw_time_width(&time); - let text_height = 5; - - let mut x = width / 2 - text_width / 2; - let y = height / 2 - text_height / 2; - for c in time.chars() { - if c == ':' { - x -= 1; - } - - draw_time_symbol(c, x, y, color)?; - x += 7; - - if c == ':' { - x -= 1; - } - } - - return Ok(()); -} - -fn draw_time_symbol(symbol: char, x: u16, y: u16, color: Color) -> io::Result<()> { - let mut stdout = io::stdout(); - - let data = symbols::symbol_to_render_data(symbol); - - for oy in 0..data.len() { - for ox in 0..data[oy].len() { - if data[oy][ox] { - let cx = ox as u16; - let cy = oy as u16; - - // Render cursor at position by setting background color and using space - queue!( - stdout, - cursor::MoveTo(x + cx, y + cy), - style::SetBackgroundColor(color) - )?; - write!(stdout, " ")?; - queue!(stdout, style::ResetColor)?; - } - } - } - - return Ok(()); -} - -fn draw_text(string: &str, x: u16, y: u16, color: Color) -> io::Result<()> { - let mut stdout = io::stdout(); - - queue!( - stdout, - cursor::MoveTo(x, y), - style::SetForegroundColor(color), - style::SetAttribute(Attribute::Bold) - )?; - write!(stdout, "{}", string)?; - - return Ok(()); -} - -fn format_duration(duration: time::Duration) -> String { - let seconds = duration.as_secs(); - let hours = seconds / 3600; - let minutes = (seconds % 3600) / 60; - let seconds = seconds % 60; - - return format!("{:02}:{:02}:{:02}", hours, minutes, seconds); -} diff --git a/src/modes/chrono.rs b/src/modes/chrono.rs new file mode 100644 index 0000000..80c5ade --- /dev/null +++ b/src/modes/chrono.rs @@ -0,0 +1,98 @@ +use std::{ + cmp::min, + io::{self, Write}, + thread, + time::{self, Duration}, +}; + +use crossterm::{ + event::{self, Event, KeyCode, KeyModifiers}, + queue, + terminal::{self, ClearType}, +}; + +use crate::utils; +use crate::{ + config::Config, + rendering::{self, symbols}, +}; + +struct Lapse { + pub time: time::Duration, + pub delta: time::Duration, +} + +pub fn main_loop(config: &mut Config) -> io::Result<()> { + let mut stdout = io::stdout(); + + let start_time = time::Instant::now(); + let mut lapses: Vec = vec![]; + + let mut quit = false; + while !quit { + // Handle events + while event::poll(Duration::ZERO)? { + match event::read()? { + Event::Key(e) => match e.code { + // Handle CTRL-C + KeyCode::Char('c') => { + if e.modifiers.contains(KeyModifiers::CONTROL) { + quit = true; + } + } + // Handle lapse + KeyCode::Char(' ') => { + let time = start_time.elapsed(); + let delta = if let Some(last_lap) = lapses.last() { + time::Duration::from_secs(time.as_secs() - last_lap.time.as_secs()) + } else { + time + }; + + lapses.push(Lapse { time, delta }); + } + _ => {} + }, + _ => {} + } + } + + // Clear frame + queue!(stdout, terminal::Clear(ClearType::All))?; + + // Render + render_frame(&config, start_time, &lapses)?; + + config.color.update(); + + stdout.flush()?; + + thread::sleep(Duration::from_millis(1000 / config.fps)); + } + + return Ok(()); +} + +fn render_frame(config: &Config, start_time: time::Instant, lapses: &Vec) -> io::Result<()> { + let color = config.color.get_value(); + + // Display time + let elapsed = utils::format_duration(start_time.elapsed()); + rendering::draw_time(&elapsed, color)?; + + // Display lapses + let (width, height) = terminal::size()?; + let y = height / 2 + symbols::SYMBOL_HEIGHT as u16 / 2 + 2; + let max_items = min(10, height - y) as usize; + + for (i, lapse) in lapses.iter().rev().take(max_items).enumerate() { + let delta = utils::format_duration(lapse.delta); + let time = utils::format_duration(lapse.time); + + let lapse = format!("#0{} -- +{} -- {}", lapses.len() - i, delta, time); + let x = width / 2 - (lapse.len() as u16) / 2; + rendering::draw_text(&lapse, x, y + i as u16, color)?; + } + + return Ok(()); +} diff --git a/src/modes/clock.rs b/src/modes/clock.rs new file mode 100644 index 0000000..d534016 --- /dev/null +++ b/src/modes/clock.rs @@ -0,0 +1,77 @@ +use std::{ + io::{self, Write}, + thread, + time::Duration, +}; + +use chrono; +use crossterm::{ + event::{self, Event, KeyCode, KeyModifiers}, + queue, + terminal::{self, ClearType}, +}; + +use crate::{ + config::Config, + rendering::{self, symbols}, +}; + +pub fn main_loop(config: &mut Config) -> io::Result<()> { + let mut stdout = io::stdout(); + + let mut quit = false; + while !quit { + // Handle events + while event::poll(Duration::ZERO)? { + match event::read()? { + Event::Key(e) => match e.code { + // Handle CTRL-C + KeyCode::Char('c') => { + if e.modifiers.contains(KeyModifiers::CONTROL) { + quit = true; + } + } + _ => {} + }, + _ => {} + } + } + + // Clear frame + queue!(stdout, terminal::Clear(ClearType::All))?; + + // Render + render_frame(&config)?; + + config.color.update(); + + stdout.flush()?; + + thread::sleep(Duration::from_millis(1000 / config.fps)); + } + + return Ok(()); +} + +fn render_frame(config: &Config) -> io::Result<()> { + let color = config.color.get_value(); + + let date_time = chrono::Local::now(); + + // Display time + let time = date_time.time().format(&config.time_format).to_string(); + rendering::draw_time(&time, color)?; + + // Display date + let date = date_time + .date_naive() + .format(&config.date_format.to_owned()) + .to_string(); + + let (width, height) = terminal::size()?; + let x = width / 2 - (date.len() as u16) / 2; + let y = height / 2 + symbols::SYMBOL_HEIGHT as u16 / 2 + 2; + rendering::draw_text(&date, x, y, color)?; + + return Ok(()); +} diff --git a/src/debug.rs b/src/modes/debug.rs similarity index 100% rename from src/debug.rs rename to src/modes/debug.rs diff --git a/src/modes/mod.rs b/src/modes/mod.rs new file mode 100644 index 0000000..eefc37a --- /dev/null +++ b/src/modes/mod.rs @@ -0,0 +1,3 @@ +pub mod chrono; +pub mod clock; +pub mod debug; diff --git a/src/color.rs b/src/rendering/color.rs similarity index 100% rename from src/color.rs rename to src/rendering/color.rs diff --git a/src/rendering/mod.rs b/src/rendering/mod.rs new file mode 100644 index 0000000..49ab288 --- /dev/null +++ b/src/rendering/mod.rs @@ -0,0 +1,92 @@ +use std::io::{self, Write}; + +use crossterm::{ + cursor, queue, + style::{self, Attribute, Color}, + terminal, +}; + +pub mod color; +pub mod symbols; + +pub fn draw_time(time: &str, color: Color) -> io::Result<()> { + let (width, height) = terminal::size()?; + + let text_width = draw_time_width(&time); + let text_height = 5; + + let mut x = width / 2 - text_width / 2; + let y = height / 2 - text_height / 2; + for c in time.chars() { + if c == ':' { + x -= 1; + } + + draw_time_symbol(c, x, y, color)?; + x += 7; + + if c == ':' { + x -= 1; + } + } + + return Ok(()); +} + +pub fn draw_text(string: &str, x: u16, y: u16, color: Color) -> io::Result<()> { + let mut stdout = io::stdout(); + + queue!( + stdout, + cursor::MoveTo(x, y), + style::SetForegroundColor(color), + style::SetAttribute(Attribute::Bold) + )?; + write!(stdout, "{}", string)?; + + return Ok(()); +} + +fn draw_time_width(time: &str) -> u16 { + if time.len() == 0 { + return 0; + } + + let mut w = 0; + for c in time.chars() { + w += if c == ':' { + symbols::SYMBOL_HEIGHT + } else { + symbols::SYMBOL_WIDTH + 1 + }; + } + + w -= if time.len() == 1 { 1 } else { 2 }; + return w.try_into().unwrap(); +} + +fn draw_time_symbol(symbol: char, x: u16, y: u16, color: Color) -> io::Result<()> { + let mut stdout = io::stdout(); + + let data = symbols::symbol_to_render_data(symbol); + + for oy in 0..data.len() { + for ox in 0..data[oy].len() { + if data[oy][ox] { + let cx = ox as u16; + let cy = oy as u16; + + // Render cursor at position by setting background color and using space + queue!( + stdout, + cursor::MoveTo(x + cx, y + cy), + style::SetBackgroundColor(color) + )?; + write!(stdout, " ")?; + queue!(stdout, style::ResetColor)?; + } + } + } + + return Ok(()); +} diff --git a/src/symbols.rs b/src/rendering/symbols.rs similarity index 100% rename from src/symbols.rs rename to src/rendering/symbols.rs diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..6909eae --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,10 @@ +use std::time; + +pub fn format_duration(duration: time::Duration) -> String { + let seconds = duration.as_secs(); + let hours = seconds / 3600; + let minutes = (seconds % 3600) / 60; + let seconds = seconds % 60; + + return format!("{:02}:{:02}:{:02}", hours, minutes, seconds); +}