From 65af447bc732babf596a5704b764cdb9903bc5ab Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Thu, 7 Dec 2023 16:30:16 +0100 Subject: [PATCH] value hints in completions --- complete/src/fish.rs | 1 + derive/src/argument.rs | 2 ++ derive/src/complete.rs | 18 ++++++++++++++++-- derive/src/flags.rs | 2 +- derive/src/lib.rs | 13 +++++++++++++ examples/value.rs | 38 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 examples/value.rs diff --git a/complete/src/fish.rs b/complete/src/fish.rs index 8c42c7d..5bbb451 100644 --- a/complete/src/fish.rs +++ b/complete/src/fish.rs @@ -30,6 +30,7 @@ fn render_value_hint(value: &ValueHint) -> String { let joined = s.join(", "); format!(" -a {{ {joined} }}") } + ValueHint::Unknown => String::new(), _ => todo!(), } } diff --git a/derive/src/argument.rs b/derive/src/argument.rs index 7e3928a..9f0a1c6 100644 --- a/derive/src/argument.rs +++ b/derive/src/argument.rs @@ -14,6 +14,7 @@ use crate::{ pub struct Argument { pub ident: Ident, + pub field: Option, pub name: String, pub arg_type: ArgType, pub help: String, @@ -105,6 +106,7 @@ pub fn parse_argument(v: Variant) -> Vec { }; Argument { ident: ident.clone(), + field: field.clone(), name: name.clone(), arg_type, help: arg_help, diff --git a/derive/src/complete.rs b/derive/src/complete.rs index 92bcf98..b2d4bae 100644 --- a/derive/src/complete.rs +++ b/derive/src/complete.rs @@ -11,7 +11,13 @@ use quote::quote; pub fn complete(args: &[Argument]) -> TokenStream { let mut arg_specs = Vec::new(); - for Argument { help, arg_type, .. } in args { + for Argument { + help, + field, + arg_type, + .. + } in args + { let ArgType::Option { flags, hidden: false, @@ -25,21 +31,29 @@ pub fn complete(args: &[Argument]) -> TokenStream { if short.is_empty() && long.is_empty() { continue; } + let short: Vec<_> = short .iter() .map(|Flag { flag, .. }| quote!(String::from(#flag))) .collect(); + let long: Vec<_> = long .iter() .map(|Flag { flag, .. }| quote!(String::from(#flag))) .collect(); + let hint = if let Some(ty) = field { + quote!(Some(<#ty>::value_hint())) + } else { + quote!(None) + }; + arg_specs.push(quote!( Arg { short: vec![#(#short),*], long: vec![#(#long),*], help: String::from(#help), - value: None, + value: #hint, } )) } diff --git a/derive/src/flags.rs b/derive/src/flags.rs index 82cf489..3d69dca 100644 --- a/derive/src/flags.rs +++ b/derive/src/flags.rs @@ -11,7 +11,7 @@ pub struct Flags { pub dd_style: Vec<(String, String)>, } -#[derive(Clone)] +#[derive(Clone, PartialEq, Eq)] pub enum Value { No, Optional(String), diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 3793247..454359c 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -115,6 +115,7 @@ pub fn arguments(input: TokenStream) -> TokenStream { #[cfg(feature = "complete")] fn complete() -> ::uutils_args_complete::Command { use ::uutils_args_complete::{Command, Arg, ValueHint}; + use ::uutils_args::Value; #completion } } @@ -137,6 +138,7 @@ pub fn value(input: TokenStream) -> TokenStream { let mut options = Vec::new(); let mut match_arms = vec![]; + let mut all_keys = Vec::new(); for variant in data.variants { let variant_name = variant.ident.to_string(); let attrs = variant.attrs.clone(); @@ -153,6 +155,7 @@ pub fn value(input: TokenStream) -> TokenStream { keys }; + all_keys.extend(keys.clone()); options.push(quote!(&[#(#keys),*])); let stmt = if let Some(v) = value { @@ -201,6 +204,16 @@ pub fn value(input: TokenStream) -> TokenStream { _ => unreachable!("Should be caught by (None, []) case above.") }) } + + #[cfg(feature = "complete")] + fn value_hint() -> ::uutils_args_complete::ValueHint { + ::uutils_args_complete::ValueHint::Strings( + [#(#all_keys),*] + .into_iter() + .map(ToString::to_string) + .collect() + ) + } } ); diff --git a/examples/value.rs b/examples/value.rs new file mode 100644 index 0000000..155e05a --- /dev/null +++ b/examples/value.rs @@ -0,0 +1,38 @@ +use uutils_args::{Arguments, Initial, Options, Value}; + +#[derive(Arguments)] +#[arguments(file = "examples/hello_world_help.md")] +enum Arg { + /// Color! + #[arg("-c NAME", "--color=NAME")] + Color(Color), +} + +#[derive(Value, Debug, Default)] +enum Color { + #[value("never")] + Never, + #[default] + #[value("auto")] + Auto, + #[value("always")] + Always, +} + +#[derive(Initial)] +struct Settings { + color: Color, +} + +impl Options for Settings { + fn apply(&mut self, arg: Arg) { + match arg { + Arg::Color(c) => self.color = c, + } + } +} + +fn main() { + let color = Settings::parse(std::env::args_os()).color; + println!("{:?}", color); +}