Skip to main content

velesdb_core/
error.rs

1//! Error types for `VelesDB`.
2//!
3//! This module provides a unified error type for all `VelesDB` operations,
4//! designed for professional API exposure to Python/Node clients.
5
6use thiserror::Error;
7
8/// Result type alias for `VelesDB` operations.
9pub type Result<T> = std::result::Result<T, Error>;
10
11/// Errors that can occur in `VelesDB` operations.
12///
13/// Each variant includes a descriptive error message suitable for end-users.
14/// Error codes follow the pattern `VELES-XXX` for easy debugging.
15#[derive(Error, Debug)]
16#[non_exhaustive]
17pub enum Error {
18    /// Collection already exists (VELES-001).
19    #[error("[VELES-001] Collection '{0}' already exists")]
20    CollectionExists(String),
21
22    /// Collection not found (VELES-002).
23    #[error("[VELES-002] Collection '{0}' not found")]
24    CollectionNotFound(String),
25
26    /// Point not found (VELES-003).
27    #[error("[VELES-003] Point with ID '{0}' not found")]
28    PointNotFound(u64),
29
30    /// Dimension mismatch (VELES-004).
31    #[error("[VELES-004] Vector dimension mismatch: expected {expected}, got {actual}")]
32    DimensionMismatch {
33        /// Expected dimension.
34        expected: usize,
35        /// Actual dimension.
36        actual: usize,
37    },
38
39    /// Invalid vector (VELES-005).
40    #[error("[VELES-005] Invalid vector: {0}")]
41    InvalidVector(String),
42
43    /// Storage error (VELES-006).
44    #[error("[VELES-006] Storage error: {0}")]
45    Storage(String),
46
47    /// Index error (VELES-007).
48    #[error("[VELES-007] Index error: {0}")]
49    Index(String),
50
51    /// Index corrupted (VELES-008).
52    ///
53    /// Indicates that index files are corrupted and need to be rebuilt.
54    #[error("[VELES-008] Index corrupted: {0}")]
55    IndexCorrupted(String),
56
57    /// Configuration error (VELES-009).
58    #[error("[VELES-009] Configuration error: {0}")]
59    Config(String),
60
61    /// Query parsing error (VELES-010).
62    ///
63    /// Wraps `VelesQL` parse errors with position and context information.
64    #[error("[VELES-010] Query error: {0}")]
65    Query(String),
66
67    /// IO error (VELES-011).
68    #[error("[VELES-011] IO error: {0}")]
69    Io(#[from] std::io::Error),
70
71    /// Serialization error (VELES-012).
72    #[error("[VELES-012] Serialization error: {0}")]
73    Serialization(String),
74
75    /// Internal error (VELES-013).
76    ///
77    /// Indicates an unexpected internal error. Please report if encountered.
78    #[error("[VELES-013] Internal error: {0}")]
79    Internal(String),
80
81    /// Vector not allowed on metadata-only collection (VELES-014).
82    #[error("[VELES-014] Vector not allowed on metadata-only collection '{0}'")]
83    VectorNotAllowed(String),
84
85    /// Search not supported on metadata-only collection (VELES-015).
86    #[error("[VELES-015] Vector search not supported on metadata-only collection '{0}'. Use query() instead.")]
87    SearchNotSupported(String),
88
89    /// Vector required for vector collection (VELES-016).
90    #[error("[VELES-016] Vector required for collection '{0}' (not metadata-only)")]
91    VectorRequired(String),
92
93    /// Schema validation error (VELES-017).
94    #[error("[VELES-017] Schema validation error: {0}")]
95    SchemaValidation(String),
96
97    /// Graph operation not supported (VELES-018).
98    #[error("[VELES-018] Graph operation not supported: {0}")]
99    GraphNotSupported(String),
100
101    /// Edge already exists (VELES-019).
102    #[error("[VELES-019] Edge with ID '{0}' already exists")]
103    EdgeExists(u64),
104
105    /// Edge not found (VELES-020).
106    #[error("[VELES-020] Edge with ID '{0}' not found")]
107    EdgeNotFound(u64),
108
109    /// Invalid edge label (VELES-021).
110    #[error("[VELES-021] Invalid edge label: {0}")]
111    InvalidEdgeLabel(String),
112
113    /// Node not found (VELES-022).
114    #[error("[VELES-022] Node with ID '{0}' not found")]
115    NodeNotFound(u64),
116
117    /// Numeric overflow (VELES-023).
118    ///
119    /// Indicates a numeric conversion would overflow or truncate.
120    /// Use `try_from()` instead of `as` casts for user-provided data.
121    #[error("[VELES-023] Numeric overflow: {0}")]
122    Overflow(String),
123
124    /// Column store error (VELES-024).
125    ///
126    /// Indicates a column store schema or primary key validation failure.
127    #[error("[VELES-024] Column store error: {0}")]
128    ColumnStoreError(String),
129
130    /// GPU operation error (VELES-025).
131    ///
132    /// Indicates a GPU parameter validation or operation failure.
133    #[error("[VELES-025] GPU error: {0}")]
134    GpuError(String),
135
136    /// Epoch mismatch (VELES-026).
137    ///
138    /// Indicates a stale mmap guard detected after a remap operation.
139    /// This is not recoverable — the guard must be re-acquired.
140    #[error("[VELES-026] Epoch mismatch: {0}")]
141    EpochMismatch(String),
142
143    /// Guard-rail violation (VELES-027).
144    ///
145    /// A query exceeded a configured limit (timeout, depth, cardinality,
146    /// memory, rate limit, or circuit breaker).
147    #[error("[VELES-027] Guard-rail violation: {0}")]
148    GuardRail(String),
149
150    /// Invalid quantizer configuration (VELES-028).
151    ///
152    /// Indicates invalid parameters passed to a quantizer (e.g., empty training set,
153    /// zero subspaces, dimension not divisible by subspaces).
154    #[error("[VELES-028] Invalid quantizer config: {0}")]
155    InvalidQuantizerConfig(String),
156
157    /// Training failed (VELES-029).
158    ///
159    /// Indicates a quantizer training operation failed (convergence, insufficient
160    /// data, etc.).
161    #[error("[VELES-029] Training failed: {0}")]
162    TrainingFailed(String),
163
164    /// Sparse index error (VELES-030).
165    #[error("[VELES-030] Sparse index error: {0}")]
166    SparseIndexError(String),
167
168    /// Database already locked by another process (VELES-031).
169    #[error("[VELES-031] Database is already opened by another process: {0}")]
170    DatabaseLocked(String),
171
172    /// Invalid dimension (VELES-032).
173    ///
174    /// Indicates a vector dimension outside the valid range.
175    #[error("[VELES-032] Invalid dimension {dimension}: must be between {min} and {max}")]
176    InvalidDimension {
177        /// The invalid dimension provided.
178        dimension: usize,
179        /// Minimum valid dimension.
180        min: usize,
181        /// Maximum valid dimension.
182        max: usize,
183    },
184
185    /// Allocation failed (VELES-033).
186    ///
187    /// Indicates a memory allocation failure (out of memory or invalid layout).
188    #[error("[VELES-033] Allocation failed: {0}")]
189    AllocationFailed(String),
190}
191
192impl Error {
193    /// Returns the error code (e.g., "VELES-001").
194    #[must_use]
195    pub const fn code(&self) -> &'static str {
196        match self {
197            Self::CollectionExists(_) => "VELES-001",
198            Self::CollectionNotFound(_) => "VELES-002",
199            Self::PointNotFound(_) => "VELES-003",
200            Self::DimensionMismatch { .. } => "VELES-004",
201            Self::InvalidVector(_) => "VELES-005",
202            Self::Storage(_) => "VELES-006",
203            Self::Index(_) => "VELES-007",
204            Self::IndexCorrupted(_) => "VELES-008",
205            Self::Config(_) => "VELES-009",
206            Self::Query(_) => "VELES-010",
207            Self::Io(_) => "VELES-011",
208            Self::Serialization(_) => "VELES-012",
209            Self::Internal(_) => "VELES-013",
210            Self::VectorNotAllowed(_) => "VELES-014",
211            Self::SearchNotSupported(_) => "VELES-015",
212            Self::VectorRequired(_) => "VELES-016",
213            Self::SchemaValidation(_) => "VELES-017",
214            Self::GraphNotSupported(_) => "VELES-018",
215            Self::EdgeExists(_) => "VELES-019",
216            Self::EdgeNotFound(_) => "VELES-020",
217            Self::InvalidEdgeLabel(_) => "VELES-021",
218            Self::NodeNotFound(_) => "VELES-022",
219            Self::Overflow(_) => "VELES-023",
220            Self::ColumnStoreError(_) => "VELES-024",
221            Self::GpuError(_) => "VELES-025",
222            Self::EpochMismatch(_) => "VELES-026",
223            Self::GuardRail(_) => "VELES-027",
224            Self::InvalidQuantizerConfig(_) => "VELES-028",
225            Self::TrainingFailed(_) => "VELES-029",
226            Self::SparseIndexError(_) => "VELES-030",
227            Self::DatabaseLocked(_) => "VELES-031",
228            Self::InvalidDimension { .. } => "VELES-032",
229            Self::AllocationFailed(_) => "VELES-033",
230        }
231    }
232
233    /// Returns true if this error is recoverable.
234    ///
235    /// Non-recoverable errors include corruption and internal errors.
236    #[must_use]
237    pub const fn is_recoverable(&self) -> bool {
238        !matches!(
239            self,
240            Self::IndexCorrupted(_)
241                | Self::Internal(_)
242                | Self::EpochMismatch(_)
243                | Self::AllocationFailed(_)
244        )
245    }
246}
247
248/// Conversion from `VelesQL` `ParseError`.
249impl From<crate::velesql::ParseError> for Error {
250    fn from(err: crate::velesql::ParseError) -> Self {
251        Self::Query(err.to_string())
252    }
253}
254
255/// Conversion from `GuardRailViolation` — surfaces limit violations as query errors.
256#[cfg(feature = "persistence")]
257impl From<crate::guardrails::GuardRailViolation> for Error {
258    fn from(v: crate::guardrails::GuardRailViolation) -> Self {
259        Self::GuardRail(v.to_string())
260    }
261}