Browse Source

Moved Events to the their own module, and event now have results

master
Julio Biason 4 years ago
parent
commit
30635c22bc
  1. 53
      src/eventlist/event/date.rs
  2. 61
      src/eventlist/event/eventtype.rs
  3. 125
      src/eventlist/event/mod.rs
  4. 47
      src/eventlist/event/time.rs
  5. 42
      src/eventlist/eventlist.rs
  6. 32
      src/main.rs

53
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 <https://www.gnu.org/licenses/>.
*/
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<Local>> for Date {
fn from(origin: &DateTime<Local>) -> 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
}
}

61
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 <https://www.gnu.org/licenses/>.
*/
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<Local> {
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()
),
}
}
}

125
src/eventlist/event.rs → src/eventlist/event/mod.rs

@ -25,74 +25,21 @@ use serde_derive::Deserialize;
use serde_derive::Serialize; use serde_derive::Serialize;
use uuid::Uuid; use uuid::Uuid;
static DATE_FORMAT: &str = "%Y-%m-%d %H:%M:%S"; mod date;
mod eventtype;
#[derive(Serialize, Deserialize, Debug)] mod time;
pub struct Date {
year: i32,
month: u32,
day: u32,
}
impl From<&DateTime<Local>> for Date {
fn from(origin: &DateTime<Local>) -> Date {
Date {
year: origin.year(),
month: origin.month(),
day: origin.day(),
}
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Time {
hour: u32,
min: u32,
}
impl From<&DateTime<Local>> for Time { use date::Date as EventDate;
fn from(origin: &DateTime<Local>) -> Time { use eventtype::EventType;
Time { use time::Time as EventTime;
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<Local> {
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)
}
}
}
}
impl From<&EventDateType> for String { static DATE_FORMAT: &str = "%Y-%m-%d %H:%M:%S";
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)
}
}
}
}
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct Event { pub struct Event {
pub id: String, pub id: String,
pub description: String, pub description: String,
due: EventDateType, due: EventType,
} }
fn uuid() -> String { fn uuid() -> String {
@ -100,32 +47,46 @@ fn uuid() -> String {
format!("{:x}", id) format!("{:x}", id)
} }
#[derive(Debug)]
pub enum EventError {
InvalidDate(String),
TooOld,
}
impl From<chrono::format::ParseError> for EventError {
fn from(error: chrono::format::ParseError) -> EventError {
EventError::InvalidDate(error.to_string())
}
}
impl Event { impl Event {
// TODO result this pub fn new_on_date(description: &str, date: &str) -> Result<Self, EventError> {
pub fn new_on_date(description: &str, date: &str) -> Self {
let fake_datetime = format!("{} 00:00:00", date); let fake_datetime = format!("{} 00:00:00", date);
if let Ok(dt) = Local.datetime_from_str(&fake_datetime, DATE_FORMAT) { let dt = Local.datetime_from_str(&fake_datetime, DATE_FORMAT)?;
// TODO turn format into static
Self { if dt < Local::now() {
Err(EventError::TooOld)
} else {
Ok(Self {
id: uuid(), id: uuid(),
description: description.into(), description: description.into(),
due: EventDateType::AllDay(Date::from(&dt)), due: EventType::AllDay(EventDate::from(&dt)),
} })
} else {
panic!("Failed to parse the date");
} }
} }
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<Self, EventError> {
let fake_datetime = format!("{} {}:00", date, time); let fake_datetime = format!("{} {}:00", date, time);
if let Ok(dt) = Local.datetime_from_str(&fake_datetime, DATE_FORMAT) { let dt = Local.datetime_from_str(&fake_datetime, DATE_FORMAT)?;
Self {
if dt < Local::now() {
Err(EventError::TooOld)
} else {
Ok(Self {
id: uuid(), id: uuid(),
description: description.into(), description: description.into(),
due: EventDateType::AtTime(Date::from(&dt), Time::from(&dt)), due: EventType::AtTime(EventDate::from(&dt), EventTime::from(&dt)),
} })
} else {
panic!("Failed to parse the date");
} }
} }
@ -136,15 +97,11 @@ impl Event {
log::debug!("ETA for {}: {}", self.id, eta.num_minutes()); log::debug!("ETA for {}: {}", self.id, eta.num_minutes());
match self.due { match self.due {
EventDateType::AllDay(_) if eta.num_minutes() > 0 => { EventType::AllDay(_) if eta.num_minutes() > 0 => Some(format!("{}d", eta.num_days())),
Some(format!("{}d", eta.num_days())) EventType::AtTime(_, _) if eta.num_days() > 0 => {
}
EventDateType::AtTime(_, _) if eta.num_days() > 0 => {
Some(format!("{}d {}h", eta.num_days(), eta.num_hours())) Some(format!("{}d {}h", eta.num_days(), eta.num_hours()))
} }
EventDateType::AtTime(_, _) if eta.num_hours() > 0 => { EventType::AtTime(_, _) if eta.num_hours() > 0 => Some(format!("{}h", eta.num_hours())),
Some(format!("{}h", eta.num_hours()))
}
_ => None, _ => None,
} }
} }

47
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 <https://www.gnu.org/licenses/>.
*/
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<Local>> for Time {
fn from(origin: &DateTime<Local>) -> Time {
Time {
hour: origin.hour(),
min: origin.minute(),
}
}
}
impl Time {
pub fn hour(&self) -> u32 {
self.hour
}
pub fn minute(&self) -> u32 {
self.min
}
}

42
src/eventlist/eventlist.rs

@ -24,6 +24,7 @@ use serde_derive::Serialize;
use toml; use toml;
use crate::eventlist::event::Event; use crate::eventlist::event::Event;
use crate::eventlist::event::EventError;
static FILENAME: &str = "events.toml"; static FILENAME: &str = "events.toml";
@ -40,6 +41,21 @@ pub struct EventListIterator<'a> {
list: &'a Vec<Event>, list: &'a Vec<Event>,
} }
#[derive(Debug)]
pub enum EventListError {
InvalidDate,
TooOld,
}
impl From<EventError> for EventListError {
fn from(error: EventError) -> EventListError {
match error {
EventError::InvalidDate(_) => EventListError::InvalidDate,
EventError::TooOld => EventListError::TooOld,
}
}
}
// TODO separate business rule from repository // TODO separate business rule from repository
impl EventList { impl EventList {
fn empty() -> Self { fn empty() -> Self {
@ -73,6 +89,32 @@ impl EventList {
fp.write_all(content.as_bytes()).unwrap(); 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<String, EventListError> {
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<String, EventListError> {
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 { impl<'a> IntoIterator for &'a EventList {

32
src/main.rs

@ -21,7 +21,6 @@ use log;
mod args; mod args;
mod eventlist; mod eventlist;
use crate::eventlist::event::Event;
use crate::eventlist::eventlist::EventList; use crate::eventlist::eventlist::EventList;
fn main() { fn main() {
@ -31,9 +30,14 @@ fn main() {
log::debug!("Command: {:?}", command); log::debug!("Command: {:?}", command);
match command { match command {
args::Action::List => list(), 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) => { 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() { fn list() {
let event_list = EventList::load(); // TODO hide load from outside let event_list = EventList::load(); // TODO hide load from outside
println!("{:^8} | {:^7} | {}", "ID", "ETA", "Description"); println!("{:^8} | {:^7} | {}", "ID", "ETA", "Description");
// TODO: EventList::iter()
for record in event_list.into_iter() { for record in event_list.into_iter() {
let eta = if let Some(eta) = record.eta() { let eta = if let Some(eta) = record.eta() {
// TODO: "1d" == Tomorrow; "0d" == Today
eta eta
} else { } else {
"Over".into() "Over".into()
@ -52,23 +58,3 @@ fn list() {
println!("{:>8} | {:>7} | {}", record.id, eta, record.description); 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();
}

Loading…
Cancel
Save