diff --git a/Cargo.lock b/Cargo.lock index 738f310..1818ee4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -233,6 +233,7 @@ dependencies = [ "log", "lrc", "notify-rust", + "parse-display", "pretty_env_logger", "regex", "serde", @@ -1138,6 +1139,32 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "parse-display" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f96cc033d72896bb9a2c239a14e1141c3e2eae6d649e7c10ef4e598d66bc86c" +dependencies = [ + "once_cell", + "parse-display-derive", + "regex", +] + +[[package]] +name = "parse-display-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5587062be441f3d868f7c4c9d13c67f286b03aa679d7f8176ef80bf2ee79e5d" +dependencies = [ + "once_cell", + "proc-macro2", + "quote", + "regex", + "regex-syntax", + "structmeta", + "syn", +] + [[package]] name = "pin-project" version = "1.0.12" @@ -1524,6 +1551,29 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "structmeta" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bd9c2155aa89fb2c2cb87d99a610c689e7c47099b3e9f1c8a8f53faf4e3d2e3" +dependencies = [ + "proc-macro2", + "quote", + "structmeta-derive", + "syn", +] + +[[package]] +name = "structmeta-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bafede0d0a2f21910f36d47b1558caae3076ed80f6f3ad0fc85a91e6ba7e5938" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "strum" version = "0.22.0" diff --git a/Cargo.toml b/Cargo.toml index 63f1bb3..995507f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ typed-builder = "0.12.0" log = { version = "0.4.17", optional = true } pretty_env_logger = { version = "0.4.0", optional = true } thiserror = "1.0.38" +parse-display = "0.8.0" [dependencies.clap] version = "4.1.4" diff --git a/src/cmus/mod.rs b/src/cmus/mod.rs index ffe419f..516d93b 100644 --- a/src/cmus/mod.rs +++ b/src/cmus/mod.rs @@ -5,6 +5,7 @@ pub mod query; use crate::cmus::query::CmusQueryResponse; #[cfg(feature = "debug")] use log::{debug, info}; +use parse_display::Display; use std::collections::HashMap; use std::fmt::Debug; use std::num::ParseIntError; @@ -14,6 +15,27 @@ use typed_builder::TypedBuilder; pub trait TemplateProcessor { fn process(&self, template: &String) -> String; + fn get_keys(template: &String) -> Vec { + let mut keys = Vec::new(); // Just a buffer to store the keys. + let mut key = String::new(); // Just a buffer to build the key. + + for c in template.chars() { + if c == '{' { + key = String::new(); + } else if c == '}' { + #[cfg(feature = "debug")] + debug!("Found key: {}", key); + keys.push(key.clone()); + } else { + key.push(c); + } + } + + #[cfg(feature = "debug")] + debug!("Found keys: {:?}", keys); + + keys + } } #[derive(Debug, PartialEq, Default, Clone)] @@ -21,7 +43,7 @@ pub struct TrackMetadata { tags: HashMap, } -#[derive(Debug, PartialEq, Default, Clone)] +#[derive(Display, Debug, PartialEq, Default, Clone)] pub enum TrackStatus { Playing, Paused, @@ -72,26 +94,19 @@ impl TemplateProcessor for Track { } let mut processed = template.clone(); - let mut key = String::new(); // Just a buffer to store the key. - - for c in template.chars() { - if c == '{' { - key = String::new(); - } else if c == '}' { - #[cfg(feature = "debug")] - debug!("Replacing the placeholder {{{key}}} with its matching value."); - // Replace the key with their matching value if exists, if not replace with the empty string. - processed = processed.replace( - &format!("{{{}}}", key), - match key.as_str() { - "title" => self.get_name(), - _ => self.metadata.get(&key).unwrap_or(""), - }, - ); - } else { - key.push(c); + Self::get_keys(template).iter().for_each(|key| { + #[cfg(feature = "debug")] + debug!("Replacing the placeholder {{{key}}} with its matching value."); + // Replace the key with their matching value if exists, if not replace with the empty string. + let status = self.status.to_string(); + if let Some(value) = match key.as_str() { + "status" => Some(status.as_str()), + "title" => Some(self.get_name()), + _ => self.metadata.get(&key), + } { + processed = processed.replace(&format!("{{{key}}}"), value); } - } + }); #[cfg(feature = "debug")] debug!("Processed template: {processed}"); diff --git a/src/cmus/player_settings.rs b/src/cmus/player_settings.rs index b75adce..9aa6556 100644 --- a/src/cmus/player_settings.rs +++ b/src/cmus/player_settings.rs @@ -1,6 +1,8 @@ -use crate::cmus::CmusError; +use crate::cmus::{CmusError, TemplateProcessor}; #[cfg(feature = "debug")] use log::{debug, info}; +use parse_display::Display; +use std::fmt::{Display, Formatter}; use std::num::ParseIntError; use std::str::FromStr; @@ -13,7 +15,7 @@ pub struct PlayerSettings { pub volume: Volume, } -#[derive(Debug, PartialEq, Default, Clone)] +#[derive(Display, Debug, PartialEq, Default, Clone)] pub enum Shuffle { #[default] Off, @@ -27,7 +29,7 @@ pub struct Volume { pub right: u8, } -#[derive(Debug, PartialEq, Default, Clone)] +#[derive(Display, Debug, PartialEq, Default, Clone)] pub enum AAAMode { #[default] All, @@ -35,6 +37,42 @@ pub enum AAAMode { Artist, } +impl TemplateProcessor for PlayerSettings { + fn process(&self, template: &String) -> String { + #[cfg(feature = "debug")] + { + info!("Processing template: {}", template); + debug!("Processing template with player settings: {:?}", self); + } + let mut processed = template.clone(); + + Self::get_keys(template).iter().for_each(|key| { + let value = match key.as_str() { + "repeat" => self.repeat.to_string(), + "repeat_current" => self.repeat_current.to_string(), + "shuffle" => self.shuffle.to_string(), + "aaa_mode" => self.aaa_mode.to_string(), + "volume_left" => self.volume.left.to_string(), + "volume_right" => self.volume.right.to_string(), + "volume" => { + if self.volume.left == self.volume.right { + self.volume.left.to_string() + } else { + format!("{}:{}", self.volume.left, self.volume.right) + } + } + _ => "".to_string(), + }; + processed = processed.replace(&format!("{{{key}}}"), &value); + }); + + #[cfg(feature = "debug")] + info!("Processed template: {}", processed); + + processed + } +} + impl FromStr for AAAMode { type Err = CmusError; @@ -165,7 +203,7 @@ mod tests { volume: Volume { left: 46, right: 46, - } + }, }) ); } diff --git a/src/lib.rs b/src/lib.rs index 6b0290b..b8ddcbb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,9 @@ #![feature(assert_matches)] +use crate::cmus::TemplateProcessor; #[cfg(feature = "debug")] use log::{debug, info}; use std::path::Path; -use crate::cmus::TemplateProcessor; pub mod cmus; pub mod notification;