Add comments and support for wrapping to a new day

This commit is contained in:
Campbell Alden 2024-05-08 11:37:47 +09:00
parent 6137e6c046
commit 283876ebda
3 changed files with 39 additions and 12 deletions

2
Cargo.lock generated
View file

@ -360,7 +360,7 @@ dependencies = [
[[package]]
name = "time-track"
version = "0.3.5"
version = "0.4.0"
dependencies = [
"anyhow",
"atty",

View file

@ -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

View file

@ -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<NaiveTime> {
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<NaiveDateTime> {
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<Vec<Duration>> {
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<Vec<Duration>> {
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<Vec<Duration>> {
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<Vec<Duration>> {
println!("Tracking Spans Live. Press ENTER to start a span\n");
let mut durations = vec![];
@ -82,7 +107,9 @@ fn live_spans() -> Result<Vec<Duration>> {
return Ok(durations);
}
fn get_prediction() -> Result<NaiveTime> {
/// 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<NaiveTime> {
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<NaiveTime> {
let mut break_time = String::new();
let _ = stdin().read_line(&mut break_time);
let break_duration = break_time.trim().parse::<i64>()?;
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...");