use backtrace::Backtrace;
use serde::{de, ser};
use std::error::Error;
use std::fmt::{Debug, Display, Formatter};
use std::result::Result;
use crate::{atomic, Atomic};
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum ErrorKind {
FilterError,
IndexingError,
IndexNotFound,
IndexAlreadyExists,
IndexBuildFailed,
IndexCorrupted,
IndexTypeMismatch,
IndexingInProgress,
InvalidId,
NotIdentifiable,
NotFound,
InvalidOperation,
IOError,
DiskFull,
FileNotFound,
PermissionDenied,
FileCorrupted,
FileAccessError,
EncodingError,
ObjectMappingError,
SecurityError,
UniqueConstraintViolation,
ValidationError,
InvalidDataType,
InvalidFieldName,
MissingRequiredField,
CollectionNotFound,
RepositoryNotFound,
EventError,
PluginError,
PluginLoadFailed,
BackendError,
StoreNotInitialized,
StoreAlreadyClosed,
TransactionConflict,
MigrationError,
Extension(String),
InternalError,
}
impl Display for ErrorKind {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
ErrorKind::FilterError => write!(f, "Filter error"),
ErrorKind::IndexingError => write!(f, "Indexing error"),
ErrorKind::IndexNotFound => write!(f, "Index not found"),
ErrorKind::IndexAlreadyExists => write!(f, "Index already exists"),
ErrorKind::IndexBuildFailed => write!(f, "Index build failed"),
ErrorKind::IndexCorrupted => write!(f, "Index corrupted"),
ErrorKind::IndexTypeMismatch => write!(f, "Index type mismatch"),
ErrorKind::IndexingInProgress => write!(f, "Indexing in progress"),
ErrorKind::InvalidId => write!(f, "Invalid ID"),
ErrorKind::NotIdentifiable => write!(f, "Not identifiable"),
ErrorKind::NotFound => write!(f, "Not found"),
ErrorKind::InvalidOperation => write!(f, "Invalid operation"),
ErrorKind::IOError => write!(f, "IO error"),
ErrorKind::DiskFull => write!(f, "Disk full"),
ErrorKind::FileNotFound => write!(f, "File not found"),
ErrorKind::PermissionDenied => write!(f, "Permission denied"),
ErrorKind::FileCorrupted => write!(f, "File corrupted"),
ErrorKind::FileAccessError => write!(f, "File access error"),
ErrorKind::EncodingError => write!(f, "Encoding error"),
ErrorKind::ObjectMappingError => write!(f, "Object mapping error"),
ErrorKind::SecurityError => write!(f, "Security error"),
ErrorKind::UniqueConstraintViolation => write!(f, "Unique constraint violation"),
ErrorKind::ValidationError => write!(f, "Validation error"),
ErrorKind::InvalidDataType => write!(f, "Invalid data type"),
ErrorKind::InvalidFieldName => write!(f, "Invalid field name"),
ErrorKind::MissingRequiredField => write!(f, "Missing required field"),
ErrorKind::CollectionNotFound => write!(f, "Collection not found"),
ErrorKind::RepositoryNotFound => write!(f, "Repository not found"),
ErrorKind::EventError => write!(f, "Event error"),
ErrorKind::PluginError => write!(f, "Plugin error"),
ErrorKind::PluginLoadFailed => write!(f, "Plugin load failed"),
ErrorKind::BackendError => write!(f, "Backend error"),
ErrorKind::StoreNotInitialized => write!(f, "Store not initialized"),
ErrorKind::StoreAlreadyClosed => write!(f, "Store already closed"),
ErrorKind::TransactionConflict => write!(f, "Transaction conflict"),
ErrorKind::MigrationError => write!(f, "Migration error"),
ErrorKind::Extension(name) => write!(f, "{} error", name),
ErrorKind::InternalError => write!(f, "Internal error"),
}
}
}
#[derive(Clone)]
pub struct NitriteError {
message: String,
error_kind: ErrorKind,
cause: Option<Box<NitriteError>>,
backtrace: Atomic<Backtrace>,
}
impl NitriteError {
pub fn new(message: &str, error_kind: ErrorKind) -> Self {
NitriteError {
message: message.to_string(),
error_kind,
cause: None,
backtrace: atomic(Backtrace::new()),
}
}
pub fn new_with_cause(message: &str, error_type: ErrorKind, cause: NitriteError) -> Self {
NitriteError {
message: message.to_string(),
error_kind: error_type,
cause: Some(Box::new(cause)),
backtrace: atomic(Backtrace::new()),
}
}
pub fn message(&self) -> &str {
&self.message
}
pub fn kind(&self) -> &ErrorKind {
&self.error_kind
}
pub fn cause(&self) -> Option<&NitriteError> {
self.cause.as_deref()
}
}
impl Display for NitriteError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.message)
}
}
impl Debug for NitriteError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match &self.cause {
Some(cause) => write!(f, "{}\nCaused by: {:?}", self.message, cause),
None => write!(f, "{}\n{:?}", self.message, self.backtrace.read()),
}
}
}
impl Error for NitriteError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match &self.cause {
Some(cause) => Some(cause.as_ref()),
None => None,
}
}
}
pub type NitriteResult<T> = Result<T, NitriteError>;
impl de::Error for NitriteError {
fn custom<T: Display>(msg: T) -> Self {
NitriteError::new(&msg.to_string(), ErrorKind::ObjectMappingError)
}
}
impl ser::Error for NitriteError {
fn custom<T: Display>(msg: T) -> Self {
NitriteError::new(&msg.to_string(), ErrorKind::ObjectMappingError)
}
}
impl From<std::io::Error> for NitriteError {
fn from(err: std::io::Error) -> Self {
let error_kind = match err.kind() {
std::io::ErrorKind::NotFound => ErrorKind::FileNotFound,
std::io::ErrorKind::PermissionDenied => ErrorKind::PermissionDenied,
std::io::ErrorKind::AlreadyExists => ErrorKind::FileCorrupted,
_ => ErrorKind::IOError,
};
NitriteError::new(&format!("IO error: {}", err), error_kind)
}
}
impl From<std::string::FromUtf8Error> for NitriteError {
fn from(err: std::string::FromUtf8Error) -> Self {
NitriteError::new(
&format!("UTF-8 encoding error: {}", err),
ErrorKind::EncodingError,
)
}
}
impl From<std::fmt::Error> for NitriteError {
fn from(err: std::fmt::Error) -> Self {
NitriteError::new(
&format!("Formatting error: {}", err),
ErrorKind::InternalError,
)
}
}
impl From<std::num::ParseIntError> for NitriteError {
fn from(err: std::num::ParseIntError) -> Self {
NitriteError::new(
&format!("Integer parsing error: {}", err),
ErrorKind::InvalidDataType,
)
}
}
impl From<std::num::ParseFloatError> for NitriteError {
fn from(err: std::num::ParseFloatError) -> Self {
NitriteError::new(
&format!("Float parsing error: {}", err),
ErrorKind::InvalidDataType,
)
}
}
impl From<String> for NitriteError {
fn from(msg: String) -> Self {
NitriteError::new(&msg, ErrorKind::InternalError)
}
}
impl From<&str> for NitriteError {
fn from(msg: &str) -> Self {
NitriteError::new(msg, ErrorKind::InternalError)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn create_io_error() -> Box<dyn Error + Send + Sync> {
Box::new(std::io::Error::other("IO Error"))
}
#[test]
fn nitrite_error_new_creates_error() {
let error = NitriteError::new("An error occurred", ErrorKind::IOError);
assert_eq!(error.message, "An error occurred");
assert_eq!(error.error_kind, ErrorKind::IOError);
assert!(error.cause.is_none());
}
#[test]
fn nitrite_error_new_with_cause_creates_error() {
let cause = create_io_error();
let error = NitriteError::new_with_cause(
"An error occurred",
ErrorKind::IOError,
NitriteError::new(&cause.to_string(), ErrorKind::IOError),
);
assert_eq!(error.message, "An error occurred");
assert_eq!(error.error_kind, ErrorKind::IOError);
assert!(error.cause.is_some());
}
#[test]
fn nitrite_error_message_returns_message() {
let error = NitriteError::new("An error occurred", ErrorKind::IOError);
assert_eq!(error.message(), "An error occurred");
}
#[test]
fn nitrite_error_kind_returns_kind() {
let error = NitriteError::new("An error occurred", ErrorKind::IOError);
assert_eq!(error.kind(), &ErrorKind::IOError);
}
#[test]
fn nitrite_error_cause_returns_cause() {
let cause = create_io_error();
let error = NitriteError::new_with_cause(
"An error occurred",
ErrorKind::IOError,
NitriteError::new(&cause.to_string(), ErrorKind::IOError),
);
assert!(error.cause().is_some());
}
#[test]
fn nitrite_error_cause_returns_none_when_no_cause() {
let error = NitriteError::new("An error occurred", ErrorKind::IOError);
assert!(error.cause().is_none());
}
#[test]
fn nitrite_error_display_formats_correctly() {
let error = NitriteError::new("An error occurred", ErrorKind::IOError);
let formatted = format!("{}", error);
assert_eq!(formatted, "An error occurred");
}
#[test]
fn nitrite_error_debug_formats_correctly() {
let error = NitriteError::new("An error occurred", ErrorKind::IOError);
let formatted = format!("{:?}", error);
assert!(formatted.contains("An error occurred"));
}
#[test]
fn nitrite_error_debug_formats_with_cause() {
let cause = create_io_error();
let error = NitriteError::new_with_cause(
"An error occurred",
ErrorKind::IOError,
NitriteError::new(&cause.to_string(), ErrorKind::IOError),
);
let formatted = format!("{:?}", error);
assert!(formatted.contains("An error occurred"));
assert!(formatted.contains("Caused by:"));
}
#[test]
fn nitrite_error_source_returns_cause() {
let cause = create_io_error();
let error = NitriteError::new_with_cause(
"An error occurred",
ErrorKind::IOError,
NitriteError::new(&cause.to_string(), ErrorKind::IOError),
);
assert!(error.source().is_some());
}
#[test]
fn nitrite_error_source_returns_none_when_no_cause() {
let error = NitriteError::new("An error occurred", ErrorKind::IOError);
assert!(error.source().is_none());
}
#[test]
fn nitrite_error_ser_custom_creates_error() {
let error = NitriteError::new("Custom error", ErrorKind::ObjectMappingError);
assert_eq!(error.message(), "Custom error");
assert_eq!(error.kind(), &ErrorKind::ObjectMappingError);
}
#[test]
fn test_filter_errors() {
let filter_error = NitriteError::new("Invalid filter syntax", ErrorKind::FilterError);
assert_eq!(filter_error.kind(), &ErrorKind::FilterError);
}
#[test]
fn test_id_errors() {
let invalid_id = NitriteError::new("Invalid ID format", ErrorKind::InvalidId);
assert_eq!(invalid_id.kind(), &ErrorKind::InvalidId);
let not_identifiable = NitriteError::new("Entity not identifiable", ErrorKind::NotIdentifiable);
assert_eq!(not_identifiable.kind(), &ErrorKind::NotIdentifiable);
}
#[test]
fn test_indexing_errors() {
let index_error = NitriteError::new("Indexing failed", ErrorKind::IndexingError);
assert_eq!(index_error.kind(), &ErrorKind::IndexingError);
let not_found = NitriteError::new("Index not found", ErrorKind::IndexNotFound);
assert_eq!(not_found.kind(), &ErrorKind::IndexNotFound);
let exists = NitriteError::new("Index already exists", ErrorKind::IndexAlreadyExists);
assert_eq!(exists.kind(), &ErrorKind::IndexAlreadyExists);
let build_failed = NitriteError::new("Index build failed", ErrorKind::IndexBuildFailed);
assert_eq!(build_failed.kind(), &ErrorKind::IndexBuildFailed);
let corrupted = NitriteError::new("Index corrupted", ErrorKind::IndexCorrupted);
assert_eq!(corrupted.kind(), &ErrorKind::IndexCorrupted);
let type_mismatch = NitriteError::new("Index type mismatch", ErrorKind::IndexTypeMismatch);
assert_eq!(type_mismatch.kind(), &ErrorKind::IndexTypeMismatch);
let in_progress = NitriteError::new("Indexing in progress", ErrorKind::IndexingInProgress);
assert_eq!(in_progress.kind(), &ErrorKind::IndexingInProgress);
}
#[test]
fn test_io_errors() {
let io_error = NitriteError::new("IO error", ErrorKind::IOError);
assert_eq!(io_error.kind(), &ErrorKind::IOError);
let disk_full = NitriteError::new("Disk full", ErrorKind::DiskFull);
assert_eq!(disk_full.kind(), &ErrorKind::DiskFull);
let file_not_found = NitriteError::new("File not found", ErrorKind::FileNotFound);
assert_eq!(file_not_found.kind(), &ErrorKind::FileNotFound);
let permission = NitriteError::new("Permission denied", ErrorKind::PermissionDenied);
assert_eq!(permission.kind(), &ErrorKind::PermissionDenied);
let corrupted = NitriteError::new("File corrupted", ErrorKind::FileCorrupted);
assert_eq!(corrupted.kind(), &ErrorKind::FileCorrupted);
let access = NitriteError::new("File access error", ErrorKind::FileAccessError);
assert_eq!(access.kind(), &ErrorKind::FileAccessError);
}
#[test]
fn test_security_errors() {
let security = NitriteError::new("Security error", ErrorKind::SecurityError);
assert_eq!(security.kind(), &ErrorKind::SecurityError);
}
#[test]
fn test_constraint_errors() {
let unique = NitriteError::new("Unique constraint violated", ErrorKind::UniqueConstraintViolation);
assert_eq!(unique.kind(), &ErrorKind::UniqueConstraintViolation);
}
#[test]
fn test_validation_errors() {
let validation = NitriteError::new("Validation failed", ErrorKind::ValidationError);
assert_eq!(validation.kind(), &ErrorKind::ValidationError);
let invalid_type = NitriteError::new("Invalid data type", ErrorKind::InvalidDataType);
assert_eq!(invalid_type.kind(), &ErrorKind::InvalidDataType);
let invalid_name = NitriteError::new("Invalid field name", ErrorKind::InvalidFieldName);
assert_eq!(invalid_name.kind(), &ErrorKind::InvalidFieldName);
let missing = NitriteError::new("Missing required field", ErrorKind::MissingRequiredField);
assert_eq!(missing.kind(), &ErrorKind::MissingRequiredField);
}
#[test]
fn test_collection_repository_errors() {
let not_found = NitriteError::new("Collection not found", ErrorKind::CollectionNotFound);
assert_eq!(not_found.kind(), &ErrorKind::CollectionNotFound);
let repo_not_found = NitriteError::new("Repository not found", ErrorKind::RepositoryNotFound);
assert_eq!(repo_not_found.kind(), &ErrorKind::RepositoryNotFound);
}
#[test]
fn test_event_subscription_errors() {
let event = NitriteError::new("Event error", ErrorKind::EventError);
assert_eq!(event.kind(), &ErrorKind::EventError);
}
#[test]
fn test_plugin_errors() {
let plugin = NitriteError::new("Plugin error", ErrorKind::PluginError);
assert_eq!(plugin.kind(), &ErrorKind::PluginError);
let load_failed = NitriteError::new("Plugin load failed", ErrorKind::PluginLoadFailed);
assert_eq!(load_failed.kind(), &ErrorKind::PluginLoadFailed);
}
#[test]
fn test_backend_store_errors() {
let backend = NitriteError::new("Backend error", ErrorKind::BackendError);
assert_eq!(backend.kind(), &ErrorKind::BackendError);
let not_init = NitriteError::new("Store not initialized", ErrorKind::StoreNotInitialized);
assert_eq!(not_init.kind(), &ErrorKind::StoreNotInitialized);
let closed = NitriteError::new("Store already closed", ErrorKind::StoreAlreadyClosed);
assert_eq!(closed.kind(), &ErrorKind::StoreAlreadyClosed);
}
#[test]
fn test_internal_errors() {
let internal = NitriteError::new("Internal error", ErrorKind::InternalError);
assert_eq!(internal.kind(), &ErrorKind::InternalError);
}
#[test]
fn test_extension_errors() {
let spatial_ext = NitriteError::new("Spatial index error", ErrorKind::Extension("spatial".to_string()));
assert_eq!(spatial_ext.kind(), &ErrorKind::Extension("spatial".to_string()));
let fulltext_ext = NitriteError::new("Full-text search error", ErrorKind::Extension("FullText".to_string()));
assert_eq!(fulltext_ext.kind(), &ErrorKind::Extension("FullText".to_string()));
assert_ne!(spatial_ext.kind(), fulltext_ext.kind());
let display = format!("{}", ErrorKind::Extension("MyExtension".to_string()));
assert_eq!(display, "MyExtension error");
}
#[test]
fn test_error_chain_with_different_kinds() {
let root_cause = NitriteError::new("File not found", ErrorKind::FileNotFound);
let mid_level = NitriteError::new_with_cause(
"Failed to read store",
ErrorKind::IOError,
root_cause,
);
let top_level = NitriteError::new_with_cause(
"Cannot initialize database",
ErrorKind::BackendError,
mid_level,
);
assert_eq!(top_level.kind(), &ErrorKind::BackendError);
assert!(top_level.cause().is_some());
if let Some(cause_box) = top_level.cause() {
assert_eq!(cause_box.kind(), &ErrorKind::IOError);
}
}
#[test]
fn test_error_kind_equality() {
let error1 = NitriteError::new("Error 1", ErrorKind::IndexNotFound);
let error2 = NitriteError::new("Error 2", ErrorKind::IndexNotFound);
let error3 = NitriteError::new("Error 3", ErrorKind::IndexAlreadyExists);
assert_eq!(error1.kind(), error2.kind());
assert_ne!(error1.kind(), error3.kind());
}
#[test]
fn test_error_message_preservation() {
let messages = vec![
("Filter error message", ErrorKind::FilterError),
("Index not found message", ErrorKind::IndexNotFound),
("Disk full message", ErrorKind::DiskFull),
("Object mapping error message", ErrorKind::ObjectMappingError),
("Security error message", ErrorKind::SecurityError),
];
for (msg, kind) in &messages {
let error = NitriteError::new(msg, kind.clone());
assert_eq!(error.message(), *msg);
assert_eq!(error.kind(), kind);
}
}
#[test]
fn test_from_io_error_not_found() {
let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
let nitrite_err: NitriteError = io_err.into();
assert_eq!(nitrite_err.kind(), &ErrorKind::FileNotFound);
assert!(nitrite_err.message().contains("IO error"));
}
#[test]
fn test_from_io_error_permission_denied() {
let io_err = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "permission denied");
let nitrite_err: NitriteError = io_err.into();
assert_eq!(nitrite_err.kind(), &ErrorKind::PermissionDenied);
assert!(nitrite_err.message().contains("IO error"));
}
#[test]
fn test_from_io_error_other() {
let io_err = std::io::Error::other("unknown io error");
let nitrite_err: NitriteError = io_err.into();
assert_eq!(nitrite_err.kind(), &ErrorKind::IOError);
assert!(nitrite_err.message().contains("IO error"));
}
#[test]
fn test_from_utf8_error() {
let invalid_utf8 = vec![0xFF, 0xFE];
let utf8_err = String::from_utf8(invalid_utf8).unwrap_err();
let nitrite_err: NitriteError = utf8_err.into();
assert_eq!(nitrite_err.kind(), &ErrorKind::EncodingError);
assert!(nitrite_err.message().contains("UTF-8"));
}
#[test]
fn test_from_fmt_error() {
use std::fmt::Write;
struct FailingWriter;
impl Write for FailingWriter {
fn write_str(&mut self, _: &str) -> std::fmt::Result {
Err(std::fmt::Error)
}
}
let fmt_err = write!(&mut FailingWriter, "test").unwrap_err();
let nitrite_err: NitriteError = fmt_err.into();
assert_eq!(nitrite_err.kind(), &ErrorKind::InternalError);
assert!(nitrite_err.message().contains("Formatting"));
}
#[test]
fn test_from_parse_int_error() {
let parse_err = "not_a_number".parse::<i32>().unwrap_err();
let nitrite_err: NitriteError = parse_err.into();
assert_eq!(nitrite_err.kind(), &ErrorKind::InvalidDataType);
assert!(nitrite_err.message().contains("Integer parsing"));
}
#[test]
fn test_from_parse_float_error() {
let parse_err = "not_a_float".parse::<f64>().unwrap_err();
let nitrite_err: NitriteError = parse_err.into();
assert_eq!(nitrite_err.kind(), &ErrorKind::InvalidDataType);
assert!(nitrite_err.message().contains("Float parsing"));
}
#[test]
fn test_from_string() {
let msg = String::from("test error message");
let nitrite_err: NitriteError = msg.into();
assert_eq!(nitrite_err.kind(), &ErrorKind::InternalError);
assert_eq!(nitrite_err.message(), "test error message");
}
#[test]
fn test_from_str() {
let msg = "test error message";
let nitrite_err: NitriteError = msg.into();
assert_eq!(nitrite_err.kind(), &ErrorKind::InternalError);
assert_eq!(nitrite_err.message(), "test error message");
}
#[test]
fn test_from_conversion_in_result_chain() {
fn operation_that_fails_with_io() -> NitriteResult<String> {
let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
Err(io_err.into())
}
let result = operation_that_fails_with_io();
assert!(result.is_err());
if let Err(err) = result {
assert_eq!(err.kind(), &ErrorKind::FileNotFound);
}
}
#[test]
fn test_multiple_from_conversions() {
let io_err: NitriteError = std::io::Error::new(
std::io::ErrorKind::PermissionDenied,
"permission denied"
).into();
assert_eq!(io_err.kind(), &ErrorKind::PermissionDenied);
let utf8_err: NitriteError = String::from_utf8(vec![0xFF]).unwrap_err().into();
assert_eq!(utf8_err.kind(), &ErrorKind::EncodingError);
let str_err: NitriteError = "string error".into();
assert_eq!(str_err.kind(), &ErrorKind::InternalError);
}
#[test]
fn test_question_mark_operator_with_from() {
fn parse_number_operation() -> NitriteResult<i32> {
let num: i32 = "12345".parse()?;
Ok(num)
}
let result = parse_number_operation();
assert!(result.is_ok());
assert_eq!(result.unwrap(), 12345);
}
#[test]
fn test_question_mark_operator_with_parse_error() {
fn parse_number_operation() -> NitriteResult<i32> {
let num: i32 = "not_a_number".parse()?;
Ok(num)
}
let result = parse_number_operation();
assert!(result.is_err());
if let Err(err) = result {
assert_eq!(err.kind(), &ErrorKind::InvalidDataType);
}
}
}