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
25pub fn daemon_config() -> RwLockReadGuard<'static, DaemonConfig> {
31 DAEMON_CONFIG
32 .get()
33 .expect("Config wasn't intialized")
34 .read()
35 .unwrap()
36}
37
38pub 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 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 pub audio_buffer_size: usize,
97
98 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}