termusiclib/config/v2/tui/
mod.rs1use std::path::Path;
2
3use anyhow::{Context, Result};
4use serde::{Deserialize, Serialize};
5
6use super::server::ComSettings;
7
8pub mod config_extra;
9pub mod keys;
10pub mod theme;
11
12#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)]
13#[serde(default)] #[allow(clippy::module_name_repetitions)]
15pub struct TuiSettings {
16 pub com: MaybeComSettings,
17 #[serde(skip)]
19 pub com_resolved: Option<ComSettings>,
20 pub behavior: BehaviorSettings,
21 pub coverart: CoverArtPosition,
22 #[serde(flatten)]
23 pub theme: theme::ThemeWrap,
24 pub keys: keys::Keys,
25}
26
27impl TuiSettings {
28 pub fn resolve_com(&mut self, tui_path: &Path) -> Result<()> {
32 if self.com_resolved.is_some() {
33 return Ok(());
34 }
35
36 match self.com {
37 MaybeComSettings::ComSettings(ref v) => {
38 self.com_resolved = Some(*v);
40 return Ok(());
41 }
42 MaybeComSettings::Same => (),
43 }
44
45 let server_path = tui_path
46 .parent()
47 .context("tui_path should have a parent directory")?
48 .join(super::server::config_extra::FILE_NAME);
49
50 let server_settings =
51 super::server::config_extra::ServerConfigVersionedDefaulted::from_file(server_path)
52 .context("parsing server config")?;
53 self.com_resolved = Some(server_settings.into_settings().com);
54
55 Ok(())
56 }
57
58 #[must_use]
60 pub fn get_com(&self) -> Option<&ComSettings> {
61 self.com_resolved.as_ref()
62 }
63}
64
65#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
66pub struct BehaviorSettings {
67 pub quit_server_on_exit: bool,
69 pub confirm_quit: bool,
71}
72
73impl Default for BehaviorSettings {
74 fn default() -> Self {
75 Self {
76 quit_server_on_exit: true,
77 confirm_quit: true,
78 }
79 }
80}
81
82#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq)]
83#[serde(rename_all = "lowercase")]
84pub enum MaybeComSettings {
85 ComSettings(ComSettings),
86 #[default]
88 Same,
89}
90
91#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
92#[serde(default)] #[derive(Default)]
94pub struct CoverArtPosition {
95 pub align: Alignment,
98 pub size_scale: i8,
100 pub hidden: bool,
102}
103
104#[derive(Debug, Clone, Copy, Deserialize, Serialize, Default, PartialEq, Eq)]
105pub enum Alignment {
106 #[serde(rename = "top right")]
107 TopRight,
108 #[serde(rename = "top left")]
109 TopLeft,
110 #[serde(rename = "bottom right")]
111 #[default]
112 BottomRight,
113 #[serde(rename = "bottom left")]
114 BottomLeft,
115}
116
117mod v1_interop {
118 use super::{Alignment, BehaviorSettings, CoverArtPosition, MaybeComSettings, TuiSettings};
119 use crate::config::v1;
120
121 impl From<v1::Alignment> for Alignment {
122 fn from(value: v1::Alignment) -> Self {
123 match value {
124 v1::Alignment::BottomRight => Self::BottomRight,
125 v1::Alignment::BottomLeft => Self::BottomLeft,
126 v1::Alignment::TopRight => Self::TopRight,
127 v1::Alignment::TopLeft => Self::TopLeft,
128 }
129 }
130 }
131
132 #[allow(clippy::cast_possible_truncation)] impl From<v1::Xywh> for CoverArtPosition {
134 fn from(value: v1::Xywh) -> Self {
135 Self {
136 align: value.align.into(),
137 size_scale: value.width_between_1_100.clamp(0, i8::MAX as u32) as i8,
139 hidden: Self::default().hidden,
140 }
141 }
142 }
143
144 impl From<v1::Settings> for TuiSettings {
145 fn from(value: v1::Settings) -> Self {
146 let theme = (&value).into();
147 Self {
148 com: MaybeComSettings::Same,
150 com_resolved: None,
151 behavior: BehaviorSettings {
152 quit_server_on_exit: value.kill_daemon_when_quit,
153 confirm_quit: value.enable_exit_confirmation,
154 },
155 coverart: value.album_photo_xywh.into(),
156 theme,
157 keys: value.keys.into(),
158 }
159 }
160 }
161
162 #[cfg(test)]
163 mod tests {
164 use super::*;
165
166 #[test]
167 fn should_convert_default_without_error() {
168 let converted: TuiSettings = v1::Settings::default().into();
169
170 assert_eq!(converted.com, MaybeComSettings::Same);
171 assert_eq!(
172 converted.behavior,
173 BehaviorSettings {
174 quit_server_on_exit: true,
175 confirm_quit: true
176 }
177 );
178
179 assert_eq!(
180 converted.coverart,
181 CoverArtPosition {
182 align: Alignment::BottomRight,
183 size_scale: 20,
184 hidden: false
185 }
186 );
187
188 }
192 }
193}