use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("Database error")]
SQLite(#[from] rusqlite::Error),
#[error("Inference error: {0}")]
Inference(String),
#[error("Tokenization error: {0}")]
Tokenization(#[from] tokenizers::Error),
#[error("ONNX session error: {0}")]
Onnx(#[from] ort::Error),
#[error("HuggingFace Hub error: {0}")]
HfHub(#[from] hf_hub::api::sync::ApiError),
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
#[error("Configuration error: {0}")]
Config(String),
#[error("Invalid date/time: {0}")]
Chrono(#[from] chrono::ParseError),
#[error("Invalid input: {0}")]
InvalidInput(String),
#[error("Input cannot be empty")]
EmptyInput,
#[error("Input too long: {actual_length} characters (max: {max_length})")]
InputTooLong {
max_length: usize,
actual_length: usize,
},
#[error("content exceeds {max_tokens}-token embedding limit (measured: {token_count} tokens)")]
ContentTooLong {
token_count: usize,
max_tokens: usize,
},
#[error("Invalid timestamp format: {timestamp} ({error})")]
InvalidTimestamp { timestamp: String, error: String },
#[error("Memory not found: {0}")]
NotFound(String),
#[error("Database error")]
SqliteModule(String),
#[error("Validation error: {0}")]
Validation(String),
#[allow(dead_code)]
#[error("Embedder unavailable: {reason}")]
EmbedderUnavailable { reason: String },
}
impl From<crate::sqlite::Error> for Error {
fn from(err: crate::sqlite::Error) -> Self {
match err {
crate::sqlite::Error::NotFound(_) => Error::NotFound("memory not found".to_string()),
crate::sqlite::Error::InvalidInput(msg) => Error::InvalidInput(msg),
_ => Error::SqliteModule(err.to_string()),
}
}
}
impl From<String> for Error {
fn from(s: String) -> Self {
Error::InvalidInput(s)
}
}
#[cfg(test)]
mod error_conversion_tests {
use super::*;
#[test]
fn sqlite_not_found_converts_to_error_not_found() {
let sqlite_err = crate::sqlite::Error::NotFound("any-arbitrary-message".to_string());
let err: Error = sqlite_err.into();
assert!(
matches!(err, Error::NotFound(_)),
"sqlite::Error::NotFound must convert to Error::NotFound regardless of message text"
);
let Error::NotFound(msg) = err else {
unreachable!()
};
assert_eq!(msg, "memory not found");
}
#[test]
fn sqlite_invalid_input_converts_to_error_invalid_input() {
let sqlite_err =
crate::sqlite::Error::InvalidInput("At least one field must be provided".to_string());
let err: Error = sqlite_err.into();
match err {
Error::InvalidInput(msg) => {
assert_eq!(msg, "At least one field must be provided");
}
other => panic!("Expected InvalidInput, got {:?}", other),
}
}
#[test]
fn sqlite_other_errors_convert_to_sqlite_module() {
let sqlite_err = crate::sqlite::Error::Sqlite("disk I/O error".to_string());
let err: Error = sqlite_err.into();
assert!(matches!(err, Error::SqliteModule(_)));
}
}