diff --git a/Cargo.lock b/Cargo.lock index ac40f22..67b2604 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -86,6 +86,15 @@ dependencies = [ "strsim", ] +[[package]] +name = "clap_complete" +version = "4.5.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aad5b1b4de04fead402672b48897030eec1f3bfe1550776322f59f6d6e6a5677" +dependencies = [ + "clap", +] + [[package]] name = "clap_derive" version = "4.5.32" @@ -104,6 +113,16 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +[[package]] +name = "clap_mangen" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc33c849748320656a90832f54a5eeecaa598e92557fb5dedebc3355746d31e4" +dependencies = [ + "clap", + "roff", +] + [[package]] name = "colorchoice" version = "1.0.4" @@ -249,9 +268,11 @@ dependencies = [ [[package]] name = "noise" -version = "0.1.4" +version = "0.1.6" dependencies = [ "clap", + "clap_complete", + "clap_mangen", "libnotify", "mpd", ] @@ -286,6 +307,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "roff" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88f8660c1ff60292143c98d08fc6e2f654d722db50410e3f3797d40baaf9d8f3" + [[package]] name = "strsim" version = "0.11.1" diff --git a/Cargo.toml b/Cargo.toml index 8592e55..2a511bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,16 @@ [package] name = "noise" -version = "0.1.4" +version = "0.1.6" edition = "2024" +authors = ["Nox Sluijtman"] [dependencies] clap = { version = "4.5.39", features = ["derive"] } +clap_complete = "4.5.54" libnotify = "1.0.3" mpd = "0.1.0" + +[build-dependencies] +clap = { version = "4.5.39", features = ["derive"] } +clap_complete = "4.5.54" +clap_mangen = "0.2.27" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..2421fa1 --- /dev/null +++ b/build.rs @@ -0,0 +1,39 @@ +use clap::{Command, CommandFactory}; +// use clap_complete as complete; +use clap_mangen as mangen; +// use complete::Shell; +use std::{path::PathBuf, str::FromStr}; + +#[path = "src/cli.rs"] +mod cli; + +use cli::*; + +fn generate_manpage(cmd: Command, out_dir: PathBuf) -> std::io::Result<()> { + let _path = mangen::generate_to(cmd, &out_dir)?; + println!("cargo:warning=manpage is generated: {out_dir:?}"); + + Ok(()) +} + +// fn generate_completions(cmd: &mut Command, out_dir: PathBuf) -> std::io::Result<()> { +// let path = complete::generate_to(Shell::Zsh, cmd, "noise", out_dir)?; +// println!("cargo:warning=completion file is generated: {path:?}"); +// Ok(()) +// } + +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 out_dir = PathBuf::from_str("./").unwrap(); + + let cmd = Cli::command(); + + generate_manpage(cmd, out_dir.clone())?; + + // eprintln!("{out_dir:?}"); + // println!("cargo:warning=out_dir: {out_dir:?}"); + + Ok(()) +} diff --git a/flake.nix b/flake.nix index d50a68d..5c68f0d 100644 --- a/flake.nix +++ b/flake.nix @@ -23,10 +23,24 @@ src = ./.; nativeBuildInputs = with pkgs; [ libnotify + installShellFiles ]; LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath nativeBuildInputs; RUST_SRC_PATH = pkgs.rustPlatform.rustLibSrc; + + postInstall = + with pkgs; + lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) '' + # installShellCompletion --cmd noise \ + # --bash <($out/bin/fd --generate-completions bash) \ + # --fish <($out/bin/fd --generate-completions fish) + # installShellCompletion --zsh contrib/completion/_fd + installShellCompletion --cmd noise \ + --bash <($out/bin/noise --generate-completions bash) \ + --zsh <($out/bin/noise --generate-completions zsh) \ + --fish <($out/bin/noise --generate-completions fish) + ''; }; devShell = with pkgs; diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 0000000..0630136 --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,94 @@ +use clap::{Parser, Subcommand, ValueHint}; +use clap_complete::Shell; + +#[derive(Parser, Debug, PartialEq)] +#[command( + author = "Nox Sluijtman", + version, + about, + long_about = "A small, opinionated MPD client", + name = "noise" +)] +#[command(propagate_version = true)] + +pub struct Cli { + #[command(subcommand)] + pub command: Option, + + /// Enables verbose output + #[arg(short, long, global = true)] + pub verbose: bool, + /// Hostname where MPD listens at + #[arg(short = 'H', long, global = true)] + pub host: Option, + + /// Generate shell completions + #[arg(long = "generate-completions", value_enum)] + pub completions: Option, + // /// Generate manpage + // #[arg(long = "generate-manpage", value_enum)] + // pub manpage: bool, +} + +#[derive(Subcommand, Debug, PartialEq)] +pub enum Commands { + /// Toggle MPD stream + Toggle, + /// Skip to the next track + Next, + /// Revert to the previous track + Prev, + /// Stops playing + Stop, + /// Play queueueu + Play { + #[arg(short, long, value_hint = ValueHint::Other)] + track: Option, + }, + /// Set or get crossfade + Crossfade { + #[arg(short, long, value_hint = ValueHint::Other)] + seconds: Option, + }, + ///Update still needs some work + Update, + /// Return currently playing song + Current, + /// Clear current queueueu + Clear, + /// Query database + Search { + ///Search query + #[arg(trailing_var_arg = true, value_hint = ValueHint::Other)] + query: Vec, + ///Only return the first n results + #[arg(short, long, value_hint = ValueHint::Other)] + max: Option, + // #[arg(short, long)] + // append: bool, + // #[arg(short, long)] + // insert: Option, + }, + /// Query database differently + Find { + ///Search query + #[arg(trailing_var_arg = true, value_hint = ValueHint::Other)] + query: Vec, + ///Only return the first n results + #[arg(short, long, value_hint = ValueHint::Other)] + max: Option, + #[arg(short, long)] + append: bool, + }, + /// List items in the current queueueu + List { + #[arg(short, long)] + file: bool, + }, + // Add { + // #[arg(short, long)] + // position: Option, + // }, + /// Shuffles the current queueue + Shuffle, +} diff --git a/src/main.rs b/src/main.rs index 1fcbfb3..2931319 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,88 +1,37 @@ extern crate libnotify; extern crate mpd; -use clap::{Parser, Subcommand}; +mod cli; +use cli::*; + +use clap::{Command, CommandFactory, Parser}; +use clap_complete::{generate, Generator}; use libnotify::Notification; use mpd::{Client, Query, Song, State, Term}; -use std::time::Duration; +use std::{io, time::Duration}; -#[derive(Parser)] -#[command(author, version, about, long_about = "Banaan")] -#[command(propagate_version = true)] - -struct Cli { - #[command(subcommand)] - command: Option, - - #[arg(short, long, global = true)] - verbose: bool, - ///hostname where MPD listens at - #[arg(short = 'H', long, global = true)] - host: Option, -} - -#[derive(Subcommand)] -enum Commands { - /// Toggle MPD stream - Toggle, - /// Skip to the next track - Next, - /// Revert to the previous track - Prev, - /// Stops playing - Stop, - /// Play queueueu - Play { track: Option }, - /// Set or get crossfade - Crossfade { seconds: Option }, - ///Update still needs some work - Update, - /// Return currently playing song - Current, - /// Clear current queueueu - Clear, - /// Query database - Search { - ///Search query - #[arg(trailing_var_arg = true)] - query: Vec, - ///Only return the first n results - #[arg(short, long)] - max: Option, - // #[arg(short, long)] - // append: bool, - // #[arg(short, long)] - // insert: Option, - }, - /// Query database differently - Find { - ///Search query - #[arg(trailing_var_arg = true)] - query: Vec, - ///Only return the first n results - #[arg(short, long)] - max: Option, - #[arg(short, long)] - append: bool, - }, - /// List items in the current queueueu - List { - #[arg(short, long)] - file: bool, - }, - // Add { - // #[arg(short, long)] - // position: Option, - // }, - /// Shuffles the current queueue - Shuffle, +fn print_completions(generator: G, cmd: &mut Command) { + generate( + generator, + cmd, + cmd.get_name().to_string(), + &mut io::stdout(), + ); } fn main() { + let cli = Cli::parse(); + + if let Some(completions) = cli.completions { + let mut cmd = Cli::command(); + eprintln!("Generating completion file for {completions:?}..."); + print_completions(completions, &mut cmd); + return; + } + libnotify::init("noise").unwrap(); let n = libnotify::Notification::new("Noise", None, None); - let cli = Cli::parse(); let host = cli.host.unwrap_or("localhost:6600".into()); @@ -109,17 +58,20 @@ fn main() { } Commands::Next => conn.next().unwrap(), Commands::Prev => conn.prev().unwrap(), - Commands::List { file } => conn.queue().unwrap().iter().for_each(|x| { - println!( - "{:<02} {}", - x.place.unwrap().pos, - if *file { - x.file.clone() - } else { - format_song(x.clone()) - } - ) - }), + 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}")