diff --git a/src/rust/amadeus-next/lkt-rs/src/args.rs b/src/rust/amadeus-next/lkt-rs/src/args.rs index 4c8bcbcd59930fae37226c450059160abb055995..c61815f6ac9c0d273263b85441f14b8eb9007458 100644 --- a/src/rust/amadeus-next/lkt-rs/src/args.rs +++ b/src/rust/amadeus-next/lkt-rs/src/args.rs @@ -1,23 +1,35 @@ +use std::ops::Range; + use crate::types::*; use clap::{Parser, Subcommand}; #[derive(Parser, Debug)] -#[command(author, version, about, long_about = None)] +#[command(author, version, about, long_about = None, disable_help_subcommand = true, args_conflicts_with_subcommands = true)] pub struct Args { #[command(subcommand)] action: SubCommand, + + #[arg( long + , short = 'v' + , action = clap::ArgAction::Count + , help = "Make lkt more verbose, repeat to make it even more verbose." + )] + pub verbose: u8, } #[derive(Subcommand, Debug)] +#[command(long_about = None, about = None)] pub enum SubCommand { // Status commands #[command( - about = "Prints informations about the currently playing kara. Can be used to display the current kara in a status bar like xmobar or in the i3 panel." + about = "Prints informations about the currently playing kara. Can be used to display the current kara in a status bar like xmobar or in the i3 panel.", + visible_aliases = [ "cur" ] )] Current, #[command( - about = "Prints information about the state of lektor and the currently playing kara." + about = "Prints information about the state of lektor and the currently playing kara.", + visible_aliases = [ "sta" ] )] Status, @@ -31,7 +43,8 @@ pub enum SubCommand { Pause, #[command( - about = "Unpause lektord, starts at the begening of the queue if lektord was stopped." + about = "Unpause lektord, starts at the begening of the queue if lektord was stopped.", + visible_aliases = [ "unpause" ] )] UnPause, @@ -41,11 +54,12 @@ pub enum SubCommand { #[command(about = "Play next kara in the queue.")] Next, - #[command(about = "Play the previous kara in the queue.")] + #[command(about = "Play the previous kara in the queue.", visible_aliases = [ "prev" ])] Previous, #[command( - about = "Shuffle the queue. If lektord was paused it will unpause but if it was stopped it won't start. If it exists, the current kara will be placed in the first place in the queue. You can also pass a level to shuffle up to." + about = "Shuffle the queue. If lektord was paused it will unpause but if it was stopped it won't start. If it exists, the current kara will be placed in the first place in the queue. You can also pass a level to shuffle up to.", + visible_aliases = [ "shuf" ] )] Shuffle { #[arg( @@ -55,59 +69,187 @@ pub enum SubCommand { }, // Playlist commands - #[command(alias = "plt", short_flag = 'P')] + #[command(short_flag = 'P', visible_aliases = [ "plt" ])] Playlist { - #[arg( - value_parser = clap::builder::NonEmptyStringValueParser::new(), - exclusive = true, - short = 'c', - help = "Create a new playlist with a specific name.", + #[arg( value_parser = clap::builder::NonEmptyStringValueParser::new() + , exclusive = true + , short = 'c' + , visible_aliases = [ "create" ] + , help = "Create a new playlist with a specific name." )] create: Option<String>, - #[arg( - value_parser = clap::builder::NonEmptyStringValueParser::new(), - exclusive = true, - short = 'd', - help = "Delete a playlist with all its content, do nothing if the playlist didn't exists.", + #[arg( value_parser = clap::builder::NonEmptyStringValueParser::new() + , exclusive = true + , short = 'd' + , visible_aliases = [ "destroy" ] + , help = "Delete a playlist with all its content, do nothing if the playlist didn't exists." )] destroy: Option<String>, - #[arg( - short = 'l', - exclusive = true, - help = "List the content of the playlist named if a name is passed. If missing returns a list of all the available playlists." + #[arg( action = clap::ArgAction::Set + , short = 'l' + , visible_aliases = [ "list" ] + , exclusive = true + , help = "List the content of the playlist named if a name is passed. If missing returns a list of all the available playlists." )] list: Option<Option<String>>, - #[arg( - action = clap::ArgAction::Append, - exclusive = true, - num_args = 2.., - short = 'r', - help = "Deletes karas from a playlist with a valid query. The first element is the name of the playlist." + #[arg( action = clap::ArgAction::Append + , exclusive = true + , num_args = 2.. + , short = 'r' + , visible_aliases = [ "remove" ] + , help = "Deletes karas from a playlist with a valid query. The first element is the name of the playlist." )] remove: Option<Vec<String>>, - #[arg( - action = clap::ArgAction::Append, - exclusive = true, - num_args = 2.., - short = 'a', - help = "Add karas to a playlist with a valid query. The first element is the name of the playlist." + #[arg( action = clap::ArgAction::Append + , exclusive = true + , num_args = 2.. + , short = 'a' + , visible_aliases = [ "add" ] + , help = "Add karas to a playlist with a valid query. The first element is the name of the playlist." )] add: Option<Vec<String>>, }, // Queue commands #[command(short_flag = 'Q')] - Queue {}, + Queue { + #[arg( action = clap::ArgAction::Set + , exclusive = true + , short = 'S' + , help = "Goto to the kara with the specified id in the queue." + , id = "ID" + )] + seek: Option<usize>, + + #[arg( action = clap::ArgAction::Set + , value_parser = crate::parsers::RangeParser::new() + , exclusive = true + , short = 'l' + , help = "Prints the names and ids of karas in the queue. Karas are designated by a range of positions." + , id = "LOWER:UPPER" + )] + pos: Option<Range<usize>>, + + #[arg( action = clap::ArgAction::SetTrue + , exclusive = true + , short = 'C' + , help = "Crop the queue, delete every kara from it appart from the currently playing one." + )] + crop: bool, + + #[arg( action = clap::ArgAction::SetTrue + , exclusive = true + , short = 'c' + , help = "Clear the queue and set the state to stopped." + )] + clear: bool, + + #[arg( action = clap::ArgAction::Set + , exclusive = true + , num_args = 2 + , short = 's' + , visible_aliases = [ "swap" ] + , help = "Swap two karas in the queue by their position" + , id = "POS" + )] + swap: Option<Vec<String>>, + }, // Search commands - #[command(short_flag = 'S')] - Search {}, + #[command(short_flag = 'S', visible_aliases = [ "find" ])] + Search { + #[arg( action = clap::ArgAction::Append + , exclusive = true + , raw = true + , last = false + , num_args = 1.. + , short = 'd' + , visible_aliases = [ "db", "database" ] + , help = "Search kara in the whole database with a query." + )] + database: Option<Vec<String>>, + + #[arg( action = clap::ArgAction::Append + , exclusive = true + , raw = true + , last = false + , num_args = 1.. + , short = 'c' + , visible_aliases = [ "count" ] + , help = "Search kara in the whole database with a query and returns the count of matching karas." + )] + count: Option<Vec<String>>, + + #[arg( action = clap::ArgAction::Set + , exclusive = true + , short = 'g' + , visible_aliases = [ "get", "info" ] + , help = "Prints more informations about a specific kara from the database." + )] + get: Option<usize>, + + #[arg( action = clap::ArgAction::Append + , exclusive = true + , raw = true + , last = false + , num_args = 2.. + , short = 'p' + , visible_aliases = [ "playlist", "plt" ] + , help = "Search kara in a playlist with a query. The first element is the name of the playlist." + )] + plt: Option<Vec<String>>, + + #[arg( action = clap::ArgAction::Append + , exclusive = true + , raw = true + , last = false + , num_args = 1.. + , short = 'q' + , visible_aliases = [ "queue" ] + , help = "Search kara in the queue with a query." + )] + queue: Option<Vec<String>>, + }, // Admin commands - #[command(alias = "adm", short_flag = 'A')] - Admin {}, + #[command(short_flag = 'A', visible_aliases = [ "adm" ])] + Admin { + #[arg( action = clap::ArgAction::SetTrue + , short = 'p' + , group = "action" + , help = "Pings the lektord daemon, prints OK only if the ping succeeded." + )] + ping: bool, + + #[arg( action = clap::ArgAction::SetTrue + , short = 'k' + , group = "action" + , help = "Kill the lektord daemon." + )] + kill: bool, + + #[arg( action = clap::ArgAction::SetTrue + , short = 'r' + , group = "action" + , help = "Try to restart the lektord daemon." + )] + restart: bool, + + #[arg( action = clap::ArgAction::SetTrue + , short = 'u' + , group = "action" + , help = "Update the base from all the repos from the config file. Don't scan for new files in the filesystem." + )] + update: bool, + + #[arg( action = clap::ArgAction::SetTrue + , long = "dry" + , help = "Don't do the action. For update only fetch the metadata and not the karas." + )] + dry: bool, + }, } diff --git a/src/rust/amadeus-next/lkt-rs/src/parsers.rs b/src/rust/amadeus-next/lkt-rs/src/parsers.rs index 2a936f2875d39a48e720e6d33cf15c24f6ae9113..349b9224e2610de7ae53e5f8e91cf78bd88cd70e 100644 --- a/src/rust/amadeus-next/lkt-rs/src/parsers.rs +++ b/src/rust/amadeus-next/lkt-rs/src/parsers.rs @@ -1,50 +1,60 @@ -use clap::{builder::TypedValueParser, error::ErrorKind, Arg, Command, Error}; +use clap::{builder::TypedValueParser, error::ErrorKind}; +use commons::either; +use std::ops::Range; -/// A parser for the [crate::types::FilterPlaylist] structure -#[derive(Copy, Clone, Debug)] +/// A parser for the [std::ops::Range] structure +#[derive(Copy, Clone, Debug, Default)] #[non_exhaustive] -pub struct FilterPlaylistParser {} +pub struct RangeParser {} -impl FilterPlaylistParser { - /// Parse non-empty string values +impl RangeParser { pub fn new() -> Self { - Self {} + Default::default() } } -impl TypedValueParser for FilterPlaylistParser { - type Value = (String, String); +impl TypedValueParser for RangeParser { + type Value = Range<usize>; fn parse_ref( &self, - cmd: &Command, - arg: Option<&Arg>, + cmd: &clap::Command, + arg: Option<&clap::Arg>, value: &std::ffi::OsStr, - ) -> Result<Self::Value, Error> { + ) -> Result<Self::Value, clap::Error> { + let name = cmd.get_name(); let arg = arg .map(ToString::to_string) .unwrap_or_else(|| "...".to_owned()); let value = value.to_str().ok_or_else(|| { - Error::raw( + clap::Error::raw( ErrorKind::InvalidUtf8, format!("not a valid utf8 string: {}", value.to_string_lossy()), ) })?; - match value.split_once(' ') { - Some((head, tail)) => Ok((head.to_string(), tail.to_string())), - _ => Err(Error::raw( + match value.split(':').collect::<Vec<_>>()[..] { + [lower, upper] => { + let lower = lower.parse::<usize>().map_err(|e| { + clap::Error::raw( + ErrorKind::InvalidValue, + format!("in `{name} {arg}`, the value is not an integer: {e}"), + ) + })?; + let upper = upper.parse::<usize>().map_err(|e| { + clap::Error::raw( + ErrorKind::InvalidValue, + format!("in `{name} {arg}`, the value is not an integer: {e}"), + ) + })?; + either!(lower <= upper => Ok(lower..upper); Err(clap::Error::raw( + ErrorKind::InvalidValue, + format!("in `{name} {arg}`, the range must be valid, unlike `{lower}:{upper}`, did you meant to write `{upper}:{lower}` instead?"), + ))) + } + _ => Err(clap::Error::raw( ErrorKind::InvalidValue, - format!( - "in `{} {arg}`, the value should be of the form: [PLAYLIST] [QUERY]", - cmd.get_name() - ), + format!("in `{name} {arg}`, the value should be of the form: `[lower]:[upper]`"), )), } } } - -impl Default for FilterPlaylistParser { - fn default() -> Self { - Self::new() - } -}