Feedback infrastructure

This commit is contained in:
daladim 2021-10-08 23:58:17 +02:00
parent 04c8b3a2ee
commit 7fb98a471b
2 changed files with 77 additions and 10 deletions

View file

@ -14,6 +14,7 @@ use crate::calendar::CalendarId;
mod sync_progress;
use sync_progress::SyncProgress;
pub use sync_progress::FeedbackSender;
/// A data source that combines two `CalDavSource`s, which is able to sync both sources.
///
@ -64,22 +65,34 @@ where
/// To be sure `local` accurately mirrors the `remote` source, you can run [`Provider::sync`]
pub fn remote(&self) -> &R { &self.remote }
/// Performs a synchronisation between `local` and `remote`.
/// Performs a synchronisation between `local` and `remote`, and provide feeedback to the user about the progress.
///
/// This bidirectional sync applies additions/deletions made on a source to the other source.
/// In case of conflicts (the same item has been modified on both ends since the last sync, `remote` always wins)
///
/// 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.
pub async fn sync(&mut self) -> bool {
let mut result = SyncProgress::new();
if let Err(err) = self.run_sync(&mut result).await {
result.error(&format!("Sync terminated because of an error: {}", err));
}
result.is_success()
pub async fn sync_with_feedback(&mut self, feedback_sender: FeedbackSender) -> bool {
let mut progress = SyncProgress::new_with_feedback_channel(feedback_sender);
self.run_sync(&mut progress).await
}
async fn run_sync(&mut self, progress: &mut SyncProgress) -> Result<(), Box<dyn Error>> {
/// Performs a synchronisation between `local` and `remote`, without giving any feedback.
///
/// See [sync_with_feedback]
pub async fn sync(&mut self) -> bool {
let mut progress = SyncProgress::new();
self.run_sync(&mut progress).await
}
async fn run_sync(&mut self, progress: &mut SyncProgress) -> bool {
if let Err(err) = self.run_sync_inner(progress).await {
progress.error(&format!("Sync terminated because of an error: {}", err));
}
progress.is_success()
}
async fn run_sync_inner(&mut self, progress: &mut SyncProgress) -> Result<(), Box<dyn Error>> {
progress.info("Starting a sync.");
let mut handled_calendars = HashSet::new();

View file

@ -1,11 +1,65 @@
/// A counter of errors that happen during a sync
//! Utilities to track the progression of a sync
use std::fmt::{Display, Error, Formatter};
/// An event that happens during a sync
pub enum SyncEvent {
/// Sync has not started
NotStarted,
/// Sync has just started but no calendar is handled yet
Started,
/// Sync is in progress.
InProgress{ calendar: String, details: String},
/// Sync is finished
Finished{ success: bool },
}
impl Display for SyncEvent {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
match self {
SyncEvent::NotStarted => write!(f, "Not started"),
SyncEvent::Started => write!(f, "Sync has started"),
SyncEvent::InProgress{calendar, details} => write!(f, "[{}] {}", calendar, details),
SyncEvent::Finished{success} => match success {
true => write!(f, "Sync successfully finished"),
false => write!(f, "Sync finished with errors"),
}
}
}
}
impl Default for SyncEvent {
fn default() -> Self {
Self::NotStarted
}
}
pub type FeedbackSender = tokio::sync::watch::Sender<SyncEvent>;
pub type FeedbackReceiver = tokio::sync::watch::Receiver<SyncEvent>;
pub fn feedback_channel() -> (FeedbackSender, FeedbackReceiver) {
tokio::sync::watch::channel(SyncEvent::default())
}
/// A structure that tracks the progression and the errors that happen during a sync
pub struct SyncProgress {
n_errors: u32,
feedback_channel: Option<FeedbackSender>
}
impl SyncProgress {
pub fn new() -> Self {
Self { n_errors: 0 }
Self { n_errors: 0, feedback_channel: None }
}
pub fn new_with_feedback_channel(channel: FeedbackSender) -> Self {
Self { n_errors: 0, feedback_channel: Some(channel) }
}
pub fn is_success(&self) -> bool {
self.n_errors == 0
}