|
|
@ -88,6 +88,7 @@ enum TTError { |
|
|
|
RecordInsertionError(rusqlite::Error),
|
|
|
|
RecordSelectionError(rusqlite::Error),
|
|
|
|
RecordMappingError(rusqlite::Error),
|
|
|
|
BadDateFormat(chrono::ParseError),
|
|
|
|
}
|
|
|
|
impl Display for TTError {
|
|
|
|
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
|
|
|
@ -104,6 +105,7 @@ impl Error for TTError { |
|
|
|
TTError::RecordInsertionError(_) => "failed to insert record database",
|
|
|
|
TTError::RecordSelectionError(_) => "failed to select today's records",
|
|
|
|
TTError::RecordMappingError(_) => "failed to map record from database row",
|
|
|
|
TTError::BadDateFormat(_) => "bad date format",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
@ -114,6 +116,7 @@ impl Error for TTError { |
|
|
|
TTError::RecordInsertionError(err) => Some(err),
|
|
|
|
TTError::RecordSelectionError(err) => Some(err),
|
|
|
|
TTError::RecordMappingError(err) => Some(err),
|
|
|
|
TTError::BadDateFormat(err) => Some(err),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -135,17 +138,14 @@ fn prepare_database(conn: &rusqlite::Connection) -> Result { |
|
|
|
).map_err(TTError::DatabaseCreationError)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn todays_recs(conn: &rusqlite::Connection) -> Result<Vec<Record>, TTError> {
|
|
|
|
let today = Local::today();
|
|
|
|
let tomorrow = today.succ();
|
|
|
|
fn recs_at_date(conn: &rusqlite::Connection, date: NaiveDate) -> Result<Vec<Record>, TTError> {
|
|
|
|
let next_date = date.succ();
|
|
|
|
let mut stmt = conn.prepare(
|
|
|
|
"SELECT timestamp, state FROM Timestamps WHERE timestamp >= ? AND timestamp < ? ORDER BY timestamp ASC",
|
|
|
|
).map_err(TTError::RecordSelectionError)?;
|
|
|
|
let rec_iter = stmt
|
|
|
|
.query_map(
|
|
|
|
&[&today.naive_local(), &tomorrow.naive_local()],
|
|
|
|
Record::from_row,
|
|
|
|
).map_err(TTError::RecordMappingError)?;
|
|
|
|
.query_map(&[&date, &next_date], Record::from_row)
|
|
|
|
.map_err(TTError::RecordMappingError)?;
|
|
|
|
let recs: Vec<Record> = rec_iter
|
|
|
|
.filter_map(Result::ok)
|
|
|
|
.filter_map(Result::ok)
|
|
|
@ -157,10 +157,10 @@ fn usage(program: &str) -> String { |
|
|
|
format!(
|
|
|
|
"{} <cmd>
|
|
|
|
|
|
|
|
usage | help: shows this message
|
|
|
|
begin | stop: begin or stop work
|
|
|
|
pause: starts/stops a pause
|
|
|
|
show: prints today's work time",
|
|
|
|
usage | help: shows this message
|
|
|
|
begin | stop: begin or stop work
|
|
|
|
pause: starts/stops a pause
|
|
|
|
show [yyyy-mm-dd]: prints work time for given date of today's when absent",
|
|
|
|
program
|
|
|
|
)
|
|
|
|
}
|
|
|
@ -186,7 +186,7 @@ fn main() -> Result<(), TTError> { |
|
|
|
if let Some(subcommand) = args.pop_front() {
|
|
|
|
match subcommand.as_ref() {
|
|
|
|
"help" | "usage" => print_usage(&program),
|
|
|
|
"show" => show(&conn)?,
|
|
|
|
"show" => show(&conn, args.pop_front())?,
|
|
|
|
"begin" | "stop" => record(&conn, State::Work)?,
|
|
|
|
"pause" => record(&conn, State::Pause)?,
|
|
|
|
_ => {
|
|
|
@ -198,8 +198,17 @@ fn main() -> Result<(), TTError> { |
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn show(conn: &rusqlite::Connection) -> Result<(), TTError> {
|
|
|
|
println!("{}", format_duration(work_dur(&todays_recs(&conn)?)));
|
|
|
|
fn show(conn: &rusqlite::Connection, date_arg: Option<String>) -> Result<(), TTError> {
|
|
|
|
let parsed_date = if let Some(date) = date_arg {
|
|
|
|
NaiveDate::parse_from_str(&date, "%F") // ISO 8601
|
|
|
|
.map_err(TTError::BadDateFormat)?
|
|
|
|
} else {
|
|
|
|
Local::today().naive_local()
|
|
|
|
};
|
|
|
|
println!(
|
|
|
|
"{}",
|
|
|
|
format_duration(work_dur(&recs_at_date(&conn, parsed_date)?))
|
|
|
|
);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|