refactor: LOW+MEDIUM cleanups — CQRS DiaryQuery/GoalCommand+GoalQuery,

test-helpers out of production, decompose get_user_profile_html,
dedup secure_flag, remove vestigial social_query, drop
PersistedImportSession, LoginCommand rename, DeleteAccountDeps,
PersonId macro, ReviewSortBy rename, TUI Command for fs::read,
generic PaginatedResponse<T>, noop ports for production fallbacks
This commit is contained in:
2026-07-10 05:29:02 +02:00
parent 5e0dde656c
commit 6a9b4e5c00
52 changed files with 405 additions and 298 deletions

View File

@@ -11,7 +11,7 @@ use crate::{
};
#[async_trait]
pub trait DiaryRepository: Send + Sync {
pub trait DiaryQuery: Send + Sync {
async fn query_diary(&self, filter: &DiaryFilter)
-> Result<Paginated<DiaryEntry>, DomainError>;
async fn query_activity_feed(

View File

@@ -7,10 +7,14 @@ use crate::{
};
#[async_trait]
pub trait GoalRepository: Send + Sync {
pub trait GoalCommand: Send + Sync {
async fn save(&self, goal: &Goal) -> Result<(), DomainError>;
async fn update(&self, goal: &Goal) -> Result<(), DomainError>;
async fn delete(&self, id: &GoalId, user_id: &UserId) -> Result<(), DomainError>;
}
#[async_trait]
pub trait GoalQuery: Send + Sync {
async fn find_by_user_and_year(
&self,
user_id: &UserId,

View File

@@ -9,6 +9,7 @@ pub mod import;
pub mod jobs;
pub mod media_server;
pub mod movie;
pub mod noop;
pub mod person;
pub mod rss;
pub mod search;

View File

@@ -0,0 +1,65 @@
use async_trait::async_trait;
use crate::{errors::DomainError, value_objects::UserId};
// ── NoopRemoteWatchlistRepository ─────────────────────────────────────────────
/// Stub used when federation is disabled — every operation is a no-op.
pub struct NoopRemoteWatchlistRepository;
#[async_trait]
impl super::RemoteWatchlistRepository for NoopRemoteWatchlistRepository {
async fn save(&self, _: crate::models::RemoteWatchlistEntry) -> Result<(), DomainError> {
Ok(())
}
async fn remove_by_ap_id(&self, _: &str, _: &str) -> Result<(), DomainError> {
Ok(())
}
async fn get_by_actor_url(
&self,
_: &str,
) -> Result<Vec<crate::models::RemoteWatchlistEntry>, DomainError> {
Ok(vec![])
}
async fn remove_all_by_actor(&self, _: &str) -> Result<(), DomainError> {
Ok(())
}
async fn get_by_derived_uuid(
&self,
_: uuid::Uuid,
) -> Result<Vec<crate::models::RemoteWatchlistEntry>, DomainError> {
Ok(vec![])
}
}
// ── NoopSocialQueryPort ───────────────────────────────────────────────────────
/// Stub used when federation is disabled — returns empty results.
pub struct NoopSocialQueryPort;
#[async_trait]
impl super::SocialQueryPort for NoopSocialQueryPort {
async fn get_accepted_following_urls(
&self,
_: &UserId,
) -> Result<Vec<String>, DomainError> {
Ok(vec![])
}
async fn list_all_followed_remote_actors(
&self,
) -> Result<Vec<crate::models::RemoteActorInfo>, DomainError> {
Ok(vec![])
}
async fn count_following(&self, _: &UserId) -> Result<usize, DomainError> {
Ok(0)
}
async fn count_accepted_followers(&self, _: &UserId) -> Result<usize, DomainError> {
Ok(0)
}
async fn get_pending_followers(
&self,
_: &UserId,
) -> Result<Vec<crate::models::PendingFollowerInfo>, DomainError> {
Ok(vec![])
}
}

View File

@@ -16,7 +16,7 @@ use crate::{
collections::{PageParams, Paginated},
},
ports::{
AuthService, DiaryRepository, DocumentParser, MetadataClient, MovieEnrichmentClient,
AuthService, DiaryQuery, DocumentParser, MetadataClient, MovieEnrichmentClient,
PasswordHasher, PersonQuery, PosterFetcherClient, SearchCommand, SearchPort,
StatsRepository,
},
@@ -81,13 +81,13 @@ impl MetadataClient for FakeMetadataClient {
}
}
// ── FakeDiaryRepository ───────────────────────────────────────────────────────
// ── FakeDiaryQuery ────────────────────────────────────────────────────────────
pub struct FakeDiaryRepository {
pub struct FakeDiaryQuery {
histories: Mutex<HashMap<Uuid, (Movie, Vec<Review>)>>,
}
impl FakeDiaryRepository {
impl FakeDiaryQuery {
pub fn new() -> Arc<Self> {
Arc::new(Self {
histories: Mutex::new(HashMap::new()),
@@ -103,7 +103,7 @@ impl FakeDiaryRepository {
}
#[async_trait]
impl DiaryRepository for FakeDiaryRepository {
impl DiaryQuery for FakeDiaryQuery {
async fn query_diary(
&self,
_filter: &DiaryFilter,

View File

@@ -17,7 +17,7 @@ use crate::{
collections::{PageParams, Paginated},
},
ports::{
GoalRepository, ImportProfileRepository, ImportSessionRepository, MovieCommand,
GoalCommand, GoalQuery, ImportProfileRepository, ImportSessionRepository, MovieCommand,
MovieProfileRepository, MovieQuery, RefreshSessionRepository, ReviewRepository,
UserFederationSettingsQuery, UserProfileFieldsRepository, UserRepository,
UserSettingsRepository, WatchEventCommand, WatchEventQuery, WatchlistRepository,
@@ -357,7 +357,7 @@ impl InMemoryGoalRepository {
}
#[async_trait]
impl GoalRepository for InMemoryGoalRepository {
impl GoalCommand for InMemoryGoalRepository {
async fn save(&self, goal: &Goal) -> Result<(), DomainError> {
self.store
.lock()
@@ -383,7 +383,10 @@ impl GoalRepository for InMemoryGoalRepository {
self.store.lock().unwrap().remove(&id.value());
Ok(())
}
}
#[async_trait]
impl GoalQuery for InMemoryGoalRepository {
async fn find_by_user_and_year(
&self,
user_id: &UserId,

View File

@@ -62,78 +62,17 @@ impl ObjectStorage for NoopObjectStorage {
}
}
// ── NoopRemoteWatchlistRepository ─────────────────────────────────────────────
// Re-export production noop types so test code that imports from
// `domain::testing` keeps compiling without changes.
pub use crate::ports::noop::NoopRemoteWatchlistRepository;
pub use crate::ports::noop::NoopSocialQueryPort;
pub struct NoopRemoteWatchlistRepository;
// ── NoopGoalCommand ───────────────────────────────────────────────────────────
pub struct NoopGoalCommand;
#[async_trait]
impl crate::ports::RemoteWatchlistRepository for NoopRemoteWatchlistRepository {
async fn save(&self, _: crate::models::RemoteWatchlistEntry) -> Result<(), DomainError> {
Ok(())
}
async fn remove_by_ap_id(&self, _: &str, _: &str) -> Result<(), DomainError> {
Ok(())
}
async fn get_by_actor_url(
&self,
_: &str,
) -> Result<Vec<crate::models::RemoteWatchlistEntry>, DomainError> {
Ok(vec![])
}
async fn remove_all_by_actor(&self, _: &str) -> Result<(), DomainError> {
Ok(())
}
async fn get_by_derived_uuid(
&self,
_: uuid::Uuid,
) -> Result<Vec<crate::models::RemoteWatchlistEntry>, DomainError> {
Ok(vec![])
}
}
// ── NoopSocialQueryPort ───────────────────────────────────────────────────────
pub struct NoopSocialQueryPort;
#[async_trait]
impl crate::ports::SocialQueryPort for NoopSocialQueryPort {
async fn get_accepted_following_urls(
&self,
_: &crate::value_objects::UserId,
) -> Result<Vec<String>, DomainError> {
Ok(vec![])
}
async fn list_all_followed_remote_actors(
&self,
) -> Result<Vec<crate::models::RemoteActorInfo>, DomainError> {
Ok(vec![])
}
async fn count_following(
&self,
_: &crate::value_objects::UserId,
) -> Result<usize, DomainError> {
Ok(0)
}
async fn count_accepted_followers(
&self,
_: &crate::value_objects::UserId,
) -> Result<usize, DomainError> {
Ok(0)
}
async fn get_pending_followers(
&self,
_: &crate::value_objects::UserId,
) -> Result<Vec<crate::models::PendingFollowerInfo>, DomainError> {
Ok(vec![])
}
}
// ── NoopGoalRepository ────────────────────────────────────────────────────────
pub struct NoopGoalRepository;
#[async_trait]
impl crate::ports::GoalRepository for NoopGoalRepository {
impl crate::ports::GoalCommand for NoopGoalCommand {
async fn save(&self, _: &crate::models::Goal) -> Result<(), DomainError> {
Ok(())
}
@@ -147,6 +86,14 @@ impl crate::ports::GoalRepository for NoopGoalRepository {
) -> Result<(), DomainError> {
Ok(())
}
}
// ── NoopGoalQuery ─────────────────────────────────────────────────────────────
pub struct NoopGoalQuery;
#[async_trait]
impl crate::ports::GoalQuery for NoopGoalQuery {
async fn find_by_user_and_year(
&self,
_: &UserId,

View File

@@ -11,7 +11,7 @@ use crate::{
collections::{PageParams, Paginated},
},
ports::{
DiaryExporter, DiaryRepository, DocumentParser, ImportProfileRepository,
DiaryExporter, DiaryQuery, DocumentParser, ImportProfileRepository,
ImportSessionRepository, MovieProfileRepository, PersonCommand, PersonQuery,
PosterFetcherClient, RefreshSessionRepository, SearchCommand, SearchPort, StatsRepository,
UserProfileFieldsRepository,
@@ -19,20 +19,20 @@ use crate::{
value_objects::{ImportProfileId, ImportSessionId, MovieId, PosterUrl, UserId},
};
// ── PanicDiaryRepository ──────────────────────────────────────────────────────
// ── PanicDiaryQuery ───────────────────────────────────────────────────────────
pub struct PanicDiaryRepository;
pub struct PanicDiaryQuery;
#[async_trait]
impl DiaryRepository for PanicDiaryRepository {
impl DiaryQuery for PanicDiaryQuery {
async fn query_diary(&self, _: &DiaryFilter) -> Result<Paginated<DiaryEntry>, DomainError> {
panic!("PanicDiaryRepository called")
panic!("PanicDiaryQuery called")
}
async fn query_activity_feed(
&self,
_: &PageParams,
) -> Result<Paginated<FeedEntry>, DomainError> {
panic!("PanicDiaryRepository called")
panic!("PanicDiaryQuery called")
}
async fn query_activity_feed_filtered(
&self,
@@ -41,32 +41,32 @@ impl DiaryRepository for PanicDiaryRepository {
_: Option<&str>,
_: Option<&FollowingFilter>,
) -> Result<Paginated<FeedEntry>, DomainError> {
panic!("PanicDiaryRepository called")
panic!("PanicDiaryQuery called")
}
async fn get_review_history(&self, _: &MovieId) -> Result<ReviewHistory, DomainError> {
panic!("PanicDiaryRepository called")
panic!("PanicDiaryQuery called")
}
async fn get_user_history(&self, _: &UserId) -> Result<Vec<DiaryEntry>, DomainError> {
panic!("PanicDiaryRepository called")
panic!("PanicDiaryQuery called")
}
fn stream_user_history(
&self,
_: UserId,
) -> futures::stream::BoxStream<'static, Result<DiaryEntry, DomainError>> {
panic!("PanicDiaryRepository called")
panic!("PanicDiaryQuery called")
}
async fn get_movie_stats(&self, _: &MovieId) -> Result<MovieStats, DomainError> {
panic!("PanicDiaryRepository called")
panic!("PanicDiaryQuery called")
}
async fn get_movie_social_feed(
&self,
_: &MovieId,
_: &PageParams,
) -> Result<Paginated<FeedEntry>, DomainError> {
panic!("PanicDiaryRepository called")
panic!("PanicDiaryQuery called")
}
async fn count_local_posts(&self) -> Result<u64, DomainError> {
panic!("PanicDiaryRepository called")
panic!("PanicDiaryQuery called")
}
}