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