debate-derive 1.0.0

derive macro for the `debate` CLI arguments parser
Documentation
use heck::ToSnakeCase;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::{Ident, Token, Variant, punctuated::Punctuated};

use crate::{
    common::{
        FlagTags,
        enumeration::{
            ValueEnumAttr,
            flag_set::{
                FlagSetFlag, FlagSetFlagExtra, FlagSetFlagInfo, FlagType, ParsedFlagSetInfo,
                VariantFieldSourcesMode, compute_grouped_flags,
            },
        },
    },
    generics::AngleBracedLifetime,
    usage::common::{FlagUsageInfo, flag_usage},
};

impl<'a, T: FlagSetFlagExtra<'a>> FlagUsageInfo for &'a FlagSetFlagInfo<T> {
    fn docs(&self) -> &crate::common::Description {
        &self.docs
    }

    fn defaulted(&self) -> bool {
        self.default.is_some()
    }

    fn ty(&self) -> FlagType<'_> {
        self.info.as_flag_set_type()
    }

    fn tags(&self) -> FlagTags<&str, char> {
        self.tags.simplify()
    }

    fn placeholder(&self) -> &str {
        self.placeholder.as_str()
    }
}

impl FlagUsageInfo for &FlagSetFlag<'_> {
    fn docs(&self) -> &crate::common::Description {
        self.docs
    }

    fn defaulted(&self) -> bool {
        true
    }

    fn ty(&self) -> FlagType<'_> {
        self.ty
    }

    fn tags(&self) -> FlagTags<&str, char> {
        self.tags
    }

    fn placeholder(&self) -> &str {
        self.placeholder
    }
}

pub fn derive_usage_enum_flag_set(
    name: &Ident,
    variants: &Punctuated<Variant, Token![,]>,
    lifetime: Option<&AngleBracedLifetime>,
    attr: &ValueEnumAttr,
) -> syn::Result<TokenStream2> {
    let parsed = ParsedFlagSetInfo::from_variants(
        variants,
        attr.long.map(|long| long.span()).as_ref(),
        attr.short.map(|short| short.span()).as_ref(),
    )?;
    let (variant_sources, parsed_flags) = compute_grouped_flags(&parsed.variants)?;
    let command_name = name.to_string().to_snake_case();

    let groups = variant_sources.values().map(|variant| match variant.mode {
        VariantFieldSourcesMode::Plain(ref source) => {
            let flag = flag_usage(source.field);
            quote! { &[ #flag ] }
        }
        VariantFieldSourcesMode::Struct(ref sources) => {
            let flags = sources.iter().map(|source| flag_usage(source.field));
            quote! { &[ #(#flags,)* ] }
        }
    });

    let all_flags = parsed_flags.iter().map(flag_usage);

    Ok(quote! {
        impl #lifetime ::debate::help::Usage for #name #lifetime {
            const NAME: &'static str = #command_name;
            const DESCRIPTION: ::debate::help::Description<'static> =
                ::debate::help::Description::new("TODO FLAG SETS");
            const ITEMS: ::debate::help::UsageItems<'static> =
                ::debate::help::UsageItems::Exclusive {
                    requirement: ::debate::help::Requirement::Mandatory,
                    groups: &[ #(#groups,)* ],
                    all_flags: &[ #(#all_flags,)* ],
                };
        }
    })
}