bubbly-bub-test 0.3.1

Interactive mode extension crate to Command Line Arguments Parser (https://crates.io/crates/clap) (derive macros helper crate)
Documentation
extern crate proc_macro;

use proc_macro2::Span;
use proc_macro_error::abort_call_site;
use quote::quote;
use syn;

pub fn from_cli_for_enum(
    ast: &syn::DeriveInput,
    variants: &syn::punctuated::Punctuated<syn::Variant, syn::token::Comma>,
) -> 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);
    if interactive_clap_attrs_context.is_skip_default_from_cli {
        return quote!();
    };

    let from_cli_variants = variants.iter().map(|variant| {
        let variant_ident = &variant.ident;

        let output_context = match &interactive_clap_attrs_context.output_context_dir {
            Some(output_context_dir) => {
                quote! {
                    type Alias = <#name as interactive_clap::ToInteractiveClapContextScope>::InteractiveClapContextScope;
                    let new_context_scope = Alias::#variant_ident;
                    let output_context = match #output_context_dir::from_previous_context(context.clone(), &new_context_scope) {
                        Ok(new_context) => new_context,
                        Err(err) => return interactive_clap::ResultFromCli::Err(Some(#cli_name::#variant_ident(inner_cli_args)), err),
                    };
                }
            }
            None => {
                quote! {
                    let output_context = context.clone();
                }
            }
        };

        match &variant.fields {
            syn::Fields::Unnamed(fields) => {
                let ty = &fields.unnamed[0].ty;
                quote! {
                    Some(#cli_name::#variant_ident(inner_cli_args)) => {
                        #output_context
                        let cli_inner_args = <#ty as interactive_clap::FromCli>::from_cli(Some(inner_cli_args), output_context.into());
                        match cli_inner_args {
                            interactive_clap::ResultFromCli::Ok(cli_args) => {
                                interactive_clap::ResultFromCli::Ok(#cli_name::#variant_ident(cli_args))
                            }
                            interactive_clap::ResultFromCli::Back => {
                                optional_clap_variant = None;
                                continue;
                            },
                            interactive_clap::ResultFromCli::Cancel(Some(cli_args)) => {
                                interactive_clap::ResultFromCli::Cancel(Some(#cli_name::#variant_ident(cli_args)))
                            }
                            interactive_clap::ResultFromCli::Cancel(None) => {
                                interactive_clap::ResultFromCli::Cancel(None)
                            }
                            interactive_clap::ResultFromCli::Err(Some(cli_args), err) => {
                                interactive_clap::ResultFromCli::Err(Some(#cli_name::#variant_ident(cli_args)), err)
                            }
                            interactive_clap::ResultFromCli::Err(None, err) => {
                                interactive_clap::ResultFromCli::Err(None, err)
                            }
                        }
                    }
                }
            },
            syn::Fields::Unit => {
                match &interactive_clap_attrs_context.output_context_dir {
                    Some(output_context_dir) => quote! {
                        Some(#cli_name::#variant_ident) => {
                            type Alias = <#name as interactive_clap::ToInteractiveClapContextScope>::InteractiveClapContextScope;
                            let new_context_scope = Alias::#variant_ident;
                            let output_context = match #output_context_dir::from_previous_context(context.clone(), &new_context_scope) {
                                Ok(new_context) => new_context,
                                Err(err) => return interactive_clap::ResultFromCli::Err(Some(#cli_name::#variant_ident), err),
                            };
                            interactive_clap::ResultFromCli::Ok(#cli_name::#variant_ident)
                        }
                    },
                    None => quote! {
                        Some(#cli_name::#variant_ident) => {
                            interactive_clap::ResultFromCli::Ok(#cli_name::#variant_ident)
                        },
                    }
                }
            },
            _ => abort_call_site!("Only option `Fields::Unnamed` or `Fields::Unit` is needed")
        }
    });

    let input_context_dir = interactive_clap_attrs_context
        .clone()
        .get_input_context_dir();

    quote! {
        impl interactive_clap::FromCli for #name {
            type FromCliContext = #input_context_dir;
            type FromCliError = color_eyre::eyre::Error;
            fn from_cli(
                mut optional_clap_variant: Option<<Self as interactive_clap::ToCli>::CliVariant>,
                context: Self::FromCliContext,
            ) -> interactive_clap::ResultFromCli<<Self as interactive_clap::ToCli>::CliVariant, Self::FromCliError> where Self: Sized + interactive_clap::ToCli {
                loop {
                    return match optional_clap_variant {
                        #(#from_cli_variants)*
                        None => match Self::choose_variant(context.clone()) {
                            interactive_clap::ResultFromCli::Ok(cli_args) => {
                                optional_clap_variant = Some(cli_args);
                                continue;
                            },
                            result => return result,
                        },
                    }
                }
            }
        }
    }
}