From 283876ebdae0dbb1187450e04eae4527122dce5b Mon Sep 17 00:00:00 2001 From: Campbell Alden Date: Wed, 8 May 2024 11:37:47 +0900 Subject: [PATCH] Add comments and support for wrapping to a new day --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/main.rs | 47 +++++++++++++++++++++++++++++++++++++---------- 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4fdc7c4..c7d9595 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -360,7 +360,7 @@ dependencies = [ [[package]] name = "time-track" -version = "0.3.5" +version = "0.4.0" dependencies = [ "anyhow", "atty", diff --git a/Cargo.toml b/Cargo.toml index f020b22..2a0eb97 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "time-track" -version = "0.3.5" +version = "0.4.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/src/main.rs b/src/main.rs index 70cd406..9fefea1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,9 @@ use std::io::{stdin, BufRead}; -use chrono::{NaiveTime, Duration, Local}; +use chrono::{NaiveTime, Duration, Local, NaiveDateTime}; use clap::Parser; use atty::Stream; -use anyhow::Result; +use anyhow::{anyhow, Result}; mod modes; use modes::Modes; @@ -16,11 +16,23 @@ struct Args { #[clap(value_enum, default_value_t = Modes::default())] mode: Modes, } -fn parse_time(time_str: &str) -> Result { - let time = NaiveTime::parse_from_str(time_str, "%H:%M")?; - Ok(time) + +fn epoch() -> NaiveTime { + NaiveTime::from_hms_opt(0, 0, 0).expect("0, 0, 0 to be valid arguments to from_hms_opt") } +/// Given an arbitrary string, attempt to parse it as a naive time in the format HH:mm +fn parse_time(time_str: &str) -> Result { + let time = NaiveTime::parse_from_str(time_str, "%H:%M")?; + let naive_date = NaiveDateTime::from_timestamp_millis( + time.signed_duration_since(epoch()).num_milliseconds(), + ).ok_or(anyhow!("Should be able to make a datetime"))?; + Ok(naive_date) +} + +/// The default way of calculating time. Time values are given one per line and subsequent pairs of +/// lines are considered time spans. If an odd number of spans is given, then the final time value +/// is ignored. fn all_at_once(is_terminal: bool) -> Result> { if is_terminal { println!("Enter simple times one per line. Send an EOF character to sum all time spans"); @@ -33,8 +45,19 @@ fn all_at_once(is_terminal: bool) -> Result> { if let Some(previous) = &seen { let first = parse_time(previous)?; let mut second = parse_time(&cleaned)?; - if second < first { + + // The following logic attempts to handle wrapping from AM -> PM and also from PM -> + // AM but cannot handle multiple days in a single span + if (second < first) && first.time().signed_duration_since(epoch()).num_hours() < 12 { + // Assuming wrapped to PM + // first = 8:00 + // second: 1:00 second = second + Duration::hours(12); + } else if second < first { + // Wrapped to a new day because first was after noon. + // first = 14:40 + // second = 1:30 + second = second + Duration::hours(24) } durations.push(second - first); seen = None; @@ -48,6 +71,8 @@ fn all_at_once(is_terminal: bool) -> Result> { return Ok(durations); } +/// A "live" time tracker. Each time the user presses enter a new span is started or an existing +/// span is closed. The user may end the tracking at any time by sending an EOF command. fn live_spans() -> Result> { println!("Tracking Spans Live. Press ENTER to start a span\n"); let mut durations = vec![]; @@ -82,7 +107,9 @@ fn live_spans() -> Result> { return Ok(durations); } -fn get_prediction() -> Result { +/// Predict when the given amount of work has completed based on when the user started working and +/// how long they expect to be on break total. +fn get_prediction(hours: i64) -> Result { println!("Calculating an end time prediction...\nWhen did you start?\n"); let mut start_time = String::new(); let _ = stdin().read_line(&mut start_time); @@ -92,8 +119,8 @@ fn get_prediction() -> Result { let mut break_time = String::new(); let _ = stdin().read_line(&mut break_time); let break_duration = break_time.trim().parse::()?; - let work_time = Duration::hours(8) + Duration::minutes(break_duration); - return Ok(start + work_time); + let work_time = Duration::hours(hours) + Duration::minutes(break_duration); + return Ok(start.time() + work_time); } fn main() { @@ -105,7 +132,7 @@ fn main() { } if args.mode == Modes::Prediction { - if let Ok(prediction) = get_prediction() { + if let Ok(prediction) = get_prediction(8 /* hours */) { println!("Your work will end at {}", prediction); } else { println!("Something went wrong...");