use super::traits::{Service, ServiceError, ServiceResult, ValidationContext};
use crate::core::models::{common::CardId, Card};
use crate::repository::{
card::{CardFilterParams, CardRepository},
traits::PaginationParams,
};
use async_trait::async_trait;
use std::sync::Arc;
#[async_trait]
pub trait CardService: Service {
async fn get_card(&self, id: CardId) -> ServiceResult<Card>;
async fn list_cards(
&self,
pagination: Option<PaginationParams>,
filters: Option<CardFilterParams>,
) -> ServiceResult<Vec<Card>>;
async fn create_card(&self, card: Card) -> ServiceResult<Card>;
async fn update_card(&self, id: CardId, card: Card) -> ServiceResult<Card>;
async fn delete_card(&self, id: CardId) -> ServiceResult<()>;
async fn archive_card(&self, id: CardId) -> ServiceResult<()>;
async fn unarchive_card(&self, id: CardId) -> ServiceResult<()>;
async fn copy_card(
&self,
id: CardId,
new_name: &str,
collection_id: Option<i32>,
) -> ServiceResult<Card>;
async fn validate_card(&self, card: &Card) -> ServiceResult<()>;
async fn execute_card_query(
&self,
id: CardId,
parameters: Option<serde_json::Value>,
) -> ServiceResult<crate::core::models::QueryResult>;
async fn export_card_query(
&self,
id: CardId,
format: crate::core::models::common::ExportFormat,
parameters: Option<serde_json::Value>,
) -> ServiceResult<Vec<u8>>;
async fn execute_card_pivot_query(
&self,
id: CardId,
parameters: Option<serde_json::Value>,
) -> ServiceResult<crate::core::models::QueryResult>;
}
pub struct HttpCardService {
repository: Arc<dyn CardRepository>,
}
impl HttpCardService {
pub fn new(repository: Arc<dyn CardRepository>) -> Self {
Self { repository }
}
fn validate_card_rules(&self, card: &Card) -> ServiceResult<()> {
let mut context = ValidationContext::new();
if card.name.trim().is_empty() {
context.add_error("Card name cannot be empty");
}
if card.name.len() > 255 {
context.add_error("Card name cannot exceed 255 characters");
}
if let Some(desc) = &card.description {
if desc.len() > 5000 {
context.add_error("Card description cannot exceed 5000 characters");
}
}
if let Some(query) = &card.dataset_query {
if query.is_null() {
context.add_error("Dataset query cannot be null");
}
}
context.to_result()
}
}
#[async_trait]
impl Service for HttpCardService {
fn name(&self) -> &str {
"CardService"
}
}
#[async_trait]
impl CardService for HttpCardService {
async fn get_card(&self, id: CardId) -> ServiceResult<Card> {
self.repository.get(&id).await.map_err(ServiceError::from)
}
async fn list_cards(
&self,
pagination: Option<PaginationParams>,
filters: Option<CardFilterParams>,
) -> ServiceResult<Vec<Card>> {
self.repository
.list_with_filters(pagination, filters)
.await
.map_err(ServiceError::from)
}
async fn create_card(&self, card: Card) -> ServiceResult<Card> {
self.validate_card_rules(&card)?;
self.repository
.create(&card)
.await
.map_err(ServiceError::from)
}
async fn update_card(&self, id: CardId, mut card: Card) -> ServiceResult<Card> {
card.id = Some(id);
self.validate_card_rules(&card)?;
self.repository.get(&id).await.map_err(ServiceError::from)?;
self.repository
.update(&id, &card)
.await
.map_err(ServiceError::from)
}
async fn delete_card(&self, id: CardId) -> ServiceResult<()> {
self.repository.get(&id).await.map_err(ServiceError::from)?;
self.repository
.delete(&id)
.await
.map_err(ServiceError::from)
}
async fn archive_card(&self, id: CardId) -> ServiceResult<()> {
self.repository
.archive(&id)
.await
.map_err(ServiceError::from)
}
async fn unarchive_card(&self, id: CardId) -> ServiceResult<()> {
self.repository
.unarchive(&id)
.await
.map_err(ServiceError::from)
}
async fn copy_card(
&self,
id: CardId,
new_name: &str,
_collection_id: Option<i32>,
) -> ServiceResult<Card> {
if new_name.trim().is_empty() {
return Err(ServiceError::Validation(
"New card name cannot be empty".to_string(),
));
}
self.repository
.copy(&id, new_name)
.await
.map_err(ServiceError::from)
}
async fn validate_card(&self, card: &Card) -> ServiceResult<()> {
self.validate_card_rules(card)
}
async fn execute_card_query(
&self,
id: CardId,
parameters: Option<serde_json::Value>,
) -> ServiceResult<crate::core::models::QueryResult> {
self.repository.get(&id).await.map_err(ServiceError::from)?;
self.repository
.execute_query(&id, parameters)
.await
.map_err(ServiceError::from)
}
async fn export_card_query(
&self,
id: CardId,
format: crate::core::models::common::ExportFormat,
parameters: Option<serde_json::Value>,
) -> ServiceResult<Vec<u8>> {
self.repository.get(&id).await.map_err(ServiceError::from)?;
self.repository
.export_query(&id, format, parameters)
.await
.map_err(ServiceError::from)
}
async fn execute_card_pivot_query(
&self,
id: CardId,
parameters: Option<serde_json::Value>,
) -> ServiceResult<crate::core::models::QueryResult> {
self.repository.get(&id).await.map_err(ServiceError::from)?;
self.repository
.execute_pivot_query(&id, parameters)
.await
.map_err(ServiceError::from)
}
}