From ba5f37d3bcc7a6cbf09581d474c26f3c69c60130 Mon Sep 17 00:00:00 2001 From: Ruben Pollan Date: Sun, 29 Apr 2018 03:42:10 +0200 Subject: Simple ics parser --- src/ics.rs | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 src/ics.rs (limited to 'src/ics.rs') diff --git a/src/ics.rs b/src/ics.rs new file mode 100644 index 0000000..3373d14 --- /dev/null +++ b/src/ics.rs @@ -0,0 +1,136 @@ +use std::io; +use std::io::BufReader; +use std::fs::File; +use std::path::Path; +use std::str::FromStr; +use std::num::ParseIntError; +use std::fmt; + +use ical::parser; +use ical::IcalParser; +use chrono; +use chrono_tz::{Tz, UTC}; +use chrono::prelude::{DateTime, TimeZone}; + + +#[derive(Debug)] +pub enum Date { + Time(DateTime), + AllDay(chrono::Date), +} + +#[derive(Debug)] +pub struct Event { + pub start: Date, + pub end: Date, + pub summary: String, + pub location: String, + pub description: String, +} + +pub fn parse>(ics: P) -> Result, IcsError> { + let buf = BufReader::new(File::open(ics)?); + let reader = IcalParser::new(buf); + let mut events = Vec::new(); + + for line in reader { + for ev in line?.events { + let mut event = Event::new(); + for property in ev.properties { + let value = property.value.unwrap_or("".to_string()); + let mut time_zone = "".to_string(); + + for (param, value) in property.params.unwrap_or(vec![]) { + 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, + "DTSTART" => event.start = parse_date(value, time_zone)?, + "DTEND" => event.end = parse_date(value, time_zone)?, + _ => (), + }; + } + events.push(event); + } + } + + Ok(events) +} + +fn parse_date(date: String, time_zone: String) -> Result { + let tz: Tz = time_zone.parse().unwrap_or(UTC); + let date = match date.find("T") { + Some(_) => { + let time = tz.datetime_from_str(&date, "%Y%m%dT%H%M%S").unwrap_or( + UTC.timestamp( + 0, + 0, + ), + ); + Date::Time(time) + } + None => { + Date::AllDay(tz.ymd( + i32::from_str(&date[0..4])?, + u32::from_str(&date[4..6])?, + u32::from_str(&date[6..8])?, + )) + } + }; + Ok(date) +} + +impl Event { + fn new() -> Event { + return Event { + summary: "".to_string(), + location: "".to_string(), + description: "".to_string(), + start: Date::Time(UTC.timestamp(0, 0)), + end: Date::Time(UTC.timestamp(0, 0)), + }; + } +} + +impl fmt::Display for Event { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}: {}", self.start, self.summary)?; + if !self.location.is_empty() { + write!(f, " ({})", self.location)?; + } + if !self.description.is_empty() { + write!(f, "\n\t{}", self.description)?; + } + Ok(()) + } +} + +#[derive(Debug)] +pub enum IcsError { + IoError(io::Error), + IcalError(parser::errors::Error), + IntError(ParseIntError), +} + +impl From for IcsError { + fn from(err: io::Error) -> IcsError { + IcsError::IoError(err) + } +} + +impl From for IcsError { + fn from(err: parser::errors::Error) -> IcsError { + IcsError::IcalError(err) + } +} + +impl From for IcsError { + fn from(err: ParseIntError) -> IcsError { + IcsError::IntError(err) + } +} -- cgit v1.2.3