Skip to main content

yyds_types/
errors.rs

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/// The primary error type for YYDS/YYKV.
9#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
10pub struct DsError {
11    pub kind: Box<DsErrorKind>,
12}
13
14/// Detailed error categories.
15#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
16pub enum DsErrorKind {
17    // --- I/O & Physical Storage ---
18    InputOutput {
19        path: Option<PathBuf>,
20        kind: String, // From io::ErrorKind
21        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    // --- Data & Serialization ---
46    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    // --- Logic & Schema ---
64    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    // --- Infrastructure & System ---
82    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    // --- Auth & Resource Management ---
117    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    /// Gets the i18n key for this error kind.
134    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}