Skip to main content

dig_store_cache/
error.rs

1//! The cache's error taxonomy.
2//!
3//! Every fallible cache operation returns [`CacheError`]. The variants are stable, catalogued, and
4//! carry the identity/path context a caller (or an operator reading a log) needs to act — the DIG
5//! agent-friendly contract (§6.2): no stringly-typed failures, no scraping prose to learn what broke.
6
7use dig_store::CapsuleIdentity;
8use std::path::PathBuf;
9
10/// A failure of a cache operation.
11#[derive(Debug, thiserror::Error)]
12pub enum CacheError {
13    /// A filesystem operation failed. Carries the path it was acting on so the fault is locatable.
14    #[error("i/o error at {path}: {source}")]
15    Io {
16        /// The path the failing operation touched.
17        path: PathBuf,
18        /// The underlying OS error.
19        #[source]
20        source: std::io::Error,
21    },
22
23    /// A single capsule is larger than the whole cache capacity. Admitting it would require evicting
24    /// everything and STILL not fit, so it is rejected outright — nothing is evicted for a doomed put.
25    #[error("capsule {id:?} is {size} bytes, larger than the cache capacity of {capacity} bytes")]
26    EntryTooLarge {
27        /// The capsule that could never fit.
28        // Boxed to keep `CacheError` (and every `Result<_, CacheError>`) small: a bare
29        // `CapsuleIdentity` is 64 bytes, which would otherwise bloat the common `Result` on the hot
30        // admit path (clippy::result_large_err).
31        id: Box<CapsuleIdentity>,
32        /// Its size in bytes.
33        size: u64,
34        /// The cache's total capacity in bytes.
35        capacity: u64,
36    },
37
38    /// `PutOptions::check_identity` was set and the bytes' recovered `(store_id, root_hash)` did not
39    /// equal the claimed identity. The bytes are NOT admitted.
40    #[error("capsule bytes declare {recovered:?} but the caller claimed {claimed:?}")]
41    IdentityMismatch {
42        /// The identity the caller passed to `put` (boxed — see [`CacheError::EntryTooLarge`]).
43        claimed: Box<CapsuleIdentity>,
44        /// The identity recovered from the bytes.
45        recovered: Box<CapsuleIdentity>,
46    },
47
48    /// A cached `.dig` file could not be read back as a capsule (during `open` orphan recovery, or a
49    /// `check_identity` read of unparseable bytes). Disk stays authoritative — the entry is skipped.
50    #[error("cached capsule at {path} is corrupt: {reason}")]
51    CorruptEntry {
52        /// The offending file.
53        path: PathBuf,
54        /// Why it could not be read as a capsule.
55        reason: String,
56    },
57
58    /// The cache root cannot be created or written to (bad path, permissions, read-only volume).
59    #[error("cache root {root} is not writable: {source}")]
60    RootNotWritable {
61        /// The root directory the caller passed to [`Cache::open`](crate::Cache::open).
62        root: PathBuf,
63        /// The underlying OS error.
64        #[source]
65        source: std::io::Error,
66    },
67}
68
69impl CacheError {
70    /// Build an [`CacheError::Io`] bound to the path the operation touched.
71    pub(crate) fn io(path: impl Into<PathBuf>, source: std::io::Error) -> Self {
72        Self::Io {
73            path: path.into(),
74            source,
75        }
76    }
77}