1use std::fmt;
2
3pub type Result<T> = std::result::Result<T, Error>;
4
5#[derive(Debug)]
6pub enum Error {
7 Io(std::io::Error),
8 Corrupt {
10 segment: String,
11 offset: u64,
12 reason: String,
13 },
14 Encode(String),
15 Crypto(String),
16 Schema(String),
18 NotFound(String),
19 Locked(String),
21 InvalidCursor(String),
24}
25
26impl fmt::Display for Error {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28 match self {
29 Error::Io(e) => write!(f, "io error: {e}"),
30 Error::Corrupt {
31 segment,
32 offset,
33 reason,
34 } => {
35 write!(
36 f,
37 "corrupt record in {segment} at offset {offset}: {reason}"
38 )
39 }
40 Error::Encode(m) => write!(f, "encode/decode error: {m}"),
41 Error::Crypto(m) => write!(f, "crypto error: {m}"),
42 Error::Schema(m) => write!(f, "schema error: {m}"),
43 Error::NotFound(m) => write!(f, "not found: {m}"),
44 Error::Locked(root) => write!(
45 f,
46 "store root '{root}' is locked by another process (the store is single-process)"
47 ),
48 Error::InvalidCursor(m) => write!(f, "invalid cursor: {m}"),
49 }
50 }
51}
52
53impl Error {
54 pub fn is_storage_full(&self) -> bool {
63 match self {
64 Error::Io(e) => io_is_storage_full(e),
65 _ => false,
66 }
67 }
68}
69
70pub fn io_is_storage_full(e: &std::io::Error) -> bool {
75 e.kind() == std::io::ErrorKind::StorageFull || e.raw_os_error() == Some(ENOSPC)
76}
77
78const ENOSPC: i32 = 28;
80
81impl std::error::Error for Error {}
82
83impl From<std::io::Error> for Error {
84 fn from(e: std::io::Error) -> Self {
85 Error::Io(e)
86 }
87}
88
89impl From<bincode::Error> for Error {
90 fn from(e: bincode::Error) -> Self {
91 Error::Encode(e.to_string())
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98 use std::io::ErrorKind;
99
100 #[test]
101 fn classifies_enospc_by_raw_os_error() {
102 let e = Error::Io(std::io::Error::from_raw_os_error(ENOSPC));
103 assert!(e.is_storage_full());
104 }
105
106 #[test]
107 fn classifies_enospc_by_error_kind() {
108 let e = Error::Io(std::io::Error::new(ErrorKind::StorageFull, "disk full"));
111 assert!(e.is_storage_full());
112 }
113
114 #[test]
115 fn other_errors_are_not_storage_full() {
116 for e in [
117 Error::Io(std::io::Error::from_raw_os_error(13)), Error::Io(std::io::Error::new(ErrorKind::PermissionDenied, "nope")),
119 Error::Schema("x".into()),
120 Error::NotFound("x".into()),
121 Error::Encode("x".into()),
122 ] {
123 assert!(!e.is_storage_full(), "{e} misclassified as storage-full");
124 }
125 }
126}