From 75482004ececcaa8045a0976aad93f5906705a3a Mon Sep 17 00:00:00 2001 From: ryan Date: Thu, 21 Nov 2024 11:43:13 -0500 Subject: [PATCH] make it work more oneshot like --- src/main.rs | 125 +++++++++++++++++++++++++++------------------------- 1 file changed, 65 insertions(+), 60 deletions(-) diff --git a/src/main.rs b/src/main.rs index 8200d6c..7b46ad3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,10 @@ -use std::path::PathBuf; use std::io::Write; -use std::time::{SystemTime, Duration}; +use std::path::PathBuf; +use std::time::{Duration, SystemTime}; -use chrono::{DateTime, offset::Utc}; -use serde::{Deserialize, Serialize}; +use chrono::{offset::Utc, DateTime}; use clap::Parser; +use serde::{Deserialize, Serialize}; const SECONDS_IN_MINUTE: u64 = 60; @@ -68,7 +68,7 @@ enum PunchIn { /// The file to store time-tracking information. file: PathBuf, - } + }, } #[derive(Serialize, Deserialize, Clone, Debug)] @@ -82,7 +82,10 @@ fn main() { let args = PunchIn::parse(); match args { - PunchIn::Start { file: path, round_up_to } => { + PunchIn::Start { + file: path, + round_up_to, + } => { let tt = if std::fs::exists(&path).is_ok_and(|v| v) { let file = std::fs::File::open(&path).unwrap(); let mut tt: Vec = serde_json::from_reader(&file).unwrap(); @@ -106,36 +109,49 @@ fn main() { }; println!("Writing to file: {tt:?}"); std::fs::write(&path, serde_json::to_string(&tt).unwrap()).unwrap(); - }, - PunchIn::Report { format: ReportFormat::Markdown, timescale, file: path } => { + } + PunchIn::Report { + format: ReportFormat::Markdown, + timescale, + file: path, + } => { let header = timescale.header(); println!("| Date | {header} | Project |"); - println!("|------------|-{}-|---------|", String::from("-").repeat(header.len())); + println!( + "|------------|-{}-|---------|", + String::from("-").repeat(header.len()) + ); let file = std::fs::File::open(&path).unwrap(); let tt: Vec = serde_json::from_reader(&file).unwrap(); for entry in tt { let date = DateTime::::from(entry.start); - println!("| {date} | {duration:>4}{timescale} | {project} |", - date = date.date_naive(), - duration = entry.end.as_secs() as f64 / timescale.divisor(), - project = entry.project, - ); + println!( + "| {date} | {duration:>4}{timescale} | {project} |", + date = date.date_naive(), + duration = entry.end.as_secs() as f64 / timescale.divisor(), + project = entry.project, + ); } - }, - PunchIn::Report { format: ReportFormat::CSV, timescale, file: path } => { + } + PunchIn::Report { + format: ReportFormat::CSV, + timescale, + file: path, + } => { let header = timescale.header().to_lowercase(); println!("date,{header},project"); let file = std::fs::File::open(&path).unwrap(); let tt: Vec = serde_json::from_reader(&file).unwrap(); for entry in tt { let date = DateTime::::from(entry.start); - println!("{date},{duration}{timescale},{project}", - date = date.date_naive(), - duration = entry.end.as_secs() as f64 / timescale.divisor(), - project = entry.project, - ); + println!( + "{date},{duration}{timescale},{project}", + date = date.date_naive(), + duration = entry.end.as_secs() as f64 / timescale.divisor(), + project = entry.project, + ); } - }, + } } } @@ -144,42 +160,31 @@ fn start(tt: &mut Vec, round_up_to: u64) { let time_modulus = round_up_to * SECONDS_IN_MINUTE; let mut lines = std::io::stdin().lines(); let stdout = std::io::stdout(); - let mut last_time = SystemTime::now(); - let mut last_project = String::new(); - loop { - println!(); - println!("Enter the project you're working on, or send EOL to terminate. When starting"); - println!("work on a new project, or when sending EOL, your time will be locked in for"); - println!("the current project"); - print!("Project: "); - stdout.lock().flush().unwrap(); - let mut has_line = false; - let line = lines.next(); - // invariant: because line is never empty, last_project is only empty on first run - if !last_project.is_empty() { - // append the last project to the tt list - let elapsed_secs = last_time.elapsed().unwrap().as_secs(); - // TODO: Rounding error: If this somehow gets to _exactly_ a round_up_to boundary, to - // the second, it will round up. Is there a better math way to do this? - let rounded_secs = elapsed_secs + time_modulus - - ((elapsed_secs + time_modulus) % time_modulus); - tt.push(TimeTracking { - start: last_time, - // Assume the human is not faster than the machine's ability to go back in time. - end: Duration::from_secs(rounded_secs), - project: last_project.clone(), - }); - } - if let Some(Ok(line)) = line { - if !line.is_empty() { - has_line = true; - println!("Starting tracking for: {line}"); - last_project = line; - last_time = SystemTime::now(); - } - } - if !has_line { - break; - } - } + + print!("Project: "); + stdout.lock().flush().unwrap(); + let project = lines + .next() + .transpose() + .ok() + .flatten() + .filter(|l| !l.is_empty()) + .unwrap(); + let start_time = SystemTime::now(); + + print!("Press Enter to save the current project and elapsed time."); + stdout.lock().flush().unwrap(); + lines.next(); + + // append the project to the tt list + let elapsed_secs = start_time.elapsed().unwrap().as_secs(); + // TODO: Rounding error: If this somehow gets to _exactly_ a round_up_to boundary, to + // the second, it will round up. Is there a better math way to do this? + let rounded_secs = elapsed_secs + time_modulus - ((elapsed_secs + time_modulus) % time_modulus); + tt.push(TimeTracking { + start: start_time, + // Assume the human is not faster than the machine's ability to go back in time. + end: Duration::from_secs(rounded_secs), + project: project.clone(), + }); }