151 lines
4.1 KiB
Rust
151 lines
4.1 KiB
Rust
use anyhow::{Context as _, Result};
|
|
use serde::{Deserialize, Serialize};
|
|
use std::path::PathBuf;
|
|
|
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum LibrarySourceMode {
|
|
Local,
|
|
My,
|
|
#[default]
|
|
Global,
|
|
}
|
|
|
|
impl LibrarySourceMode {
|
|
pub fn label(self) -> &'static str {
|
|
match self {
|
|
LibrarySourceMode::Local => "Local",
|
|
LibrarySourceMode::My => "My",
|
|
LibrarySourceMode::Global => "Global",
|
|
}
|
|
}
|
|
|
|
pub fn includes_network(self) -> bool {
|
|
!matches!(self, LibrarySourceMode::Local)
|
|
}
|
|
|
|
pub fn includes_global_peers(self) -> bool {
|
|
matches!(self, LibrarySourceMode::Global)
|
|
}
|
|
|
|
pub fn next(self) -> Self {
|
|
match self {
|
|
LibrarySourceMode::Local => LibrarySourceMode::My,
|
|
LibrarySourceMode::My => LibrarySourceMode::Global,
|
|
LibrarySourceMode::Global => LibrarySourceMode::Local,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
|
|
pub struct LibraryFilters {
|
|
#[serde(default)]
|
|
pub hide_featured_only: bool,
|
|
#[serde(default)]
|
|
pub source_mode: LibrarySourceMode,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
pub struct AppSettings {
|
|
#[serde(default = "default_volume")]
|
|
pub volume: u8,
|
|
#[serde(default)]
|
|
pub library: LibraryFilters,
|
|
/// Root used for music materialized from federation peers.
|
|
#[serde(default = "default_music_dir")]
|
|
pub music_dir: PathBuf,
|
|
}
|
|
|
|
impl Default for AppSettings {
|
|
fn default() -> Self {
|
|
Self {
|
|
volume: default_volume(),
|
|
library: LibraryFilters::default(),
|
|
music_dir: default_music_dir(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl AppSettings {
|
|
pub fn normalized(mut self) -> Self {
|
|
self.volume = self.volume.min(100);
|
|
if self.music_dir.as_os_str().is_empty() {
|
|
self.music_dir = default_music_dir();
|
|
}
|
|
self
|
|
}
|
|
}
|
|
|
|
fn default_volume() -> u8 {
|
|
80
|
|
}
|
|
|
|
/// The historical permanent-download location, kept as the default for
|
|
/// backward compatibility with existing installations.
|
|
pub fn default_music_dir() -> PathBuf {
|
|
crate::config::project_dirs()
|
|
.map(|dirs| dirs.data_dir().join("federation-media"))
|
|
.unwrap_or_else(|| PathBuf::from("federation-media"))
|
|
}
|
|
|
|
pub fn load() -> (AppSettings, Option<String>) {
|
|
let Some(path) = settings_path() else {
|
|
return (AppSettings::default(), None);
|
|
};
|
|
match std::fs::read_to_string(&path) {
|
|
Ok(text) => match toml::from_str::<AppSettings>(&text) {
|
|
Ok(settings) => (settings.normalized(), None),
|
|
Err(err) => (
|
|
AppSettings::default(),
|
|
Some(format!("settings.toml is malformed: {err}")),
|
|
),
|
|
},
|
|
Err(err) if err.kind() == std::io::ErrorKind::NotFound => (AppSettings::default(), None),
|
|
Err(err) => (
|
|
AppSettings::default(),
|
|
Some(format!("settings.toml could not be read: {err}")),
|
|
),
|
|
}
|
|
}
|
|
|
|
pub fn save(settings: &AppSettings) -> Result<()> {
|
|
let path = settings_path().context("cannot determine the config directory")?;
|
|
if let Some(parent) = path.parent() {
|
|
std::fs::create_dir_all(parent)?;
|
|
}
|
|
std::fs::write(
|
|
&path,
|
|
toml::to_string_pretty(&settings.clone().normalized())?,
|
|
)
|
|
.with_context(|| format!("writing {}", path.display()))?;
|
|
Ok(())
|
|
}
|
|
|
|
fn settings_path() -> Option<std::path::PathBuf> {
|
|
crate::config::project_dirs().map(|dirs| dirs.config_dir().join("settings.toml"))
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn settings_parse_and_normalize() {
|
|
let settings: AppSettings = toml::from_str(
|
|
r#"
|
|
volume = 150
|
|
|
|
[library]
|
|
hide_featured_only = true
|
|
"#,
|
|
)
|
|
.unwrap();
|
|
let settings = settings.normalized();
|
|
|
|
assert_eq!(settings.volume, 100);
|
|
assert!(settings.library.hide_featured_only);
|
|
assert_eq!(settings.library.source_mode, LibrarySourceMode::Global);
|
|
assert_eq!(settings.music_dir, default_music_dir());
|
|
}
|
|
}
|