feat(domain): add StoredSong, SongSummary, SongRepositoryPort
This commit is contained in:
@@ -17,7 +17,7 @@ thiserror = "2.0.18"
|
|||||||
tokio = { version = "1.51.0", features = ["full"] }
|
tokio = { version = "1.51.0", features = ["full"] }
|
||||||
tracing = "0.1.44"
|
tracing = "0.1.44"
|
||||||
tracing-subscriber = { version = "0.3.23", features = ["env-filter"] }
|
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"
|
rand = "0.10.0"
|
||||||
sqlx = { version = "0.8.6", features = ["runtime-tokio-rustls"] }
|
sqlx = { version = "0.8.6", features = ["runtime-tokio-rustls"] }
|
||||||
async-trait = "0.1.89"
|
async-trait = "0.1.89"
|
||||||
|
|||||||
@@ -7,5 +7,7 @@ pub mod transposer;
|
|||||||
pub use note::Note;
|
pub use note::Note;
|
||||||
pub use chord::Chord;
|
pub use chord::Chord;
|
||||||
pub use song::{ChordPosition, LyricLine, Section, SectionKind, SongMeta, Song};
|
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::{FetchError, ParseError, TabFetcherPort, TabParserPort, TabSource};
|
||||||
|
pub use ports::{RepositoryError, SongRepositoryPort};
|
||||||
pub use transposer::{ChordTransposer, TransposeError};
|
pub use transposer::{ChordTransposer, TransposeError};
|
||||||
|
|||||||
@@ -35,3 +35,22 @@ pub trait TabFetcherPort: Send + Sync {
|
|||||||
pub trait TabParserPort: Send + Sync {
|
pub trait TabParserPort: Send + Sync {
|
||||||
fn parse(&self, html: &str) -> Result<Song, ParseError>;
|
fn parse(&self, html: &str) -> Result<Song, ParseError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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<StoredSong, RepositoryError>;
|
||||||
|
async fn list(&self) -> Result<Vec<SongSummary>, RepositoryError>;
|
||||||
|
async fn get(&self, id: Uuid) -> Result<Option<Song>, RepositoryError>;
|
||||||
|
async fn delete(&self, id: Uuid) -> Result<(), RepositoryError>;
|
||||||
|
}
|
||||||
|
|||||||
@@ -60,6 +60,40 @@ pub struct Song {
|
|||||||
pub sections: Vec<Section>,
|
pub sections: Vec<Section>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn song_preview_chords(song: &Song) -> Vec<String> {
|
||||||
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
Reference in New Issue
Block a user