|
|
|
@ -1,10 +1,12 @@
|
|
|
|
|
use std::str::FromStr;
|
|
|
|
|
use std::mem::replace;
|
|
|
|
|
use std::io::Read;
|
|
|
|
|
use std::sync::RwLock;
|
|
|
|
|
use std::collections::{HashMap, VecDeque};
|
|
|
|
|
use chrono::offset::Utc;
|
|
|
|
|
use chrono::{Duration, offset::Utc};
|
|
|
|
|
use reqwest::header::{IF_NONE_MATCH, IF_MODIFIED_SINCE, ETAG, LAST_MODIFIED, USER_AGENT};
|
|
|
|
|
use diesel::{Connection, pg::PgConnection, prelude::*};
|
|
|
|
|
use rrule::RRule;
|
|
|
|
|
|
|
|
|
|
use libticker::{
|
|
|
|
|
config::{Config, CalendarOptions},
|
|
|
|
@ -23,30 +25,71 @@ fn extract_vevent_objs(results: &mut Vec<Object>, mut obj: Object) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn obj_to_event(calendar: String, obj: &Object) -> Option<Event> {
|
|
|
|
|
const RRULE_LOOKBACK: i64 = 30;
|
|
|
|
|
const RRULE_LOOKAHEAD: i64 = 366;
|
|
|
|
|
|
|
|
|
|
fn obj_to_events(calendar: String, obj: &Object) -> Vec<Event> {
|
|
|
|
|
if obj.name != "VEVENT" {
|
|
|
|
|
return None;
|
|
|
|
|
return vec![];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let dtstart: Timestamp = obj.get("DTSTART")?;
|
|
|
|
|
let (dtstart, dtstart_str): (Timestamp, &str) = match (obj.get("DTSTART"), obj.get("DTSTART")) {
|
|
|
|
|
(Some(dtstart), Some(dtstart_str)) => (dtstart, dtstart_str),
|
|
|
|
|
_ => return vec![],
|
|
|
|
|
};
|
|
|
|
|
let dtstart = dtstart.or_hms(0, 0, 0);
|
|
|
|
|
let dtend: Option<Timestamp> = obj.get("DTEND");
|
|
|
|
|
let dtend = dtend.map(|time| time.or_hms(23, 59, 59));
|
|
|
|
|
let summary = obj.get("SUMMARY")?;
|
|
|
|
|
let id = format!("{}{}{}{}",
|
|
|
|
|
obj.get("UID").unwrap_or(""),
|
|
|
|
|
dtstart,
|
|
|
|
|
obj.get("DTSTAMP").unwrap_or(""),
|
|
|
|
|
obj.get("RECURRENCE-ID").unwrap_or(""));
|
|
|
|
|
// TODO: DESCRIPTION
|
|
|
|
|
Some(Event {
|
|
|
|
|
calendar, id,
|
|
|
|
|
dtstart,
|
|
|
|
|
dtend,
|
|
|
|
|
summary,
|
|
|
|
|
location: obj.get("LOCATION"),
|
|
|
|
|
url: obj.get("URL"),
|
|
|
|
|
})
|
|
|
|
|
let summary: &str = match obj.get("SUMMARY") {
|
|
|
|
|
Some(summary) => summary,
|
|
|
|
|
None => return vec![],
|
|
|
|
|
};
|
|
|
|
|
let uid = obj.get("UID").unwrap_or("");
|
|
|
|
|
let dtstamp = obj.get("DTSTAMP").unwrap_or("");
|
|
|
|
|
let location = obj.get("LOCATION");
|
|
|
|
|
let url = obj.get("URL");
|
|
|
|
|
|
|
|
|
|
let generate_event = |dtstart| {
|
|
|
|
|
let id = format!("{}{}{}", uid, dtstart, dtstamp);
|
|
|
|
|
Event {
|
|
|
|
|
calendar: calendar.clone(),
|
|
|
|
|
id,
|
|
|
|
|
dtstart,
|
|
|
|
|
dtend,
|
|
|
|
|
summary: summary.to_owned(),
|
|
|
|
|
location: location.clone(),
|
|
|
|
|
url: url.clone(),
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let rrule_str: Option<&str> = obj.get("rrule");
|
|
|
|
|
let rrule = rrule_str.and_then(
|
|
|
|
|
|rrule_str| RRule::from_str(
|
|
|
|
|
&format!("DTSTART:{}\nRRULE:{}", dtstart_str, rrule_str)
|
|
|
|
|
).map_err(|e| println!("Error parsing RRULE: {}", e))
|
|
|
|
|
.ok()
|
|
|
|
|
);
|
|
|
|
|
match rrule {
|
|
|
|
|
Some(rrule) => {
|
|
|
|
|
let now = Utc::now();
|
|
|
|
|
let start = now - Duration::days(RRULE_LOOKBACK);
|
|
|
|
|
let end = now + Duration::days(RRULE_LOOKAHEAD);
|
|
|
|
|
println!("rrule {}:\n{:?}", rrule_str.unwrap_or(""), rrule.into_iter()
|
|
|
|
|
.skip_while(|d| *d < start)
|
|
|
|
|
.take_while(|d| *d <= end)
|
|
|
|
|
.map(|dtstart| dtstart.naive_utc())
|
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
|
);
|
|
|
|
|
rrule.into_iter()
|
|
|
|
|
.skip_while(|d| *d < start)
|
|
|
|
|
.take_while(|d| *d <= end)
|
|
|
|
|
.map(|dtstart| dtstart.naive_utc())
|
|
|
|
|
.map(generate_event)
|
|
|
|
|
.collect()
|
|
|
|
|
}
|
|
|
|
|
None =>
|
|
|
|
|
vec![generate_event(dtstart)],
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct Resources {
|
|
|
|
@ -105,13 +148,8 @@ impl Resources {
|
|
|
|
|
let mut objs = vec![];
|
|
|
|
|
extract_vevent_objs(&mut objs, obj);
|
|
|
|
|
for obj in objs {
|
|
|
|
|
if let Some(event) = obj_to_event(cal_id.to_owned(), &obj) {
|
|
|
|
|
for event in obj_to_events(cal_id.to_owned(), &obj) {
|
|
|
|
|
events.insert(event.id.clone(), event);
|
|
|
|
|
} else {
|
|
|
|
|
let dtstart: Option<&str> = obj.get("DTSTART");
|
|
|
|
|
let summary: Option<&str> = obj.get("SUMMARY");
|
|
|
|
|
println!("cannot convert {} {:?} {:?}",
|
|
|
|
|
obj.name, dtstart, summary);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|