From f1add467d0ec08cce3aab3f00d30c0ae60b9c0a1 Mon Sep 17 00:00:00 2001 From: Marty Sluijtman Date: Tue, 8 Jul 2025 18:55:41 +0200 Subject: [PATCH] Refactor --- build.rs | 19 --- src/main.rs | 214 ++++---------------------------- src/noise.rs | 2 + src/noise/commands.rs | 213 +++++++++++++++++++++++++++++++ src/{cli.rs => noise/parser.rs} | 10 +- 5 files changed, 242 insertions(+), 216 deletions(-) delete mode 100644 build.rs create mode 100644 src/noise.rs create mode 100644 src/noise/commands.rs rename src/{cli.rs => noise/parser.rs} (95%) diff --git a/build.rs b/build.rs deleted file mode 100644 index 2e95170..0000000 --- a/build.rs +++ /dev/null @@ -1,19 +0,0 @@ -use clap::CommandFactory; -use clap_mangen::generate_to; - -#[path = "src/cli.rs"] -mod cli; - -use cli::*; - -fn main() -> std::io::Result<()> { - let out_dir = - std::path::PathBuf::from(std::env::var_os("OUT_DIR").ok_or(std::io::ErrorKind::NotFound)?); - - let cmd = Cli::command(); - - generate_to(cmd, &out_dir)?; - println!("cargo:warning=OUT_DIR: {out_dir:?}"); - - Ok(()) -} diff --git a/src/main.rs b/src/main.rs index 0be8bea..bc9d85f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,12 @@ -extern crate libnotify; extern crate mpd; -mod cli; -use cli::*; +mod noise; +use noise::{commands::Noise, parser::*}; use clap::{Command, CommandFactory, Parser}; use clap_complete::{generate, Generator}; -use libnotify::Notification; -use mpd::{Client, Query, Song, State, Term}; -use std::{io, time::Duration}; +use mpd::Client; +use std::io; fn print_completions(generator: G, cmd: &mut Command) { generate( @@ -24,129 +22,40 @@ fn main() { if let Some(completions) = cli.completions { let mut cmd = Cli::command(); - eprintln!("Generating completion file for {completions:?}..."); + eprintln!("Generatig completion file for {completions:?}..."); print_completions(completions, &mut cmd); return; } - libnotify::init("noise").unwrap(); - - let n = libnotify::Notification::new("Noise", None, None); - let host = cli.host.unwrap_or("localhost:6600".into()); let mut conn = Client::connect(host).expect("Connection failed"); if let Some(cmd) = &cli.command { match cmd { - Commands::Play { track } => { - if let Some(i) = track { - conn.switch(*i).unwrap(); - } else { - conn.play().unwrap(); - } - } - Commands::Stop => { - conn.stop().unwrap(); - } - Commands::Toggle => { - if conn.status().unwrap().state == State::Stop { - conn.play().unwrap(); - } else { - conn.toggle_pause().unwrap(); - } - } + Commands::Play { track } => conn.noise_play(*track), + Commands::Stop => conn.stop().unwrap(), + Commands::Toggle => conn.toggle(), Commands::Next => conn.next().unwrap(), Commands::Prev => conn.prev().unwrap(), - Commands::List { file } => { - let _current_song = conn.currentsong().unwrap(); - conn.queue().unwrap().iter().for_each(|x| { - println!( - "{:<02} {}", - x.place.unwrap().pos, - if *file { - x.file.clone() - } else { - format_song(x.clone()) - } - ) - }); - } - Commands::Update => { - let thing = conn.update().unwrap(); - println!("{thing}") - } + Commands::List { file } => conn.list_queue(*file, cli.verbose), Commands::Current => (), Commands::Clear => conn.clear().unwrap(), - Commands::Search { query, max } => { - let query = query.join(" "); - let max = max.unwrap_or(conn.stats().unwrap().songs); - - conn.search(&Query::new().and(Term::Any, query), (0, max)) - .unwrap() - .iter() - .for_each(|x| println!("{}", x.file)); - } - Commands::Find { query, max, append } => { - let query = query.join(" "); - let max = max.unwrap_or(conn.stats().unwrap().songs); - - if *append { - conn.findadd(&Query::new().and(Term::Any, query)).unwrap(); - } else { - conn.find(&Query::new().and(Term::Any, query), (0, max)) - .unwrap() - .iter() - .for_each(|x| println!("{}", x.file)); - } - } - Commands::Crossfade { seconds } => { - if let Some(crossfade) = seconds { - conn.crossfade(*crossfade).unwrap(); - } else { - println!( - "Crossfade: {}", - conn.status() - .unwrap() - .crossfade - .map_or("Disabled".into(), |d| d.as_secs().to_string()) - ); - } - } - Commands::Shuffle => { - send_notification("Shuffling queueueu...", &n); - println!("Shuffling queueueu..."); - conn.shuffle(..).unwrap(); - } - Commands::Repeat => { - let repeat_state = conn.status().unwrap().repeat; - conn.repeat(!repeat_state).unwrap(); - } - Commands::Random => { - let random_state = conn.status().unwrap().random; - // conn.repeat(!random_state).unwrap(); - println!("{}", random_state) - } - Commands::Single => { - let single_state = conn.status().unwrap().single; - conn.single(!single_state).unwrap(); - } - Commands::Consume => { - let consume_state = conn.status().unwrap().consume; - conn.consume(!consume_state).unwrap(); - } - Commands::Volume { percentage } => { - if let Some(volume) = percentage { - conn.volume(*volume).unwrap() - } - let vol = conn.status().unwrap().volume; - println!("Volume at {vol}%") - } + Commands::Search { query, max } => conn.noise_search(query, *max), + Commands::Find { query, max, append } => conn.noise_find(query, *max, *append), + Commands::Crossfade { seconds } => conn.noise_crossfade(*seconds), + Commands::Shuffle => conn.shuf(), + Commands::Repeat => conn.toggle_repeat(), + Commands::Random => conn.toggle_random(), + Commands::Single => conn.toggle_single(), + Commands::Consume => conn.toggle_consume(), + Commands::Volume { percentage } => conn.vol(*percentage), + Commands::Munch => conn.munch(), + Commands::Length => conn.length(), } if let Commands::Stop | Commands::List { .. } - | Commands::Update | Commands::Clear | Commands::Search { .. } | Commands::Find { .. } @@ -156,86 +65,5 @@ fn main() { return; } } - println!("{}", get_status(conn, cli.verbose, n)); -} - -// fn toggle_state(mut client: Client, state: ) -> () {} - -fn format_song(song: Song) -> String { - format!( - "{} - {}", - song.artist.unwrap_or("Unknown".into()), - song.title.unwrap() - ) -} - -trait QuickFmt { - fn quick_fmt(&self) -> &'static str; -} - -impl QuickFmt for bool { - fn quick_fmt(&self) -> &'static str { - if *self { - "on" - } else { - "off" - } - } -} - -fn get_status(mut client: Client, verbose: bool, notify: Notification) -> String { - let current_song = if let Some(song) = client.currentsong().unwrap() { - format_song(song) - } else { - "No song playing".into() - }; - - send_notification(¤t_song, ¬ify); - if !verbose { - return current_song; - } - - let mut output = Vec::with_capacity(3); - output.push(current_song); - - let status = client.status().unwrap(); - - output.push(format!( - "volume: {vol:<6} repeat: {repeat:<6} random: {rnd:<6} single: {single:<6} consume: {consume:<6}", - vol = format!("{}%", status.volume), - repeat = status.repeat.quick_fmt(), - rnd = status.random.quick_fmt(), - single = status.single.quick_fmt(), - consume = status.consume.quick_fmt() - )); - - let state = match status.state { - State::Stop => "stopped", - State::Pause => "paused", - State::Play => "playing", - }; - - let duration = status.time.map_or("".into(), |(stamp, total)| { - let stamp = to_mins_string(stamp); - let total = to_mins_string(total); - format!("{stamp}/{total}") - }); - - output.push(format!("[{state}] {duration}")); - - output.join("\n") -} - -fn send_notification(body: &str, notify: &Notification) { - let _ = notify.update("Noise", body, None); - notify.show().unwrap(); -} - -fn to_mins_string(dur: Duration) -> String { - let seconds = dur.as_secs(); - - let minutes = seconds / 60; - let rem_seconds = seconds % 60; - - format!("{minutes:02}:{rem_seconds:02}") + println!("{}", conn.get_status(cli.verbose)); } diff --git a/src/noise.rs b/src/noise.rs new file mode 100644 index 0000000..1882986 --- /dev/null +++ b/src/noise.rs @@ -0,0 +1,2 @@ +pub mod commands; +pub mod parser; diff --git a/src/noise/commands.rs b/src/noise/commands.rs new file mode 100644 index 0000000..1696ef1 --- /dev/null +++ b/src/noise/commands.rs @@ -0,0 +1,213 @@ +extern crate libnotify; +extern crate mpd; + +use libnotify::Notification; +use mpd::{Client, Query, Song, State, Term}; +use std::time::Duration; + +pub trait Noise { + fn noise_find(&mut self, query: &Vec, max: Option, append: bool); + fn noise_search(&mut self, query: &Vec, max: Option); + fn noise_crossfade(&mut self, seconds: Option); + fn noise_play(&mut self, track: Option); + fn toggle(&mut self); + fn munch(&mut self); + fn length(&mut self); + fn vol(&mut self, percentage: Option); + fn toggle_consume(&mut self); + fn toggle_repeat(&mut self); + fn toggle_random(&mut self); + fn toggle_single(&mut self); + fn shuf(&mut self); + fn get_status(&mut self, verbose: bool) -> String; + fn list_queue(&mut self, file: bool, _verbose: bool); +} + +impl Noise for Client { + fn noise_find(&mut self, query: &Vec, max: Option, append: bool) { + let query = query.join(" "); + let max = max.unwrap_or(self.stats().unwrap().songs); + + if append { + self.findadd(&Query::new().and(Term::Any, query)).unwrap(); + } else { + self.find(&Query::new().and(Term::Any, query), (0, max)) + .unwrap() + .iter() + .for_each(|x| println!("{}", x.file)); + } + } + + fn noise_search(&mut self, query: &Vec, max: Option) { + let query = query.join(" "); + let max = max.unwrap_or(self.stats().unwrap().songs); + + self.search(&Query::new().and(Term::Any, query), (0, max)) + .unwrap() + .iter() + .for_each(|x| println!("{}", x.file)); + } + + fn noise_crossfade(&mut self, seconds: Option) { + if let Some(seconds) = seconds { + self.crossfade(seconds).unwrap(); + } else { + println!( + "Crossfade: {}", + self.status() + .unwrap() + .crossfade + .map_or("Disabled".into(), |d| d.as_secs().to_string()) + ) + } + } + + fn noise_play(&mut self, track: Option) { + if let Some(i) = track { + self.switch(i).unwrap(); + } else { + self.play().unwrap(); + } + } + + fn toggle(&mut self) { + if self.status().unwrap().state == State::Stop { + self.play().unwrap(); + } else { + self.toggle_pause().unwrap(); + } + } + + fn munch(&mut self) { + // let current_song = self.currentsong().unwrap().unwrap().place.unwrap(); + // self.delete(current_song).unwrap(); + todo!("Not net implemented") + } + + fn length(&mut self) { + let length = self.status().unwrap().queue_len; + println!("{length}"); + } + + fn vol(&mut self, percentage: Option) { + if let Some(volume) = percentage { + self.volume(volume).unwrap() + } + let vol = self.status().unwrap().volume; + println!("Volume at {vol}%") + } + + fn toggle_consume(&mut self) { + let consume_state = self.status().unwrap().consume; + self.consume(!consume_state).unwrap(); + } + + fn toggle_repeat(&mut self) { + let repeat_state = self.status().unwrap().repeat; + self.repeat(!repeat_state).unwrap(); + } + + fn toggle_random(&mut self) { + let random_state = self.status().unwrap().random; + self.random(!random_state).unwrap(); + } + + fn toggle_single(&mut self) { + let single_state = self.status().unwrap().single; + self.single(!single_state).unwrap(); + } + + fn shuf(&mut self) { + send_notification("Shuffling queueueu..."); + println!("Shuffling queueueu..."); + self.shuffle(..).unwrap(); + } + + fn list_queue(&mut self, file: bool, _verbose: bool) { + self.queue().unwrap().iter().for_each(|x| { + println!( + "{:<02} {}", + x.place.unwrap().pos, + if file { + x.file.clone() + } else { + format_song(x.clone()) + } + ) + }); + } + + fn get_status(&mut self, verbose: bool) -> String { + let current_song = if let Some(song) = self.currentsong().unwrap() { + format_song(song) + } else { + "No song playing".into() + }; + send_notification(¤t_song); + + if !verbose { + return current_song; + } + + let mut output = Vec::with_capacity(3); + + output.push(current_song); + + let status = self.status().unwrap(); + + let fmt = |x| match x { + true => "On", + false => "Off", + }; + + output.push(format!( + "volume: {vol:<6} repeat: {repeat:<6} random: {rnd:<6} single: {single:<6} consume: {consume:<6}", + vol = format!("{}%", status.volume), + repeat = fmt(status.repeat), + rnd = fmt(status.random), + single = fmt(status.single), + consume = fmt(status.consume) + )); + + let state = match status.state { + State::Stop => "stopped", + State::Pause => "paused", + State::Play => "playing", + }; + + let duration = status.time.map_or("".into(), |(stamp, total)| { + let stamp = to_mins_string(stamp); + let total = to_mins_string(total); + format!("{stamp}/{total}") + }); + + output.push(format!("[{state}] {duration}")); + + output.join("\n") + } +} + +fn format_song(song: Song) -> String { + format!( + "{} - {}", + song.artist.unwrap_or("Unknown".into()), + song.title.unwrap() + ) +} + +// fn get_status(client: &mut Client, verbose: bool) -> String {} + +fn send_notification(body: &str) { + libnotify::init("noise").unwrap(); + let notify = Notification::new("Noise", Some(body), None); + notify.show().unwrap(); +} + +fn to_mins_string(dur: Duration) -> String { + let seconds = dur.as_secs(); + + let minutes = seconds / 60; + let rem_seconds = seconds % 60; + + format!("{minutes:02}:{rem_seconds:02}") +} diff --git a/src/cli.rs b/src/noise/parser.rs similarity index 95% rename from src/cli.rs rename to src/noise/parser.rs index 3a13e00..4505ae6 100644 --- a/src/cli.rs +++ b/src/noise/parser.rs @@ -9,7 +9,7 @@ use clap_complete::Shell; long_about = "I like how 'mpc' works for the most part, but I don't use most of its features and there are some parts of it that I feel could be more ergonomic. In comes 'noise', an opinionated, even more minimalist 'mpd' client than 'mpc'.", name = "noise" )] -#[command(propagate_version = true)] +// #[command(propagate_version = true)] pub struct Cli { #[command(subcommand)] @@ -51,8 +51,8 @@ pub enum Commands { #[arg(value_hint = ValueHint::Other)] seconds: Option, }, - ///Update still needs some work - Update, + // ///Update still needs some work + // Update, /// Return currently playing song Current, /// Clear current queueueu @@ -94,7 +94,7 @@ pub enum Commands { // position: Option, // }, /// Shuffles the current queueue - #[command(visible_alias = "scramble")] + #[command(visible_alias = "shuf")] Shuffle, /// Toggles repeat Repeat, @@ -110,4 +110,6 @@ pub enum Commands { #[arg(value_hint = ValueHint::Other)] percentage: Option, }, + Munch, + Length, }