diff --git a/Cargo.toml b/Cargo.toml index e650491..d56eeb8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ thiserror = "2.0.18" tokio = { version = "1.51.0", features = ["full"] } tracing = "0.1.44" tracing-subscriber = { version = "0.3.23", features = ["env-filter"] } -uuid = { version = "1.23.0", features = ["v4"] } +uuid = { version = "1.23.0", features = ["v4", "serde"] } rand = "0.10.0" sqlx = { version = "0.8.6", features = ["runtime-tokio-rustls"] } async-trait = "0.1.89" diff --git a/crates/domain/src/lib.rs b/crates/domain/src/lib.rs index 59755c1..f0ee140 100644 --- a/crates/domain/src/lib.rs +++ b/crates/domain/src/lib.rs @@ -7,5 +7,7 @@ pub mod transposer; pub use note::Note; pub use chord::Chord; pub use song::{ChordPosition, LyricLine, Section, SectionKind, SongMeta, Song}; +pub use song::{song_preview_chords, StoredSong, SongSummary}; pub use ports::{FetchError, ParseError, TabFetcherPort, TabParserPort, TabSource}; +pub use ports::{RepositoryError, SongRepositoryPort}; pub use transposer::{ChordTransposer, TransposeError}; diff --git a/crates/domain/src/ports.rs b/crates/domain/src/ports.rs index fc70f25..7a358c2 100644 --- a/crates/domain/src/ports.rs +++ b/crates/domain/src/ports.rs @@ -35,3 +35,22 @@ pub trait TabFetcherPort: Send + Sync { pub trait TabParserPort: Send + Sync { fn parse(&self, html: &str) -> Result; } + +use uuid::Uuid; +use crate::song::{StoredSong, SongSummary}; + +#[derive(Debug, Error)] +pub enum RepositoryError { + #[error("Song not found")] + NotFound, + #[error("Database error: {0}")] + Internal(String), +} + +#[async_trait] +pub trait SongRepositoryPort: Send + Sync { + async fn save(&self, song: &Song) -> Result; + async fn list(&self) -> Result, RepositoryError>; + async fn get(&self, id: Uuid) -> Result, RepositoryError>; + async fn delete(&self, id: Uuid) -> Result<(), RepositoryError>; +} diff --git a/crates/domain/src/song.rs b/crates/domain/src/song.rs index e3f26f6..7eaaba4 100644 --- a/crates/domain/src/song.rs +++ b/crates/domain/src/song.rs @@ -60,6 +60,40 @@ pub struct Song { pub sections: Vec
, } +use uuid::Uuid; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct StoredSong { + pub id: Uuid, + pub song: Song, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SongSummary { + pub id: Uuid, + pub meta: SongMeta, + pub preview_chords: Vec, +} + +pub fn song_preview_chords(song: &Song) -> Vec { + let mut seen = std::collections::HashSet::new(); + let mut result = Vec::new(); + 'outer: for section in &song.sections { + for line in §ion.lines { + for cp in &line.chords { + let name = cp.chord.name(true); + if seen.insert(name.clone()) { + result.push(name); + if result.len() >= 5 { + break 'outer; + } + } + } + } + } + result +} + #[cfg(test)] mod tests { use super::*;