use crate::{Result};
use crate::task::Task;
use async_trait::async_trait;
use std::sync::Arc;
#[async_trait]
pub trait Middleware: Send + Sync {
async fn before(&self, _task: &Task) -> Result<()> {
Ok(())
}
async fn after(&self, _task: &Task, _result: &Result<()>) -> Result<()> {
Ok(())
}
}
type MiddlewareArc = Arc<dyn Middleware>;
#[derive(Default)]
pub struct MiddlewareChain {
middlewares: Vec<MiddlewareArc>,
}
impl std::fmt::Debug for MiddlewareChain {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MiddlewareChain")
.field("count", &self.middlewares.len())
.finish()
}
}
impl MiddlewareChain {
pub fn new() -> Self {
Self {
middlewares: Vec::new(),
}
}
#[allow(clippy::should_implement_trait)]
pub fn add<M: Middleware + 'static>(mut self, middleware: M) -> Self {
self.middlewares.push(Arc::new(middleware));
self
}
pub async fn before(&self, task: &Task) -> Result<()> {
for middleware in &self.middlewares {
middleware.before(task).await?;
}
Ok(())
}
pub async fn after(&self, task: &Task, result: &Result<()>) -> Result<()> {
for middleware in &self.middlewares {
middleware.after(task, result).await?;
}
Ok(())
}
pub fn is_empty(&self) -> bool {
self.middlewares.is_empty()
}
}
#[derive(Debug, Clone)]
pub struct LoggingMiddleware {
log_details: bool,
}
impl LoggingMiddleware {
pub fn new() -> Self {
Self {
log_details: false,
}
}
pub fn with_details(mut self) -> Self {
self.log_details = true;
self
}
}
impl Default for LoggingMiddleware {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl Middleware for LoggingMiddleware {
async fn before(&self, task: &Task) -> Result<()> {
if self.log_details {
tracing::info!(
"Processing task: type={}, id={}, queue={}, retry={}",
task.task_type,
task.id,
task.queue,
task.retry_cnt
);
} else {
tracing::info!("Processing task: {}", task.id);
}
Ok(())
}
async fn after(&self, task: &Task, result: &Result<()>) -> Result<()> {
match result {
Ok(()) => {
tracing::info!("Task completed: {}", task.id);
}
Err(e) => {
tracing::error!("Task failed: {} - error: {}", task.id, e);
}
}
Ok(())
}
}
#[derive(Debug, Clone, Default)]
pub struct MetricsMiddleware {
_private: (),
}
impl MetricsMiddleware {
pub fn new() -> Self {
Self::default()
}
}
#[async_trait]
impl Middleware for MetricsMiddleware {
async fn before(&self, task: &Task) -> Result<()> {
tracing::debug!("Task started: {}", task.id);
Ok(())
}
async fn after(&self, task: &Task, result: &Result<()>) -> Result<()> {
match result {
Ok(()) => {
tracing::debug!("Task succeeded: {}", task.id);
}
Err(_) => {
tracing::debug!("Task failed: {}", task.id);
}
}
Ok(())
}
}