From 30635c22bc86080f19636875e3a4209f13b121e6 Mon Sep 17 00:00:00 2001 From: Julio Biason Date: Thu, 21 May 2020 21:21:25 -0300 Subject: [PATCH] Moved Events to the their own module, and event now have results --- src/eventlist/event/date.rs | 53 ++++++++++ src/eventlist/event/eventtype.rs | 61 +++++++++++ src/eventlist/{event.rs => event/mod.rs} | 125 ++++++++--------------- src/eventlist/event/time.rs | 47 +++++++++ src/eventlist/eventlist.rs | 42 ++++++++ src/main.rs | 32 ++---- 6 files changed, 253 insertions(+), 107 deletions(-) create mode 100644 src/eventlist/event/date.rs create mode 100644 src/eventlist/event/eventtype.rs rename src/eventlist/{event.rs => event/mod.rs} (52%) create mode 100644 src/eventlist/event/time.rs diff --git a/src/eventlist/event/date.rs b/src/eventlist/event/date.rs new file mode 100644 index 0000000..6ccd462 --- /dev/null +++ b/src/eventlist/event/date.rs @@ -0,0 +1,53 @@ +/* + 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 chrono::DateTime; +use serde_derive::Deserialize; +use serde_derive::Serialize; + +#[derive(Serialize, Deserialize, Debug)] +pub struct Date { + year: i32, + month: u32, + day: u32, +} + +impl From<&DateTime> for Date { + fn from(origin: &DateTime) -> Date { + Date { + year: origin.year(), + month: origin.month(), + day: origin.day(), + } + } +} + +impl Date { + pub fn year(&self) -> i32 { + self.year + } + + pub fn month(&self) -> u32 { + self.month + } + + pub fn day(&self) -> u32 { + self.day + } +} diff --git a/src/eventlist/event/eventtype.rs b/src/eventlist/event/eventtype.rs new file mode 100644 index 0000000..b9c1faf --- /dev/null +++ b/src/eventlist/event/eventtype.rs @@ -0,0 +1,61 @@ +/* + 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 chrono::DateTime; +use serde_derive::Deserialize; +use serde_derive::Serialize; + +use crate::eventlist::event::date; +use crate::eventlist::event::time; + +#[derive(Serialize, Deserialize, Debug)] +#[serde(tag = "due", content = "datetime")] +pub enum EventType { + AllDay(date::Date), + AtTime(date::Date, time::Time), +} + +impl From<&EventType> for DateTime { + fn from(origin: &EventType) -> Self { + match origin { + EventType::AllDay(d) => Local.ymd(d.year(), d.month(), d.day()).and_hms(23, 59, 59), + EventType::AtTime(d, t) => { + Local + .ymd(d.year(), d.month(), d.day()) + .and_hms(t.hour(), t.minute(), 59) + } + } + } +} + +impl From<&EventType> for String { + fn from(origin: &EventType) -> String { + match origin { + EventType::AllDay(d) => format!("{}{}{}0000", d.year(), d.month(), d.day()), + EventType::AtTime(d, t) => format!( + "{}{}{}{}{}", + d.year(), + d.month(), + d.day(), + t.hour(), + t.minute() + ), + } + } +} diff --git a/src/eventlist/event.rs b/src/eventlist/event/mod.rs similarity index 52% rename from src/eventlist/event.rs rename to src/eventlist/event/mod.rs index 143468a..aeb779c 100644 --- a/src/eventlist/event.rs +++ b/src/eventlist/event/mod.rs @@ -25,74 +25,21 @@ use serde_derive::Deserialize; use serde_derive::Serialize; use uuid::Uuid; -static DATE_FORMAT: &str = "%Y-%m-%d %H:%M:%S"; - -#[derive(Serialize, Deserialize, Debug)] -pub struct Date { - year: i32, - month: u32, - day: u32, -} - -impl From<&DateTime> for Date { - fn from(origin: &DateTime) -> Date { - Date { - year: origin.year(), - month: origin.month(), - day: origin.day(), - } - } -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct Time { - hour: u32, - min: u32, -} +mod date; +mod eventtype; +mod time; -impl From<&DateTime> for Time { - fn from(origin: &DateTime) -> Time { - Time { - hour: origin.hour(), - min: origin.minute(), - } - } -} - -#[derive(Serialize, Deserialize, Debug)] -#[serde(tag = "due", content = "datetime")] -pub enum EventDateType { - AllDay(Date), - AtTime(Date, Time), -} - -impl From<&EventDateType> for DateTime { - fn from(origin: &EventDateType) -> Self { - match origin { - EventDateType::AllDay(d) => Local.ymd(d.year, d.month, d.day).and_hms(0, 0, 0), - EventDateType::AtTime(d, t) => { - Local.ymd(d.year, d.month, d.day).and_hms(t.hour, t.min, 0) - } - } - } -} +use date::Date as EventDate; +use eventtype::EventType; +use time::Time as EventTime; -impl From<&EventDateType> for String { - fn from(origin: &EventDateType) -> String { - match origin { - EventDateType::AllDay(d) => format!("{}{}{}0000", d.year, d.month, d.day), - EventDateType::AtTime(d, t) => { - format!("{}{}{}{}{}", d.year, d.month, d.day, t.hour, t.min) - } - } - } -} +static DATE_FORMAT: &str = "%Y-%m-%d %H:%M:%S"; #[derive(Serialize, Deserialize, Debug)] pub struct Event { pub id: String, pub description: String, - due: EventDateType, + due: EventType, } fn uuid() -> String { @@ -100,32 +47,46 @@ fn uuid() -> String { format!("{:x}", id) } +#[derive(Debug)] +pub enum EventError { + InvalidDate(String), + TooOld, +} + +impl From for EventError { + fn from(error: chrono::format::ParseError) -> EventError { + EventError::InvalidDate(error.to_string()) + } +} + impl Event { - // TODO result this - pub fn new_on_date(description: &str, date: &str) -> Self { + pub fn new_on_date(description: &str, date: &str) -> Result { let fake_datetime = format!("{} 00:00:00", date); - if let Ok(dt) = Local.datetime_from_str(&fake_datetime, DATE_FORMAT) { - // TODO turn format into static - Self { + let dt = Local.datetime_from_str(&fake_datetime, DATE_FORMAT)?; + + if dt < Local::now() { + Err(EventError::TooOld) + } else { + Ok(Self { id: uuid(), description: description.into(), - due: EventDateType::AllDay(Date::from(&dt)), - } - } else { - panic!("Failed to parse the date"); + due: EventType::AllDay(EventDate::from(&dt)), + }) } } - pub fn new_on_date_time(description: &str, date: &str, time: &str) -> Self { + pub fn new_on_date_time(description: &str, date: &str, time: &str) -> Result { let fake_datetime = format!("{} {}:00", date, time); - if let Ok(dt) = Local.datetime_from_str(&fake_datetime, DATE_FORMAT) { - Self { + let dt = Local.datetime_from_str(&fake_datetime, DATE_FORMAT)?; + + if dt < Local::now() { + Err(EventError::TooOld) + } else { + Ok(Self { id: uuid(), description: description.into(), - due: EventDateType::AtTime(Date::from(&dt), Time::from(&dt)), - } - } else { - panic!("Failed to parse the date"); + due: EventType::AtTime(EventDate::from(&dt), EventTime::from(&dt)), + }) } } @@ -136,15 +97,11 @@ impl Event { log::debug!("ETA for {}: {}", self.id, eta.num_minutes()); match self.due { - EventDateType::AllDay(_) if eta.num_minutes() > 0 => { - Some(format!("{}d", eta.num_days())) - } - EventDateType::AtTime(_, _) if eta.num_days() > 0 => { + EventType::AllDay(_) if eta.num_minutes() > 0 => Some(format!("{}d", eta.num_days())), + EventType::AtTime(_, _) if eta.num_days() > 0 => { Some(format!("{}d {}h", eta.num_days(), eta.num_hours())) } - EventDateType::AtTime(_, _) if eta.num_hours() > 0 => { - Some(format!("{}h", eta.num_hours())) - } + EventType::AtTime(_, _) if eta.num_hours() > 0 => Some(format!("{}h", eta.num_hours())), _ => None, } } diff --git a/src/eventlist/event/time.rs b/src/eventlist/event/time.rs new file mode 100644 index 0000000..ce9a0a5 --- /dev/null +++ b/src/eventlist/event/time.rs @@ -0,0 +1,47 @@ +/* + 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 chrono::DateTime; +use serde_derive::Deserialize; +use serde_derive::Serialize; + +#[derive(Serialize, Deserialize, Debug)] +pub struct Time { + hour: u32, + min: u32, +} + +impl From<&DateTime> for Time { + fn from(origin: &DateTime) -> Time { + Time { + hour: origin.hour(), + min: origin.minute(), + } + } +} + +impl Time { + pub fn hour(&self) -> u32 { + self.hour + } + + pub fn minute(&self) -> u32 { + self.min + } +} diff --git a/src/eventlist/eventlist.rs b/src/eventlist/eventlist.rs index 41462eb..1028dbf 100644 --- a/src/eventlist/eventlist.rs +++ b/src/eventlist/eventlist.rs @@ -24,6 +24,7 @@ use serde_derive::Serialize; use toml; use crate::eventlist::event::Event; +use crate::eventlist::event::EventError; static FILENAME: &str = "events.toml"; @@ -40,6 +41,21 @@ pub struct EventListIterator<'a> { list: &'a Vec, } +#[derive(Debug)] +pub enum EventListError { + InvalidDate, + TooOld, +} + +impl From for EventListError { + fn from(error: EventError) -> EventListError { + match error { + EventError::InvalidDate(_) => EventListError::InvalidDate, + EventError::TooOld => EventListError::TooOld, + } + } +} + // TODO separate business rule from repository impl EventList { fn empty() -> Self { @@ -73,6 +89,32 @@ impl EventList { fp.write_all(content.as_bytes()).unwrap(); } } + + /// Load the event list, add an all day event, and save it back. + /// Returns the ID of the new event. + pub fn add_event_with_date(description: &str, date: &str) -> Result { + let mut list = EventList::load(); + let event = Event::new_on_date(description, date)?; + let id = String::from(&event.id); + list.push(event); + list.save(); + Ok(id) + } + + /// Load the event list, add an event with date and time, and save it back. + /// Returns the ID of the new event. + pub fn add_event_with_date_and_time( + description: &str, + date: &str, + time: &str, + ) -> Result { + let mut list = EventList::load(); + let event = Event::new_on_date_time(description, date, time).unwrap(); + let id = String::from(&event.id); + list.push(event); + list.save(); + Ok(id) + } } impl<'a> IntoIterator for &'a EventList { diff --git a/src/main.rs b/src/main.rs index 39e7929..3afdb5e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,7 +21,6 @@ use log; mod args; mod eventlist; -use crate::eventlist::event::Event; use crate::eventlist::eventlist::EventList; fn main() { @@ -31,9 +30,14 @@ fn main() { log::debug!("Command: {:?}", command); match command { args::Action::List => list(), - args::Action::Add(description, date) => add_with_date(&description, &date), + args::Action::Add(description, date) => { + let event_id = EventList::add_event_with_date(&description, &date).unwrap(); + println!("Created new event {}", event_id); + } args::Action::AddWithTime(description, date, time) => { - add_with_date_time(&description, &date, &time) + let event_id = + EventList::add_event_with_date_and_time(&description, &date, &time).unwrap(); + println!("Created new event {}", event_id); } } } @@ -42,8 +46,10 @@ fn main() { fn list() { let event_list = EventList::load(); // TODO hide load from outside println!("{:^8} | {:^7} | {}", "ID", "ETA", "Description"); + // TODO: EventList::iter() for record in event_list.into_iter() { let eta = if let Some(eta) = record.eta() { + // TODO: "1d" == Tomorrow; "0d" == Today eta } else { "Over".into() @@ -52,23 +58,3 @@ fn list() { println!("{:>8} | {:>7} | {}", record.id, eta, record.description); } } - -// TODO business rule (should be in EventList) -fn add_with_date(description: &str, date: &str) { - let event = Event::new_on_date(description, date); - add_event(event); -} - -fn add_with_date_time(description: &str, date: &str, time: &str) { - let event = Event::new_on_date_time(description, date, time); - add_event(event); -} - -fn add_event(event: Event) { - println!("Adding event {}", event.id); - - let mut event_list = EventList::load(); - log::debug!("EventList: {:?}", event_list); - event_list.push(event); - event_list.save(); -}