Skip to main content

graph_r/
error.rs

1// Author: support@lituus.io
2// Copyright: lituus-io, all rights reserved
3//
4//! Error taxonomy. Decoders return [`Error::Format`] rather than panicking on
5//! any malformed input — the property the fuzz targets enforce — and every
6//! concurrency- or lock-related refusal has its own variant so callers can
7//! distinguish "retry later" from "corrupt file".
8
9/// Crate-wide result alias.
10pub type Result<T, E = Error> = std::result::Result<T, E>;
11
12/// All failure modes surfaced by graph-r.
13#[derive(Debug)]
14#[non_exhaustive]
15pub enum Error {
16    /// A file or byte stream failed structural validation.
17    Format {
18        /// What was malformed and where.
19        message: String,
20    },
21
22    /// Another process holds the writer lock for this store directory.
23    Locked,
24
25    /// A bounded wait for internal capacity expired (e.g. every generation
26    /// slot is pinned by long-lived snapshots during compaction).
27    Busy {
28        /// Which capacity was exhausted.
29        message: String,
30    },
31
32    /// The on-disk state is inconsistent in a way replay cannot repair.
33    Corrupt {
34        /// What invariant was violated.
35        message: String,
36    },
37
38    /// A mutating operation was attempted on a read-only store handle.
39    ReadOnly,
40
41    /// An underlying I/O failure.
42    Io {
43        /// The originating I/O error.
44        source: std::io::Error,
45    },
46}
47
48impl std::fmt::Display for Error {
49    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50        match self {
51            Self::Format { message } => write!(f, "format error: {message}"),
52            Self::Locked => f.write_str("store is locked by another writer"),
53            Self::Busy { message } => write!(f, "store is busy: {message}"),
54            Self::Corrupt { message } => write!(f, "store is corrupt: {message}"),
55            Self::ReadOnly => f.write_str("store was opened read-only"),
56            Self::Io { source } => write!(f, "io error: {source}"),
57        }
58    }
59}
60
61impl std::error::Error for Error {
62    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
63        match self {
64            Self::Io { source } => Some(source),
65            _ => None,
66        }
67    }
68}
69
70impl From<std::io::Error> for Error {
71    fn from(source: std::io::Error) -> Self {
72        Self::Io { source }
73    }
74}
75
76impl Error {
77    /// A structural-validation failure.
78    #[must_use]
79    pub fn format(message: impl Into<String>) -> Self {
80        Self::Format { message: message.into() }
81    }
82
83    /// A capacity-exhaustion refusal.
84    #[must_use]
85    pub fn busy(message: impl Into<String>) -> Self {
86        Self::Busy { message: message.into() }
87    }
88
89    /// An unrepairable inconsistency.
90    #[must_use]
91    pub fn corrupt(message: impl Into<String>) -> Self {
92        Self::Corrupt { message: message.into() }
93    }
94}