refactor: restructure project

This commit is contained in:
Pihkaal
2024-01-21 13:18:54 +01:00
parent a2a6697afd
commit c115e7b450
10 changed files with 297 additions and 218 deletions

View File

@@ -5,8 +5,10 @@ use crossterm::style::Color;
use ini::configparser::ini::Ini; use ini::configparser::ini::Ini;
use crate::{ use crate::{
color::{generate_gradient, parse_hex_color, ComputableColor}, get_app_mode,
debug, get_app_mode, AppMode, modes::debug,
rendering::color::{generate_gradient, parse_hex_color, ComputableColor},
AppMode,
}; };
pub struct Config { pub struct Config {

View File

@@ -1,31 +1,22 @@
use core::panic; use core::panic;
use std::{ use std::{
cmp::min,
io::{self, Write}, io::{self, Write},
path::PathBuf, path::PathBuf,
sync::atomic::Ordering, sync::atomic::Ordering,
thread,
time::{self, Duration},
}; };
use atomic_enum::atomic_enum; use atomic_enum::atomic_enum;
use chrono;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use config::{write_default_config, Config}; use config::write_default_config;
use crossterm::{ use crossterm::{cursor, execute, terminal};
cursor,
event::{self, Event, KeyCode, KeyModifiers},
execute, queue,
style::{self, Attribute, Color},
terminal::{self, ClearType},
};
use debug::print_debug_infos;
use dirs::config_dir; use dirs::config_dir;
mod color; use crate::modes::debug;
mod config; mod config;
mod debug; mod modes;
mod symbols; mod rendering;
mod utils;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)] #[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); return APP_MODE.store(mode, Ordering::Relaxed);
} }
struct Lapse {
pub time: time::Duration,
pub delta: time::Duration,
}
fn main() -> io::Result<()> { fn main() -> io::Result<()> {
let cli = Cli::parse(); let cli = Cli::parse();
@@ -129,7 +115,7 @@ fn main() -> io::Result<()> {
match get_app_mode() { match get_app_mode() {
AppMode::Debug => { AppMode::Debug => {
print_debug_infos(&mut config)?; debug::print_debug_infos(&mut config)?;
return Ok(()); return Ok(());
} }
_ => {} _ => {}
@@ -139,59 +125,12 @@ fn main() -> io::Result<()> {
execute!(stdout, terminal::EnterAlternateScreen, cursor::Hide)?; execute!(stdout, terminal::EnterAlternateScreen, cursor::Hide)?;
let _ = terminal::enable_raw_mode()?; let _ = terminal::enable_raw_mode()?;
// For the chronometer
let start_time = time::Instant::now();
let mut lapses: Vec<Lapse> = 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() { match get_app_mode() {
AppMode::Clock => render_clock(&config)?, AppMode::Clock => modes::clock::main_loop(&mut config)?,
AppMode::Chrono => render_chrono(&config, start_time, &lapses)?, AppMode::Chrono => modes::chrono::main_loop(&mut config)?,
AppMode::Debug => unreachable!(), AppMode::Debug => unreachable!(),
}; };
config.color.update();
stdout.flush()?;
thread::sleep(Duration::from_millis(1000 / config.fps));
}
// 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().unwrap();
execute!(stdout, terminal::LeaveAlternateScreen, cursor::Show)?; execute!(stdout, terminal::LeaveAlternateScreen, cursor::Show)?;
@@ -203,145 +142,3 @@ fn main() -> io::Result<()> {
return Ok(()); 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<Lapse>,
) -> 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);
}

98
src/modes/chrono.rs Normal file
View File

@@ -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<Lapse> = 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<Lapse>) -> 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(());
}

77
src/modes/clock.rs Normal file
View File

@@ -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(());
}

3
src/modes/mod.rs Normal file
View File

@@ -0,0 +1,3 @@
pub mod chrono;
pub mod clock;
pub mod debug;

92
src/rendering/mod.rs Normal file
View File

@@ -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(());
}

10
src/utils.rs Normal file
View File

@@ -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);
}