use std::borrow::Cow;
use serde::Serialize;
use serde_with::{serde_as, DisplayFromStr};
use sqlx::error::DatabaseError;
use thiserror::Error;
use crate::model::store::dbx;
use crate::security;
use super::base::Id;
pub type Result<T> = core::result::Result<T, Error>;
#[serde_as]
#[derive(Debug, Serialize, Error)]
pub enum Error {
#[error("Invalid argment, error message: {message}")]
InvalidArgument { message: String },
#[error("Entity not found. entity: '{schema}.{entity}', id: {id}")]
EntityNotFound { schema: &'static str, entity: &'static str, id: Id },
#[error("Data not found. table is '{schema}.{table}'")]
NotFound { schema: &'static str, table: &'static str, sql: String },
#[error("List limit over max. max: {max}, actual: {actual}")]
ListLimitOverMax { max: i64, actual: i64 },
#[error("Count fail")]
CountFail,
#[error("User already exists. {key}: '{value}'")]
UserAlreadyExists { key: &'static str, value: String },
#[error("Unique violation. table: '{table}', constraint: {constraint}")]
UniqueViolation { table: String, constraint: String },
#[error("Can't create ModelManagerProvider. provider: {0}")]
CantCreateModelManagerProvider(String),
#[error(transparent)]
Pwd(#[from] security::Error),
#[error(transparent)]
Dbx(#[from] dbx::Error),
#[error(transparent)]
SeaQuery(
#[from]
#[serde_as(as = "DisplayFromStr")]
sea_query::error::Error,
),
#[error(transparent)]
ModqlIntoSea(
#[from]
#[serde_as(as = "DisplayFromStr")]
modql::filter::IntoSeaError,
),
#[error(transparent)]
JsonError(
#[from]
#[serde_as(as = "DisplayFromStr")]
serde_json::Error,
),
}
impl Error {
pub fn resolve_unique_violation<F>(self, resolver: Option<F>) -> Self
where
F: FnOnce(&str, &str) -> Option<Self>,
{
match self.as_database_error().map(|db_error| (db_error.code(), db_error.table(), db_error.constraint())) {
Some((Some(Cow::Borrowed("23505")), Some(table), Some(constraint))) => resolver
.and_then(|fun| fun(table, constraint))
.unwrap_or_else(|| Error::UniqueViolation { table: table.to_string(), constraint: constraint.to_string() }),
_ => self,
}
}
pub fn as_database_error(&self) -> Option<&(dyn DatabaseError + 'static)> {
match self {
Error::Dbx(dbx::Error::Sqlx(sqlx_error)) => sqlx_error.as_database_error(),
_ => None,
}
}
}