make it work more oneshot like

This commit is contained in:
Ryan Heywood 2024-11-21 11:43:13 -05:00
parent 93c2a806f1
commit 75482004ec
Signed by: ryan
GPG Key ID: 8E401478A3FBEF72
1 changed files with 65 additions and 60 deletions

View File

@ -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<TimeTracking> = 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<TimeTracking> = serde_json::from_reader(&file).unwrap();
for entry in tt {
let date = DateTime::<Utc>::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<TimeTracking> = serde_json::from_reader(&file).unwrap();
for entry in tt {
let date = DateTime::<Utc>::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<TimeTracking>, 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(),
});
}