use crate::library::Library;
use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};
use std::fs;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ColorMode {
#[default]
Auto,
Truecolor,
Ansi16,
}
impl ColorMode {
pub const fn truecolor(self, detected: bool) -> bool {
match self {
Self::Auto => detected,
Self::Truecolor => true,
Self::Ansi16 => false,
}
}
pub const fn override_for(detected: bool) -> Self {
if detected {
Self::Ansi16
} else {
Self::Truecolor
}
}
pub const fn toggle(self, detected: bool) -> Self {
match self {
Self::Auto => Self::override_for(detected),
_ => Self::Auto,
}
}
pub const fn label(self) -> &'static str {
match self {
Self::Auto => "auto",
Self::Truecolor => "truecolor",
Self::Ansi16 => "16-color",
}
}
}
fn default_console_theme() -> String {
"native".to_string()
}
const SETTINGS_VERSION: u32 = 1;
const fn default_version() -> u32 {
SETTINGS_VERSION
}
const fn default_volume() -> f32 {
0.7
}
const fn default_seek() -> u64 {
1
}
const fn default_true() -> bool {
true
}
fn default_theme() -> String {
"dark".to_string()
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Settings {
#[serde(default = "default_version")]
version: u32,
#[serde(default = "default_volume")]
pub default_volume: f32,
#[serde(default = "default_seek")]
pub seek_step_secs: u64,
#[serde(default = "default_true")]
pub resume_playback: bool,
#[serde(default)]
pub color_mode: ColorMode,
#[serde(default = "default_theme")]
pub theme_name: String,
#[serde(default = "default_console_theme")]
pub console_theme_name: String,
#[serde(default)]
pub transparent: bool,
}
impl Default for Settings {
fn default() -> Self {
Self {
version: SETTINGS_VERSION,
default_volume: 0.7,
seek_step_secs: 1,
resume_playback: true,
color_mode: ColorMode::Auto,
theme_name: "dark".to_string(),
console_theme_name: default_console_theme(),
transparent: false,
}
}
}
impl Settings {
const FILE: &'static str = "settings.json";
pub fn load() -> Self {
let Some(path) = Library::find_config_file(Self::FILE) else {
return Self::default();
};
let Ok(raw) = fs::read_to_string(&path) else {
return Self::default();
};
serde_json::from_str(&raw).unwrap_or_default()
}
pub fn save(&self) -> Result<()> {
let path = Library::place_config_file(Self::FILE)?;
let tmp = path.with_extension("json.tmp");
let raw = serde_json::to_string_pretty(self)?;
fs::write(&tmp, &raw).with_context(|| format!("writing settings to {}", tmp.display()))?;
fs::rename(&tmp, &path)
.with_context(|| format!("replacing settings at {}", path.display()))?;
Ok(())
}
pub const fn set_default_volume(&mut self, v: f32) {
self.default_volume = v.clamp(0.0, 1.0);
}
pub fn set_seek_step_secs(&mut self, s: u64) {
self.seek_step_secs = s.clamp(1, 120);
}
pub fn set_theme(&mut self, name: &str) {
self.theme_name = name.to_string();
}
pub fn set_console_theme(&mut self, name: &str) {
self.console_theme_name = name.to_string();
}
}