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:
2026-07-10 05:44:42 +02:00
parent 6a9b4e5c00
commit c224cc6bd2
66 changed files with 924 additions and 971 deletions

View File

@@ -22,23 +22,18 @@ impl SqliteDiaryRepository {
Self { pool }
}
fn map_err(e: sqlx::Error) -> DomainError {
tracing::error!("Database error: {:?}", e);
DomainError::InfrastructureError("Database operation failed".into())
}
async fn count_diary_entries(&self, movie_id: Option<&str>) -> Result<i64, DomainError> {
match movie_id {
None => sqlx::query_scalar::<_, i64>("SELECT COUNT(*) FROM reviews")
.fetch_one(&self.pool)
.await
.map_err(Self::map_err),
.map_err(adapter_common::map_sqlx_error),
Some(id) => {
sqlx::query_scalar::<_, i64>("SELECT COUNT(*) FROM reviews WHERE movie_id = ?")
.bind(id)
.fetch_one(&self.pool)
.await
.map_err(Self::map_err)
.map_err(adapter_common::map_sqlx_error)
}
}
}
@@ -69,7 +64,7 @@ impl SqliteDiaryRepository {
.bind(offset)
.fetch_all(&self.pool)
.await
.map_err(Self::map_err)
.map_err(adapter_common::map_sqlx_error)
}
async fn fetch_movie_diary_rows(
@@ -101,7 +96,7 @@ impl SqliteDiaryRepository {
.bind(offset)
.fetch_all(&self.pool)
.await
.map_err(Self::map_err)
.map_err(adapter_common::map_sqlx_error)
}
async fn count_user_diary_entries(
@@ -130,7 +125,7 @@ impl SqliteDiaryRepository {
if has_search {
q = q.bind(search.unwrap());
}
q.fetch_one(&self.pool).await.map_err(Self::map_err)
q.fetch_one(&self.pool).await.map_err(adapter_common::map_sqlx_error)
}
async fn fetch_user_diary_rows(
@@ -176,7 +171,7 @@ impl SqliteDiaryRepository {
.bind(offset)
.fetch_all(&self.pool)
.await
.map_err(Self::map_err)
.map_err(adapter_common::map_sqlx_error)
}
}
@@ -340,7 +335,7 @@ impl DiaryQuery for SqliteDiaryRepository {
}
let count_q = bind_filter_params!(sqlx::query_scalar::<_, i64>(&count_sql));
let total = count_q.fetch_one(&self.pool).await.map_err(Self::map_err)?;
let total = count_q.fetch_one(&self.pool).await.map_err(adapter_common::map_sqlx_error)?;
let rows_q = bind_filter_params!(sqlx::query_as::<_, FeedRow>(&select_sql));
let rows = rows_q
@@ -348,7 +343,7 @@ impl DiaryQuery for SqliteDiaryRepository {
.bind(offset)
.fetch_all(&self.pool)
.await
.map_err(Self::map_err)?;
.map_err(adapter_common::map_sqlx_error)?;
let items = rows
.into_iter()
@@ -373,7 +368,7 @@ impl DiaryQuery for SqliteDiaryRepository {
.bind(&id_str)
.fetch_optional(&self.pool)
.await
.map_err(Self::map_err)?
.map_err(adapter_common::map_sqlx_error)?
.ok_or_else(|| DomainError::NotFound(format!("Movie {}", id_str)))?
.into_domain()?;
@@ -384,7 +379,7 @@ impl DiaryQuery for SqliteDiaryRepository {
.bind(&id_str)
.fetch_all(&self.pool)
.await
.map_err(Self::map_err)?
.map_err(adapter_common::map_sqlx_error)?
.into_iter()
.map(ReviewRow::into_domain)
.collect::<Result<Vec<_>, _>>()?;
@@ -405,7 +400,7 @@ impl DiaryQuery for SqliteDiaryRepository {
.bind(&uid)
.fetch_all(&self.pool)
.await
.map_err(Self::map_err)?;
.map_err(adapter_common::map_sqlx_error)?;
rows.into_iter().map(DiaryRow::into_domain).collect()
}
@@ -430,7 +425,7 @@ impl DiaryQuery for SqliteDiaryRepository {
while let Some(row) = futures::StreamExt::next(&mut rows).await {
yield match row {
Ok(r) => r.into_domain(),
Err(e) => Err(Self::map_err(e)),
Err(e) => Err(adapter_common::map_sqlx_error(e)),
};
}
})
@@ -453,7 +448,7 @@ impl DiaryQuery for SqliteDiaryRepository {
.bind(id_str)
.fetch_one(&self.pool)
.await
.map_err(Self::map_err)
.map_err(adapter_common::map_sqlx_error)
.map(MovieStatsRow::into_domain)
}
@@ -471,7 +466,7 @@ impl DiaryQuery for SqliteDiaryRepository {
.bind(&id_str)
.fetch_one(&self.pool)
.await
.map_err(Self::map_err)?;
.map_err(adapter_common::map_sqlx_error)?;
let rows = sqlx::query_as::<_, FeedRow>(
"SELECT m.id, m.external_metadata_id, m.title, m.release_year, m.director, m.poster_path,
@@ -492,7 +487,7 @@ impl DiaryQuery for SqliteDiaryRepository {
.bind(offset)
.fetch_all(&self.pool)
.await
.map_err(Self::map_err)?;
.map_err(adapter_common::map_sqlx_error)?;
let items = rows
.into_iter()
@@ -512,7 +507,7 @@ impl DiaryQuery for SqliteDiaryRepository {
sqlx::query_scalar("SELECT COUNT(*) FROM reviews WHERE remote_actor_url IS NULL")
.fetch_one(&self.pool)
.await
.map_err(Self::map_err)?;
.map_err(adapter_common::map_sqlx_error)?;
Ok(count as u64)
}
}