Turning calendars into Arc<Mutex>
This commit is contained in:
parent
d07a2b9493
commit
060f33b4bd
8 changed files with 199 additions and 91 deletions
37
src/cache.rs
37
src/cache.rs
|
@ -6,6 +6,7 @@ use std::error::Error;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
@ -28,7 +29,7 @@ pub struct Cache {
|
||||||
|
|
||||||
#[derive(Default, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Default, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
struct CachedData {
|
struct CachedData {
|
||||||
calendars: HashMap<CalendarId, CachedCalendar>,
|
calendars: HashMap<CalendarId, Arc<Mutex<CachedCalendar>>>,
|
||||||
last_sync: Option<DateTime<Utc>>,
|
last_sync: Option<DateTime<Utc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,8 +82,9 @@ impl Cache {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn add_calendar(&mut self, calendar: CachedCalendar) {
|
pub fn add_calendar(&mut self, calendar: Arc<Mutex<CachedCalendar>>) {
|
||||||
self.data.calendars.insert(calendar.id().clone(), calendar);
|
let id = calendar.lock().unwrap().id().clone();
|
||||||
|
self.data.calendars.insert(id, calendar);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compares two Caches to check they have the same current content
|
/// Compares two Caches to check they have the same current content
|
||||||
|
@ -97,8 +99,9 @@ impl Cache {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (id, cal_l) in calendars_l {
|
for (id, cal_l) in calendars_l {
|
||||||
let cal_r = match calendars_r.get(id) {
|
let cal_l = cal_l.lock().unwrap();
|
||||||
Some(c) => c,
|
let cal_r = match calendars_r.get(&id) {
|
||||||
|
Some(c) => c.lock().unwrap(),
|
||||||
None => return Err("should not happen, we've just tested keys are the same".into()),
|
None => return Err("should not happen, we've just tested keys are the same".into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -138,23 +141,15 @@ where
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl CalDavSource<CachedCalendar> for Cache {
|
impl CalDavSource<CachedCalendar> for Cache {
|
||||||
async fn get_calendars(&self) -> Result<&HashMap<CalendarId, CachedCalendar>, Box<dyn Error>> {
|
async fn get_calendars(&self) -> Result<HashMap<CalendarId, Arc<Mutex<CachedCalendar>>>, Box<dyn Error>> {
|
||||||
Ok(&self.data.calendars)
|
Ok(self.data.calendars.iter()
|
||||||
|
.map(|(id, cal)| (id.clone(), cal.clone()))
|
||||||
|
.collect()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_calendars_mut(&mut self) -> Result<HashMap<CalendarId, &mut CachedCalendar>, Box<dyn Error>> {
|
async fn get_calendar(&self, id: CalendarId) -> Option<Arc<Mutex<CachedCalendar>>> {
|
||||||
let mut hm = HashMap::new();
|
self.data.calendars.get(&id).map(|arc| arc.clone())
|
||||||
for (id, val) in self.data.calendars.iter_mut() {
|
|
||||||
hm.insert(id.clone(), val);
|
|
||||||
}
|
|
||||||
Ok(hm)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_calendar(&self, id: CalendarId) -> Option<&CachedCalendar> {
|
|
||||||
self.data.calendars.get(&id)
|
|
||||||
}
|
|
||||||
async fn get_calendar_mut(&mut self, id: CalendarId) -> Option<&mut CachedCalendar> {
|
|
||||||
self.data.calendars.get_mut(&id)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,7 +179,7 @@ mod tests {
|
||||||
let cal1 = CachedCalendar::new("shopping list".to_string(),
|
let cal1 = CachedCalendar::new("shopping list".to_string(),
|
||||||
Url::parse("https://caldav.com/shopping").unwrap(),
|
Url::parse("https://caldav.com/shopping").unwrap(),
|
||||||
SupportedComponents::TODO);
|
SupportedComponents::TODO);
|
||||||
cache.add_calendar(cal1);
|
cache.add_calendar(Arc::new(Mutex::new(cal1)));
|
||||||
|
|
||||||
cache.save_to_file();
|
cache.save_to_file();
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
pub mod cached_calendar;
|
pub mod cached_calendar;
|
||||||
|
pub mod remote_calendar;
|
||||||
|
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
|
@ -1,21 +1,39 @@
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
use url::Url;
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
|
||||||
use crate::traits::PartialCalendar;
|
use crate::traits::PartialCalendar;
|
||||||
|
use crate::calendar::SupportedComponents;
|
||||||
|
use crate::calendar::CalendarId;
|
||||||
|
use crate::item::ItemId;
|
||||||
|
use crate::item::Item;
|
||||||
|
|
||||||
/// A CalDAV calendar created by a [`Client`](crate::client::Client).
|
/// A CalDAV calendar created by a [`Client`](crate::client::Client).
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct RemoteCalendar {
|
pub struct RemoteCalendar {
|
||||||
name: String,
|
name: String,
|
||||||
url: Url,
|
url: Url,
|
||||||
supported_components: SupportedComponents
|
supported_components: SupportedComponents
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialCalendar for RemoteCalendar {
|
impl RemoteCalendar {
|
||||||
fn name(&self) -> &str {
|
pub fn new(name: String, url: Url, supported_components: SupportedComponents) -> Self {
|
||||||
&self.name
|
Self {
|
||||||
|
name, url, supported_components
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialCalendar for RemoteCalendar {
|
||||||
|
fn name(&self) -> &str { &self.name }
|
||||||
|
fn id(&self) -> &CalendarId { &self.url }
|
||||||
fn supported_components(&self) -> crate::calendar::SupportedComponents {
|
fn supported_components(&self) -> crate::calendar::SupportedComponents {
|
||||||
self.supported_components
|
self.supported_components
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the items that have been last-modified after `since`
|
||||||
fn get_items_modified_since(&self, since: Option<DateTime<Utc>>, filter: Option<crate::calendar::SearchFilter>)
|
fn get_items_modified_since(&self, since: Option<DateTime<Utc>>, filter: Option<crate::calendar::SearchFilter>)
|
||||||
-> HashMap<ItemId, &Item>
|
-> HashMap<ItemId, &Item>
|
||||||
{
|
{
|
||||||
|
@ -23,16 +41,28 @@ impl PartialCalendar for RemoteCalendar {
|
||||||
HashMap::new()
|
HashMap::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the IDs of all current items in this calendar
|
||||||
|
fn get_item_ids(&mut self) -> HashSet<ItemId> {
|
||||||
|
log::error!("Not implemented");
|
||||||
|
HashSet::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a particular item
|
||||||
fn get_item_by_id_mut(&mut self, id: &ItemId) -> Option<&mut Item> {
|
fn get_item_by_id_mut(&mut self, id: &ItemId) -> Option<&mut Item> {
|
||||||
log::error!("Not implemented");
|
log::error!("Not implemented");
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add an item into this calendar
|
||||||
fn add_item(&mut self, item: Item) {
|
fn add_item(&mut self, item: Item) {
|
||||||
log::error!("Not implemented");
|
log::error!("Not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete_item(&mut self, item_id: &ItemId) {
|
/// Remove an item from this calendar
|
||||||
|
fn delete_item(&mut self, item_id: &ItemId) -> Result<(), Box<dyn Error>> {
|
||||||
log::error!("Not implemented");
|
log::error!("Not implemented");
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
161
src/client.rs
161
src/client.rs
|
@ -3,15 +3,18 @@
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
use reqwest::Method;
|
use reqwest::Method;
|
||||||
use reqwest::header::CONTENT_TYPE;
|
use reqwest::header::CONTENT_TYPE;
|
||||||
use minidom::Element;
|
use minidom::Element;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::utils::{find_elem, find_elems};
|
use crate::utils::{find_elem, find_elems};
|
||||||
use crate::calendar::cached_calendar::CachedCalendar;
|
use crate::calendar::remote_calendar::RemoteCalendar;
|
||||||
use crate::calendar::CalendarId;
|
use crate::calendar::CalendarId;
|
||||||
|
use crate::traits::CalDavSource;
|
||||||
use crate::traits::PartialCalendar;
|
use crate::traits::PartialCalendar;
|
||||||
|
|
||||||
|
|
||||||
|
@ -71,9 +74,17 @@ pub struct Client {
|
||||||
username: String,
|
username: String,
|
||||||
password: String,
|
password: String,
|
||||||
|
|
||||||
|
/// The interior mutable part of a Client.
|
||||||
|
/// This data may be retrieved once and then cached
|
||||||
|
cached_replies: Mutex<CachedReplies>,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct CachedReplies {
|
||||||
principal: Option<Url>,
|
principal: Option<Url>,
|
||||||
calendar_home_set: Option<Url>,
|
calendar_home_set: Option<Url>,
|
||||||
calendars: Option<HashMap<CalendarId, CachedCalendar>>,
|
calendars: Option<HashMap<CalendarId, Arc<Mutex<RemoteCalendar>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
|
@ -85,9 +96,7 @@ impl Client {
|
||||||
url,
|
url,
|
||||||
username: username.to_string(),
|
username: username.to_string(),
|
||||||
password: password.to_string(),
|
password: password.to_string(),
|
||||||
principal: None,
|
cached_replies: Mutex::new(CachedReplies::default()),
|
||||||
calendar_home_set: None,
|
|
||||||
calendars: None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,33 +120,30 @@ impl Client {
|
||||||
let text = self.sub_request(url, body, 0).await?;
|
let text = self.sub_request(url, body, 0).await?;
|
||||||
|
|
||||||
let mut current_element: &Element = &text.parse().unwrap();
|
let mut current_element: &Element = &text.parse().unwrap();
|
||||||
items.iter()
|
for item in items {
|
||||||
.map(|item| {
|
|
||||||
current_element = find_elem(¤t_element, item).unwrap();
|
current_element = find_elem(¤t_element, item).unwrap();
|
||||||
})
|
}
|
||||||
.collect::<()>();
|
|
||||||
|
|
||||||
Ok(current_element.text())
|
Ok(current_element.text())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the Principal URL, or fetch it from server if not known yet
|
/// Return the Principal URL, or fetch it from server if not known yet
|
||||||
async fn get_principal(&mut self) -> Result<Url, Box<dyn Error>> {
|
async fn get_principal(&self) -> Result<Url, Box<dyn Error>> {
|
||||||
if let Some(p) = &self.principal {
|
if let Some(p) = &self.cached_replies.lock().unwrap().principal {
|
||||||
return Ok(p.clone());
|
return Ok(p.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
let href = self.sub_request_and_process(&self.url, DAVCLIENT_BODY.into(), &["current-user-principal", "href"]).await?;
|
let href = self.sub_request_and_process(&self.url, DAVCLIENT_BODY.into(), &["current-user-principal", "href"]).await?;
|
||||||
let mut principal_url = self.url.clone();
|
let mut principal_url = self.url.clone();
|
||||||
principal_url.set_path(&href);
|
principal_url.set_path(&href);
|
||||||
self.principal = Some(principal_url.clone());
|
self.cached_replies.lock().unwrap().principal = Some(principal_url.clone());
|
||||||
log::debug!("Principal URL is {}", href);
|
log::debug!("Principal URL is {}", href);
|
||||||
|
|
||||||
return Ok(principal_url);
|
return Ok(principal_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the Homeset URL, or fetch it from server if not known yet
|
/// Return the Homeset URL, or fetch it from server if not known yet
|
||||||
async fn get_cal_home_set(&mut self) -> Result<Url, Box<dyn Error>> {
|
async fn get_cal_home_set(&self) -> Result<Url, Box<dyn Error>> {
|
||||||
if let Some(h) = &self.calendar_home_set {
|
if let Some(h) = &self.cached_replies.lock().unwrap().calendar_home_set {
|
||||||
return Ok(h.clone());
|
return Ok(h.clone());
|
||||||
}
|
}
|
||||||
let principal_url = self.get_principal().await?;
|
let principal_url = self.get_principal().await?;
|
||||||
|
@ -145,16 +151,88 @@ impl Client {
|
||||||
let href = self.sub_request_and_process(&principal_url, HOMESET_BODY.into(), &["calendar-home-set", "href"]).await?;
|
let href = self.sub_request_and_process(&principal_url, HOMESET_BODY.into(), &["calendar-home-set", "href"]).await?;
|
||||||
let mut chs_url = self.url.clone();
|
let mut chs_url = self.url.clone();
|
||||||
chs_url.set_path(&href);
|
chs_url.set_path(&href);
|
||||||
self.calendar_home_set = Some(chs_url.clone());
|
self.cached_replies.lock().unwrap().calendar_home_set = Some(chs_url.clone());
|
||||||
log::debug!("Calendar home set URL is {:?}", chs_url.path());
|
log::debug!("Calendar home set URL is {:?}", chs_url.path());
|
||||||
|
|
||||||
Ok(chs_url)
|
Ok(chs_url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn populate_calendars(&self) -> Result<(), Box<dyn Error>> {
|
||||||
|
let cal_home_set = self.get_cal_home_set().await?;
|
||||||
|
|
||||||
|
let text = self.sub_request(&cal_home_set, CAL_BODY.into(), 1).await?;
|
||||||
|
|
||||||
|
let root: Element = text.parse().unwrap();
|
||||||
|
let reps = find_elems(&root, "response");
|
||||||
|
let mut calendars = HashMap::new();
|
||||||
|
for rep in reps {
|
||||||
|
let display_name = find_elem(rep, "displayname").map(|e| e.text()).unwrap_or("<no name>".to_string());
|
||||||
|
log::debug!("Considering calendar {}", display_name);
|
||||||
|
|
||||||
|
// We filter out non-calendar items
|
||||||
|
let resource_types = match find_elem(rep, "resourcetype") {
|
||||||
|
None => continue,
|
||||||
|
Some(rt) => rt,
|
||||||
|
};
|
||||||
|
let mut found_calendar_type = false;
|
||||||
|
for resource_type in resource_types.children() {
|
||||||
|
if resource_type.name() == "calendar" {
|
||||||
|
found_calendar_type = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if found_calendar_type == false {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We filter out the root calendar collection, that has an empty supported-calendar-component-set
|
||||||
|
let el_supported_comps = match find_elem(rep, "supported-calendar-component-set") {
|
||||||
|
None => continue,
|
||||||
|
Some(comps) => comps,
|
||||||
|
};
|
||||||
|
if el_supported_comps.children().count() == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let calendar_href = match find_elem(rep, "href") {
|
||||||
|
None => {
|
||||||
|
log::warn!("Calendar {} has no URL! Ignoring it.", display_name);
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
Some(h) => h.text(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut this_calendar_url = self.url.clone();
|
||||||
|
this_calendar_url.set_path(&calendar_href);
|
||||||
|
|
||||||
|
let supported_components = match crate::calendar::SupportedComponents::try_from(el_supported_comps.clone()) {
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!("Calendar {} has invalid supported components ({})! Ignoring it.", display_name, err);
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
Ok(sc) => sc,
|
||||||
|
};
|
||||||
|
let this_calendar = RemoteCalendar::new(display_name, this_calendar_url, supported_components);
|
||||||
|
log::info!("Found calendar {}", this_calendar.name());
|
||||||
|
calendars.insert(this_calendar.id().clone(), Arc::new(Mutex::new(this_calendar)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut replies = self.cached_replies.lock().unwrap();
|
||||||
|
replies.calendars = Some(calendars);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl CalDavSource<RemoteCalendar> for Client {
|
||||||
/// Return the list of calendars, or fetch from server if not known yet
|
/// Return the list of calendars, or fetch from server if not known yet
|
||||||
pub async fn get_calendars(&mut self) -> Result<HashMap<CalendarId, CachedCalendar>, Box<dyn Error>> {
|
/*
|
||||||
if let Some(c) = &self.calendars {
|
async fn get_calendars(&self) -> Result<&HashMap<CalendarId, RemoteCalendar>, Box<dyn Error>> {
|
||||||
return Ok(c.clone());
|
let mut replies = self.cached_replies.lock().unwrap();
|
||||||
|
|
||||||
|
if let Some(c) = &replies.calendars {
|
||||||
|
return Ok(c);
|
||||||
}
|
}
|
||||||
let cal_home_set = self.get_cal_home_set().await?;
|
let cal_home_set = self.get_cal_home_set().await?;
|
||||||
|
|
||||||
|
@ -210,37 +288,36 @@ impl Client {
|
||||||
},
|
},
|
||||||
Ok(sc) => sc,
|
Ok(sc) => sc,
|
||||||
};
|
};
|
||||||
let this_calendar = CachedCalendar::new(display_name, this_calendar_url, supported_components);
|
let this_calendar = RemoteCalendar::new(display_name, this_calendar_url, supported_components);
|
||||||
log::info!("Found calendar {}", this_calendar.name());
|
log::info!("Found calendar {}", this_calendar.name());
|
||||||
calendars.insert(this_calendar.id().clone(), this_calendar);
|
calendars.insert(this_calendar.id().clone(), this_calendar);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.calendars = Some(calendars.clone());
|
replies.calendars = Some(calendars);
|
||||||
Ok(calendars)
|
Ok(&calendars)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
async fn get_calendars(&self) -> Result<HashMap<CalendarId, Arc<Mutex<RemoteCalendar>>>, Box<dyn Error>> {
|
||||||
|
self.populate_calendars().await?;
|
||||||
|
|
||||||
|
match &self.cached_replies.lock().unwrap().calendars {
|
||||||
|
Some(cals) => {
|
||||||
|
return Ok(cals.clone())
|
||||||
|
},
|
||||||
|
None => return Err("No calendars available".into())
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_tasks(&mut self, calendar: &CalendarId) -> Result<(), Box<dyn Error>> {
|
|
||||||
let method = Method::from_bytes(b"REPORT")
|
|
||||||
.expect("cannot create REPORT method.");
|
|
||||||
|
|
||||||
let res = reqwest::Client::new()
|
async fn get_calendar(&self, id: CalendarId) -> Option<Arc<Mutex<RemoteCalendar>>> {
|
||||||
.request(method, calendar.as_str())
|
self.cached_replies.lock().unwrap()
|
||||||
.header("Depth", 1)
|
.calendars
|
||||||
.header(CONTENT_TYPE, "application/xml")
|
.as_ref()
|
||||||
.basic_auth(self.username.clone(), Some(self.password.clone()))
|
.and_then(|cals| cals.get(&id))
|
||||||
.body(TASKS_BODY)
|
.map(|cal| cal.clone())
|
||||||
.send()
|
|
||||||
.await?;
|
|
||||||
let text = res.text().await?;
|
|
||||||
|
|
||||||
let el: Element = text.parse().unwrap();
|
|
||||||
let responses = find_elems(&el, "response");
|
|
||||||
|
|
||||||
for _response in responses {
|
|
||||||
println!("(a response)\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,16 +63,19 @@ where
|
||||||
pub async fn sync(&mut self) -> Result<(), Box<dyn Error>> {
|
pub async fn sync(&mut self) -> Result<(), Box<dyn Error>> {
|
||||||
let last_sync = self.local.get_last_sync();
|
let last_sync = self.local.get_last_sync();
|
||||||
log::info!("Starting a sync. Last sync was at {:?}", last_sync);
|
log::info!("Starting a sync. Last sync was at {:?}", last_sync);
|
||||||
let cals_server = self.server.get_calendars_mut().await?;
|
let cals_server = self.server.get_calendars().await?;
|
||||||
|
|
||||||
for (id, cal_server) in cals_server {
|
for (id, mut cal_server) in cals_server {
|
||||||
let cal_local = match self.local.get_calendar_mut(id).await {
|
let mut cal_server = cal_server.lock().unwrap();
|
||||||
|
|
||||||
|
let cal_local = match self.local.get_calendar(id).await {
|
||||||
None => {
|
None => {
|
||||||
log::error!("TODO: implement here");
|
log::error!("TODO: implement here");
|
||||||
continue;
|
continue;
|
||||||
},
|
},
|
||||||
Some(cal) => cal,
|
Some(cal) => cal,
|
||||||
};
|
};
|
||||||
|
let mut cal_local = cal_local.lock().unwrap();
|
||||||
|
|
||||||
// Pull remote changes from the server
|
// Pull remote changes from the server
|
||||||
let mut tasks_id_to_remove_from_local = match last_sync {
|
let mut tasks_id_to_remove_from_local = match last_sync {
|
||||||
|
@ -93,7 +96,7 @@ where
|
||||||
tasks_to_add_to_local.push((*new_item).clone());
|
tasks_to_add_to_local.push((*new_item).clone());
|
||||||
}
|
}
|
||||||
// Even in case of conflicts, "the server always wins", so it is safe to remove tasks from the local cache as soon as now
|
// Even in case of conflicts, "the server always wins", so it is safe to remove tasks from the local cache as soon as now
|
||||||
remove_from_calendar(&tasks_id_to_remove_from_local, cal_local);
|
remove_from_calendar(&tasks_id_to_remove_from_local, &mut *cal_local);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -121,9 +124,9 @@ where
|
||||||
tasks_to_add_to_server.push((*new_item).clone());
|
tasks_to_add_to_server.push((*new_item).clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
remove_from_calendar(&tasks_id_to_remove_from_server, cal_server);
|
remove_from_calendar(&tasks_id_to_remove_from_server, &mut *cal_server);
|
||||||
move_to_calendar(&mut tasks_to_add_to_local, cal_local);
|
move_to_calendar(&mut tasks_to_add_to_local, &mut *cal_local);
|
||||||
move_to_calendar(&mut tasks_to_add_to_server, cal_server);
|
move_to_calendar(&mut tasks_to_add_to_server, &mut *cal_server);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.local.update_last_sync(None);
|
self.local.update_last_sync(None);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
|
@ -12,16 +13,9 @@ use crate::calendar::CalendarId;
|
||||||
pub trait CalDavSource<T: PartialCalendar> {
|
pub trait CalDavSource<T: PartialCalendar> {
|
||||||
/// Returns the current calendars that this source contains
|
/// Returns the current calendars that this source contains
|
||||||
/// This function may trigger an update (that can be a long process, or that can even fail, e.g. in case of a remote server)
|
/// This function may trigger an update (that can be a long process, or that can even fail, e.g. in case of a remote server)
|
||||||
async fn get_calendars(&self) -> Result<&HashMap<CalendarId, T>, Box<dyn Error>>;
|
async fn get_calendars(&self) -> Result<HashMap<CalendarId, Arc<Mutex<T>>>, Box<dyn Error>>;
|
||||||
/// Returns the current calendars that this source contains
|
|
||||||
/// This function may trigger an update (that can be a long process, or that can even fail, e.g. in case of a remote server)
|
|
||||||
async fn get_calendars_mut(&mut self) -> Result<HashMap<CalendarId, &mut T>, Box<dyn Error>>;
|
|
||||||
|
|
||||||
/// Returns the calendar matching the ID
|
/// Returns the calendar matching the ID
|
||||||
async fn get_calendar(&self, id: CalendarId) -> Option<&T>;
|
async fn get_calendar(&self, id: CalendarId) -> Option<Arc<Mutex<T>>>;
|
||||||
/// Returns the calendar matching the ID
|
|
||||||
async fn get_calendar_mut(&mut self, id: CalendarId) -> Option<&mut T>;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait SyncSlave {
|
pub trait SyncSlave {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
///! Some utility functions
|
///! Some utility functions
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use minidom::Element;
|
use minidom::Element;
|
||||||
|
|
||||||
|
@ -56,10 +57,13 @@ pub fn print_xml(element: &Element) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A debug utility that pretty-prints calendars
|
/// A debug utility that pretty-prints calendars
|
||||||
pub fn print_calendar_list<C: CompleteCalendar>(cals: &HashMap<CalendarId, C>) {
|
pub fn print_calendar_list<C>(cals: &HashMap<CalendarId, Arc<Mutex<C>>>)
|
||||||
|
where
|
||||||
|
C: CompleteCalendar,
|
||||||
|
{
|
||||||
for (id, cal) in cals {
|
for (id, cal) in cals {
|
||||||
println!("CAL {}", id);
|
println!("CAL {}", id);
|
||||||
for (_, item) in cal.get_items() {
|
for (_, item) in cal.lock().unwrap().get_items() {
|
||||||
let task = item.unwrap_task();
|
let task = item.unwrap_task();
|
||||||
let completion = if task.completed() {"✓"} else {" "};
|
let completion = if task.completed() {"✓"} else {" "};
|
||||||
println!(" {} {}\t{}", completion, task.name(), task.id());
|
println!(" {} {}\t{}", completion, task.name(), task.id());
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use chrono::{Utc, TimeZone};
|
use chrono::{Utc, TimeZone};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
@ -20,10 +21,11 @@ async fn test_regular_sync() {
|
||||||
let mut provider = populate_test_provider().await;
|
let mut provider = populate_test_provider().await;
|
||||||
provider.sync().await.unwrap();
|
provider.sync().await.unwrap();
|
||||||
|
|
||||||
|
|
||||||
let cals_server = provider.server().get_calendars().await.unwrap();
|
let cals_server = provider.server().get_calendars().await.unwrap();
|
||||||
|
my_tasks::utils::print_calendar_list(&cals_server);
|
||||||
let cals_local = provider.local().get_calendars().await.unwrap();
|
let cals_local = provider.local().get_calendars().await.unwrap();
|
||||||
my_tasks::utils::print_calendar_list(cals_local);
|
my_tasks::utils::print_calendar_list(&cals_local);
|
||||||
my_tasks::utils::print_calendar_list(cals_server);
|
|
||||||
|
|
||||||
assert!(provider.server().has_same_contents_than(provider.local()).await.unwrap());
|
assert!(provider.server().has_same_contents_than(provider.local()).await.unwrap());
|
||||||
|
|
||||||
|
@ -94,12 +96,13 @@ async fn populate_test_provider() -> Provider<Cache, CachedCalendar, Cache, Cach
|
||||||
calendar.add_item(task_l);
|
calendar.add_item(task_l);
|
||||||
calendar.add_item(task_m);
|
calendar.add_item(task_m);
|
||||||
|
|
||||||
server.add_calendar(calendar.clone());
|
server.add_calendar(Arc::new(Mutex::new(calendar.clone())));
|
||||||
local.add_calendar(calendar.clone());
|
local.add_calendar(Arc::new(Mutex::new(calendar.clone())));
|
||||||
|
|
||||||
// Step 2
|
// Step 2
|
||||||
// Edit the server calendar
|
// Edit the server calendar
|
||||||
let cal_server = server.get_calendar_mut(cal_id.clone()).await.unwrap();
|
let cal_server = server.get_calendar(cal_id.clone()).await.unwrap();
|
||||||
|
let mut cal_server = cal_server.lock().unwrap();
|
||||||
|
|
||||||
cal_server.delete_item(&task_b_id);
|
cal_server.delete_item(&task_b_id);
|
||||||
|
|
||||||
|
@ -126,7 +129,8 @@ async fn populate_test_provider() -> Provider<Cache, CachedCalendar, Cache, Cach
|
||||||
|
|
||||||
// Step 3
|
// Step 3
|
||||||
// Edit the local calendar
|
// Edit the local calendar
|
||||||
let cal_local = local.get_calendar_mut(cal_id).await.unwrap();
|
let cal_local = local.get_calendar(cal_id).await.unwrap();
|
||||||
|
let mut cal_local = cal_local.lock().unwrap();
|
||||||
|
|
||||||
cal_local.delete_item(&task_c_id);
|
cal_local.delete_item(&task_c_id);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue