cleanup: strip comments, extract constants, DRY shared helpers across adapters + infra-wiring

- strip all comments except WHY workaround notes (3 remain)
- remove all #[allow(dead_code)]; fix via _prefix rename
- extract named constants: JWT time units, token types, default config values, jellyfin fallback bitrate
- DRY: move serialize_enum_as_string, content_type_str, parse_content_type, parse_genres_blob to adapter-common
- sqlite+postgres library.rs use shared helpers instead of local copies
- sqlite+postgres channel.rs use shared serialize_enum_as_string
- remove dead `let _ = ext` in scanner.rs
This commit is contained in:
2026-07-12 04:21:21 +02:00
parent eff14228af
commit 25b33b6a0e
38 changed files with 188 additions and 630 deletions

View File

@@ -1,5 +1,3 @@
//! SQLite adapter for activity log (ActivityLogCommand + ActivityLogQuery).
use async_trait::async_trait;
use chrono::Utc;
use sqlx::SqlitePool;
@@ -62,7 +60,6 @@ impl ActivityLogQuery for SqliteActivityLog {
let mut events = Vec::with_capacity(rows.len());
for (id_str, ts_str, event_type, detail, channel_id_str) in rows {
// Silently skip rows with bad UUIDs/timestamps (mirrors old behaviour)
let Ok(id) = parse_uuid(&id_str, "activity id") else {
continue;
};

View File

@@ -1,5 +1,3 @@
//! SQLite adapter for channel persistence (ChannelCommand + ChannelQuery).
use async_trait::async_trait;
use chrono::{DateTime, Utc};
use sqlx::{Row, SqlitePool};
@@ -7,7 +5,7 @@ use uuid::Uuid;
use adapter_common::{
map_sqlx_error, parse_dt, parse_enum_or_default, parse_recycle_policy, parse_schedule_config,
parse_uuid,
parse_uuid, serialize_enum_as_string,
};
use domain::{
ports::channel::{ChannelCommand, ChannelQuery},
@@ -25,8 +23,6 @@ impl SqliteChannelRepository {
}
}
// -- Row type ----------------------------------------------------------------
const SELECT_COLS: &str = "id, owner_id, name, description, timezone, schedule_config, recycle_policy, auto_schedule, access_mode, access_password_hash, logo, logo_position, logo_opacity, webhook_url, webhook_poll_interval_secs, webhook_body_template, webhook_headers, created_at, updated_at";
#[derive(Debug, sqlx::FromRow)]
@@ -85,15 +81,6 @@ impl ChannelRow {
}
}
// -- Helpers ------------------------------------------------------------------
fn serialize_enum_as_string<T: serde::Serialize>(v: &T, fallback: &str) -> String {
serde_json::to_value(v)
.ok()
.and_then(|v| v.as_str().map(str::to_owned))
.unwrap_or_else(|| fallback.to_owned())
}
fn map_snapshot_row(
row: &sqlx::sqlite::SqliteRow,
channel_id: ChannelId,
@@ -117,8 +104,6 @@ fn map_snapshot_row(
))
}
// -- Command -----------------------------------------------------------------
#[async_trait]
impl ChannelCommand for SqliteChannelRepository {
async fn save(&self, channel: &Channel) -> DomainResult<()> {
@@ -261,8 +246,6 @@ impl ChannelCommand for SqliteChannelRepository {
}
}
// -- Query -------------------------------------------------------------------
#[async_trait]
impl ChannelQuery for SqliteChannelRepository {
async fn find_by_id(&self, id: ChannelId) -> DomainResult<Option<Channel>> {

View File

@@ -1,6 +1,3 @@
//! SQLite adapter crate — implements all CQRS-split repository port traits
//! for SQLite via sqlx.
pub mod activity;
pub mod channel;
pub mod library;

View File

@@ -1,10 +1,7 @@
//! SQLite adapter for library persistence (LibraryCommand + LibraryQuery).
use std::collections::HashSet;
use async_trait::async_trait;
use sqlx::SqlitePool;
use adapter_common::{content_type_str, parse_content_type, parse_genres_blob};
use domain::{
ports::library::{LibraryCommand, LibraryQuery},
ContentType, DomainError, DomainResult, LibraryCollection, LibraryItem, LibrarySearchFilter,
@@ -21,26 +18,6 @@ impl SqliteLibraryRepository {
}
}
// -- Helpers -----------------------------------------------------------------
fn content_type_str(ct: &ContentType) -> &'static str {
match ct {
ContentType::Movie => "movie",
ContentType::Episode => "episode",
ContentType::Short => "short",
}
}
fn parse_content_type(s: &str) -> ContentType {
match s {
"episode" => ContentType::Episode,
"short" => ContentType::Short,
_ => ContentType::Movie,
}
}
// -- Row types ---------------------------------------------------------------
#[derive(sqlx::FromRow)]
struct LibraryItemRow {
id: String,
@@ -113,8 +90,6 @@ struct SeasonSummaryRow {
thumbnail_url: Option<String>,
}
// -- Command -----------------------------------------------------------------
#[async_trait]
impl LibraryCommand for SqliteLibraryRepository {
async fn upsert_items(&self, _provider_id: &str, items: Vec<LibraryItem>) -> DomainResult<()> {
@@ -206,8 +181,6 @@ impl LibraryCommand for SqliteLibraryRepository {
}
}
// -- Query -------------------------------------------------------------------
#[async_trait]
impl LibraryQuery for SqliteLibraryRepository {
async fn search(
@@ -481,32 +454,12 @@ impl LibraryQuery for SqliteLibraryRepository {
Ok(rows
.into_iter()
.map(|r| {
let genres: Vec<String> = r
.genres_blob
.split("],[")
.flat_map(|chunk| {
let cleaned = chunk.trim_start_matches('[').trim_end_matches(']');
cleaned
.split(',')
.filter_map(|s| {
let s = s.trim().trim_matches('"');
if s.is_empty() {
None
} else {
Some(s.to_string())
}
})
.collect::<Vec<_>>()
})
.collect::<HashSet<_>>()
.into_iter()
.collect();
ShowSummary::from_persistence(
r.series_name,
r.episode_count as u32,
r.season_count as u32,
r.thumbnail_url,
genres,
parse_genres_blob(&r.genres_blob),
)
})
.collect())

View File

@@ -1,5 +1,3 @@
//! SQLite adapter for provider config (ProviderConfigCommand + ProviderConfigQuery).
use async_trait::async_trait;
use sqlx::SqlitePool;

View File

@@ -1,5 +1,3 @@
//! SQLite adapter for schedule persistence (ScheduleCommand + ScheduleQuery).
use std::collections::HashMap;
use async_trait::async_trait;
@@ -22,8 +20,6 @@ impl SqliteScheduleRepository {
}
}
// -- Row types ---------------------------------------------------------------
#[derive(Debug, sqlx::FromRow)]
struct ScheduleRow {
id: String,
@@ -36,8 +32,7 @@ struct ScheduleRow {
#[derive(Debug, sqlx::FromRow)]
struct SlotRow {
id: String,
#[allow(dead_code)]
schedule_id: String,
_schedule_id: String,
start_at: String,
end_at: String,
item: String,
@@ -59,8 +54,6 @@ struct PlaybackRecordRow {
generation: i64,
}
// -- Mapping -----------------------------------------------------------------
fn map_slot_row(row: SlotRow) -> DomainResult<ScheduledSlot> {
let id = SlotId::from_uuid(parse_uuid(&row.id, "slot id")?);
let source_block_id = BlockId::from_uuid(parse_uuid(&row.source_block_id, "block id")?);
@@ -103,8 +96,6 @@ fn map_playback_row(row: PlaybackRecordRow) -> DomainResult<PlaybackRecord> {
))
}
// -- Internal helpers --------------------------------------------------------
impl SqliteScheduleRepository {
async fn fetch_slots(&self, schedule_id: &str) -> DomainResult<Vec<SlotRow>> {
sqlx::query_as(
@@ -118,8 +109,6 @@ impl SqliteScheduleRepository {
}
}
// -- Command -----------------------------------------------------------------
#[async_trait]
impl ScheduleCommand for SqliteScheduleRepository {
async fn save(&self, schedule: &GeneratedSchedule) -> DomainResult<()> {
@@ -142,7 +131,6 @@ impl ScheduleCommand for SqliteScheduleRepository {
.await
.map_err(map_sqlx_error)?;
// Delete-then-insert all slots
sqlx::query("DELETE FROM scheduled_slots WHERE schedule_id = ?")
.bind(schedule.id().value().to_string())
.execute(&self.pool)
@@ -216,8 +204,6 @@ impl ScheduleCommand for SqliteScheduleRepository {
}
}
// -- Query -------------------------------------------------------------------
#[async_trait]
impl ScheduleQuery for SqliteScheduleRepository {
async fn find_active(

View File

@@ -1,5 +1,3 @@
//! SQLite adapter for app settings (AppSettingsRepository).
use async_trait::async_trait;
use sqlx::SqlitePool;

View File

@@ -1,5 +1,3 @@
//! SQLite adapter for transcode settings (TranscodeSettingsRepository).
use async_trait::async_trait;
use sqlx::SqlitePool;

View File

@@ -1,5 +1,3 @@
//! SQLite adapter for user persistence (UserCommand + UserQuery).
use async_trait::async_trait;
use sqlx::SqlitePool;
@@ -19,8 +17,6 @@ impl SqliteUserRepository {
}
}
// -- Row type for query_as --------------------------------------------------
#[derive(Debug, sqlx::FromRow)]
struct UserRow {
id: String,
@@ -49,8 +45,6 @@ impl UserRow {
}
}
// -- Command -----------------------------------------------------------------
#[async_trait]
impl UserCommand for SqliteUserRepository {
async fn save(&self, user: &User) -> DomainResult<()> {
@@ -98,8 +92,6 @@ impl UserCommand for SqliteUserRepository {
}
}
// -- Query -------------------------------------------------------------------
#[async_trait]
impl UserQuery for SqliteUserRepository {
async fn find_by_id(&self, id: UserId) -> DomainResult<Option<User>> {

View File

@@ -1,6 +1,3 @@
//! Wiring function that instantiates all SQLite repositories and returns them
//! as trait-object Arcs.
use std::sync::Arc;
use sqlx::SqlitePool;
@@ -27,7 +24,6 @@ use crate::{
user::SqliteUserRepository,
};
/// All SQLite adapter outputs, ready to be injected into the application layer.
pub struct SqliteWireOutput {
pub user_command: Arc<dyn UserCommand>,
pub user_query: Arc<dyn UserQuery>,
@@ -45,10 +41,6 @@ pub struct SqliteWireOutput {
pub transcode_settings: Arc<dyn TranscodeSettingsRepository>,
}
/// Create all SQLite repository implementations from a single pool.
///
/// Each struct wraps a clone of the same pool. Repositories that implement
/// both Command and Query traits share a single `Arc` via `.clone()`.
pub fn wire(pool: SqlitePool) -> SqliteWireOutput {
let user = Arc::new(SqliteUserRepository::new(pool.clone()));
let channel = Arc::new(SqliteChannelRepository::new(pool.clone()));