use std::fmt;
pub type Result<T> = std::result::Result<T, NoSQLError>;
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum NoSQLError {
ConnectionError(String),
ExecutionError(String),
NotFound(String),
SerializationError(String),
InvalidOperation(String),
ConfigError(String),
Timeout(String),
AuthenticationError(String),
PermissionDenied(String),
DatabaseError(String),
UnsupportedFeature(String),
}
impl fmt::Display for NoSQLError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
NoSQLError::ConnectionError(msg) => write!(f, "Connection error: {}", msg),
NoSQLError::ExecutionError(msg) => write!(f, "Execution error: {}", msg),
NoSQLError::NotFound(msg) => write!(f, "Not found: {}", msg),
NoSQLError::SerializationError(msg) => write!(f, "Serialization error: {}", msg),
NoSQLError::InvalidOperation(msg) => write!(f, "Invalid operation: {}", msg),
NoSQLError::ConfigError(msg) => write!(f, "Configuration error: {}", msg),
NoSQLError::Timeout(msg) => write!(f, "Timeout: {}", msg),
NoSQLError::AuthenticationError(msg) => write!(f, "Authentication error: {}", msg),
NoSQLError::PermissionDenied(msg) => write!(f, "Permission denied: {}", msg),
NoSQLError::DatabaseError(msg) => write!(f, "Database error: {}", msg),
NoSQLError::UnsupportedFeature(msg) => write!(f, "Unsupported feature: {}", msg),
}
}
}
impl std::error::Error for NoSQLError {}
impl From<serde_json::Error> for NoSQLError {
fn from(err: serde_json::Error) -> Self {
NoSQLError::SerializationError(err.to_string())
}
}
#[cfg(feature = "mongodb")]
impl From<mongodb::error::Error> for NoSQLError {
fn from(err: mongodb::error::Error) -> Self {
use mongodb::error::ErrorKind;
match *err.kind {
ErrorKind::Authentication { .. } => NoSQLError::AuthenticationError(err.to_string()),
ErrorKind::InvalidArgument { .. } => NoSQLError::InvalidOperation(err.to_string()),
ErrorKind::Io(_) => NoSQLError::ConnectionError(err.to_string()),
_ => NoSQLError::DatabaseError(err.to_string()),
}
}
}
#[cfg(feature = "mongodb")]
impl From<bson::error::Error> for NoSQLError {
fn from(err: bson::error::Error) -> Self {
NoSQLError::SerializationError(err.to_string())
}
}
#[cfg(feature = "redis")]
impl From<redis::RedisError> for NoSQLError {
fn from(err: redis::RedisError) -> Self {
if err.is_timeout() {
NoSQLError::Timeout(err.to_string())
} else if err.is_connection_refusal() || err.is_connection_dropped() {
NoSQLError::ConnectionError(err.to_string())
} else {
NoSQLError::DatabaseError(err.to_string())
}
}
}
pub type OdmResult<T> = std::result::Result<T, OdmError>;
#[non_exhaustive]
#[derive(Debug)]
pub enum OdmError {
Validation(ValidationError),
#[cfg(feature = "mongodb")]
Mongo(mongodb::error::Error),
NotFound,
DuplicateKey {
field: String,
},
Serialization(String),
BackendError(String),
#[cfg(feature = "mongodb")]
Deserialization(bson::error::Error),
}
impl fmt::Display for OdmError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
OdmError::Validation(err) => write!(f, "Validation failed: {}", err),
#[cfg(feature = "mongodb")]
OdmError::Mongo(err) => write!(f, "MongoDB error: {}", err),
OdmError::NotFound => write!(f, "Document not found"),
OdmError::DuplicateKey { field } => write!(f, "Duplicate key: {}", field),
OdmError::Serialization(msg) => write!(f, "Serialization error: {}", msg),
OdmError::BackendError(msg) => write!(f, "Backend error: {}", msg),
#[cfg(feature = "mongodb")]
OdmError::Deserialization(err) => write!(f, "Deserialization error: {}", err),
}
}
}
impl std::error::Error for OdmError {}
impl From<ValidationError> for OdmError {
fn from(err: ValidationError) -> Self {
OdmError::Validation(err)
}
}
impl From<NoSQLError> for OdmError {
fn from(err: NoSQLError) -> Self {
match err {
NoSQLError::SerializationError(msg) => OdmError::Serialization(msg),
NoSQLError::NotFound(_) => OdmError::NotFound,
other => OdmError::BackendError(other.to_string()),
}
}
}
#[cfg(feature = "mongodb")]
impl From<mongodb::error::Error> for OdmError {
fn from(err: mongodb::error::Error) -> Self {
convert_mongo_error(err)
}
}
#[cfg(feature = "mongodb")]
pub(crate) fn convert_mongo_error(err: mongodb::error::Error) -> OdmError {
if let mongodb::error::ErrorKind::Write(mongodb::error::WriteFailure::WriteError(
ref write_error,
)) = *err.kind
&& write_error.code == 11000
{
let field = extract_duplicate_field(&write_error.message);
return OdmError::DuplicateKey { field };
}
OdmError::Mongo(err)
}
#[cfg(feature = "mongodb")]
fn extract_duplicate_field(message: &str) -> String {
if let Some(start) = message.find("dup key: {") {
let after_brace = &message[start + 11..];
if let Some(colon_pos) = after_brace.find(':') {
return after_brace[..colon_pos].trim().to_string();
}
}
if let Some(start) = message.find(".$") {
let after_dollar = &message[start + 2..];
if let Some(underscore_pos) = after_dollar.find('_') {
return after_dollar[..underscore_pos].to_string();
}
}
"unknown".to_string()
}
#[cfg(feature = "mongodb")]
impl From<bson::error::Error> for OdmError {
fn from(err: bson::error::Error) -> Self {
OdmError::Deserialization(err)
}
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ValidationError {
Required(&'static str),
InvalidEmail,
InvalidUrl,
OutOfRange {
field: &'static str,
min: i64,
max: i64,
},
Custom(String),
}
impl fmt::Display for ValidationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ValidationError::Required(field) => write!(f, "Field required: {}", field),
ValidationError::InvalidEmail => write!(f, "Invalid email format"),
ValidationError::InvalidUrl => write!(f, "Invalid URL format"),
ValidationError::OutOfRange { field, min, max } => {
write!(
f,
"Value out of range: {} must be between {} and {}",
field, min, max
)
}
ValidationError::Custom(msg) => write!(f, "Custom validation failed: {}", msg),
}
}
}
impl std::error::Error for ValidationError {}