Skip to main content

omgbase_sync/
error.rs

1//! The crate's error type.
2
3use std::fmt;
4use std::path::PathBuf;
5
6/// Everything that can go wrong around a sync.
7#[derive(Debug)]
8pub enum Error {
9    /// The store refused (SQLite, a bad timestamp, …).
10    Store(omgbase_store::Error),
11    /// A filesystem operation failed.
12    Io {
13        what: String,
14        path: PathBuf,
15        source: std::io::Error,
16    },
17    /// A JSON column or protocol line did not parse.
18    Json(serde_json::Error),
19    /// `spec/sync` §1: no repo matched; `candidates` are the slugs that exist.
20    RepoNotFound {
21        message: String,
22        candidates: Vec<String>,
23    },
24    /// §5: the adapter could not be spawned.
25    AdapterSpawn { command: String, message: String },
26    /// §5: the adapter exited before answering.
27    AdapterExited { command: String },
28    /// §5: the handshake was missing, unparsable or not protocol 1.
29    AdapterHandshake { command: String, line: String },
30    /// §5: the adapter answered a request with `{"error": …}`.
31    AdapterError { method: String, message: String },
32    /// A method the source's capabilities do not include (`write`/`remove`
33    /// without `writeThrough`, `watch` without `watch`).
34    Unsupported(String),
35    /// §7: the writer lock stayed held past the timeout.
36    WriterLockTimeout {
37        lock_path: PathBuf,
38        holder_pid: Option<i64>,
39    },
40    /// Anything else, with a message.
41    Other(String),
42}
43
44impl fmt::Display for Error {
45    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46        match self {
47            Error::Store(e) => write!(f, "store: {e}"),
48            Error::Io { what, path, source } => write!(f, "{what} {}: {source}", path.display()),
49            Error::Json(e) => write!(f, "json: {e}"),
50            Error::RepoNotFound { message, .. } => f.write_str(message),
51            Error::AdapterSpawn { command, message } => {
52                write!(f, "sync adapter '{command}' failed to spawn: {message}")
53            }
54            Error::AdapterExited { command } => {
55                write!(f, "sync adapter '{command}' exited early")
56            }
57            Error::AdapterHandshake { command, line } => {
58                let shown: String = line.chars().take(120).collect();
59                write!(
60                    f,
61                    "sync adapter '{command}' sent an invalid handshake: {shown}"
62                )
63            }
64            Error::AdapterError { method, message } => {
65                write!(f, "sync adapter error ({method}): {message}")
66            }
67            Error::Unsupported(what) => write!(f, "the source does not support {what}"),
68            Error::WriterLockTimeout {
69                lock_path,
70                holder_pid,
71            } => write!(
72                f,
73                "could not acquire writer lock {} (held by pid {})",
74                lock_path.display(),
75                holder_pid.map_or_else(|| "?".to_owned(), |p| p.to_string())
76            ),
77            Error::Other(msg) => f.write_str(msg),
78        }
79    }
80}
81
82impl std::error::Error for Error {
83    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
84        match self {
85            Error::Store(e) => Some(e),
86            Error::Io { source, .. } => Some(source),
87            Error::Json(e) => Some(e),
88            _ => None,
89        }
90    }
91}
92
93impl From<omgbase_store::Error> for Error {
94    fn from(e: omgbase_store::Error) -> Self {
95        Error::Store(e)
96    }
97}
98
99impl From<rusqlite::Error> for Error {
100    fn from(e: rusqlite::Error) -> Self {
101        Error::Store(omgbase_store::Error::Sqlite(e))
102    }
103}
104
105impl From<serde_json::Error> for Error {
106    fn from(e: serde_json::Error) -> Self {
107        Error::Json(e)
108    }
109}
110
111impl Error {
112    /// An [`Error::Io`] for `what` at `path`.
113    pub(crate) fn io(what: &str, path: impl Into<PathBuf>, source: std::io::Error) -> Self {
114        Error::Io {
115            what: what.to_owned(),
116            path: path.into(),
117            source,
118        }
119    }
120}
121
122/// `Result` with this crate's [`Error`].
123pub type Result<T> = std::result::Result<T, Error>;