diff --git a/Cargo.lock b/Cargo.lock index 7e1b1a8..70d526d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -893,6 +893,18 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.134" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -1049,6 +1061,7 @@ dependencies = [ "futures", "ratatui", "serde", + "serde_json", "strum", "tokio", "tokio-stream", diff --git a/Cargo.toml b/Cargo.toml index a5fa56d..8048e6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ crossterm = {version = "0.28.1", features = ["event-stream", "serde"] } color-eyre = "0.6.2" futures = "0.3" serde = { version = "1", features = ["derive"] } +serde_json = "1.0" strum = { version = "0.26.3", features = ["derive"] } tokio = { version = "1.41.1", features = ["full"] } tokio-stream = "0.1.16" diff --git a/src/app.rs b/src/app.rs index fb8d437..cb44099 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,17 +1,19 @@ use crate::{ - args::{Args, ClockStyle, Content}, + args::Args, constants::TICK_VALUE_MS, events::{Event, EventHandler, Events}, + storage::AppStorage, terminal::Terminal, widgets::{ - clock::{self, Clock, ClockArgs}, + clock::{self, Clock, ClockArgs, Style}, countdown::{Countdown, CountdownWidget}, footer::Footer, header::Header, - pomodoro::{Pomodoro, PomodoroArgs, PomodoroWidget}, + pomodoro::{Mode as PomodoroMode, Pomodoro, PomodoroArgs, PomodoroWidget}, timer::{Timer, TimerWidget}, }, }; +use clap::ValueEnum; use color_eyre::Result; use ratatui::{ buffer::Buffer, @@ -19,9 +21,23 @@ use ratatui::{ layout::{Constraint, Layout, Rect}, widgets::{StatefulWidget, Widget}, }; +use serde::{Deserialize, Serialize}; use std::time::Duration; use tracing::debug; +#[derive( + Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Default, Serialize, Deserialize, +)] +pub enum Content { + #[default] + #[value(name = "countdown", alias = "c")] + Countdown, + #[value(name = "timer", alias = "t")] + Timer, + #[value(name = "pomodoro", alias = "p")] + Pomodoro, +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum Mode { Running, @@ -36,49 +52,92 @@ pub struct App { countdown: Countdown, timer: Timer, pomodoro: Pomodoro, - clock_style: ClockStyle, + style: Style, with_decis: bool, } +pub struct AppArgs { + pub style: Style, + pub with_decis: bool, + pub content: Content, + pub pomodoro_mode: PomodoroMode, + pub initial_value_work: Duration, + pub current_value_work: Duration, + pub initial_value_pause: Duration, + pub current_value_pause: Duration, + pub initial_value_countdown: Duration, + pub current_value_countdown: Duration, + pub current_value_timer: Duration, +} + +/// Getting `AppArgs` by merging `Args` and `AppStorage`. +/// `Args` wins btw. +impl From<(Args, AppStorage)> for AppArgs { + fn from((args, stg): (Args, AppStorage)) -> Self { + AppArgs { + with_decis: args.decis || stg.with_decis, + content: args.mode.unwrap_or(stg.content), + style: args.style.unwrap_or(stg.style), + pomodoro_mode: stg.pomodoro_mode, + initial_value_work: args.work.unwrap_or(stg.inital_value_work), + current_value_work: stg.current_value_work, + initial_value_pause: args.pause, + current_value_pause: stg.current_value_pause, + initial_value_countdown: args.countdown, + current_value_countdown: stg.current_value_countdown, + current_value_timer: stg.current_value_timer, + } + } +} + impl App { - pub fn new(args: Args) -> Self { - let Args { + pub fn new(args: AppArgs) -> Self { + let AppArgs { style, - work: work_initial_value, - pause: pause_initial_value, - mode: content, - countdown: countdown_initial_value, - decis: with_decis, - .. + initial_value_work, + initial_value_pause, + initial_value_countdown, + current_value_work, + current_value_pause, + current_value_countdown, + current_value_timer, + content, + with_decis, + pomodoro_mode, } = args; Self { mode: Mode::Running, content, show_menu: false, - clock_style: style, + style, with_decis, countdown: Countdown::new(Clock::::new(ClockArgs { - initial_value: countdown_initial_value, + initial_value: initial_value_countdown, + current_value: current_value_countdown, tick_value: Duration::from_millis(TICK_VALUE_MS), style, with_decis, })), timer: Timer::new(Clock::::new(ClockArgs { initial_value: Duration::ZERO, + current_value: current_value_timer, tick_value: Duration::from_millis(TICK_VALUE_MS), style, with_decis, })), pomodoro: Pomodoro::new(PomodoroArgs { - work: work_initial_value, - pause: pause_initial_value, + mode: pomodoro_mode, + initial_value_work, + current_value_work, + initial_value_pause, + current_value_pause, style, with_decis, }), } } - pub async fn run(&mut self, mut terminal: Terminal, mut events: Events) -> Result<()> { + pub async fn run(mut self, mut terminal: Terminal, mut events: Events) -> Result { while self.is_running() { if let Some(event) = events.next().await { // Pipe events into subviews and handle only 'unhandled' events afterwards @@ -97,7 +156,7 @@ impl App { } } } - Ok(()) + Ok(self) } fn is_running(&self) -> bool { @@ -113,11 +172,11 @@ impl App { KeyCode::Char('p') => self.content = Content::Pomodoro, KeyCode::Char('m') => self.show_menu = !self.show_menu, KeyCode::Char(',') => { - self.clock_style = self.clock_style.next(); + self.style = self.style.next(); // update clocks - self.timer.set_style(self.clock_style); - self.countdown.set_style(self.clock_style); - self.pomodoro.set_style(self.clock_style); + self.timer.set_style(self.style); + self.countdown.set_style(self.style); + self.pomodoro.set_style(self.style); } KeyCode::Char('.') => { self.with_decis = !self.with_decis; @@ -138,6 +197,23 @@ impl App { })?; Ok(()) } + + pub fn to_storage(&self) -> AppStorage { + AppStorage { + content: self.content, + show_menu: self.show_menu, + style: self.style, + with_decis: self.with_decis, + pomodoro_mode: self.pomodoro.get_mode().clone(), + inital_value_work: self.pomodoro.get_clock_work().initial_value, + current_value_work: self.pomodoro.get_clock_work().current_value, + inital_value_pause: self.pomodoro.get_clock_pause().initial_value, + current_value_pause: self.pomodoro.get_clock_pause().current_value, + inital_value_countdown: self.countdown.get_clock().initial_value, + current_value_countdown: self.countdown.get_clock().current_value, + current_value_timer: self.timer.get_clock().current_value, + } + } } struct AppWidget; diff --git a/src/args.rs b/src/args.rs index 03deb4d..3526ea1 100644 --- a/src/args.rs +++ b/src/args.rs @@ -1,42 +1,11 @@ -use clap::{Parser, ValueEnum}; +use clap::Parser; use color_eyre::{ eyre::{ensure, eyre}, Report, }; use std::time::Duration; -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] -pub enum Content { - #[value(name = "countdown", alias = "c")] - Countdown, - #[value(name = "timer", alias = "t")] - Timer, - #[value(name = "pomodoro", alias = "p")] - Pomodoro, -} - -#[derive(Debug, Copy, Clone, ValueEnum)] -pub enum ClockStyle { - #[value(name = "bold", alias = "b")] - Bold, - #[value(name = "empty", alias = "e")] - Empty, - #[value(name = "thick", alias = "t")] - Thick, - #[value(name = "cross", alias = "c")] - Cross, -} - -impl ClockStyle { - pub fn next(&self) -> Self { - match self { - ClockStyle::Bold => ClockStyle::Empty, - ClockStyle::Empty => ClockStyle::Thick, - ClockStyle::Thick => ClockStyle::Cross, - ClockStyle::Cross => ClockStyle::Bold, - } - } -} +use crate::{app::Content, widgets::clock::Style}; #[derive(Parser)] pub struct Args { @@ -45,41 +14,38 @@ pub struct Args { help = "Countdown time to start from. Format: 'ss', 'mm:ss', or 'hh:mm:ss'" )] pub countdown: Duration, + #[arg(long, short, value_parser = parse_duration, - default_value="25:00" /* 25min */, help = "Work time to count down from. Format: 'ss', 'mm:ss', or 'hh:mm:ss'" )] - pub work: Duration, + pub work: Option, + #[arg(long, short, value_parser = parse_duration, default_value="5:00" /* 5min */, help = "Pause time to count down from. Format: 'ss', 'mm:ss', or 'hh:mm:ss'" )] pub pause: Duration, - #[arg( - long, - short = 'd', - default_value = "false", - help = "Wether to show deciseconds or not" - )] + #[arg(long, short = 'd', help = "Whether to show deciseconds or not")] pub decis: bool, #[arg( short = 'm', value_enum, - default_value = "timer", help = "Mode to start with: [t]imer, [c]ountdown, [p]omodoro" )] - pub mode: Content, + pub mode: Option, #[arg( long, short = 's', value_enum, - default_value = "bold", help = "Style to display time with: [b]old, [t]hick, [c]ross, [e]mpty" )] - pub style: ClockStyle, + pub style: Option