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    #[error("{0}")]
88    ViewPatch(crate::traits::ViewPatchError),
89    // Future-proofing:
90    // #[error("{0}")]
91    // Index(IndexError),
92    //
93    // #[error("{0}")]
94    // Query(QueryErrorDetail),
95    //
96    // #[error("{0}")]
97    // Executor(ExecutorErrorDetail),
98}
99
100///
101/// StoreError
102///
103/// Store-specific structured error detail.
104/// Never returned directly; always wrapped in [`ErrorDetail::Store`].
105///
106
107#[derive(Debug, ThisError)]
108pub enum StoreError {
109    #[error("key not found: {key}")]
110    NotFound { key: String },
111
112    #[error("store corruption: {message}")]
113    Corrupt { message: String },
114
115    #[error("store invariant violation: {message}")]
116    InvariantViolation { message: String },
117}
118
119///
120/// ErrorClass
121/// Internal error taxonomy for runtime classification.
122/// Not a stable API; may change without notice.
123///
124
125#[derive(Clone, Copy, Debug, Eq, PartialEq)]
126pub enum ErrorClass {
127    Corruption,
128    NotFound,
129    Internal,
130    Conflict,
131    Unsupported,
132    InvariantViolation,
133}
134
135impl fmt::Display for ErrorClass {
136    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137        let label = match self {
138            Self::Corruption => "corruption",
139            Self::NotFound => "not_found",
140            Self::Internal => "internal",
141            Self::Conflict => "conflict",
142            Self::Unsupported => "unsupported",
143            Self::InvariantViolation => "invariant_violation",
144        };
145        write!(f, "{label}")
146    }
147}
148
149///
150/// ErrorOrigin
151/// Internal origin taxonomy for runtime classification.
152/// Not a stable API; may change without notice.
153///
154
155#[derive(Clone, Copy, Debug, Eq, PartialEq)]
156pub enum ErrorOrigin {
157    Serialize,
158    Store,
159    Index,
160    Query,
161    Response,
162    Executor,
163    Interface,
164}
165
166impl fmt::Display for ErrorOrigin {
167    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168        let label = match self {
169            Self::Serialize => "serialize",
170            Self::Store => "store",
171            Self::Index => "index",
172            Self::Query => "query",
173            Self::Response => "response",
174            Self::Executor => "executor",
175            Self::Interface => "interface",
176        };
177        write!(f, "{label}")
178    }
179}