selene-core 0.2.0

selene-core is the backend for Selene, a local-first music player
Documentation
use std::ops::Deref;

use blake3::{Hash, hash};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};

use crate::library::{artist::ArtistGroup, cover_art::CoverArt};

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 Deref for AlbumId {
    type Target = Hash;

    fn deref(&self) -> &Self::Target {
        &self.id
    }
}

impl AlbumId {
    fn new(name: Option<&str>, artists: &ArtistGroup) -> Self {
        let mut hash_seed = name.unwrap_or(UNKNOWN_ALBUM).as_bytes().to_vec();
        hash_seed.extend(artists.iter().flat_map(|h| h.as_bytes()));
        let id = hash(&hash_seed);

        Self { id }
    }
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Album {
    id: AlbumId,

    pub name: Option<String>,
    pub cover_art: Option<CoverArt>,

    pub artists: ArtistGroup,
    pub disc_total: Option<u16>,
    pub genre: Option<String>,
    pub track_total: Option<u16>,
    pub date: Option<DateTime<Utc>>,

    version: usize,
}

// Core
impl Album {
    pub(crate) fn new(name: Option<String>, artists: ArtistGroup) -> Self {
        let hash = AlbumId::new(name.as_deref(), &artists);

        Self {
            artists,
            cover_art: None,
            disc_total: None,
            genre: None,
            id: hash,
            name,
            track_total: None,
            version: 1,
            date: None,
        }
    }
}

// Accessors
impl Album {
    #[must_use]
    pub fn id(&self) -> AlbumId {
        self.id
    }

    #[must_use]
    pub fn safe_name(&self) -> &str {
        self.name.as_deref().unwrap_or(UNKNOWN_ALBUM)
    }
}