diff --git a/cli/src/invocation/cmd/sync.rs b/cli/src/invocation/cmd/sync.rs index a042a5bf4..7a3708421 100644 --- a/cli/src/invocation/cmd/sync.rs +++ b/cli/src/invocation/cmd/sync.rs @@ -1,5 +1,5 @@ use crate::settings::Settings; -use taskchampion::{server::Server, Replica}; +use taskchampion::{server::Server, Error as TCError, Replica}; use termcolor::WriteColor; pub(crate) fn execute( @@ -8,9 +8,32 @@ pub(crate) fn execute( settings: &Settings, server: &mut Box, ) -> Result<(), crate::Error> { - replica.sync(server, settings.avoid_snapshots)?; - writeln!(w, "sync complete.")?; - Ok(()) + match replica.sync(server, settings.avoid_snapshots) { + Ok(()) => { + writeln!(w, "sync complete.")?; + Ok(()) + } + Err(e) => match e.downcast() { + Ok(TCError::OutOfSync) => { + writeln!(w, "This replica cannot be synchronized with the server.")?; + writeln!( + w, + "It may be too old, or some other failure may have occurred." + )?; + writeln!( + w, + "To start fresh, remove the local task database and run `ta sync` again." + )?; + writeln!( + w, + "Note that doing so will lose any un-synchronized local changes." + )?; + Ok(()) + } + Ok(e) => Err(e.into()), + Err(e) => Err(e.into()), + }, + } } #[cfg(test)] diff --git a/taskchampion/src/errors.rs b/taskchampion/src/errors.rs index 44bad9881..3209b6ea9 100644 --- a/taskchampion/src/errors.rs +++ b/taskchampion/src/errors.rs @@ -4,6 +4,12 @@ use thiserror::Error; #[non_exhaustive] /// Errors returned from taskchampion operations pub enum Error { + /// A task-database-related error #[error("Task Database Error: {0}")] Database(String), + /// An error specifically indicating that the local replica cannot + /// be synchronized with the sever, due to being out of date or some + /// other irrecoverable error. + #[error("Local replica is out of sync with the server")] + OutOfSync, } diff --git a/taskchampion/src/replica.rs b/taskchampion/src/replica.rs index cc6338ceb..7afbc90a3 100644 --- a/taskchampion/src/replica.rs +++ b/taskchampion/src/replica.rs @@ -140,7 +140,7 @@ impl Replica { ) -> anyhow::Result<()> { self.taskdb .sync(server, avoid_snapshots) - .context("Failed to synchronize")?; + .context("Failed to synchronize with server")?; self.rebuild_working_set(false) .context("Failed to rebuild working set after sync")?; Ok(()) diff --git a/taskchampion/src/taskdb/sync.rs b/taskchampion/src/taskdb/sync.rs index e77a5db66..7ce1d76ce 100644 --- a/taskchampion/src/taskdb/sync.rs +++ b/taskchampion/src/taskdb/sync.rs @@ -1,6 +1,7 @@ use super::{ops, snapshot}; use crate::server::{AddVersionResult, GetVersionResult, Server, SnapshotUrgency}; use crate::storage::{Operation, StorageTxn}; +use crate::Error; use log::{info, trace, warn}; use serde::{Deserialize, Serialize}; use std::str; @@ -97,7 +98,7 @@ pub(super) fn sync( ); if let Some(requested) = requested_parent_version_id { if parent_version_id == requested { - anyhow::bail!("Server's task history has diverged from this replica"); + return Err(Error::OutOfSync.into()); } } requested_parent_version_id = Some(parent_version_id);