mirror of
https://github.com/Drop-OSS/interactive-clap.git
synced 2026-07-19 20:03:34 -04:00
fix: named_args/unnamed_args/args_without_attrs conflict (#9)
* fixed * updated examples/advanced_struct * refactored InteractiveClapAttrsCliField::new() * Update interactive-clap-derive/src/derives/to_cli_args/methods/interactive_clap_attrs_cli_field.rs Co-authored-by: Vlad Frolov <frolvlad@gmail.com> * refactored InteractiveClapAttrsCliField --------- Co-authored-by: Vlad Frolov <frolvlad@gmail.com>
This commit is contained in:
+108
-2
@@ -3,11 +3,13 @@
|
||||
// 1) build an example: cargo build --example advanced_struct
|
||||
// 2) go to the `examples` folder: cd target/debug/examples
|
||||
// 3) run an example: ./advanced_struct (without parameters) => entered interactive mode
|
||||
// ./advanced_struct --age-full-years 30 --first-name QWE --second-name QWERTY =>
|
||||
// => args: Ok(Args { age: 30, first_name: "QWE", second_name: "QWERTY" })
|
||||
// ./advanced_struct --age-full-years 30 --first-name QWE --second-name QWERTY --favorite-color red =>
|
||||
// => cli_args: CliArgs { age: Some(30), first_name: Some("QWE"), second_name: Some("QWERTY"), favorite_color: Some(Red) }
|
||||
// To learn more about the parameters, use "help" flag: ./advanced_struct --help
|
||||
|
||||
use inquire::Select;
|
||||
use interactive_clap::{ResultFromCli, ToCliArgs};
|
||||
use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator};
|
||||
|
||||
#[derive(Debug, Clone, interactive_clap::InteractiveClap)]
|
||||
#[interactive_clap(skip_default_from_cli)]
|
||||
@@ -21,6 +23,10 @@ struct Args {
|
||||
#[interactive_clap(long)]
|
||||
#[interactive_clap(skip_default_input_arg)]
|
||||
second_name: String,
|
||||
#[interactive_clap(long)]
|
||||
#[interactive_clap(value_enum)]
|
||||
#[interactive_clap(skip_default_input_arg)]
|
||||
favorite_color: ColorPalette,
|
||||
}
|
||||
|
||||
impl interactive_clap::FromCli for Args {
|
||||
@@ -57,6 +63,17 @@ impl interactive_clap::FromCli for Args {
|
||||
};
|
||||
}
|
||||
let _second_name = clap_variant.second_name.clone().expect("Unexpected error");
|
||||
if clap_variant.favorite_color.is_none() {
|
||||
clap_variant.favorite_color = match Self::input_favorite_color(&context) {
|
||||
Ok(Some(favorite_color)) => Some(favorite_color),
|
||||
Ok(None) => return ResultFromCli::Cancel(Some(clap_variant)),
|
||||
Err(err) => return ResultFromCli::Err(Some(clap_variant), err),
|
||||
};
|
||||
}
|
||||
let _favorite_color = clap_variant
|
||||
.favorite_color
|
||||
.clone()
|
||||
.expect("Unexpected error");
|
||||
ResultFromCli::Ok(clap_variant)
|
||||
}
|
||||
}
|
||||
@@ -83,6 +100,94 @@ impl Args {
|
||||
Err(err) => Err(err.into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn input_favorite_color(_context: &()) -> color_eyre::eyre::Result<Option<ColorPalette>> {
|
||||
let variants = ColorPaletteDiscriminants::iter().collect::<Vec<_>>();
|
||||
let selected = Select::new("What color is your favorite?", variants).prompt()?;
|
||||
match selected {
|
||||
ColorPaletteDiscriminants::Red => Ok(Some(ColorPalette::Red)),
|
||||
ColorPaletteDiscriminants::Orange => Ok(Some(ColorPalette::Orange)),
|
||||
ColorPaletteDiscriminants::Yellow => Ok(Some(ColorPalette::Yellow)),
|
||||
ColorPaletteDiscriminants::Green => Ok(Some(ColorPalette::Green)),
|
||||
ColorPaletteDiscriminants::Blue => Ok(Some(ColorPalette::Blue)),
|
||||
ColorPaletteDiscriminants::Indigo => Ok(Some(ColorPalette::Indigo)),
|
||||
ColorPaletteDiscriminants::Violet => Ok(Some(ColorPalette::Violet)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, EnumDiscriminants, Clone, clap::ValueEnum)]
|
||||
#[strum_discriminants(derive(EnumMessage, EnumIter))]
|
||||
pub enum ColorPalette {
|
||||
#[strum_discriminants(strum(message = "red"))]
|
||||
/// Red
|
||||
Red,
|
||||
#[strum_discriminants(strum(message = "orange"))]
|
||||
/// Orange
|
||||
Orange,
|
||||
#[strum_discriminants(strum(message = "yellow"))]
|
||||
/// Yellow
|
||||
Yellow,
|
||||
#[strum_discriminants(strum(message = "green"))]
|
||||
/// Green
|
||||
Green,
|
||||
#[strum_discriminants(strum(message = "blue"))]
|
||||
/// Blue
|
||||
Blue,
|
||||
#[strum_discriminants(strum(message = "indigo"))]
|
||||
/// Indigo
|
||||
Indigo,
|
||||
#[strum_discriminants(strum(message = "violet"))]
|
||||
/// Violet
|
||||
Violet,
|
||||
}
|
||||
|
||||
impl interactive_clap::ToCli for ColorPalette {
|
||||
type CliVariant = ColorPalette;
|
||||
}
|
||||
|
||||
impl std::str::FromStr for ColorPalette {
|
||||
type Err = String;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"red" => Ok(Self::Red),
|
||||
"orange" => Ok(Self::Orange),
|
||||
"yellow" => Ok(Self::Yellow),
|
||||
"green" => Ok(Self::Green),
|
||||
"blue" => Ok(Self::Blue),
|
||||
"indigo" => Ok(Self::Indigo),
|
||||
"violet" => Ok(Self::Violet),
|
||||
_ => Err("ColorPalette: incorrect value entered".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ColorPalette {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Red => write!(f, "red"),
|
||||
Self::Orange => write!(f, "orange"),
|
||||
Self::Yellow => write!(f, "yellow"),
|
||||
Self::Green => write!(f, "green"),
|
||||
Self::Blue => write!(f, "blue"),
|
||||
Self::Indigo => write!(f, "indigo"),
|
||||
Self::Violet => write!(f, "violet"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ColorPaletteDiscriminants {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Red => write!(f, "red"),
|
||||
Self::Orange => write!(f, "orange"),
|
||||
Self::Yellow => write!(f, "yellow"),
|
||||
Self::Green => write!(f, "green"),
|
||||
Self::Blue => write!(f, "blue"),
|
||||
Self::Indigo => write!(f, "indigo"),
|
||||
Self::Violet => write!(f, "violet"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> color_eyre::Result<()> {
|
||||
@@ -92,6 +197,7 @@ fn main() -> color_eyre::Result<()> {
|
||||
let args = <Args as interactive_clap::FromCli>::from_cli(Some(cli_args), context);
|
||||
match args {
|
||||
ResultFromCli::Ok(cli_args) | ResultFromCli::Cancel(Some(cli_args)) => {
|
||||
println!("cli_args: {cli_args:?}");
|
||||
println!(
|
||||
"Your console command: {}",
|
||||
shell_words::join(&cli_args.to_cli_args())
|
||||
|
||||
+17
-25
@@ -6,20 +6,14 @@ use quote::{quote, ToTokens};
|
||||
use syn;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct InteractiveClapAttrsCliField {
|
||||
pub ident_field: syn::Ident,
|
||||
pub args_without_attrs: Option<proc_macro2::TokenStream>,
|
||||
pub named_args: Option<proc_macro2::TokenStream>,
|
||||
pub unnamed_args: Option<proc_macro2::TokenStream>,
|
||||
pub subcommand_args: Option<proc_macro2::TokenStream>,
|
||||
pub enum InteractiveClapAttrsCliField {
|
||||
RegularField(proc_macro2::TokenStream),
|
||||
SubcommandField(proc_macro2::TokenStream),
|
||||
}
|
||||
|
||||
impl InteractiveClapAttrsCliField {
|
||||
pub fn new(field: syn::Field) -> Self {
|
||||
let ident_field = field.ident.clone().expect("this field does not exist");
|
||||
let mut subcommand_args = quote! {
|
||||
let mut args = std::collections::VecDeque::new();
|
||||
};
|
||||
let mut args_without_attrs = quote!();
|
||||
let mut named_args = quote!();
|
||||
let mut unnamed_args = quote!();
|
||||
@@ -40,14 +34,15 @@ impl InteractiveClapAttrsCliField {
|
||||
match &item {
|
||||
proc_macro2::TokenTree::Ident(ident) => {
|
||||
if ident == "subcommand" {
|
||||
subcommand_args = quote! {
|
||||
return Self::SubcommandField(quote! {
|
||||
let mut args = self
|
||||
.#ident_field
|
||||
.as_ref()
|
||||
.map(|subcommand| subcommand.to_cli_args())
|
||||
.unwrap_or_default();
|
||||
};
|
||||
} else if ident == "value_enum" {
|
||||
});
|
||||
}
|
||||
if ident == "value_enum" {
|
||||
args_without_attrs = quote! {
|
||||
if let Some(arg) = &self.#ident_field {
|
||||
args.push_front(arg.to_string())
|
||||
@@ -97,18 +92,15 @@ impl InteractiveClapAttrsCliField {
|
||||
}
|
||||
}
|
||||
};
|
||||
Self {
|
||||
ident_field,
|
||||
args_without_attrs: Some(args_without_attrs),
|
||||
named_args: Some(named_args.clone()),
|
||||
unnamed_args: {
|
||||
if !named_args.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(unnamed_args)
|
||||
}
|
||||
},
|
||||
subcommand_args: Some(subcommand_args),
|
||||
}
|
||||
let token_stream_args: proc_macro2::TokenStream = if !named_args.is_empty() {
|
||||
named_args
|
||||
} else if !unnamed_args.is_empty() {
|
||||
unnamed_args
|
||||
} else if !args_without_attrs.is_empty() {
|
||||
args_without_attrs
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
Self::RegularField(token_stream_args)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ use proc_macro_error::abort_call_site;
|
||||
use quote::quote;
|
||||
use syn;
|
||||
|
||||
use self::methods::interactive_clap_attrs_cli_field::InteractiveClapAttrsCliField;
|
||||
|
||||
mod methods;
|
||||
|
||||
pub fn impl_to_cli_args(ast: &syn::DeriveInput) -> TokenStream {
|
||||
@@ -17,24 +19,14 @@ pub fn impl_to_cli_args(ast: &syn::DeriveInput) -> TokenStream {
|
||||
let mut args_push_front_vec: Vec<proc_macro2::TokenStream> = Vec::new();
|
||||
|
||||
for field in data_struct.clone().fields.iter() {
|
||||
let interactive_clap_attrs_cli_field = self::methods::interactive_clap_attrs_cli_field::InteractiveClapAttrsCliField::new(field.clone());
|
||||
args_subcommand = if let Some(subcommand_args) =
|
||||
interactive_clap_attrs_cli_field.subcommand_args
|
||||
{
|
||||
subcommand_args
|
||||
} else {
|
||||
args_subcommand
|
||||
};
|
||||
if let Some(unnamed_args) = interactive_clap_attrs_cli_field.unnamed_args {
|
||||
args_push_front_vec.push(unnamed_args)
|
||||
} else if let Some(args_without_attrs) =
|
||||
interactive_clap_attrs_cli_field.args_without_attrs
|
||||
{
|
||||
args_push_front_vec.push(args_without_attrs)
|
||||
};
|
||||
if let Some(named_args) = interactive_clap_attrs_cli_field.named_args {
|
||||
args_push_front_vec.push(named_args)
|
||||
};
|
||||
match InteractiveClapAttrsCliField::new(field.clone()) {
|
||||
InteractiveClapAttrsCliField::RegularField(regular_field_args) => {
|
||||
args_push_front_vec.push(regular_field_args)
|
||||
}
|
||||
InteractiveClapAttrsCliField::SubcommandField(subcommand_args) => {
|
||||
args_subcommand = subcommand_args
|
||||
}
|
||||
}
|
||||
}
|
||||
let args_push_front_vec = args_push_front_vec.into_iter().rev();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user