1use std::fmt;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum StorageOp {
6 Open,
7 Read,
8 Write,
9 Delete,
10 Search,
11 Migrate,
12 Snapshot,
13}
14
15impl fmt::Display for StorageOp {
16 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17 match self {
18 StorageOp::Open => write!(f, "open"),
19 StorageOp::Read => write!(f, "read"),
20 StorageOp::Write => write!(f, "write"),
21 StorageOp::Delete => write!(f, "delete"),
22 StorageOp::Search => write!(f, "search"),
23 StorageOp::Migrate => write!(f, "migrate"),
24 StorageOp::Snapshot => write!(f, "snapshot"),
25 }
26 }
27}
28
29#[derive(Debug)]
31pub enum StorageError {
32 Io {
34 op: StorageOp,
35 detail: String,
36 source: Option<Box<dyn std::error::Error + Send + Sync>>,
37 },
38 Sqlite {
40 op: StorageOp,
41 detail: String,
42 source: Option<Box<dyn std::error::Error + Send + Sync>>,
43 },
44 NotFound {
46 op: StorageOp,
47 detail: String,
48 source: Option<Box<dyn std::error::Error + Send + Sync>>,
49 },
50 AlreadyExists {
52 op: StorageOp,
53 detail: String,
54 source: Option<Box<dyn std::error::Error + Send + Sync>>,
55 },
56 Migration {
58 op: StorageOp,
59 detail: String,
60 source: Option<Box<dyn std::error::Error + Send + Sync>>,
61 },
62 InvalidData {
64 op: StorageOp,
65 detail: String,
66 source: Option<Box<dyn std::error::Error + Send + Sync>>,
67 },
68 Corruption {
70 op: StorageOp,
71 detail: String,
72 source: Option<Box<dyn std::error::Error + Send + Sync>>,
73 },
74 DatabaseLocked {
77 op: StorageOp,
78 detail: String,
79 source: Option<Box<dyn std::error::Error + Send + Sync>>,
80 },
81 ForeignKeyViolation {
83 op: StorageOp,
84 detail: String,
85 source: Option<Box<dyn std::error::Error + Send + Sync>>,
86 },
87 SchemaMismatch {
89 expected: String,
90 found: String,
91 },
92}
93
94impl StorageError {
95 pub fn op(&self) -> StorageOp {
97 match self {
98 StorageError::Io { op, .. }
99 | StorageError::Sqlite { op, .. }
100 | StorageError::NotFound { op, .. }
101 | StorageError::AlreadyExists { op, .. }
102 | StorageError::Migration { op, .. }
103 | StorageError::InvalidData { op, .. }
104 | StorageError::Corruption { op, .. }
105 | StorageError::DatabaseLocked { op, .. }
106 | StorageError::ForeignKeyViolation { op, .. } => *op,
107 StorageError::SchemaMismatch { .. } => StorageOp::Migrate,
108 }
109 }
110
111 pub fn detail(&self) -> &str {
113 match self {
114 StorageError::Io { detail, .. }
115 | StorageError::Sqlite { detail, .. }
116 | StorageError::NotFound { detail, .. }
117 | StorageError::AlreadyExists { detail, .. }
118 | StorageError::Migration { detail, .. }
119 | StorageError::InvalidData { detail, .. }
120 | StorageError::Corruption { detail, .. }
121 | StorageError::DatabaseLocked { detail, .. }
122 | StorageError::ForeignKeyViolation { detail, .. } => detail,
123 StorageError::SchemaMismatch { expected, .. } => expected,
124 }
125 }
126}
127
128impl fmt::Display for StorageError {
129 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130 match self {
131 StorageError::Io { op, detail, .. } => {
132 write!(f, "storage I/O error during {op}: {detail}")
133 }
134 StorageError::Sqlite { op, detail, .. } => {
135 write!(f, "SQLite error during {op}: {detail}")
136 }
137 StorageError::NotFound { op, detail, .. } => {
138 write!(f, "not found during {op}: {detail}")
139 }
140 StorageError::AlreadyExists { op, detail, .. } => {
141 write!(f, "already exists during {op}: {detail}")
142 }
143 StorageError::Migration { op, detail, .. } => {
144 write!(f, "migration error during {op}: {detail}")
145 }
146 StorageError::InvalidData { op, detail, .. } => {
147 write!(f, "invalid data during {op}: {detail}")
148 }
149 StorageError::Corruption { op, detail, .. } => {
150 write!(f, "corruption detected during {op}: {detail}")
151 }
152 StorageError::DatabaseLocked { op, detail, .. } => {
153 write!(f, "database is locked during {op}: {detail}")
154 }
155 StorageError::ForeignKeyViolation { op, detail, .. } => {
156 write!(f, "foreign key violation during {op}: {detail}")
157 }
158 StorageError::SchemaMismatch { expected, found } => {
159 write!(f, "schema version mismatch: expected {expected}, found {found}")
160 }
161 }
162 }
163}
164
165impl std::error::Error for StorageError {
166 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
167 let src = match self {
168 StorageError::Io { source, .. }
169 | StorageError::Sqlite { source, .. }
170 | StorageError::NotFound { source, .. }
171 | StorageError::AlreadyExists { source, .. }
172 | StorageError::Migration { source, .. }
173 | StorageError::InvalidData { source, .. }
174 | StorageError::Corruption { source, .. }
175 | StorageError::DatabaseLocked { source, .. }
176 | StorageError::ForeignKeyViolation { source, .. } => source,
177 StorageError::SchemaMismatch { .. } => return None,
178 };
179 src.as_ref().map(|e| e.as_ref() as &(dyn std::error::Error + 'static))
180 }
181}
182
183impl From<std::io::Error> for StorageError {
184 fn from(err: std::io::Error) -> Self {
185 StorageError::Io {
186 op: StorageOp::Read,
187 detail: err.to_string(),
188 source: Some(Box::new(err)),
189 }
190 }
191}
192
193impl From<serde_json::Error> for StorageError {
194 fn from(err: serde_json::Error) -> Self {
195 StorageError::InvalidData {
196 op: StorageOp::Read,
197 detail: err.to_string(),
198 source: Some(Box::new(err)),
199 }
200 }
201}
202
203pub type StorageResult<T> = Result<T, StorageError>;