From 9dcd1696898f5931ecb662fad1bbed252130e10f Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Sun, 12 Jul 2026 04:10:58 +0200 Subject: [PATCH] refactor(application): strip comments, DRY ownership check + parse_content_type --- crates/application/src/admin/activity_log.rs | 1 - crates/application/src/admin/commands.rs | 1 - crates/application/src/admin/deps.rs | 1 - crates/application/src/admin/get_settings.rs | 1 - crates/application/src/admin/queries.rs | 2 -- .../application/src/admin/update_settings.rs | 3 --- crates/application/src/auth/commands.rs | 2 -- crates/application/src/auth/deps.rs | 4 ---- crates/application/src/auth/login.rs | 16 ++++--------- crates/application/src/auth/queries.rs | 1 - crates/application/src/auth/register.rs | 10 -------- crates/application/src/channels/commands.rs | 6 ----- crates/application/src/channels/create.rs | 3 --- crates/application/src/channels/delete.rs | 16 +++---------- crates/application/src/channels/deps.rs | 2 -- crates/application/src/channels/get.rs | 1 - crates/application/src/channels/list.rs | 1 - .../application/src/channels/list_by_owner.rs | 1 - crates/application/src/channels/mod.rs | 24 +++++++++++++++++++ crates/application/src/channels/queries.rs | 3 --- crates/application/src/channels/update.rs | 22 ++++------------- .../src/config_snapshots/commands.rs | 3 --- .../application/src/config_snapshots/deps.rs | 1 - .../application/src/config_snapshots/get.rs | 1 - .../application/src/config_snapshots/list.rs | 1 - .../src/config_snapshots/patch_label.rs | 1 - .../src/config_snapshots/queries.rs | 2 -- .../src/config_snapshots/restore.rs | 6 ----- .../application/src/config_snapshots/save.rs | 3 --- crates/application/src/iptv/deps.rs | 1 - crates/application/src/iptv/m3u.rs | 3 --- crates/application/src/iptv/queries.rs | 2 -- crates/application/src/iptv/xmltv.rs | 4 ---- crates/application/src/library/commands.rs | 2 -- crates/application/src/library/deps.rs | 2 -- crates/application/src/library/get_item.rs | 1 - .../src/library/get_sync_status.rs | 1 - .../src/library/list_collections.rs | 1 - crates/application/src/library/list_genres.rs | 16 ++----------- .../application/src/library/list_seasons.rs | 1 - crates/application/src/library/list_shows.rs | 1 - crates/application/src/library/mod.rs | 14 +++++++++++ crates/application/src/library/queries.rs | 7 ------ crates/application/src/library/search.rs | 17 +++---------- crates/application/src/library/sync.rs | 9 ------- crates/application/src/providers/commands.rs | 2 -- crates/application/src/providers/delete.rs | 1 - crates/application/src/providers/deps.rs | 1 - crates/application/src/providers/get.rs | 1 - crates/application/src/providers/list.rs | 1 - crates/application/src/providers/queries.rs | 2 -- crates/application/src/providers/upsert.rs | 1 - crates/application/src/schedule/commands.rs | 2 -- .../application/src/schedule/delete_after.rs | 1 - crates/application/src/schedule/deps.rs | 1 - crates/application/src/schedule/generate.rs | 4 ---- crates/application/src/schedule/get_active.rs | 3 --- .../src/schedule/get_current_broadcast.rs | 4 ---- crates/application/src/schedule/get_epg.rs | 4 ---- .../src/schedule/get_stream_url.rs | 3 --- .../application/src/schedule/list_history.rs | 1 - crates/application/src/schedule/queries.rs | 6 ----- 62 files changed, 56 insertions(+), 203 deletions(-) diff --git a/crates/application/src/admin/activity_log.rs b/crates/application/src/admin/activity_log.rs index d0efd2b..6c5d3c3 100644 --- a/crates/application/src/admin/activity_log.rs +++ b/crates/application/src/admin/activity_log.rs @@ -4,7 +4,6 @@ use domain::DomainResult; use super::deps::AdminDeps; use super::queries::GetActivityLogQuery; -/// Get recent activity log entries. pub async fn execute( deps: &AdminDeps, query: GetActivityLogQuery, diff --git a/crates/application/src/admin/commands.rs b/crates/application/src/admin/commands.rs index cbe9cd8..c9f795f 100644 --- a/crates/application/src/admin/commands.rs +++ b/crates/application/src/admin/commands.rs @@ -1,4 +1,3 @@ -/// Update one or more admin settings (key-value pairs). pub struct UpdateSettingsCommand { pub settings: Vec<(String, String)>, } diff --git a/crates/application/src/admin/deps.rs b/crates/application/src/admin/deps.rs index 9ab55cc..d2d72e1 100644 --- a/crates/application/src/admin/deps.rs +++ b/crates/application/src/admin/deps.rs @@ -2,7 +2,6 @@ use std::sync::Arc; use domain::ports::{ActivityLogQuery, AppSettingsRepository}; -/// Dependencies for admin use cases. pub struct AdminDeps { pub settings_repo: Arc, pub activity_query: Arc, diff --git a/crates/application/src/admin/get_settings.rs b/crates/application/src/admin/get_settings.rs index 11f20f7..53086eb 100644 --- a/crates/application/src/admin/get_settings.rs +++ b/crates/application/src/admin/get_settings.rs @@ -3,7 +3,6 @@ use domain::DomainResult; use super::deps::AdminDeps; use super::queries::GetSettingsQuery; -/// Get all admin settings as key-value pairs. pub async fn execute( deps: &AdminDeps, _query: GetSettingsQuery, diff --git a/crates/application/src/admin/queries.rs b/crates/application/src/admin/queries.rs index d2b4d9e..d151c85 100644 --- a/crates/application/src/admin/queries.rs +++ b/crates/application/src/admin/queries.rs @@ -1,7 +1,5 @@ -/// Get all admin settings. pub struct GetSettingsQuery; -/// Get recent activity log entries. pub struct GetActivityLogQuery { pub limit: u32, } diff --git a/crates/application/src/admin/update_settings.rs b/crates/application/src/admin/update_settings.rs index 4b77605..151151b 100644 --- a/crates/application/src/admin/update_settings.rs +++ b/crates/application/src/admin/update_settings.rs @@ -3,9 +3,6 @@ use domain::DomainResult; use super::commands::UpdateSettingsCommand; use super::deps::AdminDeps; -/// Update one or more admin settings. -/// -/// Iterates key/value pairs and upserts each one. pub async fn execute(deps: &AdminDeps, cmd: UpdateSettingsCommand) -> DomainResult<()> { for (key, value) in &cmd.settings { deps.settings_repo.set(key, value).await?; diff --git a/crates/application/src/auth/commands.rs b/crates/application/src/auth/commands.rs index 47f22bc..7a44ad9 100644 --- a/crates/application/src/auth/commands.rs +++ b/crates/application/src/auth/commands.rs @@ -1,10 +1,8 @@ -/// Register a new local user. pub struct RegisterCommand { pub email: String, pub password: String, } -/// Log in with email + password. pub struct LoginCommand { pub email: String, pub password: String, diff --git a/crates/application/src/auth/deps.rs b/crates/application/src/auth/deps.rs index 415d9c2..52cb941 100644 --- a/crates/application/src/auth/deps.rs +++ b/crates/application/src/auth/deps.rs @@ -2,10 +2,6 @@ use std::sync::Arc; use domain::ports::{AuthService, EventPublisher, UserCommand, UserQuery}; -/// Dependencies for auth use cases. -/// -/// Aggregates the ports required by register/login operations. -/// Built once at startup and shared via `Arc` or passed by reference. pub struct AuthDeps { pub user_command: Arc, pub user_query: Arc, diff --git a/crates/application/src/auth/login.rs b/crates/application/src/auth/login.rs index 20cc1d9..f940022 100644 --- a/crates/application/src/auth/login.rs +++ b/crates/application/src/auth/login.rs @@ -4,30 +4,24 @@ use domain::{DomainError, DomainResult, Email}; use super::commands::LoginCommand; use super::deps::AuthDeps; -/// Log in with email + password. -/// -/// Flow: validate email -> find user -> verify password -> return User. -/// JWT generation belongs in the presentation layer, not here. +const INVALID_CREDENTIALS: &str = "Invalid credentials"; + pub async fn execute(deps: &AuthDeps, cmd: LoginCommand) -> DomainResult { - // Validate email format let email = Email::new(&cmd.email)?; - // Find user let user = deps .user_query .find_by_email(email.as_ref()) .await? - .ok_or_else(|| DomainError::unauthenticated("Invalid credentials"))?; + .ok_or_else(|| DomainError::unauthenticated(INVALID_CREDENTIALS))?; - // Must have a password hash (not an OIDC-only user) let hash = user .password_hash() - .ok_or_else(|| DomainError::unauthenticated("Invalid credentials"))?; + .ok_or_else(|| DomainError::unauthenticated(INVALID_CREDENTIALS))?; - // Verify password let valid = deps.auth_service.verify_password(&cmd.password, hash)?; if !valid { - return Err(DomainError::unauthenticated("Invalid credentials")); + return Err(DomainError::unauthenticated(INVALID_CREDENTIALS)); } Ok(user) diff --git a/crates/application/src/auth/queries.rs b/crates/application/src/auth/queries.rs index a7ecfdd..e69de29 100644 --- a/crates/application/src/auth/queries.rs +++ b/crates/application/src/auth/queries.rs @@ -1 +0,0 @@ -// Auth queries (reserved for future use, e.g. GetCurrentUserQuery). diff --git a/crates/application/src/auth/register.rs b/crates/application/src/auth/register.rs index 15439d2..702dd66 100644 --- a/crates/application/src/auth/register.rs +++ b/crates/application/src/auth/register.rs @@ -5,33 +5,23 @@ use domain::{DomainResult, Email, Password}; use super::commands::RegisterCommand; use super::deps::AuthDeps; -/// Register a new local user. -/// -/// Flow: validate email/password -> check duplicate -> hash password -> -/// create User (first user gets admin) -> save -> publish event -> return User. pub async fn execute(deps: &AuthDeps, cmd: RegisterCommand) -> DomainResult { - // Validate inputs via domain value objects let email = Email::new(&cmd.email)?; let password = Password::new(&cmd.password)?; - // Check for duplicate if deps.user_query.find_by_email(email.as_ref()).await?.is_some() { return Err(domain::DomainError::UserAlreadyExists(cmd.email)); } - // Hash password let hash = deps.auth_service.hash_password(password.as_ref())?; - // Create user; first user gets admin let mut user = User::new_local(email, hash); if deps.user_query.count_users().await? == 0 { user.promote_to_admin(); } - // Persist deps.user_command.save(&user).await?; - // Publish event deps.event_publisher .publish(DomainEvent::UserRegistered { user_id: user.id(), diff --git a/crates/application/src/channels/commands.rs b/crates/application/src/channels/commands.rs index 1fcca9a..ad2f217 100644 --- a/crates/application/src/channels/commands.rs +++ b/crates/application/src/channels/commands.rs @@ -3,20 +3,16 @@ use uuid::Uuid; use domain::models::ScheduleConfig; use domain::value_objects::RecyclePolicy; -/// Create a new channel. pub struct CreateChannelCommand { pub owner_id: Uuid, pub name: String, pub timezone: String, } -/// Update an existing channel (partial — only `Some` fields are applied). pub struct UpdateChannelCommand { pub channel_id: Uuid, - /// Used for ownership check. pub owner_id: Uuid, pub name: Option, - /// `Some(None)` clears the description; `None` leaves it unchanged. pub description: Option>, pub timezone: Option, pub schedule_config: Option, @@ -24,9 +20,7 @@ pub struct UpdateChannelCommand { pub auto_schedule: Option, } -/// Delete a channel. pub struct DeleteChannelCommand { pub channel_id: Uuid, - /// Used for ownership check. pub owner_id: Uuid, } diff --git a/crates/application/src/channels/create.rs b/crates/application/src/channels/create.rs index 1e0373e..52d6f65 100644 --- a/crates/application/src/channels/create.rs +++ b/crates/application/src/channels/create.rs @@ -6,9 +6,6 @@ use domain::DomainResult; use super::commands::CreateChannelCommand; use super::deps::ChannelCommandDeps; -/// Create a new channel. -/// -/// Flow: convert raw IDs -> build Channel -> save -> publish event -> return. pub async fn execute(deps: &ChannelCommandDeps, cmd: CreateChannelCommand) -> DomainResult { let owner_id = UserId::from(cmd.owner_id); let channel = Channel::new(owner_id, cmd.name, cmd.timezone); diff --git a/crates/application/src/channels/delete.rs b/crates/application/src/channels/delete.rs index f72114a..e2025af 100644 --- a/crates/application/src/channels/delete.rs +++ b/crates/application/src/channels/delete.rs @@ -1,26 +1,16 @@ use domain::events::DomainEvent; use domain::value_objects::{ChannelId, UserId}; -use domain::{DomainError, DomainResult}; +use domain::DomainResult; use super::commands::DeleteChannelCommand; use super::deps::ChannelCommandDeps; +use super::find_owned_channel; -/// Delete a channel after verifying ownership. -/// -/// Flow: find channel -> verify ownership -> delete -> publish event. pub async fn execute(deps: &ChannelCommandDeps, cmd: DeleteChannelCommand) -> DomainResult<()> { let channel_id = ChannelId::from(cmd.channel_id); let owner_id = UserId::from(cmd.owner_id); - let channel = deps - .channel_query - .find_by_id(channel_id) - .await? - .ok_or(DomainError::ChannelNotFound(cmd.channel_id))?; - - if channel.owner_id() != owner_id { - return Err(DomainError::forbidden("You don't own this channel")); - } + find_owned_channel(deps.channel_query.as_ref(), channel_id, owner_id, cmd.channel_id).await?; deps.channel_command.delete(channel_id).await?; diff --git a/crates/application/src/channels/deps.rs b/crates/application/src/channels/deps.rs index 01d5b0c..3eec997 100644 --- a/crates/application/src/channels/deps.rs +++ b/crates/application/src/channels/deps.rs @@ -2,14 +2,12 @@ use std::sync::Arc; use domain::ports::{ChannelCommand, ChannelQuery, EventPublisher}; -/// Dependencies for channel write use cases (create, update, delete). pub struct ChannelCommandDeps { pub channel_command: Arc, pub channel_query: Arc, pub event_publisher: Arc, } -/// Dependencies for channel read use cases (get, list, list_by_owner). pub struct ChannelQueryDeps { pub channel_query: Arc, } diff --git a/crates/application/src/channels/get.rs b/crates/application/src/channels/get.rs index e9234c9..4eb9529 100644 --- a/crates/application/src/channels/get.rs +++ b/crates/application/src/channels/get.rs @@ -5,7 +5,6 @@ use domain::DomainResult; use super::deps::ChannelQueryDeps; use super::queries::GetChannelQuery; -/// Get a single channel by ID. pub async fn execute(deps: &ChannelQueryDeps, query: GetChannelQuery) -> DomainResult> { let channel_id = ChannelId::from(query.channel_id); deps.channel_query.find_by_id(channel_id).await diff --git a/crates/application/src/channels/list.rs b/crates/application/src/channels/list.rs index 4aa1003..06bce5e 100644 --- a/crates/application/src/channels/list.rs +++ b/crates/application/src/channels/list.rs @@ -4,7 +4,6 @@ use domain::DomainResult; use super::deps::ChannelQueryDeps; use super::queries::ListChannelsQuery; -/// List all channels. pub async fn execute(deps: &ChannelQueryDeps, _query: ListChannelsQuery) -> DomainResult> { deps.channel_query.find_all().await } diff --git a/crates/application/src/channels/list_by_owner.rs b/crates/application/src/channels/list_by_owner.rs index 76d1ec5..a2bb7e9 100644 --- a/crates/application/src/channels/list_by_owner.rs +++ b/crates/application/src/channels/list_by_owner.rs @@ -5,7 +5,6 @@ use domain::DomainResult; use super::deps::ChannelQueryDeps; use super::queries::ListByOwnerQuery; -/// List channels belonging to a specific owner. pub async fn execute(deps: &ChannelQueryDeps, query: ListByOwnerQuery) -> DomainResult> { let owner_id = UserId::from(query.owner_id); deps.channel_query.find_by_owner(owner_id).await diff --git a/crates/application/src/channels/mod.rs b/crates/application/src/channels/mod.rs index 1040e43..ef67bd7 100644 --- a/crates/application/src/channels/mod.rs +++ b/crates/application/src/channels/mod.rs @@ -11,3 +11,27 @@ pub mod update; pub use commands::{CreateChannelCommand, DeleteChannelCommand, UpdateChannelCommand}; pub use deps::{ChannelCommandDeps, ChannelQueryDeps}; pub use queries::{GetChannelQuery, ListByOwnerQuery, ListChannelsQuery}; + +use domain::models::Channel; +use domain::value_objects::{ChannelId, UserId}; +use domain::{DomainError, DomainResult}; + +const OWNERSHIP_DENIED: &str = "You don't own this channel"; + +pub(crate) async fn find_owned_channel( + query: &dyn domain::ports::ChannelQuery, + channel_id: ChannelId, + owner_id: UserId, + raw_channel_id: uuid::Uuid, +) -> DomainResult { + let channel = query + .find_by_id(channel_id) + .await? + .ok_or(DomainError::ChannelNotFound(raw_channel_id))?; + + if channel.owner_id() != owner_id { + return Err(DomainError::forbidden(OWNERSHIP_DENIED)); + } + + Ok(channel) +} diff --git a/crates/application/src/channels/queries.rs b/crates/application/src/channels/queries.rs index 5971a5b..ec5c443 100644 --- a/crates/application/src/channels/queries.rs +++ b/crates/application/src/channels/queries.rs @@ -1,14 +1,11 @@ use uuid::Uuid; -/// Fetch a single channel by ID. pub struct GetChannelQuery { pub channel_id: Uuid, } -/// List all channels. pub struct ListChannelsQuery; -/// List channels belonging to a specific owner. pub struct ListByOwnerQuery { pub owner_id: Uuid, } diff --git a/crates/application/src/channels/update.rs b/crates/application/src/channels/update.rs index a4de223..7fe33ee 100644 --- a/crates/application/src/channels/update.rs +++ b/crates/application/src/channels/update.rs @@ -1,38 +1,26 @@ use domain::events::DomainEvent; use domain::models::Channel; use domain::value_objects::{ChannelId, UserId}; -use domain::{DomainError, DomainResult}; +use domain::DomainResult; use super::commands::UpdateChannelCommand; use super::deps::ChannelCommandDeps; +use super::find_owned_channel; -/// Update an existing channel. -/// -/// Flow: find channel -> verify ownership -> snapshot config if changed -> -/// apply updates -> save -> publish event -> return. pub async fn execute(deps: &ChannelCommandDeps, cmd: UpdateChannelCommand) -> DomainResult { let channel_id = ChannelId::from(cmd.channel_id); let owner_id = UserId::from(cmd.owner_id); - let mut channel = deps - .channel_query - .find_by_id(channel_id) - .await? - .ok_or(DomainError::ChannelNotFound(cmd.channel_id))?; + let mut channel = + find_owned_channel(deps.channel_query.as_ref(), channel_id, owner_id, cmd.channel_id) + .await?; - // Ownership check - if channel.owner_id() != owner_id { - return Err(DomainError::forbidden("You don't own this channel")); - } - - // Auto-snapshot the current config before overwriting if cmd.schedule_config.is_some() { deps.channel_command .save_config_snapshot(channel_id, channel.schedule_config(), None) .await?; } - // Apply partial updates if let Some(name) = cmd.name { channel.set_name(name); } diff --git a/crates/application/src/config_snapshots/commands.rs b/crates/application/src/config_snapshots/commands.rs index 72d70a3..c847166 100644 --- a/crates/application/src/config_snapshots/commands.rs +++ b/crates/application/src/config_snapshots/commands.rs @@ -1,19 +1,16 @@ use uuid::Uuid; -/// Save a snapshot of the channel's current config. pub struct SaveSnapshotCommand { pub channel_id: Uuid, pub label: Option, } -/// Update the label on an existing snapshot. pub struct PatchLabelCommand { pub channel_id: Uuid, pub snapshot_id: Uuid, pub label: Option, } -/// Restore a channel's config from a snapshot. pub struct RestoreSnapshotCommand { pub channel_id: Uuid, pub snapshot_id: Uuid, diff --git a/crates/application/src/config_snapshots/deps.rs b/crates/application/src/config_snapshots/deps.rs index 3e8fc1c..57f7368 100644 --- a/crates/application/src/config_snapshots/deps.rs +++ b/crates/application/src/config_snapshots/deps.rs @@ -2,7 +2,6 @@ use std::sync::Arc; use domain::ports::{ChannelCommand, ChannelQuery}; -/// Dependencies for config snapshot use cases. pub struct ConfigSnapshotDeps { pub channel_command: Arc, pub channel_query: Arc, diff --git a/crates/application/src/config_snapshots/get.rs b/crates/application/src/config_snapshots/get.rs index 2b7e4ba..7aec656 100644 --- a/crates/application/src/config_snapshots/get.rs +++ b/crates/application/src/config_snapshots/get.rs @@ -5,7 +5,6 @@ use domain::DomainResult; use super::deps::ConfigSnapshotDeps; use super::queries::GetSnapshotQuery; -/// Get a specific config snapshot by channel and snapshot ID. pub async fn execute( deps: &ConfigSnapshotDeps, query: GetSnapshotQuery, diff --git a/crates/application/src/config_snapshots/list.rs b/crates/application/src/config_snapshots/list.rs index 3693ba0..d06cb9c 100644 --- a/crates/application/src/config_snapshots/list.rs +++ b/crates/application/src/config_snapshots/list.rs @@ -5,7 +5,6 @@ use domain::DomainResult; use super::deps::ConfigSnapshotDeps; use super::queries::ListSnapshotsQuery; -/// List all config snapshots for a channel, newest first. pub async fn execute( deps: &ConfigSnapshotDeps, query: ListSnapshotsQuery, diff --git a/crates/application/src/config_snapshots/patch_label.rs b/crates/application/src/config_snapshots/patch_label.rs index f7a2a4e..2f20dc2 100644 --- a/crates/application/src/config_snapshots/patch_label.rs +++ b/crates/application/src/config_snapshots/patch_label.rs @@ -5,7 +5,6 @@ use domain::DomainResult; use super::commands::PatchLabelCommand; use super::deps::ConfigSnapshotDeps; -/// Update the label on an existing config snapshot. pub async fn execute( deps: &ConfigSnapshotDeps, cmd: PatchLabelCommand, diff --git a/crates/application/src/config_snapshots/queries.rs b/crates/application/src/config_snapshots/queries.rs index cd26185..1a0060e 100644 --- a/crates/application/src/config_snapshots/queries.rs +++ b/crates/application/src/config_snapshots/queries.rs @@ -1,11 +1,9 @@ use uuid::Uuid; -/// List all config snapshots for a channel (newest first). pub struct ListSnapshotsQuery { pub channel_id: Uuid, } -/// Get a specific config snapshot. pub struct GetSnapshotQuery { pub channel_id: Uuid, pub snapshot_id: Uuid, diff --git a/crates/application/src/config_snapshots/restore.rs b/crates/application/src/config_snapshots/restore.rs index c495081..0b0e19a 100644 --- a/crates/application/src/config_snapshots/restore.rs +++ b/crates/application/src/config_snapshots/restore.rs @@ -5,10 +5,6 @@ use domain::{DomainError, DomainResult}; use super::commands::RestoreSnapshotCommand; use super::deps::ConfigSnapshotDeps; -/// Restore a channel's config from a snapshot. -/// -/// Flow: find snapshot -> find channel -> snapshot current config (backup) -> -/// apply snapshot config to channel -> save channel -> return updated channel. pub async fn execute( deps: &ConfigSnapshotDeps, cmd: RestoreSnapshotCommand, @@ -30,12 +26,10 @@ pub async fn execute( .await? .ok_or(DomainError::ChannelNotFound(cmd.channel_id))?; - // Auto-snapshot the current config before overwriting deps.channel_command .save_config_snapshot(channel_id, channel.schedule_config(), None) .await?; - // Apply the snapshot's config channel.set_schedule_config(snapshot.config().clone()); deps.channel_command.save(&channel).await?; diff --git a/crates/application/src/config_snapshots/save.rs b/crates/application/src/config_snapshots/save.rs index d6654bf..ad12f1a 100644 --- a/crates/application/src/config_snapshots/save.rs +++ b/crates/application/src/config_snapshots/save.rs @@ -5,9 +5,6 @@ use domain::{DomainError, DomainResult}; use super::commands::SaveSnapshotCommand; use super::deps::ConfigSnapshotDeps; -/// Save a snapshot of the channel's current schedule config. -/// -/// Flow: find channel -> snapshot its current config -> return snapshot. pub async fn execute( deps: &ConfigSnapshotDeps, cmd: SaveSnapshotCommand, diff --git a/crates/application/src/iptv/deps.rs b/crates/application/src/iptv/deps.rs index 900503b..e21ba84 100644 --- a/crates/application/src/iptv/deps.rs +++ b/crates/application/src/iptv/deps.rs @@ -2,7 +2,6 @@ use std::sync::Arc; use domain::ports::{ChannelQuery, ScheduleQuery}; -/// Dependencies for IPTV export use cases. pub struct IptvDeps { pub channel_query: Arc, pub schedule_query: Arc, diff --git a/crates/application/src/iptv/m3u.rs b/crates/application/src/iptv/m3u.rs index 3832b56..1fe7762 100644 --- a/crates/application/src/iptv/m3u.rs +++ b/crates/application/src/iptv/m3u.rs @@ -4,9 +4,6 @@ use domain::DomainResult; use super::deps::IptvDeps; use super::queries::GetM3uQuery; -/// Generate an M3U playlist for all channels. -/// -/// Flow: fetch all channels -> delegate to domain::generate_m3u -> return string. pub async fn execute(deps: &IptvDeps, query: GetM3uQuery) -> DomainResult { let channels = deps.channel_query.find_all().await?; let token = query.token.as_deref().unwrap_or(""); diff --git a/crates/application/src/iptv/queries.rs b/crates/application/src/iptv/queries.rs index 7c550d8..756b41e 100644 --- a/crates/application/src/iptv/queries.rs +++ b/crates/application/src/iptv/queries.rs @@ -1,8 +1,6 @@ -/// Generate an M3U playlist for all channels. pub struct GetM3uQuery { pub base_url: String, pub token: Option, } -/// Generate an XMLTV EPG document for all channels. pub struct GetXmltvQuery; diff --git a/crates/application/src/iptv/xmltv.rs b/crates/application/src/iptv/xmltv.rs index a6c3657..784f21d 100644 --- a/crates/application/src/iptv/xmltv.rs +++ b/crates/application/src/iptv/xmltv.rs @@ -8,10 +8,6 @@ use domain::DomainResult; use super::deps::IptvDeps; use super::queries::GetXmltvQuery; -/// Generate an XMLTV EPG document for all channels with active schedules. -/// -/// Flow: fetch all channels -> for each, find active schedule -> collect slots -/// -> delegate to domain::generate_xmltv -> return string. pub async fn execute(deps: &IptvDeps, _query: GetXmltvQuery) -> DomainResult { let channels = deps.channel_query.find_all().await?; let now = Utc::now(); diff --git a/crates/application/src/library/commands.rs b/crates/application/src/library/commands.rs index 8eb7f91..615cc99 100644 --- a/crates/application/src/library/commands.rs +++ b/crates/application/src/library/commands.rs @@ -1,5 +1,3 @@ -/// Trigger a library sync for one or all providers. pub struct TriggerSyncCommand { - /// Provider to sync. `None` means sync all registered providers. pub provider_id: Option, } diff --git a/crates/application/src/library/deps.rs b/crates/application/src/library/deps.rs index 1cb7eb3..9d82ec1 100644 --- a/crates/application/src/library/deps.rs +++ b/crates/application/src/library/deps.rs @@ -2,7 +2,6 @@ use std::sync::Arc; use domain::ports::{EventPublisher, IProviderRegistry, LibraryCommand, LibraryQuery, LibrarySyncAdapter}; -/// Dependencies for library write use cases (trigger sync). pub struct LibraryCommandDeps { pub library_command: Arc, pub library_query: Arc, @@ -11,7 +10,6 @@ pub struct LibraryCommandDeps { pub event_publisher: Arc, } -/// Dependencies for library read use cases (search, list, get). pub struct LibraryQueryDeps { pub library_query: Arc, } diff --git a/crates/application/src/library/get_item.rs b/crates/application/src/library/get_item.rs index 8f47834..6e86b94 100644 --- a/crates/application/src/library/get_item.rs +++ b/crates/application/src/library/get_item.rs @@ -4,7 +4,6 @@ use domain::DomainResult; use super::deps::LibraryQueryDeps; use super::queries::GetItemQuery; -/// Get a single library item by its composite ID. pub async fn execute( deps: &LibraryQueryDeps, query: GetItemQuery, diff --git a/crates/application/src/library/get_sync_status.rs b/crates/application/src/library/get_sync_status.rs index 98fcbd1..5316470 100644 --- a/crates/application/src/library/get_sync_status.rs +++ b/crates/application/src/library/get_sync_status.rs @@ -4,7 +4,6 @@ use domain::DomainResult; use super::deps::LibraryQueryDeps; use super::queries::GetSyncStatusQuery; -/// Get the latest sync status per provider. pub async fn execute( deps: &LibraryQueryDeps, _query: GetSyncStatusQuery, diff --git a/crates/application/src/library/list_collections.rs b/crates/application/src/library/list_collections.rs index 8121c0d..0e80314 100644 --- a/crates/application/src/library/list_collections.rs +++ b/crates/application/src/library/list_collections.rs @@ -4,7 +4,6 @@ use domain::DomainResult; use super::deps::LibraryQueryDeps; use super::queries::ListCollectionsQuery; -/// List library collections, optionally filtered by provider. pub async fn execute( deps: &LibraryQueryDeps, query: ListCollectionsQuery, diff --git a/crates/application/src/library/list_genres.rs b/crates/application/src/library/list_genres.rs index f4c8cfc..fb982df 100644 --- a/crates/application/src/library/list_genres.rs +++ b/crates/application/src/library/list_genres.rs @@ -1,10 +1,9 @@ -use domain::errors::{DomainError, DomainResult}; -use domain::value_objects::ContentType; +use domain::DomainResult; use super::deps::LibraryQueryDeps; +use super::parse_content_type; use super::queries::ListGenresQuery; -/// List genres available in the library, optionally filtered. pub async fn execute(deps: &LibraryQueryDeps, query: ListGenresQuery) -> DomainResult> { let content_type = query .content_type @@ -17,17 +16,6 @@ pub async fn execute(deps: &LibraryQueryDeps, query: ListGenresQuery) -> DomainR .await } -fn parse_content_type(s: &str) -> DomainResult { - match s { - "movie" => Ok(ContentType::Movie), - "episode" => Ok(ContentType::Episode), - "short" => Ok(ContentType::Short), - other => Err(DomainError::ValidationError(format!( - "Unknown content type '{other}'. Use movie, episode, or short." - ))), - } -} - #[cfg(test)] #[path = "tests/list_genres.rs"] mod tests; diff --git a/crates/application/src/library/list_seasons.rs b/crates/application/src/library/list_seasons.rs index ddaafc2..5a13a7b 100644 --- a/crates/application/src/library/list_seasons.rs +++ b/crates/application/src/library/list_seasons.rs @@ -4,7 +4,6 @@ use domain::DomainResult; use super::deps::LibraryQueryDeps; use super::queries::ListSeasonsQuery; -/// List season summaries for a specific series. pub async fn execute( deps: &LibraryQueryDeps, query: ListSeasonsQuery, diff --git a/crates/application/src/library/list_shows.rs b/crates/application/src/library/list_shows.rs index c4dc46d..e026c66 100644 --- a/crates/application/src/library/list_shows.rs +++ b/crates/application/src/library/list_shows.rs @@ -4,7 +4,6 @@ use domain::DomainResult; use super::deps::LibraryQueryDeps; use super::queries::ListShowsQuery; -/// List TV show summaries, optionally filtered. pub async fn execute( deps: &LibraryQueryDeps, query: ListShowsQuery, diff --git a/crates/application/src/library/mod.rs b/crates/application/src/library/mod.rs index a56f295..7df4d95 100644 --- a/crates/application/src/library/mod.rs +++ b/crates/application/src/library/mod.rs @@ -16,3 +16,17 @@ pub use queries::{ GetItemQuery, GetSyncStatusQuery, ListCollectionsQuery, ListGenresQuery, ListSeasonsQuery, ListShowsQuery, SearchItemsQuery, }; + +use domain::errors::{DomainError, DomainResult}; +use domain::value_objects::ContentType; + +pub(crate) fn parse_content_type(s: &str) -> DomainResult { + match s { + "movie" => Ok(ContentType::Movie), + "episode" => Ok(ContentType::Episode), + "short" => Ok(ContentType::Short), + other => Err(DomainError::ValidationError(format!( + "Unknown content type '{other}'. Use movie, episode, or short." + ))), + } +} diff --git a/crates/application/src/library/queries.rs b/crates/application/src/library/queries.rs index 23dec23..1d21761 100644 --- a/crates/application/src/library/queries.rs +++ b/crates/application/src/library/queries.rs @@ -1,4 +1,3 @@ -/// Search library items with filters. pub struct SearchItemsQuery { pub provider_id: Option, pub content_type: Option, @@ -12,34 +11,28 @@ pub struct SearchItemsQuery { pub limit: u32, } -/// List library collections. pub struct ListCollectionsQuery { pub provider_id: Option, } -/// List TV show summaries. pub struct ListShowsQuery { pub provider_id: Option, pub search_term: Option, pub genres: Vec, } -/// List seasons for a specific series. pub struct ListSeasonsQuery { pub series_name: String, pub provider_id: Option, } -/// List genres available in the library. pub struct ListGenresQuery { pub content_type: Option, pub provider_id: Option, } -/// Get a single library item by its composite ID. pub struct GetItemQuery { pub item_id: String, } -/// Get the latest sync status per provider. pub struct GetSyncStatusQuery; diff --git a/crates/application/src/library/search.rs b/crates/application/src/library/search.rs index 2cb5837..d6dd69e 100644 --- a/crates/application/src/library/search.rs +++ b/crates/application/src/library/search.rs @@ -1,11 +1,11 @@ -use domain::errors::{DomainError, DomainResult}; +use domain::DomainResult; use domain::models::LibraryItem; -use domain::value_objects::{ContentType, LibrarySearchFilter}; +use domain::value_objects::LibrarySearchFilter; use super::deps::LibraryQueryDeps; +use super::parse_content_type; use super::queries::SearchItemsQuery; -/// Search library items with filters. Returns `(items, total_count)`. pub async fn execute( deps: &LibraryQueryDeps, query: SearchItemsQuery, @@ -48,17 +48,6 @@ pub async fn execute( deps.library_query.search(&filter).await } -fn parse_content_type(s: &str) -> DomainResult { - match s { - "movie" => Ok(ContentType::Movie), - "episode" => Ok(ContentType::Episode), - "short" => Ok(ContentType::Short), - other => Err(DomainError::ValidationError(format!( - "Unknown content type '{other}'. Use movie, episode, or short." - ))), - } -} - #[cfg(test)] #[path = "tests/search.rs"] mod tests; diff --git a/crates/application/src/library/sync.rs b/crates/application/src/library/sync.rs index e9e5c01..d781885 100644 --- a/crates/application/src/library/sync.rs +++ b/crates/application/src/library/sync.rs @@ -3,15 +3,6 @@ use domain::errors::{DomainError, DomainResult}; use super::commands::TriggerSyncCommand; use super::deps::LibraryCommandDeps; -/// Validate and return provider IDs eligible for sync. -/// -/// Checks that no sync is already running for the targeted provider(s). -/// Returns the list of provider IDs to sync. The caller (API layer) is -/// responsible for spawning the actual sync tasks, since `LibrarySyncAdapter` -/// requires `&dyn IMediaProvider` references that only the infra layer holds. -/// -/// Returns `Err(ValidationError)` if any targeted provider is already syncing -/// (maps to 409 Conflict at the API layer). pub async fn execute( deps: &LibraryCommandDeps, cmd: TriggerSyncCommand, diff --git a/crates/application/src/providers/commands.rs b/crates/application/src/providers/commands.rs index 5eff0e9..346106b 100644 --- a/crates/application/src/providers/commands.rs +++ b/crates/application/src/providers/commands.rs @@ -1,4 +1,3 @@ -/// Insert or update a provider configuration. pub struct UpsertProviderCommand { pub id: String, pub provider_type: String, @@ -6,7 +5,6 @@ pub struct UpsertProviderCommand { pub enabled: bool, } -/// Delete a provider configuration. pub struct DeleteProviderCommand { pub id: String, } diff --git a/crates/application/src/providers/delete.rs b/crates/application/src/providers/delete.rs index 34cc6fc..46ca4e2 100644 --- a/crates/application/src/providers/delete.rs +++ b/crates/application/src/providers/delete.rs @@ -3,7 +3,6 @@ use domain::DomainResult; use super::commands::DeleteProviderCommand; use super::deps::ProviderDeps; -/// Delete a provider configuration by ID. pub async fn execute(deps: &ProviderDeps, cmd: DeleteProviderCommand) -> DomainResult<()> { deps.provider_config_command.delete(&cmd.id).await } diff --git a/crates/application/src/providers/deps.rs b/crates/application/src/providers/deps.rs index 3976cc2..5d4556f 100644 --- a/crates/application/src/providers/deps.rs +++ b/crates/application/src/providers/deps.rs @@ -2,7 +2,6 @@ use std::sync::Arc; use domain::ports::{ProviderConfigCommand, ProviderConfigQuery}; -/// Dependencies for provider config use cases. pub struct ProviderDeps { pub provider_config_command: Arc, pub provider_config_query: Arc, diff --git a/crates/application/src/providers/get.rs b/crates/application/src/providers/get.rs index 5e00c75..fd7b7b1 100644 --- a/crates/application/src/providers/get.rs +++ b/crates/application/src/providers/get.rs @@ -4,7 +4,6 @@ use domain::DomainResult; use super::deps::ProviderDeps; use super::queries::GetProviderQuery; -/// Get a provider configuration by ID. pub async fn execute( deps: &ProviderDeps, query: GetProviderQuery, diff --git a/crates/application/src/providers/list.rs b/crates/application/src/providers/list.rs index 7451580..ded566a 100644 --- a/crates/application/src/providers/list.rs +++ b/crates/application/src/providers/list.rs @@ -4,7 +4,6 @@ use domain::DomainResult; use super::deps::ProviderDeps; use super::queries::ListProvidersQuery; -/// List all provider configurations. pub async fn execute( deps: &ProviderDeps, _query: ListProvidersQuery, diff --git a/crates/application/src/providers/queries.rs b/crates/application/src/providers/queries.rs index a5290b2..dc86b5c 100644 --- a/crates/application/src/providers/queries.rs +++ b/crates/application/src/providers/queries.rs @@ -1,7 +1,5 @@ -/// List all provider configurations. pub struct ListProvidersQuery; -/// Get a provider configuration by ID. pub struct GetProviderQuery { pub id: String, } diff --git a/crates/application/src/providers/upsert.rs b/crates/application/src/providers/upsert.rs index a3e1b7b..4405fc8 100644 --- a/crates/application/src/providers/upsert.rs +++ b/crates/application/src/providers/upsert.rs @@ -4,7 +4,6 @@ use domain::DomainResult; use super::commands::UpsertProviderCommand; use super::deps::ProviderDeps; -/// Insert or update a provider configuration. pub async fn execute(deps: &ProviderDeps, cmd: UpsertProviderCommand) -> DomainResult<()> { let row = ProviderConfigRow::from_persistence( cmd.id, diff --git a/crates/application/src/schedule/commands.rs b/crates/application/src/schedule/commands.rs index e07d5c8..e1cd14a 100644 --- a/crates/application/src/schedule/commands.rs +++ b/crates/application/src/schedule/commands.rs @@ -1,11 +1,9 @@ use uuid::Uuid; -/// Generate a new schedule for a channel. pub struct GenerateScheduleCommand { pub channel_id: Uuid, } -/// Delete all schedules with generation > target_generation for a channel. pub struct DeleteSchedulesAfterCommand { pub channel_id: Uuid, pub target_generation: u32, diff --git a/crates/application/src/schedule/delete_after.rs b/crates/application/src/schedule/delete_after.rs index 8ac8bcc..223e06d 100644 --- a/crates/application/src/schedule/delete_after.rs +++ b/crates/application/src/schedule/delete_after.rs @@ -4,7 +4,6 @@ use domain::DomainResult; use super::commands::DeleteSchedulesAfterCommand; use super::deps::ScheduleDeps; -/// Delete all schedules with generation > target_generation for a channel. pub async fn execute( deps: &ScheduleDeps, cmd: DeleteSchedulesAfterCommand, diff --git a/crates/application/src/schedule/deps.rs b/crates/application/src/schedule/deps.rs index ebd61d0..7fb49ed 100644 --- a/crates/application/src/schedule/deps.rs +++ b/crates/application/src/schedule/deps.rs @@ -3,7 +3,6 @@ use std::sync::Arc; use domain::ports::{ChannelQuery, EventPublisher, ScheduleCommand, ScheduleQuery}; use domain::ScheduleEngineService; -/// Dependencies for schedule use cases. pub struct ScheduleDeps { pub schedule_engine: Arc, pub channel_query: Arc, diff --git a/crates/application/src/schedule/generate.rs b/crates/application/src/schedule/generate.rs index 8032c3f..1f0f8b3 100644 --- a/crates/application/src/schedule/generate.rs +++ b/crates/application/src/schedule/generate.rs @@ -8,10 +8,6 @@ use domain::DomainResult; use super::commands::GenerateScheduleCommand; use super::deps::ScheduleDeps; -/// Generate a new 7-day schedule for a channel. -/// -/// Delegates the heavy lifting to `ScheduleEngineService::generate_schedule`, -/// then publishes a `ScheduleGenerated` domain event. pub async fn execute( deps: &ScheduleDeps, cmd: GenerateScheduleCommand, diff --git a/crates/application/src/schedule/get_active.rs b/crates/application/src/schedule/get_active.rs index 66c4eb6..b8a7284 100644 --- a/crates/application/src/schedule/get_active.rs +++ b/crates/application/src/schedule/get_active.rs @@ -7,9 +7,6 @@ use domain::DomainResult; use super::deps::ScheduleDeps; use super::queries::GetActiveScheduleQuery; -/// Fetch the schedule currently active at `now`. -/// -/// Returns `None` when no schedule covers the current time. pub async fn execute( deps: &ScheduleDeps, query: GetActiveScheduleQuery, diff --git a/crates/application/src/schedule/get_current_broadcast.rs b/crates/application/src/schedule/get_current_broadcast.rs index bad0acd..e40f66e 100644 --- a/crates/application/src/schedule/get_current_broadcast.rs +++ b/crates/application/src/schedule/get_current_broadcast.rs @@ -7,10 +7,6 @@ use domain::{DomainResult, ScheduleEngineService}; use super::deps::ScheduleDeps; use super::queries::GetCurrentBroadcastQuery; -/// Determine what is currently broadcasting on a channel. -/// -/// Returns `None` when no schedule is active or `now` falls in a gap -/// between blocks (no-signal / static screen). pub async fn execute( deps: &ScheduleDeps, query: GetCurrentBroadcastQuery, diff --git a/crates/application/src/schedule/get_epg.rs b/crates/application/src/schedule/get_epg.rs index 2e6ae20..20926a7 100644 --- a/crates/application/src/schedule/get_epg.rs +++ b/crates/application/src/schedule/get_epg.rs @@ -7,10 +7,6 @@ use domain::{DomainResult, ScheduleEngineService}; use super::deps::ScheduleDeps; use super::queries::GetEpgQuery; -/// Return EPG (electronic program guide) data for a channel. -/// -/// Returns the slots that overlap the active schedule's validity window. -/// Returns an empty vec when no schedule is active. pub async fn execute( deps: &ScheduleDeps, query: GetEpgQuery, diff --git a/crates/application/src/schedule/get_stream_url.rs b/crates/application/src/schedule/get_stream_url.rs index fbdd27e..f994139 100644 --- a/crates/application/src/schedule/get_stream_url.rs +++ b/crates/application/src/schedule/get_stream_url.rs @@ -5,9 +5,6 @@ use domain::DomainResult; use super::deps::ScheduleDeps; use super::queries::GetStreamUrlQuery; -/// Resolve a playback URL for a media item. -/// -/// Delegates to the schedule engine which routes via the provider registry. pub async fn execute(deps: &ScheduleDeps, query: GetStreamUrlQuery) -> DomainResult { let item_id = MediaItemId::new(&query.item_id); deps.schedule_engine diff --git a/crates/application/src/schedule/list_history.rs b/crates/application/src/schedule/list_history.rs index db511bc..794d83e 100644 --- a/crates/application/src/schedule/list_history.rs +++ b/crates/application/src/schedule/list_history.rs @@ -5,7 +5,6 @@ use domain::DomainResult; use super::deps::ScheduleDeps; use super::queries::ListHistoryQuery; -/// List all generated schedule headers for a channel, newest first. pub async fn execute( deps: &ScheduleDeps, query: ListHistoryQuery, diff --git a/crates/application/src/schedule/queries.rs b/crates/application/src/schedule/queries.rs index cf0fd96..da24850 100644 --- a/crates/application/src/schedule/queries.rs +++ b/crates/application/src/schedule/queries.rs @@ -1,28 +1,22 @@ use uuid::Uuid; -/// Fetch the schedule whose validity window contains `now`. pub struct GetActiveScheduleQuery { pub channel_id: Uuid, } -/// Determine what is currently broadcasting on a channel. pub struct GetCurrentBroadcastQuery { pub channel_id: Uuid, } -/// Return EPG (electronic program guide) data for a channel. pub struct GetEpgQuery { pub channel_id: Uuid, } -/// Get a playback URL for a specific media item on a channel. pub struct GetStreamUrlQuery { pub channel_id: Uuid, - /// MediaItemId as string (e.g. "jellyfin::abc123"). pub item_id: String, } -/// List all generated schedule headers for a channel. pub struct ListHistoryQuery { pub channel_id: Uuid, }