tiempo-rs/src/commands/out.rs

120 lines
3.5 KiB
Rust

use std::io::{BufRead, Write};
use std::convert::TryFrom;
use chrono::{DateTime, Utc};
use clap::ArgMatches;
use crate::database::Database;
use crate::error::{Error, Result};
use crate::timeparse::parse_time;
use crate::old::{time_or_warning, warn_if_needed};
use crate::io::Streams;
use super::{Command, Facts};
#[derive(Default)]
pub struct Args {
at: Option<DateTime<Utc>>,
}
impl<'a> TryFrom<&'a ArgMatches> for Args {
type Error = Error;
fn try_from(matches: &'a ArgMatches) -> Result<Args> {
Ok(Args {
at: matches.value_of("at").map(parse_time).transpose()?,
})
}
}
pub struct OutCommand{}
impl<'a> Command<'a> for OutCommand {
type Args = Args;
fn handle<D, I, O, E>(args: Self::Args, streams: &mut Streams<D, I, O, E>, facts: &Facts) -> Result<()>
where
D: Database,
I: BufRead,
O: Write,
E: Write,
{
let end = args.at.unwrap_or(facts.now);
let sheet = streams.db.current_sheet()?;
let (end, needs_warning) = time_or_warning(end, &streams.db)?;
if let Some(entry) = streams.db.running_entry(&sheet)? {
writeln!(streams.out, "Checked out of sheet \"{}\".", sheet)?;
streams.db.entry_update(entry.id, entry.start, Some(end), entry.note, &entry.sheet)?;
} else {
writeln!(streams.out, "No running entry on sheet \"{}\".", sheet)?;
}
warn_if_needed(&mut streams.err, needs_warning, &facts.env)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use chrono::{TimeZone, Local};
use super::*;
#[test]
fn finishes_entry() {
let args = Default::default();
let mut streams = Streams::fake(b"");
let facts = Facts::new();
streams.db.entry_insert(facts.now, None, None, "default").unwrap();
OutCommand::handle(args, &mut streams, &facts).unwrap();
let e = streams.db.entries_full(None, None).unwrap().into_iter().next().unwrap();
assert_eq!(e.end, Some(facts.now));
assert_eq!(&String::from_utf8_lossy(&streams.out), "Checked out of sheet \"default\".\n");
assert_eq!(&String::from_utf8_lossy(&streams.err), "");
}
#[test]
fn tells_if_no_running_entry() {
let args = Default::default();
let mut streams = Streams::fake(b"");
let facts = Facts::new();
streams.db.entry_insert(facts.now, None, None, "non-default").unwrap();
OutCommand::handle(args, &mut streams, &facts).unwrap();
assert_eq!(&String::from_utf8_lossy(&streams.out), "No running entry on sheet \"default\".\n");
assert_eq!(&String::from_utf8_lossy(&streams.err), "");
}
#[test]
fn warns_if_old_database() {
let args = Default::default();
let mut streams = Streams::fake_old(b"");
let facts = Facts::new();
streams.db.entry_insert(facts.now, None, None, "default").unwrap();
OutCommand::handle(args, &mut streams, &facts).unwrap();
let e = streams.db.entries_full(None, None).unwrap().into_iter().next().unwrap();
assert_eq!(e.end, Some(Utc.from_utc_datetime(&facts.now.with_timezone(&Local).naive_local())));
assert_eq!(&String::from_utf8_lossy(&streams.out), "Checked out of sheet \"default\".\n");
assert_eq!(&String::from_utf8_lossy(&streams.err),
"[WARNING] You are using the old timetrap format, it is advised that \
you update your database using t migrate. To supress this warning set TIEMPO_SUPRESS_TIMETRAP_WARNING=1\n");
}
}