use thiserror::Error;
use crate::oauth2::OAuth2Error;
use crate::passkey::PasskeyError;
use crate::session::SessionError;
use crate::userdb::UserError;
use crate::utils::UtilError;
#[derive(Error, Debug)]
pub enum CoordinationError {
#[error("Coordination error: {0}")]
Coordination(String),
#[error("Validation error: {0}")]
Validation(String),
#[error("Internal error: {0}")]
Internal(String),
#[error("Database error: {0}")]
Database(String),
#[error("Authentication error: {0}")]
Authentication(String),
#[error("Session mismatch: {0}")]
SessionMismatch(String),
#[error("Context token is missing")]
MissingContextToken,
#[error("Unauthorized access")]
Unauthorized,
#[error("You are already authenticated")]
UnexpectedlyAuthorized,
#[error("No content")]
NoContent,
#[error("Invalid mode")]
InvalidMode,
#[error("Conflict: {0}")]
Conflict(String),
#[error("Invalid state: {0}")]
InvalidState(String),
#[error("Resource not found: {resource_type} {resource_id}")]
ResourceNotFound {
resource_type: String,
resource_id: String,
},
#[error("User error: {0}")]
UserError(UserError),
#[error("OAuth2 error: {0}")]
OAuth2Error(OAuth2Error),
#[error("Passkey error: {0}")]
PasskeyError(PasskeyError),
#[error("Session error: {0}")]
SessionError(SessionError),
#[error("Utils error: {0}")]
UtilsError(UtilError),
#[error("Invalid response mode: {0}")]
InvalidResponseMode(String),
}
impl CoordinationError {
pub fn log(self) -> Self {
match &self {
Self::Coordination(msg) => tracing::error!("Coordination error: {}", msg),
Self::Validation(msg) => tracing::error!("Validation error: {}", msg),
Self::Internal(msg) => tracing::error!("Internal error: {}", msg),
Self::Database(msg) => tracing::error!("Database error: {}", msg),
Self::Authentication(msg) => tracing::error!("Authentication error: {}", msg),
Self::SessionMismatch(msg) => tracing::error!("Session mismatch: {}", msg),
Self::MissingContextToken => tracing::error!("Context token is missing"),
Self::Unauthorized => tracing::error!("Unauthorized access"),
Self::UnexpectedlyAuthorized => tracing::error!("Unexpectedly authorized access"),
Self::NoContent => tracing::error!("No content"),
Self::InvalidMode => tracing::error!("Invalid mode"),
Self::Conflict(message) => tracing::error!("Conflict: {}", message),
Self::InvalidState(message) => tracing::error!("Invalid state: {}", message),
Self::ResourceNotFound {
resource_type,
resource_id,
} => tracing::error!("Resource not found: {} {}", resource_type, resource_id),
Self::UserError(err) => tracing::error!("User error: {}", err),
Self::OAuth2Error(err) => tracing::error!("OAuth2 error: {}", err),
Self::PasskeyError(err) => tracing::error!("Passkey error: {}", err),
Self::SessionError(err) => tracing::error!("Session error: {}", err),
Self::UtilsError(err) => tracing::error!("Utils error: {}", err),
Self::InvalidResponseMode(message) => {
tracing::error!("Invalid response mode: {}", message)
}
}
self
}
pub fn log_with_context(self) -> Self {
match &self {
Self::Coordination(msg) => {
tracing::error!(error = %self, message = %msg, "Coordination error with context");
}
Self::Validation(msg) => {
tracing::error!(error = %self, message = %msg, "Validation error with context");
}
Self::Internal(msg) => {
tracing::error!(error = %self, message = %msg, "Internal error with context");
}
Self::Database(msg) => {
tracing::error!(error = %self, message = %msg, "Database error with context");
}
Self::Authentication(msg) => {
tracing::error!(error = %self, message = %msg, "Authentication error with context");
}
Self::SessionMismatch(msg) => {
tracing::error!(error = %self, message = %msg, "Session mismatch with context");
}
Self::MissingContextToken => {
tracing::error!(error = %self, "Context token missing with span context");
}
Self::Unauthorized => {
tracing::error!(error = %self, "Unauthorized access with span context");
}
Self::UnexpectedlyAuthorized => {
tracing::error!(error = %self, "Unexpectedly authorized with span context");
}
Self::NoContent => {
tracing::error!(error = %self, "No content with span context");
}
Self::InvalidMode => {
tracing::error!(error = %self, "Invalid mode with span context");
}
Self::InvalidState(msg) => {
tracing::error!(error = %self, message = %msg, "Invalid state with context");
}
Self::Conflict(msg) => {
tracing::error!(error = %self, message = %msg, "Conflict with context");
}
Self::ResourceNotFound {
resource_type,
resource_id,
} => {
tracing::error!(
error = %self,
resource_type = %resource_type,
resource_id = %resource_id,
"Resource not found with context"
);
}
Self::UserError(err) => {
tracing::error!(error = %self, source_error = %err, "User error with context");
}
Self::OAuth2Error(err) => {
tracing::error!(error = %self, source_error = %err, "OAuth2 error with context");
}
Self::PasskeyError(err) => {
tracing::error!(error = %self, source_error = %err, "Passkey error with context");
}
Self::SessionError(err) => {
tracing::error!(error = %self, source_error = %err, "Session error with context");
}
Self::UtilsError(err) => {
tracing::error!(error = %self, source_error = %err, "Utils error with context");
}
Self::InvalidResponseMode(msg) => {
tracing::error!(error = %self, message = %msg, "Invalid response mode with context");
}
}
self
}
pub fn with_span_context(self) -> Self {
if tracing::log::log_enabled!(tracing::log::Level::Error) {
let span_name = tracing::Span::current()
.metadata()
.map(|m| m.name())
.unwrap_or("unknown");
tracing::error!(
error = %self,
span_context = span_name,
"Error with full span context captured"
);
} else {
tracing::error!(
error = %self,
"Error captured without span context"
);
}
self
}
}
impl From<OAuth2Error> for CoordinationError {
fn from(err: OAuth2Error) -> Self {
let error = Self::OAuth2Error(err);
tracing::error!("{}", error);
error
}
}
impl From<PasskeyError> for CoordinationError {
fn from(err: PasskeyError) -> Self {
let error = Self::PasskeyError(err);
tracing::error!("{}", error);
error
}
}
impl From<SessionError> for CoordinationError {
fn from(err: SessionError) -> Self {
let error = Self::SessionError(err);
tracing::error!("{}", error);
error
}
}
impl From<UserError> for CoordinationError {
fn from(err: UserError) -> Self {
let error = Self::UserError(err);
tracing::error!("{}", error);
error
}
}
impl From<UtilError> for CoordinationError {
fn from(err: UtilError) -> Self {
let error = Self::UtilsError(err);
tracing::error!("{}", error);
error
}
}
#[cfg(test)]
mod tests;