Skip to main content

graph_r/
error.rs

1// Author: spicyzhug@gmail.com
2// Copyright: lituus-io, all rights reserved
3//
4//! Error taxonomy. Decoders return [`Error::Format`] (or [`Error::Version`]
5//! for a well-formed file of another format version) rather than panicking on
6//! any malformed input — the property the fuzz targets enforce — and every
7//! concurrency- or lock-related refusal has its own variant so callers can
8//! distinguish "retry later" from "corrupt file".
9
10/// Crate-wide result alias.
11pub type Result<T, E = Error> = std::result::Result<T, E>;
12
13/// All failure modes surfaced by graph-r.
14#[derive(Debug)]
15#[non_exhaustive]
16pub enum Error {
17    /// A file or byte stream failed structural validation.
18    Format {
19        /// What was malformed and where.
20        message: String,
21    },
22
23    /// Another writer held this store's commit lock for longer than
24    /// the store's `Config::lock_wait` (in this process or another).
25    Locked,
26
27    /// A bounded wait for internal capacity expired (e.g. every generation
28    /// slot is pinned by long-lived snapshots during compaction).
29    Busy {
30        /// Which capacity was exhausted.
31        message: String,
32    },
33
34    /// The on-disk state is inconsistent in a way replay cannot repair.
35    Corrupt {
36        /// What invariant was violated.
37        message: String,
38    },
39
40    /// A mutating operation was attempted on a read-only store handle.
41    ReadOnly,
42
43    /// An underlying I/O failure.
44    Io {
45        /// The originating I/O error.
46        source: std::io::Error,
47    },
48
49    /// A file written in a format version this build does not read (an
50    /// older store to rebuild, or a newer one from a later release). Its own
51    /// variant so a caller can tell "rebuild this" from "this is damaged"
52    /// without reading the message.
53    Version {
54        /// The version the file declares.
55        found: u16,
56        /// The one version this build reads and writes.
57        supported: u16,
58    },
59}
60
61impl std::fmt::Display for Error {
62    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63        match self {
64            Self::Format { message } => write!(f, "format error: {message}"),
65            // Keeps the `format error: unsupported version N` prefix earlier
66            // releases printed, for callers that still match on the text.
67            Self::Version { found, supported } => {
68                write!(
69                    f,
70                    "format error: unsupported version {found} (this build reads {supported})"
71                )
72            }
73            Self::Locked => f.write_str("store is locked by another writer"),
74            Self::Busy { message } => write!(f, "store is busy: {message}"),
75            Self::Corrupt { message } => write!(f, "store is corrupt: {message}"),
76            Self::ReadOnly => f.write_str("store was opened read-only"),
77            Self::Io { source } => write!(f, "io error: {source}"),
78        }
79    }
80}
81
82impl std::error::Error for Error {
83    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
84        match self {
85            Self::Io { source } => Some(source),
86            _ => None,
87        }
88    }
89}
90
91impl From<std::io::Error> for Error {
92    fn from(source: std::io::Error) -> Self {
93        Self::Io { source }
94    }
95}
96
97impl Error {
98    /// A structural-validation failure.
99    #[must_use]
100    pub fn format(message: impl Into<String>) -> Self {
101        Self::Format { message: message.into() }
102    }
103
104    /// A capacity-exhaustion refusal.
105    #[must_use]
106    pub fn busy(message: impl Into<String>) -> Self {
107        Self::Busy { message: message.into() }
108    }
109
110    /// An unrepairable inconsistency.
111    #[must_use]
112    pub fn corrupt(message: impl Into<String>) -> Self {
113        Self::Corrupt { message: message.into() }
114    }
115}