selene-core 0.3.0

selene-core is the backend for Selene, a local-first music player
Documentation
use std::{
    path::{Path, PathBuf},
    sync::{LazyLock, RwLock},
};

use crate::config::Config;
use lunar_lib::error;
use serde::{Deserialize, Serialize};

pub mod accessors;
pub(crate) mod internal;
pub mod mutators;

#[derive(Serialize, Deserialize, Debug, Default)]
pub struct CommonConfig {
    pub(crate) source_dirs: Vec<PathBuf>,
    pub(crate) library_dir: Option<PathBuf>,

    pub loudnorm_config: LoudnormConfig,
    pub track_name_config: TrackNameConfig,
}

impl Config for CommonConfig {
    const CONFIG_FILE_NAME: &'static str = "common";
}

impl CommonConfig {
    #[must_use]
    pub fn library_dir(&self) -> Option<&Path> {
        self.library_dir.as_deref()
    }

    #[must_use]
    pub fn sources(&self) -> &[PathBuf] {
        &self.source_dirs
    }
}

static COMMON_CONFIG: LazyLock<RwLock<CommonConfig>> = LazyLock::new(|| {
    let config = match CommonConfig::load() {
        Ok(config) => config,
        Err(err) => {
            error!(
                "Could not load common config from file, assuming defaults. Reason: {:?}",
                err
            );
            CommonConfig::default()
        }
    };
    RwLock::new(config)
});

/// Returns a read-only guard to the common config.
///
/// # Panics
///
/// This function will panic if the read/write lock is poisoned
pub fn common_config() -> std::sync::RwLockReadGuard<'static, CommonConfig> {
    COMMON_CONFIG.read().unwrap()
}

/// Returns a mutable guard to the common config.
///
/// # Panics
///
/// This function will panic if the read/write lock is poisoned
pub fn common_config_mut() -> std::sync::RwLockWriteGuard<'static, CommonConfig> {
    COMMON_CONFIG.write().unwrap()
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct TrackNameConfig {
    pub format_string: String,
    pub artist_separator: String,
    pub alt_artist_separator: String,
}

impl Default for TrackNameConfig {
    fn default() -> Self {
        let format_string = if cfg!(windows) {
            "{$album>\\\\}$track_num{$disc_num@$track_num<-}{. @$track_num}{$main_track_artist?UNKNOWN ARTIST} - {$title?UNKNOWN TITLE}{$feat_track_artists< (feat. >)}"
        } else {
            "{$album>/}$track_num{$disc_num@$track_num<-}{. @$track_num}{$main_track_artist?UNKNOWN ARTIST} - {$title?UNKNOWN TITLE}{$feat_track_artists< (feat. >)}"
        };

        Self {
            format_string: format_string.to_owned(),
            artist_separator: ", ".to_owned(),
            alt_artist_separator: " & ".to_owned(),
        }
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize, Deserialize, Copy)]
pub struct LoudnormConfig {
    pub linear: bool,
    pub target_offset: f32,
    pub target_i: f32,
    pub target_tp: f32,
    pub target_lra: f32,
}

impl Default for LoudnormConfig {
    fn default() -> Self {
        Self {
            linear: true,
            target_offset: 0.0,
            target_i: -24.0,
            target_tp: -2.0,
            target_lra: 7.0,
        }
    }
}