use std::fmt;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum DbNexusError {
#[cfg(feature = "permission")]
#[error(transparent)]
Permission(#[from] crate::domain::permission::PermissionError),
#[cfg(feature = "permission")]
#[error(transparent)]
PermissionConfig(#[from] crate::domain::permission::PermissionConfigError),
#[error("Unsupported database scheme in URL: {0}")]
UnsupportedDatabaseScheme(String),
}
pub type DbNexusResult<T> = Result<T, DbNexusError>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ErrorCategory {
Permission,
InjectionRisk,
SyntaxError,
ShardConflict,
}
impl fmt::Display for ErrorCategory {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Permission => write!(f, "Permission"),
Self::InjectionRisk => write!(f, "InjectionRisk"),
Self::SyntaxError => write!(f, "SyntaxError"),
Self::ShardConflict => write!(f, "ShardConflict"),
}
}
}
#[derive(Debug, Clone)]
pub struct QueryErrorReport {
pub category: ErrorCategory,
pub message: String,
pub suggestion: String,
pub table: Option<String>,
pub operation: Option<String>,
}
impl QueryErrorReport {
pub fn new(category: ErrorCategory, message: impl Into<String>, suggestion: impl Into<String>) -> Self {
Self {
category,
message: message.into(),
suggestion: suggestion.into(),
table: None,
operation: None,
}
}
pub fn with_table(mut self, table: impl Into<String>) -> Self {
self.table = Some(table.into());
self
}
pub fn with_operation(mut self, operation: impl Into<String>) -> Self {
self.operation = Some(operation.into());
self
}
}
impl fmt::Display for QueryErrorReport {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"[{}] {}\nSuggestion: {}",
self.category, self.message, self.suggestion
)?;
if let Some(table) = &self.table {
write!(f, "\nTable: {}", table)?;
}
if let Some(operation) = &self.operation {
write!(f, "\nOperation: {}", operation)?;
}
Ok(())
}
}
impl std::error::Error for QueryErrorReport {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}
impl From<DbNexusError> for QueryErrorReport {
fn from(err: DbNexusError) -> Self {
match err {
#[cfg(feature = "permission")]
DbNexusError::Permission(_) => QueryErrorReport::new(
ErrorCategory::Permission,
err.to_string(),
"Verify the role has the required permissions on the target table",
),
#[cfg(feature = "permission")]
DbNexusError::PermissionConfig(_) => QueryErrorReport::new(
ErrorCategory::Permission,
err.to_string(),
"Check the permission policy configuration file for syntax or schema errors",
),
DbNexusError::UnsupportedDatabaseScheme(_) => QueryErrorReport::new(
ErrorCategory::SyntaxError,
err.to_string(),
"Use a supported database scheme: sqlite, postgres, mysql, or duckdb",
),
}
}
}