use std::error::Error as StdError;
use thiserror::Error;
pub type BoxError = Box<dyn StdError + Send + Sync + 'static>;
#[derive(Debug, Default)]
pub struct ValidationError {
pub field: String,
pub message: String,
}
impl std::fmt::Display for ValidationError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}: {}", self.field, self.message)
}
}
impl ValidationError {
pub fn new(field: impl Into<String>, message: impl Into<String>) -> Self {
Self {
field: field.into(),
message: message.into(),
}
}
}
#[derive(Debug)]
pub struct ValidationErrors(pub Vec<ValidationError>);
impl ValidationErrors {
pub fn new(validation_errors: impl Into<Vec<ValidationError>>) -> Self {
Self(validation_errors.into())
}
}
impl std::fmt::Display for ValidationErrors {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for (i, err) in self.0.iter().enumerate() {
if i > 0 {
write!(f, "; ")?;
}
write!(f, "{err}")?;
}
Ok(())
}
}
#[derive(Debug, Error)]
pub enum OxiModError {
#[error("Failed to connect to db: {msg}")]
Connection {
msg: String,
#[source]
source: BoxError,
},
#[error("Failed to set CLIENT: {msg}")]
GlobalClientInit {
msg: String,
},
#[error("CLIENT not found: {msg}")]
GlobalClientMissing {
msg: String,
},
#[error("Serialization error: {msg}")]
Serialization {
msg: String,
#[source]
source: BoxError,
},
#[error("Aggregation error: {msg}")]
Aggregation {
msg: String,
#[source]
source: BoxError,
},
#[error("Index error: {msg}")]
Index {
msg: String,
#[source]
source: BoxError,
},
#[error("Validation errors: {0}")]
Validation(ValidationErrors),
#[error("Database operation failed: {msg}")]
Database {
msg: String,
#[source]
source: BoxError,
},
#[error("Custom error: {msg}")]
Custom {
msg: String,
#[source]
source: Option<BoxError>,
},
}
impl OxiModError {
pub fn connection(msg: impl Into<String>, source: impl Into<BoxError>) -> Self {
Self::Connection {
msg: msg.into(),
source: source.into(),
}
}
pub fn global_client_init(msg: impl Into<String>) -> Self {
Self::GlobalClientInit { msg: msg.into() }
}
pub fn global_client_missing(msg: impl Into<String>) -> Self {
Self::GlobalClientMissing { msg: msg.into() }
}
pub fn serialization(msg: impl Into<String>, source: impl Into<BoxError>) -> Self {
Self::Serialization {
msg: msg.into(),
source: source.into(),
}
}
pub fn aggregation(msg: impl Into<String>, source: impl Into<BoxError>) -> Self {
Self::Aggregation {
msg: msg.into(),
source: source.into(),
}
}
pub fn index(msg: impl Into<String>, source: impl Into<BoxError>) -> Self {
Self::Index {
msg: msg.into(),
source: source.into(),
}
}
pub fn validation(field: impl Into<String>, message: impl Into<String>) -> Self {
Self::Validation(ValidationErrors(vec![ValidationError::new(field, message)]))
}
pub fn validations(errors: Vec<ValidationError>) -> Self {
Self::Validation(ValidationErrors(errors))
}
pub fn database(msg: impl Into<String>, source: impl Into<BoxError>) -> Self {
Self::Database {
msg: msg.into(),
source: source.into(),
}
}
pub fn custom(msg: impl Into<String>) -> Self {
Self::Custom {
msg: msg.into(),
source: None,
}
}
pub fn custom_with_source(msg: impl Into<String>, source: impl Into<BoxError>) -> Self {
Self::Custom {
msg: msg.into(),
source: Some(source.into()),
}
}
pub fn validation_errors(&self) -> Option<&[ValidationError]> {
match self {
Self::Validation(errors) => Some(&errors.0),
_ => None,
}
}
}