use async_trait::async_trait;
use anyhow::Result;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::domain::entity::Thumbnail;
use crate::infrastructure::persistence::ThumbnailRepository;
use super::CommandHandler;
#[deprecated(since = "0.2.0", note = "Use CommandHandler<C> trait instead")]
#[async_trait]
pub trait ThumbnailCommand: Send + Sync {
type Output;
async fn execute(&self) -> Result<Self::Output>;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateThumbnailCommand {
pub file_id: Uuid,
pub size: ThumbnailSize,
pub width: i32,
pub height: i32,
pub storage_key: String,
pub storage_backend: String,
pub mime_type: String,
pub format: String,
pub quality: i32,
pub size_bytes: i64,
pub generated_at: DateTime<Utc>,
pub generation_time_ms: Option<i32>,
pub source_version: i32,
pub cdn_url: Option<String>,
pub cache_expires_at: Option<DateTime<Utc>>,
pub is_stale: bool,
pub metadata: serde_json::Value,
}
pub struct CreateThumbnailHandler<R: ThumbnailRepository> {
repository: std::sync::Arc<R>,
}
impl<R: ThumbnailRepository> CreateThumbnailHandler<R> {
pub fn new(repository: std::sync::Arc<R>) -> Self {
Self { repository }
}
}
#[async_trait]
impl<R: ThumbnailRepository + 'static> CommandHandler<CreateThumbnailCommand> for CreateThumbnailHandler<R> {
type Output = Thumbnail;
async fn handle(&self, cmd: CreateThumbnailCommand) -> Result<Self::Output> {
let entity = Thumbnail::builder()
.id(Uuid::new_v4().to_string())
.file_id(cmd.file_id)
.size(cmd.size)
.width(cmd.width)
.height(cmd.height)
.storage_key(cmd.storage_key)
.storage_backend(cmd.storage_backend)
.mime_type(cmd.mime_type)
.format(cmd.format)
.quality(cmd.quality)
.size_bytes(cmd.size_bytes)
.generated_at(cmd.generated_at)
.generation_time_ms(cmd.generation_time_ms)
.source_version(cmd.source_version)
.cdn_url(cmd.cdn_url)
.cache_expires_at(cmd.cache_expires_at)
.is_stale(cmd.is_stale)
.metadata(cmd.metadata)
.build()?;
self.repository.save(&entity).await
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpdateThumbnailCommand {
pub id: String,
pub file_id: Option<Uuid>,
pub size: Option<ThumbnailSize>,
pub width: Option<i32>,
pub height: Option<i32>,
pub storage_key: Option<String>,
pub storage_backend: Option<String>,
pub mime_type: Option<String>,
pub format: Option<String>,
pub quality: Option<i32>,
pub size_bytes: Option<i64>,
pub generated_at: Option<DateTime<Utc>>,
pub generation_time_ms: Option<i32>,
pub source_version: Option<i32>,
pub cdn_url: Option<String>,
pub cache_expires_at: Option<DateTime<Utc>>,
pub is_stale: Option<bool>,
pub metadata: Option<serde_json::Value>,
}
pub struct UpdateThumbnailHandler<R: ThumbnailRepository> {
repository: std::sync::Arc<R>,
}
impl<R: ThumbnailRepository> UpdateThumbnailHandler<R> {
pub fn new(repository: std::sync::Arc<R>) -> Self {
Self { repository }
}
}
#[async_trait]
impl<R: ThumbnailRepository + 'static> CommandHandler<UpdateThumbnailCommand> for UpdateThumbnailHandler<R> {
type Output = Option<Thumbnail>;
async fn handle(&self, cmd: UpdateThumbnailCommand) -> Result<Self::Output> {
let existing = self.repository.find_by_id(&cmd.id).await?;
let Some(mut entity) = existing else {
return Ok(None);
};
if let Some(value) = cmd.file_id {
entity.file_id = value;
}
if let Some(value) = cmd.size {
entity.size = value;
}
if let Some(value) = cmd.width {
entity.width = value;
}
if let Some(value) = cmd.height {
entity.height = value;
}
if let Some(value) = cmd.storage_key {
entity.storage_key = value;
}
if let Some(value) = cmd.storage_backend {
entity.storage_backend = value;
}
if let Some(value) = cmd.mime_type {
entity.mime_type = value;
}
if let Some(value) = cmd.format {
entity.format = value;
}
if let Some(value) = cmd.quality {
entity.quality = value;
}
if let Some(value) = cmd.size_bytes {
entity.size_bytes = value;
}
if let Some(value) = cmd.generated_at {
entity.generated_at = value;
}
entity.generation_time_ms = cmd.generation_time_ms;
if let Some(value) = cmd.source_version {
entity.source_version = value;
}
entity.cdn_url = cmd.cdn_url;
entity.cache_expires_at = cmd.cache_expires_at;
if let Some(value) = cmd.is_stale {
entity.is_stale = value;
}
if let Some(value) = cmd.metadata {
entity.metadata = value;
}
self.repository.update(&cmd.id, &entity).await
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeleteThumbnailCommand {
pub id: String,
pub hard_delete: bool,
}
pub struct DeleteThumbnailHandler<R: ThumbnailRepository> {
repository: std::sync::Arc<R>,
}
impl<R: ThumbnailRepository> DeleteThumbnailHandler<R> {
pub fn new(repository: std::sync::Arc<R>) -> Self {
Self { repository }
}
}
#[async_trait]
impl<R: ThumbnailRepository + 'static> CommandHandler<DeleteThumbnailCommand> for DeleteThumbnailHandler<R> {
type Output = bool;
async fn handle(&self, cmd: DeleteThumbnailCommand) -> Result<Self::Output> {
if cmd.hard_delete {
self.repository.delete(&cmd.id).await
} else {
self.repository.soft_delete(&cmd.id).await
}
}
}