ecp_core/error.rs
1//! ecp-core's error type. Every fallible function in this crate returns
2//! [`Result`], `std::result::Result<T, EcpError>`.
3
4use std::fmt;
5
6use thiserror::Error;
7
8/// Why an ecp-core call failed. Each variant maps to one Python exception in
9/// ecp-py.
10#[derive(Debug, Clone, Error)]
11pub enum EcpError {
12 /// A bad argument, or bad data in the user's file: a dimension mismatch,
13 /// an unsupported dtype or file format, an unknown metric name,
14 /// `levels = 0`, a dimension too wide for one chunk, or ids and
15 /// embeddings of different lengths. Maps to Python's `ValueError`.
16 #[error("{0}")]
17 InvalidInput(String),
18 /// The embeddings file or the index directory is not there. Maps to
19 /// Python's `FileNotFoundError`.
20 #[error("{0}")]
21 NotFound(String),
22 /// The store failed to read, write or erase: permissions, a full disk, a
23 /// truncated chunk. Maps to Python's `OSError`.
24 #[error("{0}")]
25 Store(String),
26 /// The index is readable but inconsistent: a missing `info` field, a node
27 /// with embeddings but no ids, an unparsable saved query. Maps to
28 /// Python's `RuntimeError`.
29 #[error("{0}")]
30 Corrupt(String),
31 /// The API was called in the wrong order, such as `build` before
32 /// `select_representatives`. Maps to Python's `RuntimeError`.
33 #[error("{0}")]
34 Usage(String),
35 /// The index has a format version this build does not read, such as one
36 /// written by a newer ecpfs. Maps to Python's `ValueError`.
37 #[error("{0}")]
38 UnsupportedVersion(String),
39}
40
41/// This crate's result type.
42pub type Result<T> = std::result::Result<T, EcpError>;
43
44/// Turns a lower layer's `Result<T, E>` into ecp-core's [`Result`], prefixing
45/// `context` to the error's message. Every call site here is a store read,
46/// write or erase failing, so it always maps to [`EcpError::Store`].
47pub(crate) trait ResultExt<T> {
48 fn store_err(self, context: &str) -> Result<T>;
49 /// Like `store_err`, but builds the context only if the call fails.
50 fn store_err_with(self, context: impl FnOnce() -> String) -> Result<T>;
51}
52
53impl<T, E: fmt::Display> ResultExt<T> for std::result::Result<T, E> {
54 fn store_err(self, context: &str) -> Result<T> {
55 self.map_err(|e| EcpError::Store(format!("{context}: {e}")))
56 }
57
58 fn store_err_with(self, context: impl FnOnce() -> String) -> Result<T> {
59 self.map_err(|e| EcpError::Store(format!("{}: {e}", context())))
60 }
61}
62
63#[cfg(test)]
64#[path = "utests/error.rs"]
65mod tests;