refactor(domain): Row structs for from_persistence, ID newtypes, kill clippy.toml
- delete clippy.toml (too-many-arguments-threshold=20 hack) - ChannelRow/MediaItemRow/LibraryItemRow structs for from_persistence - SnapshotId/ActivityEventId/PlaybackRecordId newtypes - DomainError variants use ChannelId/UserId instead of Uuid - ActivityEvent.channel_id: Option<ChannelId> not Option<Uuid> - InMemory repos key on newtype IDs - AlgorithmicParams struct for schedule engine - update all adapters/application/presentation callers
This commit is contained in:
@@ -6,7 +6,7 @@ use uuid::Uuid;
|
||||
use adapter_common::{map_sqlx_error, parse_dt, parse_uuid};
|
||||
use domain::{
|
||||
ports::activity::{ActivityLogCommand, ActivityLogQuery},
|
||||
ActivityEvent, ChannelId, DomainResult,
|
||||
ActivityEvent, ActivityEventId, ChannelId, DomainResult,
|
||||
};
|
||||
|
||||
pub struct PgActivityLog {
|
||||
@@ -66,9 +66,11 @@ impl ActivityLogQuery for PgActivityLog {
|
||||
let Ok(timestamp) = parse_dt(&ts_str) else {
|
||||
continue;
|
||||
};
|
||||
let channel_id = channel_id_str.and_then(|s| Uuid::parse_str(&s).ok());
|
||||
let channel_id = channel_id_str
|
||||
.and_then(|s| Uuid::parse_str(&s).ok())
|
||||
.map(ChannelId::from_uuid);
|
||||
events.push(ActivityEvent::from_persistence(
|
||||
id,
|
||||
ActivityEventId::from_uuid(id),
|
||||
timestamp,
|
||||
event_type,
|
||||
detail,
|
||||
|
||||
@@ -9,8 +9,8 @@ use adapter_common::{
|
||||
};
|
||||
use domain::{
|
||||
ports::channel::{ChannelCommand, ChannelQuery},
|
||||
AccessMode, Channel, ChannelConfigSnapshot, ChannelId, DomainError, DomainResult, LogoPosition,
|
||||
ScheduleConfig, UserId,
|
||||
Channel, ChannelConfigSnapshot, ChannelId, ChannelRow as DomainChannelRow, DomainError,
|
||||
DomainResult, ScheduleConfig, SnapshotId, UserId,
|
||||
};
|
||||
|
||||
pub struct PgChannelRepository {
|
||||
@@ -50,34 +50,27 @@ struct ChannelRow {
|
||||
|
||||
impl ChannelRow {
|
||||
fn into_channel(self) -> DomainResult<Channel> {
|
||||
let id = ChannelId::from_uuid(parse_uuid(&self.id, "channel id")?);
|
||||
let owner_id = UserId::from_uuid(parse_uuid(&self.owner_id, "owner id")?);
|
||||
let schedule_config = parse_schedule_config(&self.schedule_config)?;
|
||||
let recycle_policy = parse_recycle_policy(&self.recycle_policy)?;
|
||||
let access_mode: AccessMode = parse_enum_or_default(self.access_mode);
|
||||
let logo_position: LogoPosition = parse_enum_or_default(self.logo_position);
|
||||
|
||||
Ok(Channel::from_persistence(
|
||||
id,
|
||||
owner_id,
|
||||
self.name,
|
||||
self.description,
|
||||
self.timezone,
|
||||
schedule_config,
|
||||
recycle_policy,
|
||||
self.auto_schedule,
|
||||
access_mode,
|
||||
self.access_password_hash,
|
||||
self.logo,
|
||||
logo_position,
|
||||
self.logo_opacity,
|
||||
self.webhook_url,
|
||||
self.webhook_poll_interval_secs as u32,
|
||||
self.webhook_body_template,
|
||||
self.webhook_headers,
|
||||
parse_dt(&self.created_at)?,
|
||||
parse_dt(&self.updated_at)?,
|
||||
))
|
||||
Ok(Channel::from_persistence(DomainChannelRow {
|
||||
id: ChannelId::from_uuid(parse_uuid(&self.id, "channel id")?),
|
||||
owner_id: UserId::from_uuid(parse_uuid(&self.owner_id, "owner id")?),
|
||||
name: self.name,
|
||||
description: self.description,
|
||||
timezone: self.timezone,
|
||||
schedule_config: parse_schedule_config(&self.schedule_config)?,
|
||||
recycle_policy: parse_recycle_policy(&self.recycle_policy)?,
|
||||
auto_schedule: self.auto_schedule,
|
||||
access_mode: parse_enum_or_default(self.access_mode),
|
||||
access_password_hash: self.access_password_hash,
|
||||
logo: self.logo,
|
||||
logo_position: parse_enum_or_default(self.logo_position),
|
||||
logo_opacity: self.logo_opacity,
|
||||
webhook_url: self.webhook_url,
|
||||
webhook_poll_interval_secs: self.webhook_poll_interval_secs as u32,
|
||||
webhook_body_template: self.webhook_body_template,
|
||||
webhook_headers: self.webhook_headers,
|
||||
created_at: parse_dt(&self.created_at)?,
|
||||
updated_at: parse_dt(&self.updated_at)?,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +79,7 @@ fn map_snapshot_row(
|
||||
channel_id: ChannelId,
|
||||
) -> DomainResult<ChannelConfigSnapshot> {
|
||||
let id_str: String = row.get("id");
|
||||
let id = parse_uuid(&id_str, "snapshot id")?;
|
||||
let id = SnapshotId::from_uuid(parse_uuid(&id_str, "snapshot id")?);
|
||||
let config_json: String = row.get("config_json");
|
||||
let config = parse_schedule_config(&config_json)?;
|
||||
let version_num: i64 = row.get("version_num");
|
||||
@@ -214,7 +207,7 @@ impl ChannelCommand for PgChannelRepository {
|
||||
tx.commit().await.map_err(map_sqlx_error)?;
|
||||
|
||||
Ok(ChannelConfigSnapshot::from_persistence(
|
||||
id,
|
||||
SnapshotId::from_uuid(id),
|
||||
channel_id,
|
||||
config.clone(),
|
||||
version_num,
|
||||
@@ -226,14 +219,14 @@ impl ChannelCommand for PgChannelRepository {
|
||||
async fn patch_config_snapshot_label(
|
||||
&self,
|
||||
channel_id: ChannelId,
|
||||
snapshot_id: Uuid,
|
||||
snapshot_id: SnapshotId,
|
||||
label: Option<String>,
|
||||
) -> DomainResult<Option<ChannelConfigSnapshot>> {
|
||||
let updated = sqlx::query(
|
||||
"UPDATE channel_config_snapshots SET label = $1 WHERE id = $2 AND channel_id = $3 RETURNING id",
|
||||
)
|
||||
.bind(&label)
|
||||
.bind(snapshot_id.to_string())
|
||||
.bind(snapshot_id.value().to_string())
|
||||
.bind(channel_id.value().to_string())
|
||||
.fetch_optional(&self.pool)
|
||||
.await
|
||||
@@ -316,13 +309,13 @@ impl ChannelQuery for PgChannelRepository {
|
||||
async fn get_config_snapshot(
|
||||
&self,
|
||||
channel_id: ChannelId,
|
||||
snapshot_id: Uuid,
|
||||
snapshot_id: SnapshotId,
|
||||
) -> DomainResult<Option<ChannelConfigSnapshot>> {
|
||||
let row = sqlx::query(
|
||||
"SELECT id, config_json, version_num, label, created_at
|
||||
FROM channel_config_snapshots WHERE id = $1 AND channel_id = $2",
|
||||
)
|
||||
.bind(snapshot_id.to_string())
|
||||
.bind(snapshot_id.value().to_string())
|
||||
.bind(channel_id.value().to_string())
|
||||
.fetch_optional(&self.pool)
|
||||
.await
|
||||
|
||||
@@ -4,8 +4,9 @@ use sqlx::PgPool;
|
||||
use adapter_common::{content_type_str, parse_content_type, parse_genres_blob};
|
||||
use domain::{
|
||||
ports::library::{LibraryCommand, LibraryQuery},
|
||||
ContentType, DomainError, DomainResult, LibraryCollection, LibraryItem, LibrarySearchFilter,
|
||||
LibrarySyncLogEntry, LibrarySyncResult, SeasonSummary, ShowSummary,
|
||||
ContentType, DomainError, DomainResult, LibraryCollection, LibraryItem,
|
||||
LibraryItemRow as DomainLibraryItemRow, LibrarySearchFilter, LibrarySyncLogEntry,
|
||||
LibrarySyncResult, SeasonSummary, ShowSummary,
|
||||
};
|
||||
|
||||
pub struct PgLibraryRepository {
|
||||
@@ -41,25 +42,25 @@ struct LibraryItemRow {
|
||||
|
||||
impl LibraryItemRow {
|
||||
fn into_library_item(self) -> LibraryItem {
|
||||
LibraryItem::from_persistence(
|
||||
self.id,
|
||||
self.provider_id,
|
||||
self.external_id,
|
||||
self.title,
|
||||
parse_content_type(&self.content_type),
|
||||
self.duration_secs as u32,
|
||||
self.series_name,
|
||||
self.season_number.map(|n| n as u32),
|
||||
self.episode_number.map(|n| n as u32),
|
||||
self.year.map(|n| n as u16),
|
||||
serde_json::from_str(&self.genres).unwrap_or_default(),
|
||||
serde_json::from_str(&self.tags).unwrap_or_default(),
|
||||
self.collection_id,
|
||||
self.collection_name,
|
||||
self.collection_type,
|
||||
self.thumbnail_url,
|
||||
self.synced_at,
|
||||
)
|
||||
LibraryItem::from_persistence(DomainLibraryItemRow {
|
||||
id: self.id,
|
||||
provider_id: self.provider_id,
|
||||
external_id: self.external_id,
|
||||
title: self.title,
|
||||
content_type: parse_content_type(&self.content_type),
|
||||
duration_secs: self.duration_secs as u32,
|
||||
series_name: self.series_name,
|
||||
season_number: self.season_number.map(|n| n as u32),
|
||||
episode_number: self.episode_number.map(|n| n as u32),
|
||||
year: self.year.map(|n| n as u16),
|
||||
genres: serde_json::from_str(&self.genres).unwrap_or_default(),
|
||||
tags: serde_json::from_str(&self.tags).unwrap_or_default(),
|
||||
collection_id: self.collection_id,
|
||||
collection_name: self.collection_name,
|
||||
collection_type: self.collection_type,
|
||||
thumbnail_url: self.thumbnail_url,
|
||||
synced_at: self.synced_at,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ use adapter_common::{map_sqlx_error, parse_dt, parse_json, parse_uuid};
|
||||
use domain::{
|
||||
ports::schedule::{ScheduleCommand, ScheduleQuery},
|
||||
BlockId, ChannelId, DomainError, DomainResult, GeneratedSchedule, MediaItem, MediaItemId,
|
||||
PlaybackRecord, ScheduleId, ScheduledSlot, SlotId,
|
||||
PlaybackRecord, PlaybackRecordId, ScheduleId, ScheduledSlot, SlotId,
|
||||
};
|
||||
|
||||
pub struct PgScheduleRepository {
|
||||
@@ -85,7 +85,7 @@ fn map_schedule(row: ScheduleRow, slot_rows: Vec<SlotRow>) -> DomainResult<Gener
|
||||
}
|
||||
|
||||
fn map_playback_row(row: PlaybackRecordRow) -> DomainResult<PlaybackRecord> {
|
||||
let id = parse_uuid(&row.id, "playback record id")?;
|
||||
let id = PlaybackRecordId::from_uuid(parse_uuid(&row.id, "playback record id")?);
|
||||
let channel_id = ChannelId::from_uuid(parse_uuid(&row.channel_id, "channel id")?);
|
||||
|
||||
Ok(PlaybackRecord::from_persistence(
|
||||
|
||||
Reference in New Issue
Block a user