refactor: extract adapter-common crate — shared sqlx error mapping,
row-to-domain conversions, date/uuid utils Eliminates 39 map_err copies, consolidates parse_uuid/parse_datetime/ datetime_to_str/format_year_month, extracts 7 shared row-to-domain conversion functions (movie, review, watchlist, stats, user_summary). Row structs stay per-adapter (FromRow is db-specific), only conversion logic is shared. Net -281 lines.
This commit is contained in:
@@ -13,12 +13,7 @@ use domain::{
|
||||
use sqlx::{Row, SqlitePool};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::models::{parse_datetime, parse_uuid};
|
||||
|
||||
fn map_err(e: sqlx::Error) -> DomainError {
|
||||
tracing::error!("Database error: {:?}", e);
|
||||
DomainError::InfrastructureError("Database operation failed".into())
|
||||
}
|
||||
use adapter_common::{parse_datetime, parse_uuid};
|
||||
|
||||
fn status_to_str(s: &WrapUpStatus) -> &'static str {
|
||||
match s {
|
||||
@@ -87,7 +82,7 @@ impl WrapUpRepository for SqliteWrapUpRepository {
|
||||
.bind(&completed)
|
||||
.execute(&self.pool)
|
||||
.await
|
||||
.map_err(map_err)?;
|
||||
.map_err(adapter_common::map_sqlx_error)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -107,7 +102,7 @@ impl WrapUpRepository for SqliteWrapUpRepository {
|
||||
.bind(&id_str)
|
||||
.execute(&self.pool)
|
||||
.await
|
||||
.map_err(map_err)?;
|
||||
.map_err(adapter_common::map_sqlx_error)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -126,7 +121,7 @@ impl WrapUpRepository for SqliteWrapUpRepository {
|
||||
.bind(&id_str)
|
||||
.execute(&self.pool)
|
||||
.await
|
||||
.map_err(map_err)?;
|
||||
.map_err(adapter_common::map_sqlx_error)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -142,7 +137,7 @@ impl WrapUpRepository for SqliteWrapUpRepository {
|
||||
.bind(&id_str)
|
||||
.fetch_optional(&self.pool)
|
||||
.await
|
||||
.map_err(map_err)?;
|
||||
.map_err(adapter_common::map_sqlx_error)?;
|
||||
|
||||
row.as_ref().map(row_to_record).transpose()
|
||||
}
|
||||
@@ -158,7 +153,7 @@ impl WrapUpRepository for SqliteWrapUpRepository {
|
||||
.bind(&uid)
|
||||
.fetch_all(&self.pool)
|
||||
.await
|
||||
.map_err(map_err)?;
|
||||
.map_err(adapter_common::map_sqlx_error)?;
|
||||
|
||||
rows.iter().map(row_to_record).collect()
|
||||
}
|
||||
@@ -171,7 +166,7 @@ impl WrapUpRepository for SqliteWrapUpRepository {
|
||||
)
|
||||
.fetch_all(&self.pool)
|
||||
.await
|
||||
.map_err(map_err)?;
|
||||
.map_err(adapter_common::map_sqlx_error)?;
|
||||
|
||||
rows.iter().map(row_to_record).collect()
|
||||
}
|
||||
@@ -200,7 +195,7 @@ impl WrapUpRepository for SqliteWrapUpRepository {
|
||||
.bind(&end_str)
|
||||
.fetch_optional(&self.pool)
|
||||
.await
|
||||
.map_err(map_err)?;
|
||||
.map_err(adapter_common::map_sqlx_error)?;
|
||||
|
||||
row.as_ref().map(row_to_record).transpose()
|
||||
}
|
||||
@@ -210,7 +205,7 @@ impl WrapUpRepository for SqliteWrapUpRepository {
|
||||
.bind(id.value().to_string())
|
||||
.execute(&self.pool)
|
||||
.await
|
||||
.map_err(map_err)?;
|
||||
.map_err(adapter_common::map_sqlx_error)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -224,21 +219,21 @@ impl WrapUpRepository for SqliteWrapUpRepository {
|
||||
.bind(&before_str)
|
||||
.execute(&self.pool)
|
||||
.await
|
||||
.map_err(map_err)?;
|
||||
.map_err(adapter_common::map_sqlx_error)?;
|
||||
Ok(result.rows_affected())
|
||||
}
|
||||
}
|
||||
|
||||
fn row_to_record(row: &sqlx::sqlite::SqliteRow) -> Result<WrapUpRecord, DomainError> {
|
||||
let id_str: String = row.try_get("id").map_err(map_err)?;
|
||||
let user_id_str: Option<String> = row.try_get("user_id").map_err(map_err)?;
|
||||
let start_date_str: String = row.try_get("start_date").map_err(map_err)?;
|
||||
let end_date_str: String = row.try_get("end_date").map_err(map_err)?;
|
||||
let status_str: String = row.try_get("status").map_err(map_err)?;
|
||||
let report_json: Option<String> = row.try_get("report_json").map_err(map_err)?;
|
||||
let error_message: Option<String> = row.try_get("error_message").map_err(map_err)?;
|
||||
let created_at_str: String = row.try_get("created_at").map_err(map_err)?;
|
||||
let completed_at_str: Option<String> = row.try_get("completed_at").map_err(map_err)?;
|
||||
let id_str: String = row.try_get("id").map_err(adapter_common::map_sqlx_error)?;
|
||||
let user_id_str: Option<String> = row.try_get("user_id").map_err(adapter_common::map_sqlx_error)?;
|
||||
let start_date_str: String = row.try_get("start_date").map_err(adapter_common::map_sqlx_error)?;
|
||||
let end_date_str: String = row.try_get("end_date").map_err(adapter_common::map_sqlx_error)?;
|
||||
let status_str: String = row.try_get("status").map_err(adapter_common::map_sqlx_error)?;
|
||||
let report_json: Option<String> = row.try_get("report_json").map_err(adapter_common::map_sqlx_error)?;
|
||||
let error_message: Option<String> = row.try_get("error_message").map_err(adapter_common::map_sqlx_error)?;
|
||||
let created_at_str: String = row.try_get("created_at").map_err(adapter_common::map_sqlx_error)?;
|
||||
let completed_at_str: Option<String> = row.try_get("completed_at").map_err(adapter_common::map_sqlx_error)?;
|
||||
|
||||
let user_id = user_id_str.as_deref().map(parse_uuid).transpose()?;
|
||||
|
||||
@@ -304,7 +299,7 @@ impl WrapUpStatsQuery for SqliteWrapUpStatsQuery {
|
||||
q = q.bind(uid);
|
||||
}
|
||||
|
||||
let rows = q.fetch_all(&self.pool).await.map_err(map_err)?;
|
||||
let rows = q.fetch_all(&self.pool).await.map_err(adapter_common::map_sqlx_error)?;
|
||||
|
||||
if rows.is_empty() {
|
||||
return Ok(vec![]);
|
||||
@@ -314,7 +309,7 @@ impl WrapUpStatsQuery for SqliteWrapUpStatsQuery {
|
||||
let mut movie_ids: Vec<String> = Vec::new();
|
||||
let mut seen = std::collections::HashSet::new();
|
||||
for row in &rows {
|
||||
let mid: String = row.try_get("movie_id").map_err(map_err)?;
|
||||
let mid: String = row.try_get("movie_id").map_err(adapter_common::map_sqlx_error)?;
|
||||
if seen.insert(mid.clone()) {
|
||||
movie_ids.push(mid);
|
||||
}
|
||||
@@ -330,18 +325,18 @@ impl WrapUpStatsQuery for SqliteWrapUpStatsQuery {
|
||||
// 3) Build result
|
||||
let mut result = Vec::with_capacity(rows.len());
|
||||
for row in &rows {
|
||||
let movie_id_str: String = row.try_get("movie_id").map_err(map_err)?;
|
||||
let title: String = row.try_get("title").map_err(map_err)?;
|
||||
let release_year: i64 = row.try_get("release_year").map_err(map_err)?;
|
||||
let director: Option<String> = row.try_get("director").map_err(map_err)?;
|
||||
let poster_path: Option<String> = row.try_get("poster_path").map_err(map_err)?;
|
||||
let rating: i64 = row.try_get("rating").map_err(map_err)?;
|
||||
let watched_at_str: String = row.try_get("watched_at").map_err(map_err)?;
|
||||
let user_id_str: String = row.try_get("user_id").map_err(map_err)?;
|
||||
let runtime_minutes: Option<i32> = row.try_get("runtime_minutes").map_err(map_err)?;
|
||||
let budget_usd: Option<i64> = row.try_get("budget_usd").map_err(map_err)?;
|
||||
let movie_id_str: String = row.try_get("movie_id").map_err(adapter_common::map_sqlx_error)?;
|
||||
let title: String = row.try_get("title").map_err(adapter_common::map_sqlx_error)?;
|
||||
let release_year: i64 = row.try_get("release_year").map_err(adapter_common::map_sqlx_error)?;
|
||||
let director: Option<String> = row.try_get("director").map_err(adapter_common::map_sqlx_error)?;
|
||||
let poster_path: Option<String> = row.try_get("poster_path").map_err(adapter_common::map_sqlx_error)?;
|
||||
let rating: i64 = row.try_get("rating").map_err(adapter_common::map_sqlx_error)?;
|
||||
let watched_at_str: String = row.try_get("watched_at").map_err(adapter_common::map_sqlx_error)?;
|
||||
let user_id_str: String = row.try_get("user_id").map_err(adapter_common::map_sqlx_error)?;
|
||||
let runtime_minutes: Option<i32> = row.try_get("runtime_minutes").map_err(adapter_common::map_sqlx_error)?;
|
||||
let budget_usd: Option<i64> = row.try_get("budget_usd").map_err(adapter_common::map_sqlx_error)?;
|
||||
let original_language: Option<String> =
|
||||
row.try_get("original_language").map_err(map_err)?;
|
||||
row.try_get("original_language").map_err(adapter_common::map_sqlx_error)?;
|
||||
|
||||
let genres = genres_map.get(&movie_id_str).cloned().unwrap_or_default();
|
||||
let keywords = keywords_map.get(&movie_id_str).cloned().unwrap_or_default();
|
||||
@@ -411,12 +406,12 @@ async fn fetch_genres_sqlite(
|
||||
for id in movie_ids {
|
||||
q = q.bind(id);
|
||||
}
|
||||
let rows = q.fetch_all(pool).await.map_err(map_err)?;
|
||||
let rows = q.fetch_all(pool).await.map_err(adapter_common::map_sqlx_error)?;
|
||||
|
||||
let mut map: HashMap<String, Vec<String>> = HashMap::new();
|
||||
for row in rows {
|
||||
let mid: String = row.try_get("movie_id").map_err(map_err)?;
|
||||
let name: String = row.try_get("name").map_err(map_err)?;
|
||||
let mid: String = row.try_get("movie_id").map_err(adapter_common::map_sqlx_error)?;
|
||||
let name: String = row.try_get("name").map_err(adapter_common::map_sqlx_error)?;
|
||||
map.entry(mid).or_default().push(name);
|
||||
}
|
||||
Ok(map)
|
||||
@@ -437,12 +432,12 @@ async fn fetch_keywords_sqlite(
|
||||
for id in movie_ids {
|
||||
q = q.bind(id);
|
||||
}
|
||||
let rows = q.fetch_all(pool).await.map_err(map_err)?;
|
||||
let rows = q.fetch_all(pool).await.map_err(adapter_common::map_sqlx_error)?;
|
||||
|
||||
let mut map: HashMap<String, Vec<String>> = HashMap::new();
|
||||
for row in rows {
|
||||
let mid: String = row.try_get("movie_id").map_err(map_err)?;
|
||||
let name: String = row.try_get("name").map_err(map_err)?;
|
||||
let mid: String = row.try_get("movie_id").map_err(adapter_common::map_sqlx_error)?;
|
||||
let name: String = row.try_get("name").map_err(adapter_common::map_sqlx_error)?;
|
||||
map.entry(mid).or_default().push(name);
|
||||
}
|
||||
Ok(map)
|
||||
@@ -466,15 +461,15 @@ async fn fetch_cast_sqlite(
|
||||
for id in movie_ids {
|
||||
q = q.bind(id);
|
||||
}
|
||||
let rows = q.fetch_all(pool).await.map_err(map_err)?;
|
||||
let rows = q.fetch_all(pool).await.map_err(adapter_common::map_sqlx_error)?;
|
||||
|
||||
let mut map: HashMap<String, Vec<CastEntry>> = HashMap::new();
|
||||
for row in rows {
|
||||
let mid: String = row.try_get("movie_id").map_err(map_err)?;
|
||||
let name: String = row.try_get("name").map_err(map_err)?;
|
||||
let billing_order: i32 = row.try_get("billing_order").map_err(map_err)?;
|
||||
let tmdb_person_id: i64 = row.try_get("tmdb_person_id").map_err(map_err)?;
|
||||
let profile_path: Option<String> = row.try_get("profile_path").map_err(map_err)?;
|
||||
let mid: String = row.try_get("movie_id").map_err(adapter_common::map_sqlx_error)?;
|
||||
let name: String = row.try_get("name").map_err(adapter_common::map_sqlx_error)?;
|
||||
let billing_order: i32 = row.try_get("billing_order").map_err(adapter_common::map_sqlx_error)?;
|
||||
let tmdb_person_id: i64 = row.try_get("tmdb_person_id").map_err(adapter_common::map_sqlx_error)?;
|
||||
let profile_path: Option<String> = row.try_get("profile_path").map_err(adapter_common::map_sqlx_error)?;
|
||||
map.entry(mid).or_default().push(CastEntry {
|
||||
name,
|
||||
billing_order: billing_order as u32,
|
||||
|
||||
Reference in New Issue
Block a user