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