1use serde::{Deserialize, Serialize};
2use std::error::Error;
3use std::fmt::{Display, Formatter};
4use std::path::PathBuf;
5
6pub type DsResult<T> = Result<T, DsError>;
7
8#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
10pub struct DsError {
11 pub kind: Box<DsErrorKind>,
12}
13
14#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
16pub enum DsErrorKind {
17 InputOutput {
19 path: Option<PathBuf>,
20 kind: String, errno: Option<i32>,
22 },
23 Storage {
24 op: String,
25 details: String,
26 },
27 PageNotFound {
28 id: u64,
29 },
30 PageFull,
31 PageSizeMismatch {
32 expected: usize,
33 actual: usize,
34 },
35 ChecksumMismatch {
36 id: u64,
37 },
38 InvalidPageType {
39 page_type: u8,
40 },
41 Corrupted {
42 reason: String,
43 },
44
45 Serialization {
47 format: String,
48 details: String,
49 },
50 Parse {
51 target: String,
52 details: String,
53 },
54 Type {
55 expected: String,
56 actual: String,
57 },
58 TypeConversion {
59 from: String,
60 to: String,
61 },
62
63 Query {
65 sql: String,
66 details: String,
67 },
68 Execution {
69 op: String,
70 details: String,
71 },
72 Schema {
73 table: Option<String>,
74 column: Option<String>,
75 details: String,
76 },
77 TableNotFound {
78 name: String,
79 },
80
81 Consensus {
83 op: String,
84 details: String,
85 },
86 Network {
87 addr: Option<String>,
88 details: String,
89 },
90 Protocol {
91 details: String,
92 },
93 Internal {
94 details: String,
95 },
96 Config {
97 key: String,
98 details: String,
99 },
100 Http {
101 code: u16,
102 url: String,
103 method: String,
104 },
105 Join {
106 details: String,
107 },
108 Cache {
109 op: String,
110 details: String,
111 },
112 Sync {
113 details: String,
114 },
115
116 Permission {
118 action: String,
119 resource: String,
120 },
121 Tenant {
122 id: String,
123 details: String,
124 },
125 Quota {
126 resource: String,
127 limit: u64,
128 usage: u64,
129 },
130}
131
132impl DsErrorKind {
133 pub fn key(&self) -> &'static str {
135 match self {
136 DsErrorKind::InputOutput { .. } => "kv.error.io",
137 DsErrorKind::Storage { .. } => "kv.error.storage",
138 DsErrorKind::PageNotFound { .. } => "kv.error.page_not_found",
139 DsErrorKind::PageFull => "kv.error.page_full",
140 DsErrorKind::PageSizeMismatch { .. } => "kv.error.page_size_mismatch",
141 DsErrorKind::ChecksumMismatch { .. } => "kv.error.checksum_mismatch",
142 DsErrorKind::InvalidPageType { .. } => "kv.error.invalid_page_type",
143 DsErrorKind::Corrupted { .. } => "kv.error.corrupted",
144 DsErrorKind::Serialization { .. } => "kv.error.serialization",
145 DsErrorKind::Parse { .. } => "kv.error.parse",
146 DsErrorKind::Type { .. } => "kv.error.type",
147 DsErrorKind::TypeConversion { .. } => "kv.error.type_conversion",
148 DsErrorKind::Query { .. } => "kv.error.query",
149 DsErrorKind::Execution { .. } => "kv.error.execution",
150 DsErrorKind::Schema { .. } => "kv.error.schema",
151 DsErrorKind::TableNotFound { .. } => "kv.error.table_not_found",
152 DsErrorKind::Consensus { .. } => "kv.error.consensus",
153 DsErrorKind::Network { .. } => "kv.error.network",
154 DsErrorKind::Protocol { .. } => "kv.error.protocol",
155 DsErrorKind::Internal { .. } => "kv.error.internal",
156 DsErrorKind::Config { .. } => "kv.error.config",
157 DsErrorKind::Http { .. } => "kv.error.http",
158 DsErrorKind::Join { .. } => "kv.error.join",
159 DsErrorKind::Cache { .. } => "kv.error.cache",
160 DsErrorKind::Sync { .. } => "kv.error.sync",
161 DsErrorKind::Permission { .. } => "kv.error.permission",
162 DsErrorKind::Tenant { .. } => "kv.error.tenant",
163 DsErrorKind::Quota { .. } => "kv.error.quota",
164 }
165 }
166}
167
168impl Display for DsError {
169 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
170 write!(f, "{}", self.kind)
171 }
172}
173
174impl Error for DsError {}
175
176impl Display for DsErrorKind {
177 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
178 match self {
179 DsErrorKind::InputOutput { path, kind, errno } => {
180 let p = path
181 .as_ref()
182 .map(|p| format!(" at {:?}", p))
183 .unwrap_or_default();
184 let e = errno.map(|n| format!(" (errno {})", n)).unwrap_or_default();
185 write!(f, "I/O error{}: {}{}", p, kind, e)
186 }
187 DsErrorKind::Storage { op, details } => {
188 write!(f, "Storage error ({}): {}", op, details)
189 }
190 DsErrorKind::PageNotFound { id } => write!(f, "Page not found: {}", id),
191 DsErrorKind::PageFull => write!(f, "Page is full"),
192 DsErrorKind::PageSizeMismatch { expected, actual } => {
193 write!(
194 f,
195 "Page size mismatch: expected {}, got {}",
196 expected, actual
197 )
198 }
199 DsErrorKind::ChecksumMismatch { id } => write!(f, "Checksum mismatch for page {}", id),
200 DsErrorKind::InvalidPageType { page_type } => {
201 write!(f, "Invalid page type: {}", page_type)
202 }
203 DsErrorKind::Corrupted { reason } => write!(f, "Corrupted: {}", reason),
204 DsErrorKind::Serialization { format, details } => {
205 write!(f, "Serialization error ({}): {}", format, details)
206 }
207 DsErrorKind::Parse { target, details } => {
208 write!(f, "Parse error for {}: {}", target, details)
209 }
210 DsErrorKind::Type { expected, actual } => {
211 write!(f, "Type error: expected {}, got {}", expected, actual)
212 }
213 DsErrorKind::TypeConversion { from, to } => {
214 write!(f, "Type conversion error: from {} to {}", from, to)
215 }
216 DsErrorKind::Query { sql, details } => write!(f, "Query error ({}): {}", sql, details),
217 DsErrorKind::Execution { op, details } => {
218 write!(f, "Execution error ({}): {}", op, details)
219 }
220 DsErrorKind::Schema {
221 table,
222 column,
223 details,
224 } => {
225 let t = table
226 .as_ref()
227 .map(|s| format!("table {}", s))
228 .unwrap_or_default();
229 let c = column
230 .as_ref()
231 .map(|s| format!("column {}", s))
232 .unwrap_or_default();
233 let sep = if !t.is_empty() && !c.is_empty() {
234 ", "
235 } else {
236 ""
237 };
238 write!(f, "Schema error ({}{}{}): {}", t, sep, c, details)
239 }
240 DsErrorKind::TableNotFound { name } => write!(f, "Table not found: {}", name),
241 DsErrorKind::Consensus { op, details } => {
242 write!(f, "Consensus error ({}): {}", op, details)
243 }
244 DsErrorKind::Network { addr, details } => {
245 let a = addr
246 .as_ref()
247 .map(|s| format!(" ({})", s))
248 .unwrap_or_default();
249 write!(f, "Network error{}: {}", a, details)
250 }
251 DsErrorKind::Protocol { details } => write!(f, "Protocol error: {}", details),
252 DsErrorKind::Internal { details } => write!(f, "Internal error: {}", details),
253 DsErrorKind::Config { key, details } => {
254 write!(f, "Configuration error for {}: {}", key, details)
255 }
256 DsErrorKind::Http { code, url, method } => {
257 write!(f, "HTTP {} {} {}", code, method, url)
258 }
259 DsErrorKind::Join { details } => write!(f, "Join error: {}", details),
260 DsErrorKind::Cache { op, details } => write!(f, "Cache error ({}): {}", op, details),
261 DsErrorKind::Sync { details } => write!(f, "Sync error: {}", details),
262 DsErrorKind::Permission { action, resource } => {
263 write!(f, "Permission denied for {} on {}", action, resource)
264 }
265 DsErrorKind::Tenant { id, details } => write!(f, "Tenant error ({}): {}", id, details),
266 DsErrorKind::Quota {
267 resource,
268 limit,
269 usage,
270 } => {
271 write!(
272 f,
273 "Quota exceeded for {}: limit {}, usage {}",
274 resource, limit, usage
275 )
276 }
277 }
278 }
279}
280
281impl From<DsErrorKind> for DsError {
282 fn from(kind: DsErrorKind) -> Self {
283 Self::new(kind)
284 }
285}
286
287impl From<std::io::Error> for DsError {
288 fn from(e: std::io::Error) -> Self {
289 DsErrorKind::InputOutput {
290 path: None,
291 kind: format!("{:?}", e.kind()),
292 errno: e.raw_os_error(),
293 }
294 .into()
295 }
296}
297
298impl From<std::num::ParseIntError> for DsError {
299 fn from(e: std::num::ParseIntError) -> Self {
300 DsErrorKind::Parse {
301 target: "int".into(),
302 details: e.to_string(),
303 }
304 .into()
305 }
306}
307
308impl From<std::num::ParseFloatError> for DsError {
309 fn from(e: std::num::ParseFloatError) -> Self {
310 DsErrorKind::Parse {
311 target: "float".into(),
312 details: e.to_string(),
313 }
314 .into()
315 }
316}
317
318impl From<std::string::FromUtf8Error> for DsError {
319 fn from(e: std::string::FromUtf8Error) -> Self {
320 DsErrorKind::Serialization {
321 format: "utf8".into(),
322 details: e.to_string(),
323 }
324 .into()
325 }
326}
327
328impl From<std::str::Utf8Error> for DsError {
329 fn from(e: std::str::Utf8Error) -> Self {
330 DsErrorKind::Serialization {
331 format: "utf8".into(),
332 details: e.to_string(),
333 }
334 .into()
335 }
336}
337
338impl From<std::time::SystemTimeError> for DsError {
339 fn from(e: std::time::SystemTimeError) -> Self {
340 DsErrorKind::Internal {
341 details: e.to_string(),
342 }
343 .into()
344 }
345}
346
347impl From<String> for DsError {
348 fn from(s: String) -> Self {
349 DsErrorKind::Internal { details: s }.into()
350 }
351}
352
353impl From<&str> for DsError {
354 fn from(s: &str) -> Self {
355 DsErrorKind::Internal {
356 details: s.to_string(),
357 }
358 .into()
359 }
360}
361
362impl DsError {
363 pub fn new(kind: DsErrorKind) -> Self {
364 Self {
365 kind: Box::new(kind),
366 }
367 }
368
369 pub fn io_raw(err: std::io::Error, path: Option<PathBuf>) -> Self {
370 Self::new(DsErrorKind::InputOutput {
371 path,
372 kind: format!("{:?}", err.kind()),
373 errno: err.raw_os_error(),
374 })
375 }
376
377 pub fn io(details: impl Into<String>) -> Self {
378 Self::io_str(details)
379 }
380
381 pub fn io_str(details: impl Into<String>) -> Self {
382 Self::new(DsErrorKind::InputOutput {
383 path: None,
384 kind: details.into(),
385 errno: None,
386 })
387 }
388
389 pub fn storage(details: impl Into<String>) -> Self {
390 Self::new(DsErrorKind::Storage {
391 op: "".into(),
392 details: details.into(),
393 })
394 }
395
396 pub fn storage_with_op(op: impl Into<String>, details: impl Into<String>) -> Self {
397 Self::new(DsErrorKind::Storage {
398 op: op.into(),
399 details: details.into(),
400 })
401 }
402
403 pub fn corrupted(reason: impl Into<String>) -> Self {
404 Self::new(DsErrorKind::Corrupted {
405 reason: reason.into(),
406 })
407 }
408
409 pub fn page_full() -> Self {
410 Self::new(DsErrorKind::PageFull)
411 }
412
413 pub fn page_not_found(id: u64) -> Self {
414 Self::new(DsErrorKind::PageNotFound { id })
415 }
416
417 pub fn page_size_mismatch(expected: usize, actual: usize) -> Self {
418 Self::new(DsErrorKind::PageSizeMismatch { expected, actual })
419 }
420
421 pub fn checksum_mismatch(id: u64) -> Self {
422 Self::new(DsErrorKind::ChecksumMismatch { id })
423 }
424
425 pub fn invalid_page_type(page_type: u8) -> Self {
426 Self::new(DsErrorKind::InvalidPageType { page_type })
427 }
428
429 pub fn serialization(details: impl Into<String>) -> Self {
430 Self::new(DsErrorKind::Serialization {
431 format: "".into(),
432 details: details.into(),
433 })
434 }
435
436 pub fn serialization_with_format(
437 format: impl Into<String>,
438 details: impl Into<String>,
439 ) -> Self {
440 Self::new(DsErrorKind::Serialization {
441 format: format.into(),
442 details: details.into(),
443 })
444 }
445
446 pub fn parse(details: impl Into<String>) -> Self {
447 Self::new(DsErrorKind::Parse {
448 target: "".into(),
449 details: details.into(),
450 })
451 }
452
453 pub fn parse_with_target(target: impl Into<String>, details: impl Into<String>) -> Self {
454 Self::new(DsErrorKind::Parse {
455 target: target.into(),
456 details: details.into(),
457 })
458 }
459
460 pub fn type_err(expected: impl Into<String>, actual: impl Into<String>) -> Self {
461 Self::new(DsErrorKind::Type {
462 expected: expected.into(),
463 actual: actual.into(),
464 })
465 }
466
467 pub fn type_conversion(from: impl Into<String>, to: impl Into<String>) -> Self {
468 Self::new(DsErrorKind::TypeConversion {
469 from: from.into(),
470 to: to.into(),
471 })
472 }
473
474 pub fn query(details: impl Into<String>) -> Self {
475 Self::new(DsErrorKind::Query {
476 sql: "".into(),
477 details: details.into(),
478 })
479 }
480
481 pub fn query_with_sql(sql: impl Into<String>, details: impl Into<String>) -> Self {
482 Self::new(DsErrorKind::Query {
483 sql: sql.into(),
484 details: details.into(),
485 })
486 }
487
488 pub fn execution(details: impl Into<String>) -> Self {
489 Self::new(DsErrorKind::Execution {
490 op: "".into(),
491 details: details.into(),
492 })
493 }
494
495 pub fn execution_with_op(op: impl Into<String>, details: impl Into<String>) -> Self {
496 Self::new(DsErrorKind::Execution {
497 op: op.into(),
498 details: details.into(),
499 })
500 }
501
502 pub fn schema(
503 table: Option<String>,
504 column: Option<String>,
505 details: impl Into<String>,
506 ) -> Self {
507 Self::new(DsErrorKind::Schema {
508 table,
509 column,
510 details: details.into(),
511 })
512 }
513
514 pub fn table_not_found(table: impl Into<String>) -> Self {
515 Self::new(DsErrorKind::TableNotFound { name: table.into() })
516 }
517
518 pub fn internal(details: impl Into<String>) -> Self {
519 Self::new(DsErrorKind::Internal {
520 details: details.into(),
521 })
522 }
523
524 pub fn protocol(details: impl Into<String>) -> Self {
525 Self::new(DsErrorKind::Protocol {
526 details: details.into(),
527 })
528 }
529
530 pub fn network(addr: Option<String>, details: impl Into<String>) -> Self {
531 Self::new(DsErrorKind::Network {
532 addr,
533 details: details.into(),
534 })
535 }
536
537 pub fn consensus(op: impl Into<String>, details: impl Into<String>) -> Self {
538 Self::new(DsErrorKind::Consensus {
539 op: op.into(),
540 details: details.into(),
541 })
542 }
543
544 pub fn config(key: impl Into<String>, details: impl Into<String>) -> Self {
545 Self::new(DsErrorKind::Config {
546 key: key.into(),
547 details: details.into(),
548 })
549 }
550
551 pub fn http(code: u16, url: impl Into<String>, method: impl Into<String>) -> Self {
552 Self::new(DsErrorKind::Http {
553 code,
554 url: url.into(),
555 method: method.into(),
556 })
557 }
558
559 pub fn join(details: impl Into<String>) -> Self {
560 Self::new(DsErrorKind::Join {
561 details: details.into(),
562 })
563 }
564
565 pub fn cache(op: impl Into<String>, details: impl Into<String>) -> Self {
566 Self::new(DsErrorKind::Cache {
567 op: op.into(),
568 details: details.into(),
569 })
570 }
571
572 pub fn sync(details: impl Into<String>) -> Self {
573 Self::new(DsErrorKind::Sync {
574 details: details.into(),
575 })
576 }
577
578 pub fn tenant(id: impl Into<String>, details: impl Into<String>) -> Self {
579 Self::new(DsErrorKind::Tenant {
580 id: id.into(),
581 details: details.into(),
582 })
583 }
584
585 pub fn quota(resource: impl Into<String>, limit: u64, usage: u64) -> Self {
586 Self::new(DsErrorKind::Quota {
587 resource: resource.into(),
588 limit,
589 usage,
590 })
591 }
592
593 pub fn permission(action: impl Into<String>, resource: impl Into<String>) -> Self {
594 Self::new(DsErrorKind::Permission {
595 action: action.into(),
596 resource: resource.into(),
597 })
598 }
599}