From 0cc6eb07958c2328960f2c5080c4782453e105da Mon Sep 17 00:00:00 2001 From: Julio Biason Date: Thu, 28 May 2020 20:38:09 -0300 Subject: [PATCH] Date has its own structure now --- src/{args/mod.rs => args.rs} | 28 ++++++++----- src/args/validators.rs | 40 ------------------- src/date.rs | 77 ++++++++++++++++++++++++++++++------ src/main.rs | 5 ++- 4 files changed, 88 insertions(+), 62 deletions(-) rename src/{args/mod.rs => args.rs} (83%) delete mode 100644 src/args/validators.rs diff --git a/src/args/mod.rs b/src/args.rs similarity index 83% rename from src/args/mod.rs rename to src/args.rs index fd766d1..63d1afc 100644 --- a/src/args/mod.rs +++ b/src/args.rs @@ -25,20 +25,29 @@ use clap::Arg; use clap::ArgMatches; use clap::SubCommand; -mod validators; +use crate::date::{Date, DateError}; type Description = String; -type Date = String; -type Time = String; + +pub enum ParseError { + InvalidDate, + UnknownOption, +} + +impl From for ParseError { + fn from(_: DateError) -> ParseError { + ParseError::InvalidDate + } +} #[derive(Debug)] pub enum Action { List, Add(Description, Date), - AddWithTime(Description, Date, Time), + AddWithTime(Description, String, String), } -pub fn parse() -> Result { +pub fn parse() -> Result { let params = App::new(crate_name!()) .version(crate_version!()) .author(crate_authors!()) @@ -50,7 +59,6 @@ pub fn parse() -> Result { Arg::with_name("date") .required(true) .takes_value(true) - .validator(validators::date) .help("Date for the event, in YYYY-MM-DD format"), ) .arg( @@ -65,7 +73,6 @@ pub fn parse() -> Result { .long("time") .takes_value(true) .required(false) - .validator(validators::time) .help("Time for the event"), ), ); @@ -74,13 +81,14 @@ pub fn parse() -> Result { match matches.subcommand() { ("", _) => Ok(Action::List), ("add", Some(arguments)) => parse_add(arguments), - (_, _) => Err(()), + (_, _) => Err(ParseError::UnknownOption), } } -fn parse_add(arguments: &ArgMatches) -> Result { +fn parse_add(arguments: &ArgMatches) -> Result { let description = arguments.value_of("description").unwrap(); let date = arguments.value_of("date").unwrap(); + if let Some(time) = arguments.value_of("time") { Ok(Action::AddWithTime( description.into(), @@ -88,6 +96,6 @@ fn parse_add(arguments: &ArgMatches) -> Result { time.into(), )) } else { - Ok(Action::Add(description.into(), date.into())) + Ok(Action::Add(description.into(), Date::try_from(date)?)) } } diff --git a/src/args/validators.rs b/src/args/validators.rs deleted file mode 100644 index eb1dcb4..0000000 --- a/src/args/validators.rs +++ /dev/null @@ -1,40 +0,0 @@ -/* - TU - Time's Up! - Copyright (C) 2020 Julio Biason - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ - -use chrono::prelude::*; -use log; - -pub fn date(input: String) -> Result<(), String> { - let fake_datetime = format!("{} 00:00:00", input); - if let Ok(parse) = Utc.datetime_from_str(&fake_datetime, "%Y-%m-%d %H:%M:%S") { - log::debug!("Parsing {} ({})= {:?}", input, fake_datetime, parse); - Ok(()) - } else { - Err(format!("Invalid date: '{}'", input)) - } -} - -pub fn time(input: String) -> Result<(), String> { - let fake_datetime = format!("2020-01-01 {}:00", input); - if let Ok(parse) = Utc.datetime_from_str(&fake_datetime, "%Y-%m-%d %H:%M:%S") { - log::debug!("Parsing {} ({}) = {:?}", input, fake_datetime, parse); - Ok(()) - } else { - Err(format!("Invalid time: '{}'", input)) - } -} diff --git a/src/date.rs b/src/date.rs index 26d1a5a..7b7cdaf 100644 --- a/src/date.rs +++ b/src/date.rs @@ -16,13 +16,14 @@ along with this program. If not, see . */ -use std::convert::TryFrom; +// TODO trait TryFrom use chrono::prelude::*; use chrono::LocalResult; #[derive(Debug, Eq, PartialEq)] pub enum DateError { + /// The date is not valid InvalidDate, } @@ -56,19 +57,26 @@ impl Date { pub fn day(&self) -> u8 { self.0.day() as u8 } -} - -impl TryFrom<&str> for Date { - type Error = DateError; - fn try_from(origin: &str) -> Result { - let mut frags = origin.split("-"); + /// Try to convert a string to a Date. + pub fn try_from(value: &str) -> Result { + let mut frags = value.split("-"); Date::new( frags.next().ok_or(DateError::InvalidDate)?.parse()?, frags.next().ok_or(DateError::InvalidDate)?.parse()?, frags.next().ok_or(DateError::InvalidDate)?.parse()?, ) } + + /// Number of days till the date; None if the date is in the past. + pub fn eta(&self) -> Option { + let days = (self.0 - Local::today()).num_days(); + if days >= 0 { + Some(days as u16) + } else { + None + } + } } #[cfg(test)] @@ -82,17 +90,64 @@ pub fn invalid_date() { #[cfg(test)] #[test] pub fn valid_date() { - assert!(Date::new(2020, 5, 26).is_ok()); + assert!(Date::new(2025, 5, 26).is_ok()); } #[cfg(test)] #[test] pub fn from_string() { - if let Ok(dt) = Date::try_from("2020-05-26") { - assert_eq!(dt.year(), 2020); + if let Ok(dt) = Date::try_from("2025-05-26") { + assert_eq!(dt.year(), 2025); assert_eq!(dt.month(), 5); assert_eq!(dt.day(), 26); } else { - panic!("Can't parse 2020-05-26") + panic!("Can't parse 2025-05-26") } } + +#[cfg(test)] +#[test] +pub fn failed_from_string() { + assert!(Date::try_from("2020-127-26").is_err()); +} + +#[cfg(test)] +#[test] +pub fn eta_tomorrow() { + use chrono::Duration; + let future = Local::today() + Duration::days(1); + let date = Date::new( + future.year() as u16, + future.month() as u8, + future.day() as u8, + ) + .unwrap(); + assert_eq!(date.eta(), Some(1)); +} + +#[cfg(test)] +#[test] +pub fn eta_today() { + let future = Local::today(); + let date = Date::new( + future.year() as u16, + future.month() as u8, + future.day() as u8, + ) + .unwrap(); + assert_eq!(date.eta(), Some(0)); +} + +#[cfg(test)] +#[test] +pub fn eta_yesterday() { + use chrono::Duration; + let future = Local::today() - Duration::days(1); + let date = Date::new( + future.year() as u16, + future.month() as u8, + future.day() as u8, + ) + .unwrap(); + assert_eq!(date.eta(), None); +} diff --git a/src/main.rs b/src/main.rs index 3afdb5e..49ba293 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,6 +19,7 @@ use log; mod args; +mod date; mod eventlist; use crate::eventlist::eventlist::EventList; @@ -31,7 +32,7 @@ fn main() { match command { args::Action::List => list(), args::Action::Add(description, date) => { - let event_id = EventList::add_event_with_date(&description, &date).unwrap(); + let event_id = EventList::add_event_with_date(&description, "").unwrap(); println!("Created new event {}", event_id); } args::Action::AddWithTime(description, date, time) => { @@ -40,6 +41,8 @@ fn main() { println!("Created new event {}", event_id); } } + } else { + println!("Error!"); } }