refactor: restructure project
This commit is contained in:
@@ -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 {
|
||||||
|
|||||||
223
src/main.rs
223
src/main.rs
@@ -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
98
src/modes/chrono.rs
Normal 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
77
src/modes/clock.rs
Normal 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
3
src/modes/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
pub mod chrono;
|
||||||
|
pub mod clock;
|
||||||
|
pub mod debug;
|
||||||
92
src/rendering/mod.rs
Normal file
92
src/rendering/mod.rs
Normal 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
10
src/utils.rs
Normal 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);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user