From 6263be67208f68a8c8ce1a8a18fc9921df53048e Mon Sep 17 00:00:00 2001 From: Ruben Pollan Date: Tue, 28 Aug 2018 16:48:59 +0200 Subject: Get a range of dates --- src/calendar.rs | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/event.rs | 27 ++++++++++-- src/events.rs | 93 ---------------------------------------- src/main.rs | 15 +++++-- src/periodic.rs | 45 ++++++++++++++++++- 5 files changed, 209 insertions(+), 102 deletions(-) create mode 100644 src/calendar.rs delete mode 100644 src/events.rs diff --git a/src/calendar.rs b/src/calendar.rs new file mode 100644 index 0000000..c49e747 --- /dev/null +++ b/src/calendar.rs @@ -0,0 +1,131 @@ +use std::io::BufRead; +use std::fmt; +use ical::IcalParser; +use chrono::Duration; + +use date::Date; +use event::{Event, End}; +use periodic::Periodic; +use errors::EventError; + +pub struct Calendar { + single: Vec, + periodic: Vec, +} + +impl Calendar { + pub fn parse(buf: B) -> Result { + let reader = IcalParser::new(buf); + let mut single = Vec::new(); + let mut periodic = Vec::new(); + + for line in reader { + for ev in line?.events { + let mut event = Event::new(); + let mut maybe_periodic = None; + + for property in ev.properties { + let value = property.value.unwrap_or("".to_string()); + let mut time_zone = "".to_string(); + + let params = property.params.unwrap_or(vec![]); + for (param, value) in ¶ms { + if param == "TZID" && value.len() > 0 { + time_zone = value[0].clone(); + } + } + + match property.name.as_ref() { + "SUMMARY" => event.summary = value, + "LOCATION" => event.location = value, + "DESCRIPTION" => event.description = value, + "STATUS" => event.status = value.parse()?, + "DTSTART" => event.start = Date::parse(&value, &time_zone)?, + "DTEND" => event.end = End::Date(Date::parse(&value, &time_zone)?), + "DURATION" => event.end = End::Duration(duration(&value)?), + "RRULE" => maybe_periodic = Some(rrule(&value, ¶ms)?), + _ => (), + }; + } + match maybe_periodic { + Some(mut p) => { + p.event = event; + periodic.push(p); + } + None => single.push(event), + } + } + } + + single.sort_by_key(|k| k.start); + Ok(Calendar { single, periodic }) + } + + pub fn get(&self, first: &Date, last: &Date) -> Vec { + // TODO: not all single, just the one by date + let mut events = self.single.clone(); + for p in &self.periodic { + events.append(&mut p.get(first, last)); + } + events.sort_by_key(|k| k.start); + events + } +} + +impl fmt::Display for Calendar { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for event in &self.single { + writeln!(f, "{}", event)?; + } + writeln!(f, "")?; + for periodic in &self.periodic { + writeln!(f, "{}", periodic)?; + } + Ok(()) + } +} + +fn rrule(value: &str, params: &Vec<(String, Vec)>) -> Result { + let mut periodic = Periodic::new(); + + for entry in value.split(";") { + let p: Vec<&str> = entry.splitn(2, "=").collect(); + periodic.set_param(p[0], p[1])?; + } + + for (param, values) in params { + let mut value = ""; + if values.len() > 0 { + value = &values[0]; + } + periodic.set_param(param, value)?; + } + + Ok(periodic) +} + +fn duration(value: &str) -> Result { + let mut duration = Duration::seconds(0); + let mut acc = "".to_string(); + for c in value.chars() { + match c { + '0'...'9' => acc.push(c), + '-' => duration = -duration, + 'W' | 'H' | 'M' | 'S' | 'D' => { + let count = acc.parse()?; + acc = "".to_string(); + let d = match c { + 'W' => Duration::weeks(count), + 'H' => Duration::hours(count), + 'M' => Duration::minutes(count), + 'S' => Duration::seconds(count), + 'D' => Duration::days(count), + _ => Duration::seconds(0), + }; + duration = duration + d; + } + _ => (), + } + } + Ok(Duration::weeks(1)) +} diff --git a/src/event.rs b/src/event.rs index 904b718..b54c422 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,6 +1,8 @@ use std::fmt; use std::str::FromStr; +use chrono::Duration; + use date::Date; use errors::EventError; @@ -8,7 +10,7 @@ use errors::EventError; #[derive(Debug, Clone)] pub struct Event { pub start: Date, - pub end: Date, + pub end: End, pub summary: String, pub location: String, pub description: String, @@ -22,6 +24,12 @@ pub enum Status { Canceled, } +#[derive(Debug, Copy, Clone)] +pub enum End { + Date(Date), + Duration(Duration), +} + impl Event { pub fn new() -> Event { @@ -31,14 +39,27 @@ impl Event { description: "".to_string(), status: Status::Confirmed, start: Date::empty(), - end: Date::empty(), + end: End::Date(Date::empty()), }; } + + pub fn end_date(&self) -> Date { + match self.end { + End::Date(date) => date, + End::Duration(duration) => self.start + duration, + } + } } impl fmt::Display for Event { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}: {}", self.start, self.summary)?; + write!( + f, + "{:?}-{:?}: {}", + self.start, + self.end_date(), + self.summary + )?; if !self.location.is_empty() { write!(f, " ({})", self.location)?; } diff --git a/src/events.rs b/src/events.rs deleted file mode 100644 index b0111e7..0000000 --- a/src/events.rs +++ /dev/null @@ -1,93 +0,0 @@ -use std::io::BufRead; -use std::fmt; -use ical::IcalParser; - -use date::Date; -use event::Event; -use periodic::Periodic; -use errors::EventError; - -pub struct Events { - single: Vec, - periodic: Vec, -} - -impl Events { - pub fn parse(buf: B) -> Result { - let reader = IcalParser::new(buf); - let mut single = Vec::new(); - let mut periodic = Vec::new(); - - for line in reader { - for ev in line?.events { - let mut event = Event::new(); - let mut maybe_periodic = None; - - for property in ev.properties { - let value = property.value.unwrap_or("".to_string()); - let mut time_zone = "".to_string(); - - let params = property.params.unwrap_or(vec![]); - for (param, value) in ¶ms { - if param == "TZID" && value.len() > 0 { - time_zone = value[0].clone(); - } - } - - match property.name.as_ref() { - "SUMMARY" => event.summary = value, - "LOCATION" => event.location = value, - "DESCRIPTION" => event.description = value, - "STATUS" => event.status = value.parse()?, - "DTSTART" => event.start = Date::parse(&value, &time_zone)?, - "DTEND" => event.end = Date::parse(&value, &time_zone)?, - "RRULE" => maybe_periodic = Some(rrule(&value, ¶ms)?), - _ => (), - }; - } - match maybe_periodic { - Some(mut p) => { - p.event = event; - periodic.push(p); - } - None => single.push(event), - } - } - } - - single.sort_by(|a, b| a.start.cmp(&b.start)); - Ok(Events { single, periodic }) - } -} - -impl fmt::Display for Events { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - for event in &self.single { - writeln!(f, "{}", event)?; - } - writeln!(f, "")?; - for periodic in &self.periodic { - writeln!(f, "{}", periodic)?; - } - Ok(()) - } -} - -fn rrule(value: &String, params: &Vec<(String, Vec)>) -> Result { - let mut periodic = Periodic::new(); - - for entry in value.split(";") { - let p: Vec<&str> = entry.splitn(2, "=").collect(); - periodic.set_param(p[0], p[1])?; - } - - for (param, values) in params { - let mut value = ""; - if values.len() > 0 { - value = &values[0]; - } - periodic.set_param(param, value)?; - } - - Ok(periodic) -} diff --git a/src/main.rs b/src/main.rs index 022b811..66e1df1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,18 +5,25 @@ extern crate chrono_tz; mod date; mod event; mod periodic; -mod events; +mod calendar; mod errors; use std::env; use std::io::BufReader; use std::fs::File; -use events::Events; +use chrono::Duration; +use date::Date; +use calendar::Calendar; fn main() { let args: Vec<_> = env::args().collect(); let file = File::open(&args[1]).unwrap(); let buf = BufReader::new(file); - let events = Events::parse(buf).unwrap(); - println!("{}", events); + let calendar = Calendar::parse(buf).unwrap(); + println!("{}", calendar); + println!(""); + + let now = Date::now(); + let events = calendar.get(&now, &(now + Duration::weeks(10))); + println!("{:?}", events); } diff --git a/src/periodic.rs b/src/periodic.rs index e46a13b..f6cd2d7 100644 --- a/src/periodic.rs +++ b/src/periodic.rs @@ -1,7 +1,10 @@ use std::fmt; use std::str::FromStr; -use event::Event; +use chrono::Duration; + +use date::Date; +use event::{Event, End}; use errors::EventError; #[derive(Debug)] @@ -9,7 +12,8 @@ pub struct Periodic { pub event: Event, pub freq: Freq, pub interval: i64, - // TODO: until, count, ... + pub until: Date, + // TODO: count, ... } #[derive(Debug)] @@ -29,6 +33,7 @@ impl Periodic { event: Event::new(), freq: Freq::Secondly, interval: 1, + until: Date::empty(), } } @@ -36,10 +41,32 @@ impl Periodic { match param { "FREQ" => self.freq = value.parse()?, "INTERVAL" => self.interval = value.parse()?, + "UNTIL" => self.until = Date::parse(&value, "")?, _ => (), } Ok(()) } + + pub fn get(&self, first: &Date, last: &Date) -> Vec { + let mut start = self.event.start; + let mut end = self.event.end_date(); + let mut events = Vec::new(); + while start <= *last { + if !self.until.is_empty() && start <= self.until { + break; + } + + if start >= *first { + let mut e = self.event.clone(); + e.start = start; + e.end = End::Date(end); + events.push(e); + } + start = start + self.freq.duration(self.interval); + end = end + self.freq.duration(self.interval); + } + events + } } impl fmt::Display for Periodic { @@ -53,6 +80,20 @@ impl fmt::Display for Periodic { } } +impl Freq { + pub fn duration(&self, count: i64) -> Duration { + match self { + Freq::Secondly => Duration::seconds(count), + Freq::Minutely => Duration::minutes(count), + Freq::Hourly => Duration::hours(count), + Freq::Daily => Duration::days(count), + Freq::Weekly => Duration::weeks(count), + Freq::Monthly => Duration::days(count * 30), // FIXME + Freq::Yearly => Duration::days(count * 365), // FIXME + } + } +} + impl FromStr for Freq { type Err = EventError; -- cgit v1.2.3