#![forbid(unsafe_code)]
use std::{
path::{Path, PathBuf},
sync::LazyLock,
};
use directories::{BaseDirs, UserDirs};
pub const PROGRAM_FS_NAME: &str = "selene";
pub const PROGRAM_DISPLAY_NAME: &str = "Selene";
#[cfg(feature = "lrclib")]
pub mod lrclib;
#[cfg(feature = "lastfm")]
pub mod scrobbling;
pub mod config;
pub mod database;
pub mod library;
pub mod lyrics;
pub mod media_container;
pub mod utils;
pub mod symphonia_helpers;
#[cfg(any(feature = "lrclib", feature = "lastfm"))]
static REQWEST_CLIENT: LazyLock<reqwest::Client> = LazyLock::new(|| {
reqwest::Client::builder()
.user_agent(format!(
"{PROGRAM_DISPLAY_NAME} v{ver} (https://codeberg.org/CrypticCreator/Selene)",
ver = env!("CARGO_PKG_VERSION")
))
.build()
.unwrap()
});
static BASE_DIRS: LazyLock<Option<BaseDirs>> = LazyLock::new(BaseDirs::new);
#[must_use]
pub fn base_dirs() -> &'static BaseDirs {
BASE_DIRS.as_ref().unwrap()
}
#[must_use]
pub fn config_dir() -> &'static Path {
&CONFIG_DIR
}
static CONFIG_DIR: LazyLock<PathBuf> = LazyLock::new(|| {
let config_dir = base_dirs().config_dir();
if config_dir == base_dirs().data_dir() {
DATA_DIR.join("config")
} else {
config_dir.join(PROGRAM_FS_NAME)
}
});
#[must_use]
pub fn data_dir() -> &'static Path {
&DATA_DIR
}
static DATA_DIR: LazyLock<PathBuf> = LazyLock::new(|| base_dirs().data_dir().join(PROGRAM_FS_NAME));
#[must_use]
pub fn state_dir() -> &'static Path {
&STATE_DIR
}
static STATE_DIR: LazyLock<PathBuf> = LazyLock::new(|| {
base_dirs()
.state_dir()
.map_or(DATA_DIR.join("state"), |p| p.join(PROGRAM_FS_NAME))
});
#[must_use]
pub fn cache_dir() -> PathBuf {
base_dirs().cache_dir().join(PROGRAM_FS_NAME)
}
#[must_use]
pub fn runtime_dir() -> PathBuf {
base_dirs()
.runtime_dir()
.unwrap_or_else(|| base_dirs().cache_dir())
.join(PROGRAM_FS_NAME)
}
pub fn music_dir() -> Option<&'static Path> {
MUSIC_DIR.as_deref()
}
static MUSIC_DIR: LazyLock<Option<PathBuf>> = LazyLock::new(|| {
UserDirs::new().and_then(|user_dirs| user_dirs.audio_dir().map(|p| p.join("Selene Library")))
});