use std::io::Write; use std::convert::TryFrom; use chrono::{DateTime, Utc}; use clap::ArgMatches; use crate::database::Database; use crate::config::Config; use crate::error::{Error, Result}; use crate::timeparse::parse_time; use crate::old::{time_or_warning, warn_if_needed}; use super::Command; #[derive(Default)] pub struct Args { at: Option>, } impl<'a> TryFrom<&'a ArgMatches<'a>> for Args { type Error = Error; fn try_from(matches: &'a ArgMatches) -> Result { Ok(Args { at: matches.value_of("at").map(|s| parse_time(s)).transpose()?, }) } } pub struct OutCommand{} impl<'a> Command<'a> for OutCommand { type Args = Args; fn handle(args: Self::Args, db: &mut D, out: &mut O, err: &mut E, _config: &Config, now: DateTime) -> Result<()> { let end = args.at.unwrap_or(now); let sheet = db.current_sheet()?.unwrap_or_else(|| "default".into()); let (end, needs_warning) = time_or_warning(end, db)?; if let Some(entry) = db.running_entry(&sheet)? { writeln!(out, "Checked out of sheet \"{}\".", sheet)?; db.entry_update(entry.id, entry.start, Some(end), entry.note, &entry.sheet)?; } else { writeln!(out, "No running entry on sheet \"{}\".", sheet)?; } warn_if_needed(err, needs_warning)?; Ok(()) } } #[cfg(test)] mod tests { use ansi_term::Color::Yellow; use pretty_assertions::assert_eq; use chrono::{TimeZone, Local}; use crate::test_utils::Ps; use crate::database::SqliteDatabase; use super::*; #[test] fn finishes_entry() { let mut db = SqliteDatabase::from_memory().unwrap(); let args = Default::default(); let mut out = Vec::new(); let mut err = Vec::new(); let now = Utc::now(); db.init().unwrap(); db.entry_insert(now, None, None, "default").unwrap(); OutCommand::handle(args, &mut db, &mut out, &mut err, &Default::default(), now).unwrap(); let e = db.entries_full(None, None).unwrap().into_iter().next().unwrap(); assert_eq!(e.end, Some(now)); assert_eq!(Ps(&String::from_utf8_lossy(&out)), Ps("Checked out of sheet \"default\".\n")); assert_eq!(Ps(&String::from_utf8_lossy(&err)), Ps("")); } #[test] fn tells_if_no_running_entry() { let mut db = SqliteDatabase::from_memory().unwrap(); let args = Default::default(); let mut out = Vec::new(); let mut err = Vec::new(); let now = Utc::now(); db.init().unwrap(); db.entry_insert(now, None, None, "non-default").unwrap(); OutCommand::handle(args, &mut db, &mut out, &mut err, &Default::default(), now).unwrap(); assert_eq!(Ps(&String::from_utf8_lossy(&out)), Ps("No running entry on sheet \"default\".\n")); assert_eq!(Ps(&String::from_utf8_lossy(&err)), Ps("")); } #[test] fn warns_if_old_database() { let mut db = SqliteDatabase::from_memory().unwrap(); let args = Default::default(); let mut out = Vec::new(); let mut err = Vec::new(); let now = Utc::now(); db.init_old().unwrap(); db.entry_insert(now, None, None, "default").unwrap(); OutCommand::handle(args, &mut db, &mut out, &mut err, &Default::default(), now).unwrap(); let e = db.entries_full(None, None).unwrap().into_iter().next().unwrap(); assert_eq!(e.end, Some(Utc.from_utc_datetime(&now.with_timezone(&Local).naive_local()))); assert_eq!(Ps(&String::from_utf8_lossy(&out)), Ps("Checked out of sheet \"default\".\n")); assert_eq!(Ps(&String::from_utf8_lossy(&err)), Ps(&format!( "{} You are using the old timetrap format, it is advised that \ you update your database using t migrate\n", Yellow.bold().paint("[WARNING]")))); } }