cmdparse-derive 0.1.1

Derive macro implementation for cmdparse.
Documentation
use crate::context::CodegenContext;
use crate::fields_gen::{gen_complete_struct, gen_parse_struct};
use crate::variants::{TransparentVariantView, VariantView, VariantsSet};
use proc_macro2::TokenStream;
use quote::quote;

impl<'a> VariantView<'a> {
    fn gen_parse(&self, ctx: &CodegenContext) -> TokenStream {
        let label = self.label;
        let variant_ident = self.ident;
        let ident = ctx.type_name;
        let parse_variant = gen_parse_struct(
            quote! { #ident::#variant_ident },
            ctx,
            self.fields,
            Some(label),
        );
        quote! {
            #label => {
                input = remaining;
                let result = (|| { #parse_variant })();
                return result.map_err(|err| match err {
                    error @ ::cmdparse::error::ParseFailure::Error(_) => error,
                    ::cmdparse::error::ParseFailure::Unrecognized(unrecognized) => unrecognized.into_error().into(),
                });
            }
        }
    }

    fn gen_complete(&self, ctx: &CodegenContext) -> TokenStream {
        let label = self.label;
        let complete_variant = gen_complete_struct(ctx, self.fields, Some(label));
        quote! {
            #label => {
                input = remaining;
                #complete_variant
            }
        }
    }
}

impl<'a> TransparentVariantView<'a> {
    fn gen_parse(&self, ctx: &CodegenContext) -> TokenStream {
        let variant_ident = self.ident;
        let ident = ctx.type_name;
        let parse_variant =
            gen_parse_struct(quote! { #ident::#variant_ident }, ctx, self.fields, None);
        let error_handle = match self.ignore_error {
            true => quote!(Err(_) => {}),
            false => quote!(Err(error) => return Err(error)),
        };
        quote! {
            match (||{ #parse_variant })() {
                Ok(result) => return Ok(result),
                Err(::cmdparse::error::ParseFailure::Unrecognized(_)) => {},
                #error_handle
            }
        }
    }

    fn gen_complete(&self, ctx: &CodegenContext) -> TokenStream {
        let complete_variant = gen_complete_struct(ctx, self.fields, None);
        let return_check = if self.ignore_error {
            None
        } else {
            Some(quote! {
                if result.value_consumed {
                    result.suggestions.extend(suggestions);
                    return result
                }
            })
        };
        quote! {
            let mut result = (||{ #complete_variant })();
            #return_check
            suggestions.extend(result.suggestions);
        }
    }
}

pub(crate) fn gen_parse_enum(
    codegen_ctx: &CodegenContext<'_>,
    variants: &VariantsSet<'_>,
) -> TokenStream {
    let variants_parsing = variants
        .variant_views()
        .map(|variant| variant.gen_parse(codegen_ctx));

    let transparent_parsed = variants
        .transparent_variants()
        .map(|variant| variant.gen_parse(codegen_ctx));

    quote! {
        match input.take() {
            None => Err(::cmdparse::error::ParseError::token_required().expected("variant").into()),
            Some(Err(err)) => Err(err.into()),
            Some(Ok((token @ ::cmdparse::tokens::Token::Attribute(_), remaining))) => {
                Err(::cmdparse::error::UnrecognizedToken::new(token, remaining).into())
            }
            Some(Ok((token @ ::cmdparse::tokens::Token::Text(text) , remaining))) => {
                let text = text.parse_string();
                match ::std::borrow::Borrow::<str>::borrow(&text) {
                    #(#variants_parsing)*
                   _ => {
                        #(#transparent_parsed)*
                        Err(::cmdparse::error::UnrecognizedToken::new(token, remaining).into())
                    }
                }
            }
        }
    }
}

pub(crate) fn gen_complete_enum(
    codegen_ctx: &CodegenContext<'_>,
    variants: &VariantsSet<'_>,
) -> TokenStream {
    let mut variant_names = Vec::new();
    let mut variants_complete = TokenStream::new();

    for variant in variants.variant_views() {
        variant_names.push(variant.label);
        variants_complete.extend(variant.gen_complete(codegen_ctx));
    }
    variant_names.sort_unstable();

    let transparent_complete = variants
        .transparent_variants()
        .map(|variant| variant.gen_complete(codegen_ctx));

    quote! {
        const VARIANT_NAMES: &[&str] = &[#(#variant_names),*];
        let mut suggestions = ::std::collections::BTreeSet::new();
        match input.take() {
            Some(Err(_)) => return ::cmdparse::CompletionResult::new_final(false),
            Some(Ok((::cmdparse::tokens::Token::Text(text), remaining))) => {
                let text = text.parse_string();
                if remaining.is_all_consumed() {
                    suggestions.extend(::cmdparse::tokens::complete_variants(&text, VARIANT_NAMES).map(::std::borrow::Cow::Borrowed));
                } else {
                    match ::std::borrow::Borrow::<str>::borrow(&text) {
                        #variants_complete
                        _ => {}
                    }
                }
            }
            Some(_) => {}
            None => return ::cmdparse::CompletionResult::new_final(false).add_suggestions(
                VARIANT_NAMES.iter().cloned().map(::std::borrow::Cow::Borrowed),
            ),
        }
        #(#transparent_complete)*
        ::cmdparse::CompletionResult::new(input, false).add_suggestions(suggestions)
    }
}