diff --git a/src/commands.rs b/src/commands.rs index bc6a60e..926d395 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -9,6 +9,7 @@ use crate::config::Config; pub mod r#in; pub mod display; +pub mod sheet; pub trait Command<'a> { type Args: TryFrom<&'a ArgMatches<'a>>; diff --git a/src/commands/sheet.rs b/src/commands/sheet.rs new file mode 100644 index 0000000..9f83924 --- /dev/null +++ b/src/commands/sheet.rs @@ -0,0 +1,81 @@ +use std::convert::TryFrom; +use std::io::Write; + +use clap::ArgMatches; + +use crate::database::Database; +use crate::error; +use crate::commands::Command; +use crate::config::Config; + +#[derive(Default)] +pub struct Args { + sheet: Option, +} + +impl<'a> TryFrom<&'a ArgMatches<'a>> for Args { + type Error = 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) -> error::Result<()> + where + D: Database, + O: Write, + E: Write, + { + unimplemented!() + } +} + +#[cfg(test)] +mod tests { + use chrono::{Utc, TimeZone}; + + use crate::database::SqliteDatabase; + use crate::test_utils::PrettyString; + + use super::*; + + #[test] + fn list_sheets() { + let args = Default::default(); + let mut db = SqliteDatabase::from_memory().unwrap(); + let mut out = Vec::new(); + let mut err = Vec::new(); + let config = Default::default(); + + db.entry_insert(Utc.ymd(2021, 1, 1).and_hms(0, 0, 0), None, None, "_archived".into()).unwrap(); + db.entry_insert(Utc.ymd(2021, 1, 1).and_hms(0, 0, 0), None, None, "sheet1".into()).unwrap(); + db.entry_insert(Utc.ymd(2021, 1, 1).and_hms(0, 0, 0), None, None, "sheet2".into()).unwrap(); + db.entry_insert(Utc.ymd(2021, 1, 1).and_hms(0, 0, 0), None, None, "sheet3".into()).unwrap(); + + SheetCommand::handle(args, &mut db, &mut out, &mut err, &config).unwrap(); + + assert_eq!(PrettyString(&String::from_utf8_lossy(&out)), PrettyString(" Timesheet Running Today Total Time + sheet1 0:00:00 0:00:00 10:13:55 +*sheet2 0:00:00 0:00:00 0:00:00 +-sheet3 0:00:00 1:52:45 9:32:03 +")); + } + + #[test] + fn switch_to_sheet() { + assert!(false); + } + + #[test] + fn switch_to_sheet_already_in() { + assert!(false); + } +} diff --git a/src/main.rs b/src/main.rs index 4c3ef1c..81cbbfa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,9 @@ use clap::{App, Arg, SubCommand, AppSettings, crate_version, ArgMatches}; use tiempo::error; use tiempo::database::SqliteDatabase; use tiempo::config::Config; -use tiempo::commands::{ Command, r#in::InCommand, display::DisplayCommand, }; +use tiempo::commands::{ + Command, r#in::InCommand, display::DisplayCommand, sheet::SheetCommand, +}; fn error_trap(matches: ArgMatches) -> error::Result<()> { let config = Config::read()?; @@ -19,6 +21,7 @@ fn error_trap(matches: ArgMatches) -> error::Result<()> { match matches.subcommand() { ("in", Some(matches)) => InCommand::handle(matches.try_into()?, &mut conn, &mut out, &mut err, &config), ("display", Some(matches)) => DisplayCommand::handle(matches.try_into()?, &mut conn, &mut out, &mut err, &config), + ("sheet", Some(matches)) => SheetCommand::handle(matches.try_into()?, &mut conn, &mut out, &mut err, &config), (cmd, _) => Err(error::Error::UnimplementedCommand(cmd.into())), } } @@ -155,6 +158,9 @@ fn main() { .subcommand(SubCommand::with_name("sheet") .visible_alias("s") .about("Change active timesheet or list existing timesheets") + .arg(Arg::with_name("sheet") + .takes_value(true).value_name("SHEET") + .help("The sheet to switch to, creating it if necessary")) ) .get_matches();