Skip to main content

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;