use std::{ops::Deref, str::FromStr};
use blake3::{Hash, hash};
use chrono::{DateTime, Utc};
use lunar_lib::database::{DatabaseEntry, DatabaseError, EntryId};
use serde::{Deserialize, Serialize};
use crate::{
database::{LibraryDb, Patchable, patch_option_replace},
library::{
artist::ArtistGroup,
image_art::ImageArt,
track::{Track, TrackId},
},
};
pub mod frontend_impls;
pub mod trait_impls;
pub const UNKNOWN_ALBUM: &str = "UNKNOWN ALBUM";
#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq, Hash)]
pub struct AlbumId {
id: Hash,
}
impl EntryId for AlbumId {
type Entry = Album;
type IdDb = LibraryDb;
}
impl Deref for AlbumId {
type Target = [u8; 32];
fn deref(&self) -> &Self::Target {
self.id.as_bytes()
}
}
impl FromStr for AlbumId {
type Err = <Hash as FromStr>::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self {
id: Hash::from_str(s)?,
})
}
}
impl AlbumId {
fn new(name: &str) -> Self {
Self {
id: hash(name.as_bytes()),
}
}
#[must_use]
pub fn to_hash(&self) -> Hash {
self.id
}
#[must_use]
pub fn to_selene_id(&self) -> String {
format!("album:{}", self.id)
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Album {
id: AlbumId,
pub name: String,
pub cover_art: Option<ImageArt>,
pub(crate) tracks: Vec<TrackReference>,
pub(crate) artist_group: ArtistGroup,
pub disc_total: Option<u32>,
pub genre: Vec<String>,
pub track_total: Option<u32>,
pub date: Option<DateTime<Utc>>,
version: usize,
}
impl Album {
pub(crate) fn new(name: String, artists: ArtistGroup, tracks: Vec<TrackReference>) -> Self {
let hash = AlbumId::new(&name);
Self {
artist_group: artists,
cover_art: None,
disc_total: None,
genre: Vec::new(),
id: hash,
name,
track_total: None,
version: 1,
date: None,
tracks,
}
}
pub fn tracks(&self, db: &LibraryDb) -> Result<Vec<Track>, DatabaseError> {
Track::db_get_batch_from(self.track_refs().iter().map(|t| t.id), db)
}
}
impl Album {
#[must_use]
pub fn id(&self) -> AlbumId {
self.id
}
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
#[must_use]
pub fn artists(&self) -> &ArtistGroup {
&self.artist_group
}
#[must_use]
pub fn track_refs(&self) -> &[TrackReference] {
&self.tracks
}
}
#[derive(Debug, Deserialize, Serialize, Clone, Copy)]
pub struct TrackReference {
pub id: TrackId,
pub track_num: Option<u32>,
pub disc_num: Option<u32>,
}
impl PartialEq for TrackReference {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl Eq for TrackReference {}
impl Patchable<Self> for TrackReference {
fn patch(&mut self, patch: Self) {
let Self {
id: _,
track_num,
disc_num,
} = patch;
patch_option_replace(&mut self.track_num, track_num);
patch_option_replace(&mut self.disc_num, disc_num);
}
}