Renamed SyncResult -> SyncProgress
This commit is contained in:
parent
f7ec5d29be
commit
04c8b3a2ee
2 changed files with 51 additions and 51 deletions
|
@ -13,7 +13,7 @@ use crate::item::SyncStatus;
|
||||||
use crate::calendar::CalendarId;
|
use crate::calendar::CalendarId;
|
||||||
|
|
||||||
mod sync_progress;
|
mod sync_progress;
|
||||||
use sync_progress::SyncResult;
|
use sync_progress::SyncProgress;
|
||||||
|
|
||||||
/// A data source that combines two `CalDavSource`s, which is able to sync both sources.
|
/// A data source that combines two `CalDavSource`s, which is able to sync both sources.
|
||||||
///
|
///
|
||||||
|
@ -72,15 +72,15 @@ where
|
||||||
/// It returns whether the sync was totally successful (details about errors are logged using the `log::*` macros).
|
/// It returns whether the sync was totally successful (details about errors are logged using the `log::*` macros).
|
||||||
/// In case errors happened, the sync might have been partially executed, and you can safely run this function again, since it has been designed to gracefully recover from errors.
|
/// In case errors happened, the sync might have been partially executed, and you can safely run this function again, since it has been designed to gracefully recover from errors.
|
||||||
pub async fn sync(&mut self) -> bool {
|
pub async fn sync(&mut self) -> bool {
|
||||||
let mut result = SyncResult::new();
|
let mut result = SyncProgress::new();
|
||||||
if let Err(err) = self.run_sync(&mut result).await {
|
if let Err(err) = self.run_sync(&mut result).await {
|
||||||
result.error(&format!("Sync terminated because of an error: {}", err));
|
result.error(&format!("Sync terminated because of an error: {}", err));
|
||||||
}
|
}
|
||||||
result.is_success()
|
result.is_success()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run_sync(&mut self, result: &mut SyncResult) -> Result<(), Box<dyn Error>> {
|
async fn run_sync(&mut self, progress: &mut SyncProgress) -> Result<(), Box<dyn Error>> {
|
||||||
result.info("Starting a sync.");
|
progress.info("Starting a sync.");
|
||||||
|
|
||||||
let mut handled_calendars = HashSet::new();
|
let mut handled_calendars = HashSet::new();
|
||||||
|
|
||||||
|
@ -89,14 +89,14 @@ where
|
||||||
for (cal_id, cal_remote) in cals_remote {
|
for (cal_id, cal_remote) in cals_remote {
|
||||||
let counterpart = match self.get_or_insert_local_counterpart_calendar(&cal_id, cal_remote.clone()).await {
|
let counterpart = match self.get_or_insert_local_counterpart_calendar(&cal_id, cal_remote.clone()).await {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
result.warn(&format!("Unable to get or insert local counterpart calendar for {} ({}). Skipping this time", cal_id, err));
|
progress.warn(&format!("Unable to get or insert local counterpart calendar for {} ({}). Skipping this time", cal_id, err));
|
||||||
continue;
|
continue;
|
||||||
},
|
},
|
||||||
Ok(arc) => arc,
|
Ok(arc) => arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(err) = Self::sync_calendar_pair(counterpart, cal_remote, result).await {
|
if let Err(err) = Self::sync_calendar_pair(counterpart, cal_remote, progress).await {
|
||||||
result.warn(&format!("Unable to sync calendar {}: {}, skipping this time.", cal_id, err));
|
progress.warn(&format!("Unable to sync calendar {}: {}, skipping this time.", cal_id, err));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
handled_calendars.insert(cal_id);
|
handled_calendars.insert(cal_id);
|
||||||
|
@ -111,14 +111,14 @@ where
|
||||||
|
|
||||||
let counterpart = match self.get_or_insert_remote_counterpart_calendar(&cal_id, cal_local.clone()).await {
|
let counterpart = match self.get_or_insert_remote_counterpart_calendar(&cal_id, cal_local.clone()).await {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
result.warn(&format!("Unable to get or insert remote counterpart calendar for {} ({}). Skipping this time", cal_id, err));
|
progress.warn(&format!("Unable to get or insert remote counterpart calendar for {} ({}). Skipping this time", cal_id, err));
|
||||||
continue;
|
continue;
|
||||||
},
|
},
|
||||||
Ok(arc) => arc,
|
Ok(arc) => arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(err) = Self::sync_calendar_pair(cal_local, counterpart, result).await {
|
if let Err(err) = Self::sync_calendar_pair(cal_local, counterpart, progress).await {
|
||||||
result.warn(&format!("Unable to sync calendar {}: {}, skipping this time.", cal_id, err));
|
progress.warn(&format!("Unable to sync calendar {}: {}, skipping this time.", cal_id, err));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -135,12 +135,12 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async fn sync_calendar_pair(cal_local: Arc<Mutex<T>>, cal_remote: Arc<Mutex<U>>, result: &mut SyncResult) -> Result<(), Box<dyn Error>> {
|
async fn sync_calendar_pair(cal_local: Arc<Mutex<T>>, cal_remote: Arc<Mutex<U>>, progress: &mut SyncProgress) -> Result<(), Box<dyn Error>> {
|
||||||
let mut cal_remote = cal_remote.lock().unwrap();
|
let mut cal_remote = cal_remote.lock().unwrap();
|
||||||
let mut cal_local = cal_local.lock().unwrap();
|
let mut cal_local = cal_local.lock().unwrap();
|
||||||
|
|
||||||
// Step 1 - find the differences
|
// Step 1 - find the differences
|
||||||
result.debug("Finding the differences to sync...");
|
progress.debug("Finding the differences to sync...");
|
||||||
let mut local_del = HashSet::new();
|
let mut local_del = HashSet::new();
|
||||||
let mut remote_del = HashSet::new();
|
let mut remote_del = HashSet::new();
|
||||||
let mut local_changes = HashSet::new();
|
let mut local_changes = HashSet::new();
|
||||||
|
@ -151,49 +151,49 @@ where
|
||||||
let remote_items = cal_remote.get_item_version_tags().await?;
|
let remote_items = cal_remote.get_item_version_tags().await?;
|
||||||
let mut local_items_to_handle = cal_local.get_item_ids().await?;
|
let mut local_items_to_handle = cal_local.get_item_ids().await?;
|
||||||
for (id, remote_tag) in remote_items {
|
for (id, remote_tag) in remote_items {
|
||||||
result.trace(&format!("***** Considering remote item {}...", id));
|
progress.trace(&format!("***** Considering remote item {}...", id));
|
||||||
match cal_local.get_item_by_id(&id).await {
|
match cal_local.get_item_by_id(&id).await {
|
||||||
None => {
|
None => {
|
||||||
// This was created on the remote
|
// This was created on the remote
|
||||||
result.debug(&format!("* {} is a remote addition", id));
|
progress.debug(&format!("* {} is a remote addition", id));
|
||||||
remote_additions.insert(id);
|
remote_additions.insert(id);
|
||||||
},
|
},
|
||||||
Some(local_item) => {
|
Some(local_item) => {
|
||||||
if local_items_to_handle.remove(&id) == false {
|
if local_items_to_handle.remove(&id) == false {
|
||||||
result.error(&format!("Inconsistent state: missing task {} from the local tasks", id));
|
progress.error(&format!("Inconsistent state: missing task {} from the local tasks", id));
|
||||||
}
|
}
|
||||||
|
|
||||||
match local_item.sync_status() {
|
match local_item.sync_status() {
|
||||||
SyncStatus::NotSynced => {
|
SyncStatus::NotSynced => {
|
||||||
result.error(&format!("ID reuse between remote and local sources ({}). Ignoring this item in the sync", id));
|
progress.error(&format!("ID reuse between remote and local sources ({}). Ignoring this item in the sync", id));
|
||||||
continue;
|
continue;
|
||||||
},
|
},
|
||||||
SyncStatus::Synced(local_tag) => {
|
SyncStatus::Synced(local_tag) => {
|
||||||
if &remote_tag != local_tag {
|
if &remote_tag != local_tag {
|
||||||
// This has been modified on the remote
|
// This has been modified on the remote
|
||||||
result.debug(&format!("* {} is a remote change", id));
|
progress.debug(&format!("* {} is a remote change", id));
|
||||||
remote_changes.insert(id);
|
remote_changes.insert(id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
SyncStatus::LocallyModified(local_tag) => {
|
SyncStatus::LocallyModified(local_tag) => {
|
||||||
if &remote_tag == local_tag {
|
if &remote_tag == local_tag {
|
||||||
// This has been changed locally
|
// This has been changed locally
|
||||||
result.debug(&format!("* {} is a local change", id));
|
progress.debug(&format!("* {} is a local change", id));
|
||||||
local_changes.insert(id);
|
local_changes.insert(id);
|
||||||
} else {
|
} else {
|
||||||
result.info(&format!("Conflict: task {} has been modified in both sources. Using the remote version.", id));
|
progress.info(&format!("Conflict: task {} has been modified in both sources. Using the remote version.", id));
|
||||||
result.debug(&format!("* {} is considered a remote change", id));
|
progress.debug(&format!("* {} is considered a remote change", id));
|
||||||
remote_changes.insert(id);
|
remote_changes.insert(id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
SyncStatus::LocallyDeleted(local_tag) => {
|
SyncStatus::LocallyDeleted(local_tag) => {
|
||||||
if &remote_tag == local_tag {
|
if &remote_tag == local_tag {
|
||||||
// This has been locally deleted
|
// This has been locally deleted
|
||||||
result.debug(&format!("* {} is a local deletion", id));
|
progress.debug(&format!("* {} is a local deletion", id));
|
||||||
local_del.insert(id);
|
local_del.insert(id);
|
||||||
} else {
|
} else {
|
||||||
result.info(&format!("Conflict: task {} has been locally deleted and remotely modified. Reverting to the remote version.", id));
|
progress.info(&format!("Conflict: task {} has been locally deleted and remotely modified. Reverting to the remote version.", id));
|
||||||
result.debug(&format!("* {} is a considered a remote change", id));
|
progress.debug(&format!("* {} is a considered a remote change", id));
|
||||||
remote_changes.insert(id);
|
remote_changes.insert(id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -204,10 +204,10 @@ where
|
||||||
|
|
||||||
// Also iterate on the local tasks that are not on the remote
|
// Also iterate on the local tasks that are not on the remote
|
||||||
for id in local_items_to_handle {
|
for id in local_items_to_handle {
|
||||||
result.trace(&format!("##### Considering local item {}...", id));
|
progress.trace(&format!("##### Considering local item {}...", id));
|
||||||
let local_item = match cal_local.get_item_by_id(&id).await {
|
let local_item = match cal_local.get_item_by_id(&id).await {
|
||||||
None => {
|
None => {
|
||||||
result.error(&format!("Inconsistent state: missing task {} from the local tasks", id));
|
progress.error(&format!("Inconsistent state: missing task {} from the local tasks", id));
|
||||||
continue;
|
continue;
|
||||||
},
|
},
|
||||||
Some(item) => item,
|
Some(item) => item,
|
||||||
|
@ -216,21 +216,21 @@ where
|
||||||
match local_item.sync_status() {
|
match local_item.sync_status() {
|
||||||
SyncStatus::Synced(_) => {
|
SyncStatus::Synced(_) => {
|
||||||
// This item has been removed from the remote
|
// This item has been removed from the remote
|
||||||
result.debug(&format!("# {} is a deletion from the server", id));
|
progress.debug(&format!("# {} is a deletion from the server", id));
|
||||||
remote_del.insert(id);
|
remote_del.insert(id);
|
||||||
},
|
},
|
||||||
SyncStatus::NotSynced => {
|
SyncStatus::NotSynced => {
|
||||||
// This item has just been locally created
|
// This item has just been locally created
|
||||||
result.debug(&format!("# {} has been locally created", id));
|
progress.debug(&format!("# {} has been locally created", id));
|
||||||
local_additions.insert(id);
|
local_additions.insert(id);
|
||||||
},
|
},
|
||||||
SyncStatus::LocallyDeleted(_) => {
|
SyncStatus::LocallyDeleted(_) => {
|
||||||
// This item has been deleted from both sources
|
// This item has been deleted from both sources
|
||||||
result.debug(&format!("# {} has been deleted from both sources", id));
|
progress.debug(&format!("# {} has been deleted from both sources", id));
|
||||||
remote_del.insert(id);
|
remote_del.insert(id);
|
||||||
},
|
},
|
||||||
SyncStatus::LocallyModified(_) => {
|
SyncStatus::LocallyModified(_) => {
|
||||||
result.info(&format!("Conflict: item {} has been deleted from the server and locally modified. Deleting the local copy", id));
|
progress.info(&format!("Conflict: item {} has been deleted from the server and locally modified. Deleting the local copy", id));
|
||||||
remote_del.insert(id);
|
remote_del.insert(id);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -238,44 +238,44 @@ where
|
||||||
|
|
||||||
|
|
||||||
// Step 2 - commit changes
|
// Step 2 - commit changes
|
||||||
result.trace("Committing changes...");
|
progress.trace("Committing changes...");
|
||||||
for id_del in local_del {
|
for id_del in local_del {
|
||||||
result.debug(&format!("> Pushing local deletion {} to the server", id_del));
|
progress.debug(&format!("> Pushing local deletion {} to the server", id_del));
|
||||||
match cal_remote.delete_item(&id_del).await {
|
match cal_remote.delete_item(&id_del).await {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
result.warn(&format!("Unable to delete remote item {}: {}", id_del, err));
|
progress.warn(&format!("Unable to delete remote item {}: {}", id_del, err));
|
||||||
},
|
},
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
// Change the local copy from "marked to deletion" to "actually deleted"
|
// Change the local copy from "marked to deletion" to "actually deleted"
|
||||||
if let Err(err) = cal_local.immediately_delete_item(&id_del).await {
|
if let Err(err) = cal_local.immediately_delete_item(&id_del).await {
|
||||||
result.error(&format!("Unable to permanently delete local item {}: {}", id_del, err));
|
progress.error(&format!("Unable to permanently delete local item {}: {}", id_del, err));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for id_del in remote_del {
|
for id_del in remote_del {
|
||||||
result.debug(&format!("> Applying remote deletion {} locally", id_del));
|
progress.debug(&format!("> Applying remote deletion {} locally", id_del));
|
||||||
if let Err(err) = cal_local.immediately_delete_item(&id_del).await {
|
if let Err(err) = cal_local.immediately_delete_item(&id_del).await {
|
||||||
result.warn(&format!("Unable to delete local item {}: {}", id_del, err));
|
progress.warn(&format!("Unable to delete local item {}: {}", id_del, err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for id_add in remote_additions {
|
for id_add in remote_additions {
|
||||||
result.debug(&format!("> Applying remote addition {} locally", id_add));
|
progress.debug(&format!("> Applying remote addition {} locally", id_add));
|
||||||
match cal_remote.get_item_by_id(&id_add).await {
|
match cal_remote.get_item_by_id(&id_add).await {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
result.warn(&format!("Unable to get remote item {}: {}. Skipping it.", id_add, err));
|
progress.warn(&format!("Unable to get remote item {}: {}. Skipping it.", id_add, err));
|
||||||
continue;
|
continue;
|
||||||
},
|
},
|
||||||
Ok(item) => match item {
|
Ok(item) => match item {
|
||||||
None => {
|
None => {
|
||||||
result.error(&format!("Inconsistency: new item {} has vanished from the remote end", id_add));
|
progress.error(&format!("Inconsistency: new item {} has vanished from the remote end", id_add));
|
||||||
continue;
|
continue;
|
||||||
},
|
},
|
||||||
Some(new_item) => {
|
Some(new_item) => {
|
||||||
if let Err(err) = cal_local.add_item(new_item.clone()).await {
|
if let Err(err) = cal_local.add_item(new_item.clone()).await {
|
||||||
result.error(&format!("Not able to add item {} to local calendar: {}", id_add, err));
|
progress.error(&format!("Not able to add item {} to local calendar: {}", id_add, err));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -283,20 +283,20 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
for id_change in remote_changes {
|
for id_change in remote_changes {
|
||||||
result.debug(&format!("> Applying remote change {} locally", id_change));
|
progress.debug(&format!("> Applying remote change {} locally", id_change));
|
||||||
match cal_remote.get_item_by_id(&id_change).await {
|
match cal_remote.get_item_by_id(&id_change).await {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
result.warn(&format!("Unable to get remote item {}: {}. Skipping it", id_change, err));
|
progress.warn(&format!("Unable to get remote item {}: {}. Skipping it", id_change, err));
|
||||||
continue;
|
continue;
|
||||||
},
|
},
|
||||||
Ok(item) => match item {
|
Ok(item) => match item {
|
||||||
None => {
|
None => {
|
||||||
result.error(&format!("Inconsistency: modified item {} has vanished from the remote end", id_change));
|
progress.error(&format!("Inconsistency: modified item {} has vanished from the remote end", id_change));
|
||||||
continue;
|
continue;
|
||||||
},
|
},
|
||||||
Some(item) => {
|
Some(item) => {
|
||||||
if let Err(err) = cal_local.update_item(item.clone()).await {
|
if let Err(err) = cal_local.update_item(item.clone()).await {
|
||||||
result.error(&format!("Unable to update item {} in local calendar: {}", id_change, err));
|
progress.error(&format!("Unable to update item {} in local calendar: {}", id_change, err));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -305,15 +305,15 @@ where
|
||||||
|
|
||||||
|
|
||||||
for id_add in local_additions {
|
for id_add in local_additions {
|
||||||
result.debug(&format!("> Pushing local addition {} to the server", id_add));
|
progress.debug(&format!("> Pushing local addition {} to the server", id_add));
|
||||||
match cal_local.get_item_by_id_mut(&id_add).await {
|
match cal_local.get_item_by_id_mut(&id_add).await {
|
||||||
None => {
|
None => {
|
||||||
result.error(&format!("Inconsistency: created item {} has been marked for upload but is locally missing", id_add));
|
progress.error(&format!("Inconsistency: created item {} has been marked for upload but is locally missing", id_add));
|
||||||
continue;
|
continue;
|
||||||
},
|
},
|
||||||
Some(item) => {
|
Some(item) => {
|
||||||
match cal_remote.add_item(item.clone()).await {
|
match cal_remote.add_item(item.clone()).await {
|
||||||
Err(err) => result.error(&format!("Unable to add item {} to remote calendar: {}", id_add, err)),
|
Err(err) => progress.error(&format!("Unable to add item {} to remote calendar: {}", id_add, err)),
|
||||||
Ok(new_ss) => {
|
Ok(new_ss) => {
|
||||||
// Update local sync status
|
// Update local sync status
|
||||||
item.set_sync_status(new_ss);
|
item.set_sync_status(new_ss);
|
||||||
|
@ -324,15 +324,15 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
for id_change in local_changes {
|
for id_change in local_changes {
|
||||||
result.debug(&format!("> Pushing local change {} to the server", id_change));
|
progress.debug(&format!("> Pushing local change {} to the server", id_change));
|
||||||
match cal_local.get_item_by_id_mut(&id_change).await {
|
match cal_local.get_item_by_id_mut(&id_change).await {
|
||||||
None => {
|
None => {
|
||||||
result.error(&format!("Inconsistency: modified item {} has been marked for upload but is locally missing", id_change));
|
progress.error(&format!("Inconsistency: modified item {} has been marked for upload but is locally missing", id_change));
|
||||||
continue;
|
continue;
|
||||||
},
|
},
|
||||||
Some(item) => {
|
Some(item) => {
|
||||||
match cal_remote.update_item(item.clone()).await {
|
match cal_remote.update_item(item.clone()).await {
|
||||||
Err(err) => result.error(&format!("Unable to update item {} in remote calendar: {}", id_change, err)),
|
Err(err) => progress.error(&format!("Unable to update item {} in remote calendar: {}", id_change, err)),
|
||||||
Ok(new_ss) => {
|
Ok(new_ss) => {
|
||||||
// Update local sync status
|
// Update local sync status
|
||||||
item.set_sync_status(new_ss);
|
item.set_sync_status(new_ss);
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
/// A counter of errors that happen during a sync
|
/// A counter of errors that happen during a sync
|
||||||
pub struct SyncResult {
|
pub struct SyncProgress {
|
||||||
n_errors: u32,
|
n_errors: u32,
|
||||||
}
|
}
|
||||||
impl SyncResult {
|
impl SyncProgress {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self { n_errors: 0 }
|
Self { n_errors: 0 }
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue