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