cmus_notify/cmus/
player_settings.rs1use crate::cmus::{CmusError, TemplateProcessor};
2#[cfg(feature = "debug")]
3use log::{debug, info};
4use parse_display::Display;
5use std::num::ParseIntError;
6use std::str::FromStr;
7
8#[derive(PartialEq, Clone)]
9#[cfg_attr(feature = "debug", derive(Debug))]
10pub struct PlayerSettings {
11 pub repeat: bool,
12 pub repeat_current: bool,
13 pub shuffle: Shuffle,
14 pub aaa_mode: AAAMode,
15 pub volume: Volume,
16}
17
18#[derive(Display, PartialEq, Default, Clone)]
19#[cfg_attr(feature = "debug", derive(Debug))]
20pub enum Shuffle {
21 #[default]
22 Off,
23 Tracks,
24 Albums,
25}
26
27#[derive(PartialEq, Default, Clone)]
28#[cfg_attr(feature = "debug", derive(Debug))]
29pub struct Volume {
30 pub left: u8,
31 pub right: u8,
32}
33
34#[derive(Display, PartialEq, Default, Clone)]
35#[cfg_attr(feature = "debug", derive(Debug))]
36pub enum AAAMode {
37 #[default]
38 All,
39 Album,
40 Artist,
41}
42
43impl TemplateProcessor for PlayerSettings {
44 #[inline(always)]
48 fn process(&self, template: String) -> String {
49 #[cfg(feature = "debug")]
50 {
51 info!("Processing template: {}", template);
52 debug!("Processing template with player settings: {:?}", self);
53 }
54 let mut processed = template.clone();
55
56 Self::get_keys(template.as_str()).iter().for_each(|key| {
57 let value = match key.as_str() {
58 "repeat" => self.repeat.to_string(),
59 "repeat_current" => self.repeat_current.to_string(),
60 "shuffle" => self.shuffle.to_string(),
61 "aaa_mode" => self.aaa_mode.to_string(),
62 "volume_left" => self.volume.left.to_string(),
63 "volume_right" => self.volume.right.to_string(),
64 "volume" => {
65 if self.volume.left == self.volume.right {
66 self.volume.left.to_string()
67 } else {
68 format!("{}:{}", self.volume.left, self.volume.right)
69 }
70 }
71 _ => "".to_string(),
72 };
73 processed = processed.replace(&format!("{{{key}}}"), &value);
74 });
75
76 #[cfg(feature = "debug")]
77 info!("Processed template: {}", processed);
78
79 processed
80 }
81}
82
83impl FromStr for AAAMode {
84 type Err = CmusError;
85
86 fn from_str(s: &str) -> Result<Self, Self::Err> {
87 match s {
88 "all" => Ok(Self::All),
89 "album" => Ok(Self::Album),
90 "artist" => Ok(Self::Artist),
91 _ => Err(CmusError::UnknownAAAMode(s.to_string())),
92 }
93 }
94}
95
96impl FromStr for Shuffle {
97 type Err = CmusError;
98
99 fn from_str(s: &str) -> Result<Self, Self::Err> {
100 match s {
101 "off" => Ok(Self::Off),
102 "tracks" => Ok(Self::Tracks),
103 "albums" => Ok(Self::Albums),
104 _ => Err(CmusError::UnknownShuffleMode(s.to_string())),
105 }
106 }
107}
108
109impl FromStr for PlayerSettings {
110 type Err = CmusError;
111
112 fn from_str(s: &str) -> Result<Self, Self::Err> {
113 #[cfg(feature = "debug")]
114 info!("Parsing cmus response from string: {}", s);
115 let mut repeat = false;
116 let mut repeat_current = false;
117 let mut shuffle = Shuffle::default();
118 let mut aaa_mode = AAAMode::default();
119 let mut volume = Volume::default();
120
121 for line in s.lines() {
122 #[cfg(feature = "debug")]
123 debug!("Parsing line: {}", line);
124 if line.starts_with("set ") {
125 let line = &line[4..];
126 let (key, value) = line.split_once(' ').ok_or(CmusError::UnknownError(
127 "Corrupted cmus response".to_string(),
128 ))?;
129
130 match key {
131 "repeat" => repeat = value == "true",
132 "repeat_current" => repeat_current = value == "true",
133 "shuffle" => shuffle = Shuffle::from_str(value)?,
134 "aaa_mode" => aaa_mode = AAAMode::from_str(value)?,
135 "vol_left" => {
136 volume.left = value
137 .parse()
138 .map_err(|e: ParseIntError| CmusError::UnknownError(e.to_string()))?
139 }
140 "vol_right" => {
141 volume.right = value
142 .parse()
143 .map_err(|e: ParseIntError| CmusError::UnknownError(e.to_string()))?
144 }
145 _ => {}
146 }
147 }
148 }
149
150 Ok(Self {
151 repeat,
152 repeat_current,
153 shuffle,
154 aaa_mode,
155 volume,
156 })
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163
164 #[test]
165 fn test_parse_aaamode_from_str() {
166 let all = AAAMode::from_str("all");
167 let album = AAAMode::from_str("album");
168 let artist = AAAMode::from_str("artist");
169 let unknown = AAAMode::from_str("unknown");
170
171 assert_eq!(all, Ok(AAAMode::All));
172 assert_eq!(album, Ok(AAAMode::Album));
173 assert_eq!(artist, Ok(AAAMode::Artist));
174 assert_eq!(
175 unknown,
176 Err(CmusError::UnknownAAAMode("unknown".to_string()))
177 );
178 }
179
180 #[test]
181 fn test_parse_shuffle_mode_from_str() {
182 let off = Shuffle::from_str("off");
183 let tracks = Shuffle::from_str("tracks");
184 let albums = Shuffle::from_str("albums");
185 let unknown = Shuffle::from_str("unknown");
186
187 assert_eq!(off, Ok(Shuffle::Off));
188 assert_eq!(tracks, Ok(Shuffle::Tracks));
189 assert_eq!(albums, Ok(Shuffle::Albums));
190 assert_eq!(
191 unknown,
192 Err(CmusError::UnknownShuffleMode("unknown".to_string()))
193 );
194 }
195
196 #[test]
197 fn test_parse_player_settings_from_str() {
198 let setting_sample = include_str!(
199 "../../tests/samples/player_settings_mode-artist_vol-46_repeat-false_repeat_current-false_shuffle-tracks.txt");
200
201 let settings = PlayerSettings::from_str(setting_sample);
202
203 assert_eq!(
204 settings,
205 Ok(PlayerSettings {
206 repeat: false,
207 repeat_current: false,
208 shuffle: Shuffle::Tracks,
209 aaa_mode: AAAMode::Artist,
210 volume: Volume {
211 left: 46,
212 right: 46,
213 },
214 })
215 );
216 }
217}