From f7ce792eb4c0b774f7ebdfff74f6b653eb94254d Mon Sep 17 00:00:00 2001 From: Vlad Frolov Date: Sun, 20 Nov 2022 12:30:48 +0100 Subject: [PATCH] feat: Introduced FromCli and ToCliArgs traits to unblock future generics implementation --- examples/simple_enum.rs | 5 ++- examples/simple_struct.rs | 2 +- examples/struct_with_context.rs | 12 +++++-- examples/struct_with_named_arg.rs | 3 +- examples/struct_with_subcommand.rs | 2 +- examples/to_cli_args.rs | 33 +++++++++++-------- .../methods/choose_variant.rs | 2 +- .../fields_without_skip_default_input_arg.rs | 3 +- .../methods/fields_without_subcommand.rs | 3 +- .../methods/from_cli_for_enum.rs | 22 ++++++++----- .../methods/from_cli_for_struct.rs | 31 +++++++++-------- .../interactive_clap/methods/input_arg.rs | 2 +- .../src/derives/interactive_clap/mod.rs | 6 ++-- .../src/derives/to_cli_args/mod.rs | 8 ++--- src/lib.rs | 21 +++++++----- 15 files changed, 89 insertions(+), 66 deletions(-) diff --git a/examples/simple_enum.rs b/examples/simple_enum.rs index 60f9655..f3adccf 100644 --- a/examples/simple_enum.rs +++ b/examples/simple_enum.rs @@ -7,8 +7,7 @@ // ./simple_enum offline => mode: Ok(Offline) // To learn more about the parameters, use "help" flag: ./simple_enum --help -use dialoguer::{theme::ColorfulTheme, Select}; -use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; #[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)] #[strum_discriminants(derive(EnumMessage, EnumIter))] @@ -28,6 +27,6 @@ pub enum Mode { fn main() { let cli_mode = Mode::try_parse().ok(); let context = (); // default: input_context = () - let mode = Mode::from_cli(cli_mode, context); + let mode = ::from_cli(cli_mode, context); println!("mode: {:?}", mode) } diff --git a/examples/simple_struct.rs b/examples/simple_struct.rs index cb55998..0d01f46 100644 --- a/examples/simple_struct.rs +++ b/examples/simple_struct.rs @@ -16,6 +16,6 @@ struct Args { fn main() { let cli_args = Args::parse(); let context = (); // default: input_context = () - let args = Args::from_cli(Some(cli_args), context); + let args = ::from_cli(Some(cli_args), context); println!("args: {:?}", args) } diff --git a/examples/struct_with_context.rs b/examples/struct_with_context.rs index 5471234..fd641b8 100644 --- a/examples/struct_with_context.rs +++ b/examples/struct_with_context.rs @@ -17,14 +17,19 @@ pub struct OfflineArgs { account: Sender, } -pub struct OfflineArgsContext {} +#[derive(Debug)] +pub struct OfflineArgsContext { + pub some_context_field: i64, +} impl OfflineArgsContext { fn from_previous_context( _previous_context: (), _scope: &::InteractiveClapContextScope, ) -> Self { - Self {} + Self { + some_context_field: 42, + } } } @@ -49,6 +54,7 @@ pub struct Sender { impl Sender { fn input_sender_account_id(context: &OfflineArgsContext) -> color_eyre::eyre::Result { + println!("Let's use context: {:?}", context); Ok(dialoguer::Input::new() .with_prompt("What is the account ID?") .interact_text()?) @@ -58,6 +64,6 @@ impl Sender { fn main() { let cli_offline_args = OfflineArgs::parse(); let context = (); // #[interactive_clap(input_context = ())] - let offline_args = OfflineArgs::from_cli(Some(cli_offline_args), context); + let offline_args = ::from_cli(Some(cli_offline_args), context); println!("offline_args: {:?}", offline_args) } diff --git a/examples/struct_with_named_arg.rs b/examples/struct_with_named_arg.rs index 7af8f90..c387d5b 100644 --- a/examples/struct_with_named_arg.rs +++ b/examples/struct_with_named_arg.rs @@ -7,7 +7,6 @@ // ./struct_with_named_arg account QWERTY => account: Ok(Account { account: Sender { sender_account_id: "QWERTY" } }) // To learn more about the parameters, use "help" flag: ./struct_with_named_arg --help -use clap::Parser; #[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] struct Account { #[interactive_clap(named_arg)] @@ -24,6 +23,6 @@ pub struct Sender { fn main() { let cli_account = Account::parse(); let context = (); // default: input_context = () - let account = Account::from_cli(Some(cli_account), context); + let account = ::from_cli(Some(cli_account), context); println!("account: {:?}", account) } diff --git a/examples/struct_with_subcommand.rs b/examples/struct_with_subcommand.rs index 56f17cf..8a71cd3 100644 --- a/examples/struct_with_subcommand.rs +++ b/examples/struct_with_subcommand.rs @@ -18,6 +18,6 @@ pub struct OperationMode { fn main() { let cli_operation_mode = OperationMode::parse(); let context = (); // default: input_context = () - let operation_mode = OperationMode::from_cli(Some(cli_operation_mode), context); + let operation_mode = ::from_cli(Some(cli_operation_mode), context); println!("operation_mode: {:?}", &operation_mode); } diff --git a/examples/to_cli_args.rs b/examples/to_cli_args.rs index 90a22db..df61d28 100644 --- a/examples/to_cli_args.rs +++ b/examples/to_cli_args.rs @@ -10,6 +10,8 @@ use dialoguer::{theme::ColorfulTheme, Select}; use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; +use interactive_clap::ToCliArgs; + mod common; #[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] @@ -30,9 +32,25 @@ pub enum Submit { Display, } +impl interactive_clap::FromCli for Submit { + type FromCliContext = common::ConnectionConfig; + type FromCliError = color_eyre::eyre::Error; + + fn from_cli( + optional_clap_variant: Option<::CliVariant>, + _context: Self::FromCliContext, + ) -> Result, Self::FromCliError> where Self: Sized + interactive_clap::ToCli { + let submit: Option = optional_clap_variant.clone(); + match submit { + Some(submit) => Ok(Some(submit)), + None => Ok(Some(Submit::Display)), + } + } +} + impl Submit { fn choose_variant( - connection_config: common::ConnectionConfig, + _context: common::ConnectionConfig, ) -> color_eyre::eyre::Result> { let variants = SubmitDiscriminants::iter().collect::>(); let mut submits = variants @@ -52,17 +70,6 @@ impl Submit { None => Ok(None), } } - - fn from_cli( - optional_clap_variant: Option<::CliVariant>, - context: common::ConnectionConfig, - ) -> color_eyre::eyre::Result> { - let submit: Option = optional_clap_variant.clone(); - match submit { - Some(submit) => Ok(Some(submit)), - None => Ok(Some(Submit::Display)), - } - } } impl interactive_clap::ToCli for Submit { @@ -74,7 +81,7 @@ fn main() { let context = common::ConnectionConfig::Testnet; //#[interactive_clap(context = common::ConnectionConfig)] let online_args = loop { if let Some(args) = - OnlineArgs::from_cli(Some(cli_online_args.clone()), context.clone()).unwrap() + ::from_cli(Some(cli_online_args.clone()), context.clone()).unwrap() { break args; } diff --git a/interactive_clap_derive/src/derives/interactive_clap/methods/choose_variant.rs b/interactive_clap_derive/src/derives/interactive_clap/methods/choose_variant.rs index 13cfa4e..76569c2 100644 --- a/interactive_clap_derive/src/derives/interactive_clap/methods/choose_variant.rs +++ b/interactive_clap_derive/src/derives/interactive_clap/methods/choose_variant.rs @@ -160,7 +160,7 @@ pub fn fn_choose_variant( pub fn choose_variant(context: #input_context) -> color_eyre::eyre::Result> { loop { #cli_variant - if let Some(variant) = Self::from_cli(Some(cli_variant), context.clone())? { + if let Some(variant) = ::from_cli(Some(cli_variant), context.clone())? { return Ok(Some(variant)); } } diff --git a/interactive_clap_derive/src/derives/interactive_clap/methods/fields_without_skip_default_input_arg.rs b/interactive_clap_derive/src/derives/interactive_clap/methods/fields_without_skip_default_input_arg.rs index 84dd1af..cafc3e3 100644 --- a/interactive_clap_derive/src/derives/interactive_clap/methods/fields_without_skip_default_input_arg.rs +++ b/interactive_clap_derive/src/derives/interactive_clap/methods/fields_without_skip_default_input_arg.rs @@ -1,7 +1,6 @@ extern crate proc_macro; use proc_macro_error::abort_call_site; -use quote::quote; use syn; pub fn is_field_without_skip_default_input_arg(field: &syn::Field) -> bool { @@ -30,7 +29,7 @@ pub fn is_field_without_skip_default_input_arg(field: &syn::Field) -> bool { }) .next() { - Some(token_stream) => false, + Some(_token_stream) => false, None => true, } } diff --git a/interactive_clap_derive/src/derives/interactive_clap/methods/fields_without_subcommand.rs b/interactive_clap_derive/src/derives/interactive_clap/methods/fields_without_subcommand.rs index 0d8c465..10492ca 100644 --- a/interactive_clap_derive/src/derives/interactive_clap/methods/fields_without_subcommand.rs +++ b/interactive_clap_derive/src/derives/interactive_clap/methods/fields_without_subcommand.rs @@ -1,7 +1,6 @@ extern crate proc_macro; use proc_macro_error::abort_call_site; -use quote::quote; use syn; pub fn is_field_without_subcommand(field: &syn::Field) -> bool { @@ -29,7 +28,7 @@ pub fn is_field_without_subcommand(field: &syn::Field) -> bool { }) .next() { - Some(token_stream) => false, + Some(_token_stream) => false, None => true, } } diff --git a/interactive_clap_derive/src/derives/interactive_clap/methods/from_cli_for_enum.rs b/interactive_clap_derive/src/derives/interactive_clap/methods/from_cli_for_enum.rs index c00c514..ca734e5 100644 --- a/interactive_clap_derive/src/derives/interactive_clap/methods/from_cli_for_enum.rs +++ b/interactive_clap_derive/src/derives/interactive_clap/methods/from_cli_for_enum.rs @@ -32,7 +32,7 @@ pub fn from_cli_for_enum( type Alias = <#name as interactive_clap::ToInteractiveClapContextScope>::InteractiveClapContextScope; let new_context_scope = Alias::#variant_ident; let new_context = #context_name::from_previous_context(context.clone(), &new_context_scope); - let optional_inner_args = #ty::from_cli(Some(inner_cli_args), new_context.into())?; + let optional_inner_args = <#ty as interactive_clap::FromCli>::from_cli(Some(inner_cli_args), new_context.into())?; if let Some(inner_args) = optional_inner_args { Ok(Some(Self::#variant_ident(inner_args,))) } else { @@ -42,7 +42,7 @@ pub fn from_cli_for_enum( }, None => quote! { Some(#cli_name::#variant_ident(inner_cli_args)) => { - let optional_inner_args = #ty::from_cli(Some(inner_cli_args), context.clone())?; + let optional_inner_args = <#ty as interactive_clap::FromCli>::from_cli(Some(inner_cli_args), context.clone().into())?; if let Some(inner_args) = optional_inner_args { Ok(Some(Self::#variant_ident(inner_args,))) } else { @@ -66,13 +66,17 @@ pub fn from_cli_for_enum( .get_input_context_dir(); quote! { - pub fn from_cli( - optional_clap_variant: Option<#cli_name>, - context: #input_context_dir, - ) -> color_eyre::eyre::Result> { - match optional_clap_variant { - #(#from_cli_variants)* - None => Self::choose_variant(context.clone()), + impl interactive_clap::FromCli for #name { + type FromCliContext = #input_context_dir; + type FromCliError = color_eyre::eyre::Error; + fn from_cli( + optional_clap_variant: Option<::CliVariant>, + context: Self::FromCliContext, + ) -> Result, Self::FromCliError> where Self: Sized + interactive_clap::ToCli { + match optional_clap_variant { + #(#from_cli_variants)* + None => Self::choose_variant(context.clone()), + } } } } diff --git a/interactive_clap_derive/src/derives/interactive_clap/methods/from_cli_for_struct.rs b/interactive_clap_derive/src/derives/interactive_clap/methods/from_cli_for_struct.rs index 4c87294..184b407 100644 --- a/interactive_clap_derive/src/derives/interactive_clap/methods/from_cli_for_struct.rs +++ b/interactive_clap_derive/src/derives/interactive_clap/methods/from_cli_for_struct.rs @@ -10,7 +10,6 @@ pub fn from_cli_for_struct( fields: &syn::Fields, ) -> proc_macro2::TokenStream { let name = &ast.ident; - let cli_name = syn::Ident::new(&format!("Cli{}", name), Span::call_site()); let interactive_clap_attrs_context = super::interactive_clap_attrs_context::InteractiveClapAttrsContext::new(&ast); @@ -81,15 +80,19 @@ pub fn from_cli_for_struct( }; quote! { - pub fn from_cli( - optional_clap_variant: Option<#cli_name>, - context: #input_context_dir, - ) -> color_eyre::eyre::Result> { - #(#fields_value)* - #new_context_scope - #field_value_named_arg - #field_value_subcommand; - Ok(Some(Self{ #(#struct_fields,)* })) + impl interactive_clap::FromCli for #name { + type FromCliContext = #input_context_dir; + type FromCliError = color_eyre::eyre::Error; + fn from_cli( + optional_clap_variant: Option<::CliVariant>, + context: Self::FromCliContext, + ) -> Result, Self::FromCliError> where Self: Sized + interactive_clap::ToCli { + #(#fields_value)* + #new_context_scope + #field_value_named_arg + #field_value_subcommand; + Ok(Some(Self{ #(#struct_fields,)* })) + } } } } @@ -155,7 +158,7 @@ fn field_value_named_arg( let context_for_struct = syn::Ident::new(&format!("{}Context", &name), Span::call_site()); quote! { let new_context = #context_for_struct::from_previous_context(context, &new_context_scope); - let #ident_field = #ty::from_cli( + let #ident_field = <#ty as interactive_clap::FromCli>::from_cli( optional_clap_variant.and_then(|clap_variant| match clap_variant.#ident_field { Some(#enum_for_clap_named_arg::#variant_name(cli_arg)) => Some(cli_arg), None => None, @@ -170,7 +173,7 @@ fn field_value_named_arg( } }, None => quote! { - let #ident_field = #ty::from_cli( + let #ident_field = <#ty as interactive_clap::FromCli>::from_cli( optional_clap_variant.and_then(|clap_variant| match clap_variant.#ident_field { Some(#enum_for_clap_named_arg::#variant_name(cli_sender)) => Some(cli_sender), None => None, @@ -225,7 +228,7 @@ fn field_value_subcommand( quote! { let new_context = #context_for_struct::from_previous_context(context, &new_context_scope); let #ident_field = match optional_clap_variant.and_then(|clap_variant| clap_variant.#ident_field) { - Some(cli_arg) => #ty::from_cli(Some(cli_arg), new_context.into())?, + Some(cli_arg) => <#ty as interactive_clap::FromCli>::from_cli(Some(cli_arg), new_context.into())?, None => #ty::choose_variant(new_context.into())?, }; let #ident_field = if let Some(value) = #ident_field { @@ -237,7 +240,7 @@ fn field_value_subcommand( }, None => quote! { let #ident_field = match optional_clap_variant.and_then(|clap_variant| clap_variant.#ident_field) { - Some(cli_arg) => #ty::from_cli(Some(cli_arg), context)?, + Some(cli_arg) => <#ty as interactive_clap::FromCli>::from_cli(Some(cli_arg), context)?, None => #ty::choose_variant(context.into())?, }; let #ident_field = if let Some(value) = #ident_field { diff --git a/interactive_clap_derive/src/derives/interactive_clap/methods/input_arg.rs b/interactive_clap_derive/src/derives/interactive_clap/methods/input_arg.rs index c942b3e..80149b6 100644 --- a/interactive_clap_derive/src/derives/interactive_clap/methods/input_arg.rs +++ b/interactive_clap_derive/src/derives/interactive_clap/methods/input_arg.rs @@ -2,7 +2,7 @@ extern crate proc_macro; use proc_macro2::Span; use proc_macro_error::abort_call_site; -use quote::{__private::ext::RepToTokensExt, quote}; +use quote::quote; use syn; pub fn vec_fn_input_arg( diff --git a/interactive_clap_derive/src/derives/interactive_clap/mod.rs b/interactive_clap_derive/src/derives/interactive_clap/mod.rs index 3769c94..85617c0 100644 --- a/interactive_clap_derive/src/derives/interactive_clap/mod.rs +++ b/interactive_clap_derive/src/derives/interactive_clap/mod.rs @@ -197,9 +197,10 @@ pub fn impl_interactive_clap(ast: &syn::DeriveInput) -> TokenStream { #context_scope_for_struct + #fn_from_cli_for_struct + impl #name { #(#fn_get_arg)* - #fn_from_cli_for_struct #(#vec_fn_input_arg)* fn try_parse() -> Result<#cli_name, clap::Error> { @@ -333,9 +334,10 @@ pub fn impl_interactive_clap(ast: &syn::DeriveInput) -> TokenStream { } } + #fn_from_cli_for_enum + impl #name { #fn_choose_variant - #fn_from_cli_for_enum fn try_parse() -> Result<#cli_name, clap::Error> { <#cli_name as clap::Parser>::try_parse() diff --git a/interactive_clap_derive/src/derives/to_cli_args/mod.rs b/interactive_clap_derive/src/derives/to_cli_args/mod.rs index 9fd6010..f7e388f 100644 --- a/interactive_clap_derive/src/derives/to_cli_args/mod.rs +++ b/interactive_clap_derive/src/derives/to_cli_args/mod.rs @@ -41,8 +41,8 @@ pub fn impl_to_cli_args(ast: &syn::DeriveInput) -> TokenStream { let args_push_front_vec = args_push_front_vec.into_iter().rev(); let gen = quote! { - impl #cli_name { - pub fn to_cli_args(&self) -> std::collections::VecDeque { + impl interactive_clap::ToCliArgs for #cli_name { + fn to_cli_args(&self) -> std::collections::VecDeque { #args_subcommand; #(#args_push_front_vec; )* args @@ -83,8 +83,8 @@ pub fn impl_to_cli_args(ast: &syn::DeriveInput) -> TokenStream { } }); let gen = quote! { - impl #cli_name { - pub fn to_cli_args(&self) -> std::collections::VecDeque { + impl interactive_clap::ToCliArgs for #cli_name { + fn to_cli_args(&self) -> std::collections::VecDeque { match self { #( #enum_variants, )* } diff --git a/src/lib.rs b/src/lib.rs index eb701f1..522be49 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,14 +6,6 @@ pub use interactive_clap_derive::{InteractiveClap, ToCliArgs}; -// pub trait InteractiveClap { -// fn interactive_clap(); -// } - -// pub trait ToCliArgs { -// fn to_cli_args(); -// } - pub trait ToCli { type CliVariant; } @@ -33,3 +25,16 @@ impl ToCli for u64 { pub trait ToInteractiveClapContextScope { type InteractiveClapContextScope; } + +pub trait ToCliArgs { + fn to_cli_args(&self) -> std::collections::VecDeque; +} + +pub trait FromCli { + type FromCliContext; + type FromCliError; + fn from_cli( + optional_clap_variant: Option<::CliVariant>, + context: Self::FromCliContext, + ) -> Result, Self::FromCliError> where Self: Sized + ToCli; +}