diff --git a/src/calendar/cached_calendar.rs b/src/calendar/cached_calendar.rs index af495bb..86b176c 100644 --- a/src/calendar/cached_calendar.rs +++ b/src/calendar/cached_calendar.rs @@ -326,6 +326,14 @@ impl DavCalendar for CachedCalendar { Ok(self.items.get(url).cloned()) } + async fn get_items_by_url(&self, urls: &[Url]) -> Result>, Box> { + let mut v = Vec::new(); + for url in urls { + v.push(DavCalendar::get_item_by_url(self, url).await?); + } + Ok(v) + } + async fn delete_item(&mut self, item_url: &Url) -> Result<(), Box> { #[cfg(feature = "local_calendar_mocks_remote_calendars")] self.mock_behaviour.as_ref().map_or(Ok(()), |b| b.lock().unwrap().can_delete_item())?; diff --git a/src/calendar/remote_calendar.rs b/src/calendar/remote_calendar.rs index f4f1ab1..2acded3 100644 --- a/src/calendar/remote_calendar.rs +++ b/src/calendar/remote_calendar.rs @@ -14,6 +14,7 @@ use crate::item::Item; use crate::item::VersionTag; use crate::item::SyncStatus; use crate::resource::Resource; +use crate::utils::find_elem; static TASKS_BODY: &str = r#" @@ -28,6 +29,15 @@ static TASKS_BODY: &str = r#" "#; +static MULTIGET_BODY_PREFIX: &str = r#" + + + + +"#; +static MULTIGET_BODY_SUFFIX: &str = r#" + +"#; @@ -191,6 +201,40 @@ impl DavCalendar for RemoteCalendar { Ok(Some(item)) } + async fn get_items_by_url(&self, urls: &[Url]) -> Result>, Box> { + // Build the request body + let mut hrefs = String::new(); + for url in urls { + hrefs.push_str(&format!(" {}\n", url.path())); + } + let body = format!("{}{}{}", MULTIGET_BODY_PREFIX, hrefs, MULTIGET_BODY_SUFFIX); + + // Send the request + let xml_replies = crate::client::sub_request_and_extract_elems(&self.resource, "REPORT", body, "response").await?; + + // This is supposed to be cached + let version_tags = self.get_item_version_tags().await?; + + // Parse the results + let mut results = Vec::new(); + for xml_reply in xml_replies { + let href = find_elem(&xml_reply, "href").ok_or("Missing HREF")?.text(); + let mut url = self.resource.url().clone(); + url.set_path(&href); + let ical_data = find_elem(&xml_reply, "calendar-data").ok_or("Missing calendar-data")?.text(); + + let vt = match version_tags.get(&url) { + None => return Err(format!("Inconsistent data: {} has no version tag", url).into()), + Some(vt) => vt, + }; + + let item = crate::ical::parse(&ical_data, url.clone(), SyncStatus::Synced(vt.clone()))?; + results.push(Some(item)); + } + + Ok(results) + } + async fn delete_item(&mut self, item_url: &Url) -> Result<(), Box> { let del_response = reqwest::Client::new() .delete(item_url.clone()) diff --git a/src/traits.rs b/src/traits.rs index c707e50..ed4a0d8 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -83,6 +83,10 @@ pub trait DavCalendar : BaseCalendar { /// Returns a particular item async fn get_item_by_url(&self, url: &Url) -> Result, Box>; + /// Returns a set of items. + /// This is usually faster than calling multiple consecutive [`get_item_by_url`], since it only issues one HTTP request. + async fn get_items_by_url(&self, urls: &[Url]) -> Result>, Box>; + /// Delete an item async fn delete_item(&mut self, item_url: &Url) -> Result<(), Box>;