Files
pocket-chords/crates/adapters/sqlite/src/search.rs
Gabriel Kaszewski d13df586dd refactor: DDD/CQRS architecture, unified crate layout
- crates: common→application, api→presentation, infrastructure/*→adapters/*
- new crates: api-types, infra-wiring
- domain: errors/, models/, value_objects/, ports/, services/
- application: CQRS use cases (songs/, tabs/) w/ commands, queries, deps
- unified DomainError replaces RepositoryError
- workspace deps, unused dep cleanup
- fix: parse plain-text chord lines (UG drops spans mid-song)
- tests extracted to separate modules (tests/ dirs)
2026-07-11 21:02:10 +02:00

35 lines
1.1 KiB
Rust

use async_trait::async_trait;
use domain::{DomainError, SongSearchPort, SongSummary, SortField, SortOrder};
use crate::repository::SqliteSongRepository;
use crate::row::{SongRow, row_to_summary, sort_clause};
#[async_trait]
impl SongSearchPort for SqliteSongRepository {
async fn search(
&self,
query: &str,
sort: SortField,
order: SortOrder,
) -> Result<Vec<SongSummary>, DomainError> {
let escaped = query
.replace('\\', "\\\\")
.replace('%', "\\%")
.replace('_', "\\_");
let pattern = format!("%{}%", escaped);
let sql = format!(
"SELECT id, title, artist, original_key, preview_chords, body FROM songs \
WHERE (title LIKE ? ESCAPE '\\' OR artist LIKE ? ESCAPE '\\') {}",
sort_clause(sort, order)
);
let rows = sqlx::query_as::<_, SongRow>(&sql)
.bind(&pattern)
.bind(&pattern)
.fetch_all(&self.pool)
.await
.map_err(|e| DomainError::InfrastructureError(e.to_string()))?;
rows.into_iter().map(row_to_summary).collect()
}
}