Skip to main content

coven_database/
write_failure.rs

1//! Preserve the facade's callback failure when a transaction or cleanup also fails.
2//! A committed result never becomes a failure because of cleanup (ยง2.1, Appendix C.3).
3
4use crate::DbError;
5
6/// A caller's write error can retain both its original cause and failures that
7/// happen while rolling back or removing bytes, and says which bug it carries,
8/// for a write whose caller stopped waiting (E15). A write fails with its
9/// closure's own error type, which implements this: `DbError` for ordinary
10/// database clients, sync's error for sync's writes, and an app's own type
11/// for the app's writes through the facade (E3).
12pub trait WriteFailure:
13    From<DbError> + From<rusqlite::Error> + coven_foundation::task::CarriesBug
14{
15    /// Retain a failed rollback alongside the original failure.
16    fn with_rollback(self, rollback: rusqlite::Error) -> Self;
17    /// Retain the failed write's error and every failed byte-removal operation.
18    fn with_cleanup(write: Self, failures: Vec<DbError>) -> Self
19    where
20        Self: Sized;
21}
22
23/// The outcome of an operation whose released bytes were then removed. A
24/// failed removal stays recorded for the next write or open (Appendix C.3): it
25/// joins a failed operation's error, and leaves a committed result as it is.
26pub(crate) fn after_cleanup<R, E: WriteFailure>(
27    result: Result<R, E>,
28    failures: Vec<DbError>,
29) -> Result<R, E> {
30    if failures.is_empty() {
31        return result;
32    }
33    match result {
34        Ok(result) => {
35            tracing::error!(
36                ?failures,
37                "removing released file bytes failed; their records stay for the next write or open"
38            );
39            Ok(result)
40        }
41        Err(error) => Err(E::with_cleanup(error, failures)),
42    }
43}
44
45impl WriteFailure for DbError {
46    fn with_rollback(self, rollback: rusqlite::Error) -> Self {
47        Self::Rollback { operation: Box::new(self), rollback }
48    }
49    fn with_cleanup(write: Self, failures: Vec<DbError>) -> Self {
50        Self::FileCleanup { write: Err(Box::new(write)), failures }
51    }
52}