tiempo-rs/src/commands/resume.rs

171 lines
4.9 KiB
Rust
Raw Normal View History

2021-07-26 16:55:24 -05:00
use std::convert::TryFrom;
2021-08-25 14:30:27 -05:00
use std::io::{BufRead, Write};
2021-07-26 16:55:24 -05:00
use clap::ArgMatches;
use chrono::{DateTime, Utc};
use crate::error::{Error, Result};
use crate::timeparse::parse_time;
use crate::database::Database;
2021-08-25 14:30:27 -05:00
use crate::io::Streams;
2021-07-26 16:55:24 -05:00
use crate::interactive::note_from_last_entries;
2021-08-25 14:30:27 -05:00
use super::{Command, Facts, r#in, sheet};
2021-07-26 16:55:24 -05:00
#[derive(Default)]
enum SelectedEntry {
Id(u64),
Interactive,
#[default]
NotSpecified,
}
2021-07-26 16:55:24 -05:00
#[derive(Default)]
pub struct Args {
entry: SelectedEntry,
2021-07-26 16:55:24 -05:00
at: Option<DateTime<Utc>>,
}
2023-02-13 18:17:47 -06:00
impl<'a> TryFrom<&'a ArgMatches> for Args {
2021-07-26 16:55:24 -05:00
type Error = Error;
fn try_from(matches: &'a ArgMatches) -> Result<Self> {
let entry = if let Some(m) = matches.value_of("id") {
SelectedEntry::Id(m.parse().unwrap())
} else if matches.is_present("interactive") {
SelectedEntry::Interactive
} else {
SelectedEntry::NotSpecified
};
2021-07-26 16:55:24 -05:00
Ok(Args {
2021-12-13 14:20:56 -06:00
at: matches.value_of("at").map(parse_time).transpose()?,
entry,
2021-07-26 16:55:24 -05:00
})
}
}
fn resume<D, I, O, E>(args: Args, streams: &mut Streams<D, I, O, E>, facts: &Facts, note: String) -> Result<()>
2021-07-28 20:51:33 -05:00
where
D: Database,
2021-08-25 14:30:27 -05:00
I: BufRead,
2021-07-28 20:51:33 -05:00
O: Write,
E: Write,
{
writeln!(
2021-08-25 14:30:27 -05:00
streams.out,
"Resuming \"{note}\"",
2021-07-28 20:51:33 -05:00
)?;
r#in::InCommand::handle(r#in::Args {
at: args.at,
note: Some(note),
2021-08-25 14:30:27 -05:00
}, streams, facts)
2021-07-28 20:51:33 -05:00
}
2021-07-26 16:55:24 -05:00
pub struct ResumeCommand;
impl<'a> Command<'a> for ResumeCommand {
type Args = Args;
2021-08-25 14:30:27 -05:00
fn handle<D, I, O, E>(args: Args, streams: &mut Streams<D, I, O, E>, facts: &Facts) -> Result<()>
2021-07-26 16:55:24 -05:00
where
D: Database,
2021-08-25 14:30:27 -05:00
I: BufRead,
2021-07-26 16:55:24 -05:00
O: Write,
E: Write,
{
let current_sheet = streams.db.current_sheet()?;
2021-07-26 16:55:24 -05:00
2021-07-28 20:51:33 -05:00
// First try to process using the given id
match args.entry {
SelectedEntry::Id(entry_id) => {
if let Some(entry) = streams.db.entry_by_id(entry_id)? {
if entry.sheet != current_sheet {
// first swith to the sheet
sheet::SheetCommand::handle(sheet::Args {
sheet: Some(entry.sheet.clone()),
}, streams, facts)?;
}
resume(args, streams, facts, entry.note.unwrap_or_default())
} else {
writeln!(streams.out, "The entry with id '{}' could not be found to be resumed. Perhaps it was deleted?", entry_id)?;
Ok(())
2021-07-28 20:51:33 -05:00
}
2021-07-26 16:55:24 -05:00
}
SelectedEntry::Interactive => {
if let Some(note) = note_from_last_entries(streams, facts, &current_sheet)? {
resume(args, streams, facts, note)
} else {
Ok(())
}
}
SelectedEntry::NotSpecified => {
// No id specified, try to find something suitable to switch to in the
// database
if let Some(entry) = streams.db.last_checkout_of_sheet(&current_sheet)? {
resume(args, streams, facts, entry.note.unwrap_or_default())
} else {
writeln!(streams.out, "No entry to resume in the sheet '{}'. Perhaps start a new one?
2021-07-28 20:51:33 -05:00
Hint: use t in", current_sheet)?;
2021-07-26 16:55:24 -05:00
Ok(())
}
}
2021-07-26 16:55:24 -05:00
}
}
}
#[cfg(test)]
mod tests {
use chrono::Duration;
2022-07-19 05:38:37 -05:00
use pretty_assertions::{assert_eq, assert_str_eq};
2021-07-26 16:55:24 -05:00
use super::*;
#[test]
fn resume_an_entry() {
let args = Default::default();
2021-08-25 14:30:27 -05:00
let mut streams = Streams::fake(b"");
let facts = Facts::new();
let one_hour_ago = facts.now - Duration::hours(1);
let two_hours_ago = facts.now - Duration::hours(2);
2021-07-26 16:55:24 -05:00
2021-08-25 14:30:27 -05:00
streams.db.entry_insert(two_hours_ago, Some(one_hour_ago), Some("fake note".into()), "default").unwrap();
2021-07-26 16:55:24 -05:00
2021-08-25 14:30:27 -05:00
assert_eq!(streams.db.entries_full(None, None).unwrap().len(), 1);
2021-07-26 16:55:24 -05:00
2021-08-25 14:30:27 -05:00
ResumeCommand::handle(args, &mut streams, &facts).unwrap();
2021-07-26 16:55:24 -05:00
2021-08-25 14:30:27 -05:00
let all_entries = streams.db.entries_full(None, None).unwrap();
2021-07-26 16:55:24 -05:00
assert_eq!(all_entries.len(), 2);
assert_eq!(all_entries[1].id, 2);
2021-08-25 14:30:27 -05:00
assert_eq!(all_entries[1].start, facts.now);
2021-07-26 16:55:24 -05:00
assert_eq!(all_entries[1].end, None);
assert_eq!(all_entries[1].note, Some("fake note".into()));
assert_eq!(all_entries[1].sheet, "default");
2022-07-19 05:38:37 -05:00
assert_str_eq!(&String::from_utf8_lossy(&streams.out), "Resuming \"fake note\"
Checked into sheet \"default\".\n");
assert_str_eq!(&String::from_utf8_lossy(&streams.err), "");
2021-07-26 16:55:24 -05:00
}
#[test]
fn no_entries_to_resume() {
let args = Default::default();
2021-08-25 14:30:27 -05:00
let mut streams = Streams::fake(b"");
let facts = Facts::new();
2021-07-26 16:55:24 -05:00
2021-08-25 14:30:27 -05:00
ResumeCommand::handle(args, &mut streams, &facts).unwrap();
2021-07-26 16:55:24 -05:00
2022-07-19 05:39:06 -05:00
assert_str_eq!(&String::from_utf8_lossy(&streams.out), "\
2021-07-28 20:51:33 -05:00
No entry to resume in the sheet 'default'. Perhaps start a new one?
Hint: use t in
2022-07-19 05:39:06 -05:00
");
}
2021-07-26 16:55:24 -05:00
}