use std::convert::TryFrom; use std::io::Write; use clap::ArgMatches; use chrono::{DateTime, Utc}; use crate::database::Database; use crate::error::{Error, Result}; use crate::commands::Command; use crate::config::Config; use crate::commands::list::ListCommand; #[derive(Default)] pub struct Args { pub sheet: Option, } impl<'a> TryFrom<&'a ArgMatches<'a>> for Args { type Error = Error; fn try_from(matches: &'a ArgMatches) -> Result { Ok(Args { sheet: matches.value_of("sheet").map(|s| s.into()), }) } } pub struct SheetCommand {} impl<'a> Command<'a> for SheetCommand { type Args = Args; fn handle(args: Args, db: &mut D, out: &mut O, err: &mut E, config: &Config, now: DateTime) -> Result<()> where D: Database, O: Write, E: Write, { if let Some(sheet) = args.sheet { let current_sheet = db.current_sheet()?.unwrap_or_else(|| "default".into()); // sheet given, switch to it let move_to = if sheet == "-" { if let Some(move_to) = db.last_sheet()? { move_to } else { writeln!(out, "No previous sheet to move to. Staying on '{}'. Hint: remember that giving - (a dash) as argument to t sheet switches to the last active sheet", current_sheet)?; return Ok(()); } } else if sheet == current_sheet { writeln!(out, "Already on sheet '{}'", sheet)?; return Ok(()); } else { sheet }; db.set_last_sheet(¤t_sheet)?; db.set_current_sheet(&move_to)?; writeln!(out, "Switching to sheet '{}'", move_to)?; Ok(()) } else { // call list ListCommand::handle(Default::default(), db, out, err, config, now) } } } #[cfg(test)] mod tests { use pretty_assertions::assert_eq; use crate::database::SqliteDatabase; use crate::test_utils::Ps; use super::*; #[test] fn switch_to_sheet() { let args = Args { sheet: Some("new_sheet".into()), }; let mut db = SqliteDatabase::from_memory().unwrap(); let mut out = Vec::new(); let mut err = Vec::new(); let now = Utc::now(); db.init().unwrap(); SheetCommand::handle(args, &mut db, &mut out, &mut err, &Default::default(), now).unwrap(); assert_eq!(db.current_sheet().unwrap().unwrap(), "new_sheet"); assert_eq!(Ps(&String::from_utf8_lossy(&out)), Ps("Switching to sheet 'new_sheet'\n")); assert_eq!(Ps(&String::from_utf8_lossy(&err)), Ps("")); } #[test] fn switch_to_sheet_already_in() { let args = Args { sheet: Some("foo".into()), }; let mut db = SqliteDatabase::from_memory().unwrap(); let mut out = Vec::new(); let mut err = Vec::new(); let now = Utc::now(); db.init().unwrap(); db.set_current_sheet("foo").unwrap(); SheetCommand::handle(args, &mut db, &mut out, &mut err, &Default::default(), now).unwrap(); assert_eq!(db.current_sheet().unwrap().unwrap(), "foo"); assert_eq!(Ps(&String::from_utf8_lossy(&out)), Ps("Already on sheet 'foo'\n")); assert_eq!(Ps(&String::from_utf8_lossy(&err)), Ps("")); } #[test] fn switch_to_last_sheet() { let args = Args { sheet: Some("-".into()), }; let mut db = SqliteDatabase::from_memory().unwrap(); let mut out = Vec::new(); let mut err = Vec::new(); let now = Utc::now(); db.init().unwrap(); db.set_current_sheet("foo").unwrap(); db.set_last_sheet("var").unwrap(); SheetCommand::handle(args, &mut db, &mut out, &mut err, &Default::default(), now).unwrap(); assert_eq!(db.current_sheet().unwrap().unwrap(), "var"); assert_eq!(db.last_sheet().unwrap().unwrap(), "foo"); assert_eq!(Ps(&String::from_utf8_lossy(&out)), Ps("Switching to sheet 'var'\n")); assert_eq!(Ps(&String::from_utf8_lossy(&err)), Ps("")); } }