fix edits (#26)

incl. editing `deciseconds` + more tests
This commit is contained in:
Jens K. 2024-12-26 20:03:22 +01:00 committed by GitHub
parent ed7c17caf9
commit ca81094a36
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 498 additions and 19 deletions

View File

@ -1,6 +1,7 @@
use std::fmt;
use std::time::Duration;
pub const ONE_DECI_SECOND: Duration = Duration::from_millis(100);
pub const ONE_SECOND: Duration = Duration::from_secs(1);
pub const ONE_MINUTE: Duration = Duration::from_secs(SECS_PER_MINUTE);
pub const ONE_HOUR: Duration = Duration::from_secs(MINS_PER_HOUR * SECS_PER_MINUTE);
@ -140,4 +141,20 @@ mod tests {
let ex: DurationEx = Duration::from_secs(1).into();
assert_eq!(format!("{}", ex), "1");
}
#[test]
fn test_saturating_sub() {
let ex: DurationEx = Duration::from_secs(10).into();
let ex2: DurationEx = Duration::from_secs(1).into();
let ex3 = ex.saturating_sub(ex2);
assert_eq!(format!("{}", ex3), "9");
}
#[test]
fn test_saturating_add() {
let ex: DurationEx = Duration::from_secs(10).into();
let ex2: DurationEx = Duration::from_secs(1).into();
let ex3 = ex.saturating_add(ex2);
assert_eq!(format!("{}", ex3), "11");
}
}

View File

@ -1,4 +1,6 @@
pub mod clock;
#[cfg(test)]
pub mod clock_test;
pub mod countdown;
pub mod footer;
pub mod header;

View File

@ -13,7 +13,10 @@ use ratatui::{
};
use crate::{
duration::{DurationEx, MINS_PER_HOUR, ONE_HOUR, ONE_MINUTE, ONE_SECOND, SECS_PER_MINUTE},
duration::{
DurationEx, MINS_PER_HOUR, ONE_DECI_SECOND, ONE_HOUR, ONE_MINUTE, ONE_SECOND,
SECS_PER_MINUTE,
},
utils::center_horizontal,
};
@ -23,6 +26,7 @@ const MAX_DURATION: Duration =
#[derive(Debug, Copy, Clone, Display, PartialEq, Eq)]
pub enum Time {
Decis,
Seconds,
Minutes,
Hours,
@ -47,6 +51,7 @@ impl fmt::Display for Mode {
Mode::Tick => write!(f, ">"),
Mode::Pause => write!(f, "||"),
Mode::Editable(time, _) => match time {
Time::Decis => write!(f, "[edit deciseconds]"),
Time::Seconds => write!(f, "[edit seconds]"),
Time::Minutes => write!(f, "[edit minutes]"),
Time::Hours => write!(f, "[edit hours]"),
@ -168,7 +173,18 @@ impl<T> Clock<T> {
}
pub fn edit_current_up(&mut self) {
match self.mode {
self.current_value = match self.mode {
Mode::Editable(Time::Decis, _) => {
if self
.current_value
// < 99:59:58
.le(&MAX_DURATION.saturating_sub(ONE_DECI_SECOND).into())
{
self.current_value.saturating_add(ONE_DECI_SECOND.into())
} else {
self.current_value
}
}
Mode::Editable(Time::Seconds, _) => {
if self
.current_value
@ -208,6 +224,9 @@ impl<T> Clock<T> {
}
pub fn edit_current_down(&mut self) {
self.current_value = match self.mode {
Mode::Editable(Time::Decis, _) => {
self.current_value.saturating_sub(ONE_DECI_SECOND.into())
}
Mode::Editable(Time::Seconds, _) => {
self.current_value.saturating_sub(ONE_SECOND.into())
}
@ -236,14 +255,25 @@ impl<T> Clock<T> {
fn edit_mode_next(&mut self) {
let mode = self.mode.clone();
self.mode = match mode {
Mode::Editable(Time::Seconds, prev) if self.format >= Format::MSs => {
Mode::Editable(Time::Minutes, prev)
Mode::Editable(Time::Decis, prev) => Mode::Editable(Time::Seconds, prev),
Mode::Editable(Time::Seconds, prev) if self.format <= Format::Ss && self.with_decis => {
Mode::Editable(Time::Decis, prev)
}
Mode::Editable(Time::Seconds, prev) if self.format <= Format::Ss => {
Mode::Editable(Time::Seconds, prev)
}
Mode::Editable(Time::Seconds, prev) => Mode::Editable(Time::Minutes, prev),
Mode::Editable(Time::Minutes, prev)
if self.format <= Format::MmSs && self.with_decis =>
{
Mode::Editable(Time::Decis, prev)
}
Mode::Editable(Time::Minutes, prev) if self.format <= Format::MmSs => {
Mode::Editable(Time::Seconds, prev)
}
Mode::Editable(Time::Minutes, prev) if self.format >= Format::MmSs => {
Mode::Editable(Time::Hours, prev)
Mode::Editable(Time::Minutes, prev) => Mode::Editable(Time::Hours, prev),
Mode::Editable(Time::Hours, prev) if self.with_decis => {
Mode::Editable(Time::Decis, prev)
}
Mode::Editable(Time::Hours, prev) => Mode::Editable(Time::Seconds, prev),
_ => mode,
@ -254,12 +284,27 @@ impl<T> Clock<T> {
fn edit_mode_prev(&mut self) {
let mode = self.mode.clone();
self.mode = match mode {
Mode::Editable(Time::Seconds, prev) if self.format >= Format::HMmSs => {
Mode::Editable(Time::Decis, prev) if self.format <= Format::Ss => {
Mode::Editable(Time::Seconds, prev)
}
Mode::Editable(Time::Decis, prev) if self.format <= Format::MmSs => {
Mode::Editable(Time::Minutes, prev)
}
Mode::Editable(Time::Decis, prev) if self.format <= Format::HhMmSs => {
Mode::Editable(Time::Hours, prev)
}
Mode::Editable(Time::Seconds, prev) if self.with_decis => {
Mode::Editable(Time::Decis, prev)
}
Mode::Editable(Time::Seconds, prev) if self.format <= Format::Ss => {
Mode::Editable(Time::Seconds, prev)
}
Mode::Editable(Time::Seconds, prev) if self.format <= Format::MmSs => {
Mode::Editable(Time::Minutes, prev)
}
Mode::Editable(Time::Seconds, prev) if self.format <= Format::HhMmSs => {
Mode::Editable(Time::Hours, prev)
}
Mode::Editable(Time::Minutes, prev) => Mode::Editable(Time::Seconds, prev),
Mode::Editable(Time::Hours, prev) => Mode::Editable(Time::Minutes, prev),
_ => mode,
@ -374,18 +419,14 @@ impl Clock<Countdown> {
pub fn edit_up(&mut self) {
self.edit_current_up();
// update `initial_value` if needed
// re-align `current_value` if needed
if self.initial_value.lt(&self.current_value) {
self.initial_value = self.current_value;
self.current_value = self.initial_value;
}
}
pub fn edit_down(&mut self) {
self.edit_current_down();
// update `initial_value` if needed
if self.initial_value.gt(&self.current_value) {
self.initial_value = self.current_value;
}
}
}
@ -797,6 +838,7 @@ where
let edit_hours = matches!(state.mode, Mode::Editable(Time::Hours, _));
let edit_minutes = matches!(state.mode, Mode::Editable(Time::Minutes, _));
let edit_secs = matches!(state.mode, Mode::Editable(Time::Seconds, _));
let edit_deci = matches!(state.mode, Mode::Editable(Time::Decis, _));
match format {
Format::HhMmSs if with_decis => {
let [hh, _, h, c_hm, mm, _, m, c_ms, ss, _, s, d, ds] =
@ -840,7 +882,7 @@ where
buf,
);
self.render_dot(symbol, d, buf);
self.render_digit(state.current_value.decis(), symbol, false, ds, buf);
self.render_digit(state.current_value.decis(), symbol, edit_deci, ds, buf);
}
Format::HhMmSs => {
let [hh, _, h, c_hm, mm, _, m, c_ms, ss, _, s] =
@ -919,7 +961,7 @@ where
buf,
);
self.render_dot(symbol, d, buf);
self.render_digit(state.current_value.decis(), symbol, false, ds, buf);
self.render_digit(state.current_value.decis(), symbol, edit_deci, ds, buf);
}
Format::HMmSs => {
let [h, c_hm, mm, _, m, c_ms, ss, _, s] =
@ -989,7 +1031,7 @@ where
buf,
);
self.render_dot(symbol, d, buf);
self.render_digit(state.current_value.decis(), symbol, false, ds, buf);
self.render_digit(state.current_value.decis(), symbol, edit_deci, ds, buf);
}
Format::MmSs => {
let [mm, _, m, c_ms, ss, _, s] =
@ -1050,7 +1092,7 @@ where
buf,
);
self.render_dot(symbol, d, buf);
self.render_digit(state.current_value.decis(), symbol, false, ds, buf);
self.render_digit(state.current_value.decis(), symbol, edit_deci, ds, buf);
}
Format::MSs => {
let [m, c_ms, ss, _, s] =
@ -1096,7 +1138,7 @@ where
buf,
);
self.render_dot(symbol, d, buf);
self.render_digit(state.current_value.decis(), symbol, false, ds, buf);
self.render_digit(state.current_value.decis(), symbol, edit_deci, ds, buf);
}
Format::Ss => {
let [ss, _, s] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area);
@ -1125,7 +1167,7 @@ where
buf,
);
self.render_dot(symbol, d, buf);
self.render_digit(state.current_value.decis(), symbol, false, ds, buf);
self.render_digit(state.current_value.decis(), symbol, edit_deci, ds, buf);
}
Format::S => {
let [s] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area);

418
src/widgets/clock_test.rs Normal file
View File

@ -0,0 +1,418 @@
use crate::{
duration::{ONE_DECI_SECOND, ONE_HOUR, ONE_MINUTE, ONE_SECOND},
widgets::clock::*,
};
use std::time::Duration;
#[test]
fn test_toggle_edit() {
let mut c = Clock::<Timer>::new(ClockArgs {
initial_value: ONE_HOUR,
current_value: ONE_HOUR,
tick_value: ONE_DECI_SECOND,
style: Style::default(),
with_decis: true,
});
// off by default
assert!(!c.is_edit_mode());
// toggle on
c.toggle_edit();
assert!(c.is_edit_mode());
// toggle off
c.toggle_edit();
assert!(!c.is_edit_mode());
}
#[test]
fn test_default_edit_mode_hhmmss() {
let mut c = Clock::<Timer>::new(ClockArgs {
initial_value: ONE_HOUR,
current_value: ONE_HOUR,
tick_value: ONE_DECI_SECOND,
style: Style::default(),
with_decis: true,
});
// toggle on
c.toggle_edit();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Minutes, _)));
}
#[test]
fn test_default_edit_mode_mmss() {
let mut c = Clock::<Timer>::new(ClockArgs {
initial_value: ONE_MINUTE,
current_value: ONE_MINUTE,
tick_value: ONE_DECI_SECOND,
style: Style::default(),
with_decis: true,
});
// toggle on
c.toggle_edit();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Minutes, _)));
}
#[test]
fn test_default_edit_mode_ss() {
let mut c = Clock::<Timer>::new(ClockArgs {
initial_value: ONE_SECOND,
current_value: ONE_SECOND,
tick_value: ONE_DECI_SECOND,
style: Style::default(),
with_decis: true,
});
// toggle on
c.toggle_edit();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Seconds, _)));
}
#[test]
fn test_edit_next_hhmmssd() {
let mut c = Clock::<Timer>::new(ClockArgs {
initial_value: ONE_HOUR,
current_value: ONE_HOUR,
tick_value: ONE_DECI_SECOND,
style: Style::default(),
with_decis: true,
});
// toggle on
c.toggle_edit();
c.edit_next();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Hours, _)));
c.edit_next();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Decis, _)));
c.edit_next();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Seconds, _)));
c.edit_next();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Minutes, _)));
}
#[test]
fn test_edit_next_hhmmss() {
let mut c = Clock::<Timer>::new(ClockArgs {
initial_value: ONE_HOUR,
current_value: ONE_HOUR,
tick_value: ONE_DECI_SECOND,
style: Style::default(),
with_decis: false,
});
// toggle on
c.toggle_edit();
c.edit_next();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Hours, _)));
c.edit_next();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Seconds, _)));
c.edit_next();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Minutes, _)));
}
#[test]
fn test_edit_next_mmssd() {
let mut c = Clock::<Timer>::new(ClockArgs {
initial_value: ONE_MINUTE,
current_value: ONE_MINUTE,
tick_value: ONE_DECI_SECOND,
style: Style::default(),
with_decis: true,
});
// toggle on
c.toggle_edit();
c.edit_next();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Decis, _)));
c.edit_next();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Seconds, _)));
c.edit_next();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Minutes, _)));
}
#[test]
fn test_edit_next_mmss() {
let mut c = Clock::<Timer>::new(ClockArgs {
initial_value: ONE_MINUTE,
current_value: ONE_MINUTE,
tick_value: ONE_DECI_SECOND,
style: Style::default(),
with_decis: false,
});
// toggle on
c.toggle_edit();
c.edit_next();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Seconds, _)));
c.edit_next();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Minutes, _)));
}
#[test]
fn test_edit_next_ssd() {
let mut c = Clock::<Timer>::new(ClockArgs {
initial_value: ONE_SECOND * 3,
current_value: ONE_SECOND * 3,
tick_value: ONE_DECI_SECOND,
style: Style::default(),
with_decis: true,
});
// toggle on
c.toggle_edit();
c.edit_next();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Decis, _)));
}
#[test]
fn test_edit_next_ss() {
let mut c = Clock::<Timer>::new(ClockArgs {
initial_value: ONE_SECOND * 3,
current_value: ONE_SECOND * 3,
tick_value: ONE_DECI_SECOND,
style: Style::default(),
with_decis: false,
});
// toggle on
c.toggle_edit();
c.edit_next();
println!("mode -> {:?}", c.get_mode());
assert!(matches!(c.get_mode(), Mode::Editable(Time::Seconds, _)));
}
#[test]
fn test_edit_prev_hhmmssd() {
let mut c = Clock::<Timer>::new(ClockArgs {
initial_value: ONE_HOUR,
current_value: ONE_HOUR,
tick_value: ONE_DECI_SECOND,
style: Style::default(),
with_decis: true,
});
// toggle on
c.toggle_edit();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Minutes, _)));
c.edit_prev();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Seconds, _)));
c.edit_prev();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Decis, _)));
c.edit_prev();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Hours, _)));
}
#[test]
fn test_edit_prev_hhmmss() {
let mut c = Clock::<Timer>::new(ClockArgs {
initial_value: ONE_HOUR,
current_value: ONE_HOUR,
tick_value: ONE_DECI_SECOND,
style: Style::default(),
with_decis: false,
});
// toggle on
c.toggle_edit();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Minutes, _)));
c.edit_prev();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Seconds, _)));
c.edit_prev();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Hours, _)));
}
#[test]
fn test_edit_prev_mmssd() {
let mut c = Clock::<Timer>::new(ClockArgs {
initial_value: ONE_MINUTE,
current_value: ONE_MINUTE,
tick_value: ONE_DECI_SECOND,
style: Style::default(),
with_decis: true,
});
// toggle on
c.toggle_edit();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Minutes, _)));
c.edit_prev();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Seconds, _)));
c.edit_prev();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Decis, _)));
c.edit_prev();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Minutes, _)));
}
#[test]
fn test_edit_prev_mmss() {
let mut c = Clock::<Timer>::new(ClockArgs {
initial_value: ONE_MINUTE,
current_value: ONE_MINUTE,
tick_value: ONE_DECI_SECOND,
style: Style::default(),
with_decis: false,
});
// toggle on
c.toggle_edit();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Minutes, _)));
c.edit_prev();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Seconds, _)));
c.edit_prev();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Minutes, _)));
}
#[test]
fn test_edit_prev_ssd() {
let mut c = Clock::<Timer>::new(ClockArgs {
initial_value: ONE_SECOND,
current_value: ONE_SECOND,
tick_value: ONE_DECI_SECOND,
style: Style::default(),
with_decis: true,
});
// toggle on
c.toggle_edit();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Seconds, _)));
c.edit_prev();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Decis, _)));
c.edit_prev();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Seconds, _)));
}
#[test]
fn test_edit_prev_ss() {
let mut c = Clock::<Timer>::new(ClockArgs {
initial_value: ONE_SECOND,
current_value: ONE_SECOND,
tick_value: ONE_DECI_SECOND,
style: Style::default(),
with_decis: false,
});
// toggle on
c.toggle_edit();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Seconds, _)));
c.edit_prev();
assert!(matches!(c.get_mode(), Mode::Editable(Time::Seconds, _)));
}
#[test]
fn test_edit_up_ss() {
let mut c = Clock::<Timer>::new(ClockArgs {
initial_value: Duration::ZERO,
current_value: Duration::ZERO,
tick_value: ONE_DECI_SECOND,
style: Style::default(),
with_decis: false,
});
// toggle on
c.toggle_edit();
// +1s
c.edit_up();
assert_eq!(Duration::from(*c.get_current_value()), ONE_SECOND);
}
#[test]
fn test_edit_up_mmss() {
let mut c = Clock::<Timer>::new(ClockArgs {
initial_value: Duration::ZERO,
current_value: Duration::from_secs(60),
tick_value: ONE_DECI_SECOND,
style: Style::default(),
with_decis: false,
});
// toggle on
c.toggle_edit();
// +1m
c.edit_up();
assert_eq!(
Duration::from(*c.get_current_value()),
Duration::from_secs(120)
);
}
#[test]
fn test_edit_up_hhmmss() {
let mut c = Clock::<Timer>::new(ClockArgs {
initial_value: Duration::ZERO,
current_value: Duration::from_secs(3600),
tick_value: ONE_DECI_SECOND,
style: Style::default(),
with_decis: false,
});
// toggle on
c.toggle_edit();
// edit hh
c.edit_next();
// +1h
c.edit_up();
assert_eq!(
Duration::from(*c.get_current_value()),
Duration::from_secs(3600 + 3600)
);
}
#[test]
fn test_edit_down_ss() {
let mut c = Clock::<Timer>::new(ClockArgs {
initial_value: Duration::ZERO,
current_value: ONE_SECOND,
tick_value: ONE_DECI_SECOND,
style: Style::default(),
with_decis: false,
});
// toggle on
c.toggle_edit();
// -1s
c.edit_down();
assert_eq!(Duration::from(*c.get_current_value()), Duration::ZERO);
// and again: -1s
c.edit_down();
// still ZERO
assert_eq!(Duration::from(*c.get_current_value()), Duration::ZERO);
}
#[test]
fn test_edit_down_mmss() {
let mut c = Clock::<Timer>::new(ClockArgs {
initial_value: Duration::ZERO,
current_value: Duration::from_secs(120),
tick_value: ONE_DECI_SECOND,
style: Style::default(),
with_decis: false,
});
// toggle on
c.toggle_edit();
// -1m
c.edit_down();
assert_eq!(
Duration::from(*c.get_current_value()),
Duration::from_secs(60)
);
// and again: -1m
c.edit_down();
assert_eq!(Duration::from(*c.get_current_value()), Duration::ZERO);
}
#[test]
fn test_edit_down_hhmmss() {
let mut c = Clock::<Timer>::new(ClockArgs {
initial_value: Duration::ZERO,
current_value: Duration::from_secs(3600),
tick_value: ONE_DECI_SECOND,
style: Style::default(),
with_decis: false,
});
// toggle on
c.toggle_edit();
// edit hh
c.edit_next();
// +1h
c.edit_down();
assert_eq!(Duration::from(*c.get_current_value()), Duration::ZERO);
}