use async_trait::async_trait;
use anyhow::Result;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::domain::entity::UserQuota;
use crate::infrastructure::persistence::UserQuotaRepository;
use super::CommandHandler;
#[deprecated(since = "0.2.0", note = "Use CommandHandler<C> trait instead")]
#[async_trait]
pub trait UserQuotaCommand: Send + Sync {
type Output;
async fn execute(&self) -> Result<Self::Output>;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateUserQuotaCommand {
pub user_id: Uuid,
pub limit_bytes: i64,
pub used_bytes: i64,
pub file_count: i32,
pub max_file_size: Option<i64>,
pub max_file_count: Option<i32>,
pub tier: String,
pub quota_status: QuotaStatus,
pub warning_threshold_percent: i32,
pub last_warning_sent_at: Option<DateTime<Utc>>,
pub peak_usage_bytes: i64,
pub peak_usage_at: Option<DateTime<Utc>>,
pub metadata: serde_json::Value,
}
pub struct CreateUserQuotaHandler<R: UserQuotaRepository> {
repository: std::sync::Arc<R>,
}
impl<R: UserQuotaRepository> CreateUserQuotaHandler<R> {
pub fn new(repository: std::sync::Arc<R>) -> Self {
Self { repository }
}
}
#[async_trait]
impl<R: UserQuotaRepository + 'static> CommandHandler<CreateUserQuotaCommand> for CreateUserQuotaHandler<R> {
type Output = UserQuota;
async fn handle(&self, cmd: CreateUserQuotaCommand) -> Result<Self::Output> {
let entity = UserQuota::builder()
.id(Uuid::new_v4().to_string())
.user_id(cmd.user_id)
.limit_bytes(cmd.limit_bytes)
.used_bytes(cmd.used_bytes)
.file_count(cmd.file_count)
.max_file_size(cmd.max_file_size)
.max_file_count(cmd.max_file_count)
.tier(cmd.tier)
.quota_status(cmd.quota_status)
.warning_threshold_percent(cmd.warning_threshold_percent)
.last_warning_sent_at(cmd.last_warning_sent_at)
.peak_usage_bytes(cmd.peak_usage_bytes)
.peak_usage_at(cmd.peak_usage_at)
.metadata(cmd.metadata)
.build()?;
self.repository.save(&entity).await
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpdateUserQuotaCommand {
pub id: String,
pub user_id: Option<Uuid>,
pub limit_bytes: Option<i64>,
pub used_bytes: Option<i64>,
pub file_count: Option<i32>,
pub max_file_size: Option<i64>,
pub max_file_count: Option<i32>,
pub tier: Option<String>,
pub quota_status: Option<QuotaStatus>,
pub warning_threshold_percent: Option<i32>,
pub last_warning_sent_at: Option<DateTime<Utc>>,
pub peak_usage_bytes: Option<i64>,
pub peak_usage_at: Option<DateTime<Utc>>,
pub metadata: Option<serde_json::Value>,
}
pub struct UpdateUserQuotaHandler<R: UserQuotaRepository> {
repository: std::sync::Arc<R>,
}
impl<R: UserQuotaRepository> UpdateUserQuotaHandler<R> {
pub fn new(repository: std::sync::Arc<R>) -> Self {
Self { repository }
}
}
#[async_trait]
impl<R: UserQuotaRepository + 'static> CommandHandler<UpdateUserQuotaCommand> for UpdateUserQuotaHandler<R> {
type Output = Option<UserQuota>;
async fn handle(&self, cmd: UpdateUserQuotaCommand) -> 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.user_id {
entity.user_id = value;
}
if let Some(value) = cmd.limit_bytes {
entity.limit_bytes = value;
}
if let Some(value) = cmd.used_bytes {
entity.used_bytes = value;
}
if let Some(value) = cmd.file_count {
entity.file_count = value;
}
entity.max_file_size = cmd.max_file_size;
entity.max_file_count = cmd.max_file_count;
if let Some(value) = cmd.tier {
entity.tier = value;
}
if let Some(value) = cmd.quota_status {
entity.quota_status = value;
}
if let Some(value) = cmd.warning_threshold_percent {
entity.warning_threshold_percent = value;
}
entity.last_warning_sent_at = cmd.last_warning_sent_at;
if let Some(value) = cmd.peak_usage_bytes {
entity.peak_usage_bytes = value;
}
entity.peak_usage_at = cmd.peak_usage_at;
if let Some(value) = cmd.metadata {
entity.metadata = value;
}
self.repository.update(&cmd.id, &entity).await
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeleteUserQuotaCommand {
pub id: String,
pub hard_delete: bool,
}
pub struct DeleteUserQuotaHandler<R: UserQuotaRepository> {
repository: std::sync::Arc<R>,
}
impl<R: UserQuotaRepository> DeleteUserQuotaHandler<R> {
pub fn new(repository: std::sync::Arc<R>) -> Self {
Self { repository }
}
}
#[async_trait]
impl<R: UserQuotaRepository + 'static> CommandHandler<DeleteUserQuotaCommand> for DeleteUserQuotaHandler<R> {
type Output = bool;
async fn handle(&self, cmd: DeleteUserQuotaCommand) -> Result<Self::Output> {
if cmd.hard_delete {
self.repository.delete(&cmd.id).await
} else {
self.repository.soft_delete(&cmd.id).await
}
}
}