From 6cd332f7586992a77d3ac795b072006e40923a44 Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Sat, 9 May 2026 14:30:11 +0200 Subject: [PATCH] todo: exporter --- Dockerfile | 6 +- crates/application/src/commands.rs | 6 ++ .../application/src/use_cases/export_diary.rs | 23 +++++++ crates/application/src/use_cases/mod.rs | 1 + crates/domain/src/models/mod.rs | 64 +++++++++++++++---- crates/domain/src/ports.rs | 12 +++- 6 files changed, 96 insertions(+), 16 deletions(-) create mode 100644 crates/application/src/use_cases/export_diary.rs diff --git a/Dockerfile b/Dockerfile index 221c11b..697341e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -36,7 +36,11 @@ RUN sqlite3 /build/dev.db \ sqlite3 /build/dev.db \ < crates/adapters/sqlite/migrations/0002_users.sql && \ sqlite3 /build/dev.db \ - < crates/adapters/sqlite/migrations/0003_activitypub.sql + < crates/adapters/sqlite/migrations/0003_activitypub.sql && \ + sqlite3 /build/dev.db \ + < crates/adapters/sqlite/migrations/0004_username.sql && \ + sqlite3 /build/dev.db \ + < crates/adapters/sqlite/migrations/0005_activitypub_v2.sql ENV DATABASE_URL=sqlite:///build/dev.db diff --git a/crates/application/src/commands.rs b/crates/application/src/commands.rs index a2b1360..9a48d08 100644 --- a/crates/application/src/commands.rs +++ b/crates/application/src/commands.rs @@ -1,4 +1,5 @@ use chrono::NaiveDateTime; +use domain::models::ExportFormat; use uuid::Uuid; pub struct LogReviewCommand { @@ -35,3 +36,8 @@ pub struct DeleteReviewCommand { pub review_id: Uuid, pub requesting_user_id: Uuid, } + +pub struct ExportCommand { + pub user_id: Uuid, + pub format: ExportFormat, +} diff --git a/crates/application/src/use_cases/export_diary.rs b/crates/application/src/use_cases/export_diary.rs new file mode 100644 index 0000000..3e17e25 --- /dev/null +++ b/crates/application/src/use_cases/export_diary.rs @@ -0,0 +1,23 @@ +use std::sync::Arc; + +use domain::{ + errors::DomainError, + ports::{DiaryExporter, MovieRepository}, +}; + +use crate::commands::ExportCommand; + +pub struct ExportDiary { + repository: Arc, + exporter: Arc, +} + +impl ExportDiary { + pub async fn execute(&self, req: ExportCommand) -> Result, DomainError> { + // 1. fetch all diary entries for the user + // 2. delegate serialization to the port (exporter) + + // Return bytes of the exported diary, which can be written to a file or returned in an HTTP response + Ok(vec![]) + } +} diff --git a/crates/application/src/use_cases/mod.rs b/crates/application/src/use_cases/mod.rs index 8f8bdb7..0ca71bd 100644 --- a/crates/application/src/use_cases/mod.rs +++ b/crates/application/src/use_cases/mod.rs @@ -1,4 +1,5 @@ pub mod delete_review; +pub mod export_diary; pub mod get_activity_feed; pub mod get_diary; pub mod get_review_history; diff --git a/crates/domain/src/models/mod.rs b/crates/domain/src/models/mod.rs index 0fc95cb..378cddf 100644 --- a/crates/domain/src/models/mod.rs +++ b/crates/domain/src/models/mod.rs @@ -262,7 +262,12 @@ pub struct User { impl User { pub fn new(email: Email, username: Username, password_hash: PasswordHash) -> Self { - Self { id: UserId::generate(), email, username, password_hash } + Self { + id: UserId::generate(), + email, + username, + password_hash, + } } pub fn from_persistence( @@ -271,17 +276,30 @@ impl User { username: Username, password_hash: PasswordHash, ) -> Self { - Self { id, email, username, password_hash } + Self { + id, + email, + username, + password_hash, + } } pub fn update_password(&mut self, new_hash: PasswordHash) { self.password_hash = new_hash; } - pub fn email(&self) -> &Email { &self.email } - pub fn username(&self) -> &Username { &self.username } - pub fn id(&self) -> &UserId { &self.id } - pub fn password_hash(&self) -> &PasswordHash { &self.password_hash } + pub fn email(&self) -> &Email { + &self.email + } + pub fn username(&self) -> &Username { + &self.username + } + pub fn id(&self) -> &UserId { + &self.id + } + pub fn password_hash(&self) -> &PasswordHash { + &self.password_hash + } } #[derive(Clone, Debug)] @@ -294,11 +312,20 @@ impl FeedEntry { pub fn new(entry: DiaryEntry, user_email: String) -> Self { Self { entry, user_email } } - pub fn movie(&self) -> &Movie { self.entry.movie() } - pub fn review(&self) -> &Review { self.entry.review() } - pub fn user_email(&self) -> &str { &self.user_email } + pub fn movie(&self) -> &Movie { + self.entry.movie() + } + pub fn review(&self) -> &Review { + self.entry.review() + } + pub fn user_email(&self) -> &str { + &self.user_email + } pub fn user_display_name(&self) -> &str { - self.user_email.split('@').next().unwrap_or(&self.user_email) + self.user_email + .split('@') + .next() + .unwrap_or(&self.user_email) } } @@ -312,9 +339,16 @@ pub struct UserSummary { impl UserSummary { pub fn new(user_id: UserId, email: Email, total_movies: i64, avg_rating: Option) -> Self { - Self { user_id, email, total_movies, avg_rating } + Self { + user_id, + email, + total_movies, + avg_rating, + } + } + pub fn email(&self) -> &str { + self.email.value() } - pub fn email(&self) -> &str { self.email.value() } } #[derive(Clone, Debug)] @@ -325,7 +359,6 @@ pub struct UserStats { pub most_active_month: Option, } - #[derive(Clone, Debug)] pub struct MonthActivity { pub year_month: String, @@ -354,3 +387,8 @@ pub struct UserTrends { pub top_directors: Vec, pub max_director_count: i64, } + +pub enum ExportFormat { + Csv, + Json, +} diff --git a/crates/domain/src/ports.rs b/crates/domain/src/ports.rs index 2f84716..631f423 100644 --- a/crates/domain/src/ports.rs +++ b/crates/domain/src/ports.rs @@ -6,7 +6,7 @@ use crate::{ events::DomainEvent, models::{ DiaryEntry, DiaryFilter, FeedEntry, Movie, Review, ReviewHistory, User, UserStats, - UserTrends, UserSummary, + UserSummary, UserTrends, collections::{PageParams, Paginated}, }, value_objects::{ @@ -57,7 +57,10 @@ pub trait MovieRepository: Send + Sync { pub enum MetadataSearchCriteria { ImdbId(ExternalMetadataId), - Title { title: MovieTitle, year: Option }, + Title { + title: MovieTitle, + year: Option, + }, } #[async_trait] @@ -119,3 +122,8 @@ pub trait PasswordHasher: Send + Sync { async fn verify(&self, plain_password: &str, hash: &PasswordHash) -> Result; } + +#[async_trait] +pub trait DiaryExporter: Send + Sync { + async fn serialize_reviews(&self, reviews: &[Review]) -> Result, DomainError>; +}