Skip to main content

icydb_core/
error.rs

1use std::fmt;
2use thiserror::Error as ThisError;
3
4///
5/// InternalError
6///
7/// Structured runtime error with a stable internal classification.
8/// Not a stable API; intended for internal use and may change without notice.
9///
10#[derive(Debug, ThisError)]
11#[error("{message}")]
12pub struct InternalError {
13    pub class: ErrorClass,
14    pub origin: ErrorOrigin,
15    pub message: String,
16
17    /// Optional structured error detail.
18    /// The variant (if present) must correspond to `origin`.
19    pub detail: Option<ErrorDetail>,
20}
21
22impl InternalError {
23    pub fn new(class: ErrorClass, origin: ErrorOrigin, message: impl Into<String>) -> Self {
24        let message = message.into();
25
26        let detail = match (class, origin) {
27            (ErrorClass::Corruption, ErrorOrigin::Store) => {
28                Some(ErrorDetail::Store(StoreError::Corrupt {
29                    message: message.clone(),
30                }))
31            }
32            (ErrorClass::InvariantViolation, ErrorOrigin::Store) => {
33                Some(ErrorDetail::Store(StoreError::InvariantViolation {
34                    message: message.clone(),
35                }))
36            }
37            _ => None,
38        };
39
40        Self {
41            class,
42            origin,
43            message,
44            detail,
45        }
46    }
47
48    pub fn store_not_found(key: impl Into<String>) -> Self {
49        let key = key.into();
50
51        Self {
52            class: ErrorClass::NotFound,
53            origin: ErrorOrigin::Store,
54            message: format!("data key not found: {key}"),
55            detail: Some(ErrorDetail::Store(StoreError::NotFound { key })),
56        }
57    }
58
59    #[must_use]
60    pub const fn is_not_found(&self) -> bool {
61        matches!(
62            self.detail,
63            Some(ErrorDetail::Store(StoreError::NotFound { .. }))
64        )
65    }
66
67    #[must_use]
68    pub fn display_with_class(&self) -> String {
69        format!("{}:{}: {}", self.origin, self.class, self.message)
70    }
71}
72
73///
74/// ErrorDetail
75///
76/// Structured, origin-specific error detail carried by [`InternalError`].
77/// This enum is intentionally extensible.
78///
79
80#[derive(Debug, ThisError)]
81pub enum ErrorDetail {
82    #[error("{0}")]
83    Store(StoreError),
84    // Future-proofing:
85    // #[error("{0}")]
86    // Index(IndexError),
87    //
88    // #[error("{0}")]
89    // Query(QueryErrorDetail),
90    //
91    // #[error("{0}")]
92    // Executor(ExecutorErrorDetail),
93}
94
95///
96/// StoreError
97///
98/// Store-specific structured error detail.
99/// Never returned directly; always wrapped in [`ErrorDetail::Store`].
100///
101
102#[derive(Debug, ThisError)]
103pub enum StoreError {
104    #[error("key not found: {key}")]
105    NotFound { key: String },
106
107    #[error("store corruption: {message}")]
108    Corrupt { message: String },
109
110    #[error("store invariant violation: {message}")]
111    InvariantViolation { message: String },
112}
113
114///
115/// ErrorClass
116/// Internal error taxonomy for runtime classification.
117/// Not a stable API; may change without notice.
118///
119
120#[derive(Clone, Copy, Debug, Eq, PartialEq)]
121pub enum ErrorClass {
122    Corruption,
123    NotFound,
124    Internal,
125    Conflict,
126    Unsupported,
127    InvariantViolation,
128}
129
130impl fmt::Display for ErrorClass {
131    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132        let label = match self {
133            Self::Corruption => "corruption",
134            Self::NotFound => "not_found",
135            Self::Internal => "internal",
136            Self::Conflict => "conflict",
137            Self::Unsupported => "unsupported",
138            Self::InvariantViolation => "invariant_violation",
139        };
140        write!(f, "{label}")
141    }
142}
143
144///
145/// ErrorOrigin
146/// Internal origin taxonomy for runtime classification.
147/// Not a stable API; may change without notice.
148///
149
150#[derive(Clone, Copy, Debug, Eq, PartialEq)]
151pub enum ErrorOrigin {
152    Serialize,
153    Store,
154    Index,
155    Query,
156    Response,
157    Executor,
158    Interface,
159}
160
161impl fmt::Display for ErrorOrigin {
162    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163        let label = match self {
164            Self::Serialize => "serialize",
165            Self::Store => "store",
166            Self::Index => "index",
167            Self::Query => "query",
168            Self::Response => "response",
169            Self::Executor => "executor",
170            Self::Interface => "interface",
171        };
172        write!(f, "{label}")
173    }
174}