use std::collections::HashMap;
use crate::caldav;
use crate::ical;
use ureq::Agent;
use url::Url;
pub fn get_calendars(
agent: Agent,
username: &str,
password: &str,
base_url: &Url,
) -> Result<Vec<Calendar>, Error> {
let calendar_refs = caldav::get_calendars(agent, username, password, base_url)?;
let mut calendars = Vec::new();
for calendar_ref in calendar_refs {
calendars.push(Calendar {
base_url: base_url.clone(),
inner: calendar_ref,
});
}
Ok(calendars)
}
pub fn get_todos(
agent: Agent,
username: &str,
password: &str,
calendar: &Calendar,
) -> Result<(Vec<Event>, Vec<Error>), Error> {
let todo_refs = caldav::get_todos(
agent,
username,
password,
&calendar.base_url,
&calendar.inner,
)?;
let mut todos = Vec::new();
let mut errors = Vec::new();
for todo_ref in todo_refs {
let lines = ical::LineIterator::new(&todo_ref.data);
match ical::Ical::parse(&lines) {
Ok(ical) => todos.push(Event {
url: todo_ref.url.clone(),
etag: todo_ref.etag.clone(),
ical,
}),
Err(e) => errors.push(Error::Ical(format!(
"Could not parse todo {}: {:?}",
todo_ref.data, e
))),
}
}
Ok((todos, errors))
}
pub fn get_events(
agent: Agent,
username: &str,
password: &str,
calendar: &Calendar,
) -> Result<(Vec<Event>, Vec<Error>), Error> {
let event_refs = caldav::get_events(
agent,
username,
password,
&calendar.base_url,
&calendar.inner,
)?;
let mut events = Vec::new();
let mut errors = Vec::new();
for event_ref in event_refs {
let lines = ical::LineIterator::new(&event_ref.data);
match ical::Ical::parse(&lines) {
Ok(ical) => events.push(Event {
url: event_ref.url.clone(),
etag: event_ref.etag.clone(),
ical,
}),
Err(e) => errors.push(Error::Ical(format!(
"Could not parse event {}: {:?}",
event_ref.data, e
))),
}
}
Ok((events, errors))
}
pub fn save_event(
agent: Agent,
username: &str,
password: &str,
mut event: Event,
) -> Result<Event, Error> {
for prop in &mut event.ical.properties {
if prop.name == "SEQUENCE" {
if let Ok(num) = prop.value.parse::<i64>() {
prop.value = format!("{}", num + 1);
}
}
}
let event_ref = caldav::EventRef {
data: event.ical.serialize(),
etag: None,
url: event.url,
};
let event_ref = caldav::save_event(agent, username, password, event_ref)?;
Ok(Event {
etag: event_ref.etag,
url: event_ref.url,
..event
})
}
pub fn remove_event(
agent: Agent,
username: &str,
password: &str,
event: Event,
) -> Result<(), Error> {
let event_ref = caldav::EventRef {
data: event.ical.serialize(),
etag: event.etag,
url: event.url,
};
caldav::remove_event(agent, username, password, event_ref)?;
Ok(())
}
#[derive(Debug)]
pub struct Calendar {
base_url: Url,
inner: caldav::CalendarRef,
}
impl Calendar {
pub fn url(&self) -> &Url {
&self.inner.url
}
pub fn name(&self) -> &String {
&self.inner.name
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Event {
etag: Option<String>,
url: Url,
ical: ical::Ical,
}
impl Event {
pub fn url(&self) -> &Url {
&self.url
}
fn get_property(&self, name: &str, datatype: &str) -> Option<Property> {
self.ical.get(datatype).and_then(|ical| {
ical.properties.iter().find_map(|p| {
if p.name == name {
Some(Property::from(p.clone()))
} else {
None
}
})
})
}
pub fn pop_property(&mut self, name: &str) -> Option<Property> {
self.ical.get_mut("VEVENT").and_then(|ical| {
let index = ical.properties.iter().enumerate().find_map(|(i, p)| {
if p.name == name {
Some(i)
} else {
None
}
});
if let Some(index) = index {
Some(Property::from(ical.properties.remove(index)))
} else {
None
}
})
}
pub fn add(&mut self, property: Property) {
if let Some(ical) = self.ical.get_mut("VEVENT") {
ical.properties.push(property.into());
}
}
pub fn property(&self, name: &str) -> Option<Property> {
self.get_property(name, "VEVENT")
}
pub fn property_todo(&self, name: &str) -> Option<Property> {
self.get_property(name, "VTODO")
}
pub fn get(&self, name: &str) -> Option<&String> {
self.ical.get("VEVENT").and_then(|ical| {
ical.properties
.iter()
.find_map(|p| if p.name == name { Some(&p.value) } else { None })
})
}
fn get_properties(&self, datatype: &str) -> Vec<(&String, &String)> {
self.ical
.get(datatype)
.map(|ical| {
ical.properties
.iter()
.map(|p| (&p.name, &p.value))
.collect::<Vec<(&String, &String)>>()
})
.unwrap_or_else(|| {
error!("Could not get properties: No VEVENT section.");
Vec::new()
})
}
pub fn into_properties(self) -> Vec<Property> {
for ical in self.ical.children {
if ical.name == "VEVENT" {
return ical.properties.into_iter().map(Property::from).collect();
}
}
Vec::new()
}
pub fn properties(&self) -> Vec<(&String, &String)> {
self.get_properties("VEVENT")
}
pub fn properties_todo(&self) -> Vec<(&String, &String)> {
self.get_properties("VTODO")
}
pub fn etag(&self) -> Option<&String> {
self.etag.as_ref()
}
pub fn builder(url: Url) -> EventBuilder {
EventBuilder {
url,
etag: None,
properties: vec![],
}
}
}
#[cfg(not(feature = "ser_de"))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Property {
name: String,
value: String,
attributes: HashMap<String, String>,
}
#[cfg(feature = "ser_de")]
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct Property {
name: String,
value: String,
attributes: HashMap<String, String>,
}
impl Property {
pub fn name(&self) -> &String {
&self.name
}
pub fn value(&self) -> &String {
&self.value
}
pub fn into_value(self) -> String {
self.value
}
pub fn attribute(&self, name: &str) -> Option<&String> {
self.attributes.get(name)
}
}
impl From<ical::Property> for Property {
fn from(p: ical::Property) -> Self {
Self {
name: p.name,
value: p.value,
attributes: p.attributes,
}
}
}
impl From<Property> for ical::Property {
fn from(p: Property) -> Self {
Self {
name: p.name,
value: p.value,
attributes: p.attributes,
}
}
}
#[derive(Debug)]
pub enum Error {
Ical(String),
Caldav(String),
}
impl From<caldav::Error> for Error {
fn from(e: caldav::Error) -> Self {
Error::Ical(e.message)
}
}
impl From<ical::Error> for Error {
fn from(e: ical::Error) -> Self {
Error::Caldav(e.message)
}
}
#[derive(Debug)]
pub struct EventBuilder {
url: Url,
etag: Option<String>,
properties: Vec<ical::Property>,
}
impl EventBuilder {
fn build_event(self, name: String) -> Event {
Event {
etag: self.etag,
url: self.url,
ical: ical::Ical {
name: "VCALENDAR".into(),
properties: vec![],
children: vec![ical::Ical {
name,
properties: self.properties,
children: vec![],
}],
},
}
}
pub fn build(self) -> Event {
self.build_event("VEVENT".into())
}
pub fn build_todo(self) -> Event {
self.build_event("VTODO".into())
}
pub fn etag(mut self, etag: Option<String>) -> Self {
self.etag = etag;
self
}
pub fn uid(mut self, value: String) -> Self {
self.properties.push(ical::Property {
name: "UID".to_string(),
value,
attributes: HashMap::new(),
});
self
}
pub fn timestamp(mut self, value: String) -> Self {
self.properties.push(ical::Property {
name: "DTSTAMP".to_string(),
value,
attributes: HashMap::new(),
});
self
}
pub fn summary(mut self, value: String) -> Self {
self.properties.push(ical::Property {
name: "SUMMARY".to_string(),
value,
attributes: HashMap::new(),
});
self
}
pub fn priority(mut self, value: String) -> Self {
self.properties.push(ical::Property {
name: "PRIORITY".to_string(),
value,
attributes: HashMap::new(),
});
self
}
pub fn duedate(mut self, value: String) -> Self {
self.properties.push(ical::Property {
name: "DUE".to_string(),
value,
attributes: HashMap::new(),
});
self
}
pub fn status(mut self, value: String) -> Self {
self.properties.push(ical::Property {
name: "STATUS".to_string(),
value,
attributes: HashMap::new(),
});
self
}
pub fn generic(mut self, name: String, value: String) -> Self {
self.properties.push(ical::Property {
name,
value,
attributes: HashMap::new(),
});
self
}
pub fn location(mut self, value: Option<String>) -> Self {
if let Some(value) = value {
self.properties.push(ical::Property {
name: "LOCATION".to_string(),
value,
attributes: HashMap::new(),
});
}
self
}
pub fn start(mut self, value: String, attributes: Vec<(&str, &str)>) -> Self {
let mut attribs = HashMap::new();
for (k, v) in attributes {
attribs.insert(k.into(), v.into());
}
self.properties.push(ical::Property {
name: "DTSTART".to_string(),
value,
attributes: attribs,
});
self
}
pub fn end(mut self, value: String, attributes: Vec<(&str, &str)>) -> Self {
let mut attribs = HashMap::new();
for (k, v) in attributes {
attribs.insert(k.into(), v.into());
}
self.properties.push(ical::Property {
name: "DTEND".to_string(),
value,
attributes: attribs,
});
self
}
pub fn description(mut self, value: Option<String>) -> Self {
if let Some(value) = value {
self.properties.push(ical::Property {
name: "DESCRIPTION".to_string(),
value,
attributes: HashMap::new(),
});
}
self
}
pub fn rrule(mut self, value: Option<String>) -> Self {
if let Some(value) = value {
self.properties.push(ical::Property {
name: "RRULE".to_string(),
value,
attributes: HashMap::new(),
});
}
self
}
}