Browse Source

Date has its own structure now

master
Julio Biason 4 years ago
parent
commit
0cc6eb0795
  1. 28
      src/args.rs
  2. 40
      src/args/validators.rs
  3. 77
      src/date.rs
  4. 5
      src/main.rs

28
src/args/mod.rs → src/args.rs

@ -25,20 +25,29 @@ use clap::Arg;
use clap::ArgMatches; use clap::ArgMatches;
use clap::SubCommand; use clap::SubCommand;
mod validators; use crate::date::{Date, DateError};
type Description = String; type Description = String;
type Date = String;
type Time = String; pub enum ParseError {
InvalidDate,
UnknownOption,
}
impl From<DateError> for ParseError {
fn from(_: DateError) -> ParseError {
ParseError::InvalidDate
}
}
#[derive(Debug)] #[derive(Debug)]
pub enum Action { pub enum Action {
List, List,
Add(Description, Date), Add(Description, Date),
AddWithTime(Description, Date, Time), AddWithTime(Description, String, String),
} }
pub fn parse() -> Result<Action, ()> { pub fn parse() -> Result<Action, ParseError> {
let params = App::new(crate_name!()) let params = App::new(crate_name!())
.version(crate_version!()) .version(crate_version!())
.author(crate_authors!()) .author(crate_authors!())
@ -50,7 +59,6 @@ pub fn parse() -> Result<Action, ()> {
Arg::with_name("date") Arg::with_name("date")
.required(true) .required(true)
.takes_value(true) .takes_value(true)
.validator(validators::date)
.help("Date for the event, in YYYY-MM-DD format"), .help("Date for the event, in YYYY-MM-DD format"),
) )
.arg( .arg(
@ -65,7 +73,6 @@ pub fn parse() -> Result<Action, ()> {
.long("time") .long("time")
.takes_value(true) .takes_value(true)
.required(false) .required(false)
.validator(validators::time)
.help("Time for the event"), .help("Time for the event"),
), ),
); );
@ -74,13 +81,14 @@ pub fn parse() -> Result<Action, ()> {
match matches.subcommand() { match matches.subcommand() {
("", _) => Ok(Action::List), ("", _) => Ok(Action::List),
("add", Some(arguments)) => parse_add(arguments), ("add", Some(arguments)) => parse_add(arguments),
(_, _) => Err(()), (_, _) => Err(ParseError::UnknownOption),
} }
} }
fn parse_add(arguments: &ArgMatches) -> Result<Action, ()> { fn parse_add(arguments: &ArgMatches) -> Result<Action, ParseError> {
let description = arguments.value_of("description").unwrap(); let description = arguments.value_of("description").unwrap();
let date = arguments.value_of("date").unwrap(); let date = arguments.value_of("date").unwrap();
if let Some(time) = arguments.value_of("time") { if let Some(time) = arguments.value_of("time") {
Ok(Action::AddWithTime( Ok(Action::AddWithTime(
description.into(), description.into(),
@ -88,6 +96,6 @@ fn parse_add(arguments: &ArgMatches) -> Result<Action, ()> {
time.into(), time.into(),
)) ))
} else { } else {
Ok(Action::Add(description.into(), date.into())) Ok(Action::Add(description.into(), Date::try_from(date)?))
} }
} }

40
src/args/validators.rs

@ -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 <https://www.gnu.org/licenses/>.
*/
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))
}
}

77
src/date.rs

@ -16,13 +16,14 @@
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
use std::convert::TryFrom; // TODO trait TryFrom
use chrono::prelude::*; use chrono::prelude::*;
use chrono::LocalResult; use chrono::LocalResult;
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
pub enum DateError { pub enum DateError {
/// The date is not valid
InvalidDate, InvalidDate,
} }
@ -56,19 +57,26 @@ impl Date {
pub fn day(&self) -> u8 { pub fn day(&self) -> u8 {
self.0.day() as u8 self.0.day() as u8
} }
}
impl TryFrom<&str> for Date {
type Error = DateError;
fn try_from(origin: &str) -> Result<Self, Self::Error> { /// Try to convert a string to a Date.
let mut frags = origin.split("-"); pub fn try_from(value: &str) -> Result<Self, DateError> {
let mut frags = value.split("-");
Date::new( Date::new(
frags.next().ok_or(DateError::InvalidDate)?.parse()?, frags.next().ok_or(DateError::InvalidDate)?.parse()?,
frags.next().ok_or(DateError::InvalidDate)?.parse()?, 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<u16> {
let days = (self.0 - Local::today()).num_days();
if days >= 0 {
Some(days as u16)
} else {
None
}
}
} }
#[cfg(test)] #[cfg(test)]
@ -82,17 +90,64 @@ pub fn invalid_date() {
#[cfg(test)] #[cfg(test)]
#[test] #[test]
pub fn valid_date() { pub fn valid_date() {
assert!(Date::new(2020, 5, 26).is_ok()); assert!(Date::new(2025, 5, 26).is_ok());
} }
#[cfg(test)] #[cfg(test)]
#[test] #[test]
pub fn from_string() { pub fn from_string() {
if let Ok(dt) = Date::try_from("2020-05-26") { if let Ok(dt) = Date::try_from("2025-05-26") {
assert_eq!(dt.year(), 2020); assert_eq!(dt.year(), 2025);
assert_eq!(dt.month(), 5); assert_eq!(dt.month(), 5);
assert_eq!(dt.day(), 26); assert_eq!(dt.day(), 26);
} else { } 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);
}

5
src/main.rs

@ -19,6 +19,7 @@
use log; use log;
mod args; mod args;
mod date;
mod eventlist; mod eventlist;
use crate::eventlist::eventlist::EventList; use crate::eventlist::eventlist::EventList;
@ -31,7 +32,7 @@ fn main() {
match command { match command {
args::Action::List => list(), args::Action::List => list(),
args::Action::Add(description, date) => { 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); println!("Created new event {}", event_id);
} }
args::Action::AddWithTime(description, date, time) => { args::Action::AddWithTime(description, date, time) => {
@ -40,6 +41,8 @@ fn main() {
println!("Created new event {}", event_id); println!("Created new event {}", event_id);
} }
} }
} else {
println!("Error!");
} }
} }

Loading…
Cancel
Save