Skip to main content

selene_daemon/
config.rs

1use std::{
2    path::{Path, PathBuf},
3    sync::{OnceLock, RwLock, RwLockReadGuard, RwLockWriteGuard},
4};
5
6use lunar_lib::{
7    config::{Config, ConfigError},
8    debug,
9};
10use selene_core::runtime_dir;
11use serde::{Deserialize, Serialize};
12
13pub static DAEMON_CONFIG: OnceLock<RwLock<DaemonConfig>> = OnceLock::new();
14pub fn initialize_daemon_config() -> Result<(), ConfigError> {
15    if DAEMON_CONFIG.get().is_some() {
16        return Ok(());
17    }
18
19    let config = DaemonConfig::load()?;
20    let _ = DAEMON_CONFIG.set(RwLock::new(config));
21
22    Ok(())
23}
24
25/// Returns a read guard to the current [`DaemonConfig`]
26///
27/// # Panics
28///
29/// Panics if the config hasn't been intialized or the internal [`RwLock`] is poisoned
30pub fn daemon_config() -> RwLockReadGuard<'static, DaemonConfig> {
31    DAEMON_CONFIG
32        .get()
33        .expect("Config wasn't intialized")
34        .read()
35        .unwrap()
36}
37
38/// Returns a write guard to the current [`DaemonConfig`]
39///
40/// # Panics
41///
42/// Panics if the config hasn't been intialized or the internal [`RwLock`] is poisoned
43pub fn daemon_config_mut() -> RwLockWriteGuard<'static, DaemonConfig> {
44    DAEMON_CONFIG
45        .get()
46        .expect("Config wasn't initialized")
47        .write()
48        .unwrap()
49}
50
51#[derive(Debug, Serialize, Deserialize, Default)]
52pub struct DaemonConfig {
53    pub main: MainSettings,
54    pub playback: PlaybackSettings,
55}
56
57impl Config for DaemonConfig {
58    const CONFIG_FILE_NAME: &'static str = "daemon";
59
60    fn config_dir() -> Option<&'static Path> {
61        Some(selene_core::config_dir())
62    }
63}
64
65impl DaemonConfig {
66    /// Reloads the current config
67    pub fn reload() -> Result<(), ConfigError> {
68        debug!("Reloading daemon config");
69        initialize_daemon_config()?;
70
71        let mut current_config = daemon_config_mut();
72        let loaded_config = Self::load()?;
73
74        *current_config = loaded_config;
75
76        Ok(())
77    }
78}
79
80#[derive(Debug, Serialize, Deserialize)]
81pub struct MainSettings {
82    pub socket_path: PathBuf,
83}
84
85impl Default for MainSettings {
86    fn default() -> Self {
87        Self {
88            socket_path: runtime_dir().join("selene.sock"),
89        }
90    }
91}
92
93#[derive(Debug, Serialize, Deserialize)]
94pub struct PlaybackSettings {
95    /// Size of the audio buffer in kibibytes (1 = 1024 bytes)
96    pub audio_buffer_size: usize,
97
98    /// How far into a song you must be for 'previous' to restart the current song
99    pub restart_on_previous_thresh: Option<f64>,
100}
101
102impl Default for PlaybackSettings {
103    fn default() -> Self {
104        Self {
105            audio_buffer_size: 16,
106            restart_on_previous_thresh: Some(5.0),
107        }
108    }
109}
110
111#[derive(Debug, Serialize, Deserialize)]
112pub struct NotificationSettings {
113    pub enabled: bool,
114    pub body_format: String,
115    pub header_format: String,
116}
117
118impl Default for NotificationSettings {
119    fn default() -> Self {
120        Self {
121            enabled: true,
122            header_format: "Now playing: {$title?UNKNOWN TITLE}".to_owned(),
123            body_format: "by {$main_track_artist?UNKNOWN_ARTIST} {$feat_track_artists< (feat. >)}"
124                .to_owned(),
125        }
126    }
127}