feat: Introduced FromCli and ToCliArgs traits to unblock future generics implementation

This commit is contained in:
Vlad Frolov
2022-11-20 12:30:48 +01:00
committed by FroVolod
parent d7855fe83f
commit f7ce792eb4
15 changed files with 89 additions and 66 deletions
+2 -3
View File
@@ -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 = <Mode as interactive_clap::FromCli>::from_cli(cli_mode, context);
println!("mode: {:?}", mode)
}
+1 -1
View File
@@ -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 = <Args as interactive_clap::FromCli>::from_cli(Some(cli_args), context);
println!("args: {:?}", args)
}
+9 -3
View File
@@ -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: &<OfflineArgs as interactive_clap::ToInteractiveClapContextScope>::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<String> {
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 = <OfflineArgs as interactive_clap::FromCli>::from_cli(Some(cli_offline_args), context);
println!("offline_args: {:?}", offline_args)
}
+1 -2
View File
@@ -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 = <Account as interactive_clap::FromCli>::from_cli(Some(cli_account), context);
println!("account: {:?}", account)
}
+1 -1
View File
@@ -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 = <OperationMode as interactive_clap::FromCli>::from_cli(Some(cli_operation_mode), context);
println!("operation_mode: {:?}", &operation_mode);
}
+20 -13
View File
@@ -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<<Self as interactive_clap::ToCli>::CliVariant>,
_context: Self::FromCliContext,
) -> Result<Option<Self>, Self::FromCliError> where Self: Sized + interactive_clap::ToCli {
let submit: Option<Submit> = 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<Option<Self>> {
let variants = SubmitDiscriminants::iter().collect::<Vec<_>>();
let mut submits = variants
@@ -52,17 +70,6 @@ impl Submit {
None => Ok(None),
}
}
fn from_cli(
optional_clap_variant: Option<<Submit as interactive_clap::ToCli>::CliVariant>,
context: common::ConnectionConfig,
) -> color_eyre::eyre::Result<Option<Self>> {
let submit: Option<Submit> = 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()
<OnlineArgs as interactive_clap::FromCli>::from_cli(Some(cli_online_args.clone()), context.clone()).unwrap()
{
break args;
}
@@ -160,7 +160,7 @@ pub fn fn_choose_variant(
pub fn choose_variant(context: #input_context) -> color_eyre::eyre::Result<Option<Self>> {
loop {
#cli_variant
if let Some(variant) = Self::from_cli(Some(cli_variant), context.clone())? {
if let Some(variant) = <Self as interactive_clap::FromCli>::from_cli(Some(cli_variant), context.clone())? {
return Ok(Some(variant));
}
}
@@ -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,
}
}
@@ -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,
}
}
@@ -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<Option<Self>> {
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<<Self as interactive_clap::ToCli>::CliVariant>,
context: Self::FromCliContext,
) -> Result<Option<Self>, Self::FromCliError> where Self: Sized + interactive_clap::ToCli {
match optional_clap_variant {
#(#from_cli_variants)*
None => Self::choose_variant(context.clone()),
}
}
}
}
@@ -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<Option<Self>> {
#(#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<<Self as interactive_clap::ToCli>::CliVariant>,
context: Self::FromCliContext,
) -> Result<Option<Self>, 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 {
@@ -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(
@@ -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()
@@ -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<String> {
impl interactive_clap::ToCliArgs for #cli_name {
fn to_cli_args(&self) -> std::collections::VecDeque<String> {
#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<String> {
impl interactive_clap::ToCliArgs for #cli_name {
fn to_cli_args(&self) -> std::collections::VecDeque<String> {
match self {
#( #enum_variants, )*
}
+13 -8
View File
@@ -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<String>;
}
pub trait FromCli {
type FromCliContext;
type FromCliError;
fn from_cli(
optional_clap_variant: Option<<Self as ToCli>::CliVariant>,
context: Self::FromCliContext,
) -> Result<Option<Self>, Self::FromCliError> where Self: Sized + ToCli;
}