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 process holds the writer lock for this store directory.
24 Locked,
25
26 /// A bounded wait for internal capacity expired (e.g. every generation
27 /// slot is pinned by long-lived snapshots during compaction).
28 Busy {
29 /// Which capacity was exhausted.
30 message: String,
31 },
32
33 /// The on-disk state is inconsistent in a way replay cannot repair.
34 Corrupt {
35 /// What invariant was violated.
36 message: String,
37 },
38
39 /// A mutating operation was attempted on a read-only store handle.
40 ReadOnly,
41
42 /// An underlying I/O failure.
43 Io {
44 /// The originating I/O error.
45 source: std::io::Error,
46 },
47
48 /// A file written in a format version this build does not read (an
49 /// older store to rebuild, or a newer one from a later release). Its own
50 /// variant so a caller can tell "rebuild this" from "this is damaged"
51 /// without reading the message.
52 Version {
53 /// The version the file declares.
54 found: u16,
55 /// The one version this build reads and writes.
56 supported: u16,
57 },
58}
59
60impl std::fmt::Display for Error {
61 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62 match self {
63 Self::Format { message } => write!(f, "format error: {message}"),
64 // Keeps the `format error: unsupported version N` prefix earlier
65 // releases printed, for callers that still match on the text.
66 Self::Version { found, supported } => {
67 write!(
68 f,
69 "format error: unsupported version {found} (this build reads {supported})"
70 )
71 }
72 Self::Locked => f.write_str("store is locked by another writer"),
73 Self::Busy { message } => write!(f, "store is busy: {message}"),
74 Self::Corrupt { message } => write!(f, "store is corrupt: {message}"),
75 Self::ReadOnly => f.write_str("store was opened read-only"),
76 Self::Io { source } => write!(f, "io error: {source}"),
77 }
78 }
79}
80
81impl std::error::Error for Error {
82 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
83 match self {
84 Self::Io { source } => Some(source),
85 _ => None,
86 }
87 }
88}
89
90impl From<std::io::Error> for Error {
91 fn from(source: std::io::Error) -> Self {
92 Self::Io { source }
93 }
94}
95
96impl Error {
97 /// A structural-validation failure.
98 #[must_use]
99 pub fn format(message: impl Into<String>) -> Self {
100 Self::Format { message: message.into() }
101 }
102
103 /// A capacity-exhaustion refusal.
104 #[must_use]
105 pub fn busy(message: impl Into<String>) -> Self {
106 Self::Busy { message: message.into() }
107 }
108
109 /// An unrepairable inconsistency.
110 #[must_use]
111 pub fn corrupt(message: impl Into<String>) -> Self {
112 Self::Corrupt { message: message.into() }
113 }
114}