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}