Skip to content
Clone
1d5be35
139 lines 3.9 KB Rust
use std::error::Error;
use std::fmt::{self, Display, Formatter};
use actix_web::http::StatusCode;
use actix_web::{HttpResponse, ResponseError};
use cache::CacheError;
use cache::redis::RedisError;
#[derive(Debug)]
pub enum SessionError {
Serialize {
key: String,
source: serde_json::Error,
},
Deserialize {
key: String,
source: serde_json::Error,
},
Purged,
Unauthorized,
UpdateConflict {
key: String,
},
Config(String),
Store(CacheError),
Backend(RedisError),
}
impl SessionError {
pub fn config(message: impl Into<String>) -> Self {
Self::Config(message.into())
}
pub fn serialize(key: impl Into<String>, source: serde_json::Error) -> Self {
Self::Serialize {
key: key.into(),
source,
}
}
pub fn deserialize(key: impl Into<String>, source: serde_json::Error) -> Self {
Self::Deserialize {
key: key.into(),
source,
}
}
pub fn conflict(key: impl Into<String>) -> Self {
Self::UpdateConflict { key: key.into() }
}
pub fn is_purged(&self) -> bool {
matches!(self, Self::Purged)
}
pub fn is_retryable(&self) -> bool {
matches!(self, Self::Store(source) if source.is_retryable())
}
}
impl Display for SessionError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Serialize { key, source } => {
write!(f, "failed to serialize session field `{key}`: {source}")
}
Self::Deserialize { key, source } => {
write!(f, "failed to deserialize session field `{key}`: {source}")
}
Self::Purged => f.write_str("session record has been purged"),
Self::Unauthorized => f.write_str("authentication required"),
Self::UpdateConflict { key } => {
write!(
f,
"session field `{key}` changed before its update was committed"
)
}
Self::Config(message) => write!(f, "invalid session configuration: {message}"),
Self::Store(source) => write!(f, "session store error: {source}"),
Self::Backend(source) => write!(f, "session backend error: {source}"),
}
}
}
impl Error for SessionError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::Serialize { source, .. } | Self::Deserialize { source, .. } => Some(source),
Self::Store(source) => Some(source),
Self::Backend(source) => Some(source),
Self::Purged | Self::Unauthorized | Self::UpdateConflict { .. } | Self::Config(_) => {
None
}
}
}
}
impl ResponseError for SessionError {
fn status_code(&self) -> StatusCode {
match self {
Self::Unauthorized | Self::Purged => StatusCode::UNAUTHORIZED,
_ => StatusCode::INTERNAL_SERVER_ERROR,
}
}
fn error_response(&self) -> HttpResponse {
let status = self.status_code();
let error_code = match self {
Self::Unauthorized => "unauthenticated",
Self::Purged => "session_revoked",
_ => "internal",
};
let message = if status.is_server_error() {
"internal server error".to_owned()
} else {
self.to_string()
};
HttpResponse::build(status).json(serde_json::json!({
"code": status.as_u16(),
"message": message,
"data": null,
"error": null,
"error_code": error_code,
"request_id": null,
}))
}
}
impl From<CacheError> for SessionError {
fn from(value: CacheError) -> Self {
Self::Store(value)
}
}
impl From<RedisError> for SessionError {
fn from(value: RedisError) -> Self {
Self::Backend(value)
}
}