cmdparse-derive 0.1.1

Derive macro implementation for cmdparse.
Documentation
use linked_hash_map::LinkedHashMap;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};

#[derive(Debug, Hash, PartialEq, Eq, Clone)]
pub(crate) enum Parser<'a> {
    Explicit(Box<syn::Type>),
    FromParsable(&'a syn::Type),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct ParserIndex(usize);

impl ParserIndex {
    pub(crate) fn ident(self) -> syn::Ident {
        format_ident!("parser_{}", self.0)
    }
}

#[derive(Debug)]
pub(crate) enum ContextType {
    Generic(Box<syn::punctuated::Punctuated<syn::TypeParamBound, syn::token::Add>>),
    Concrete(Box<syn::Type>),
}

pub(crate) struct CodegenContext<'a> {
    pub(crate) visibility_mod: &'a syn::Visibility,
    pub(crate) type_name: &'a syn::Ident,
    pub(crate) context_type: Option<ContextType>,
    pub(crate) generics: &'a syn::Generics,
    pub(crate) parsers: LinkedHashMap<Parser<'a>, ParserIndex>,
}

impl<'a> CodegenContext<'a> {
    pub(crate) fn from_derive_input(derive_input: &'a syn::DeriveInput) -> Self {
        CodegenContext {
            visibility_mod: &derive_input.vis,
            type_name: &derive_input.ident,
            context_type: None,
            generics: &derive_input.generics,
            parsers: LinkedHashMap::new(),
        }
    }

    pub(crate) fn push_parser(&mut self, parser: Parser<'a>) -> ParserIndex {
        let items_count = self.parsers.len();
        *self
            .parsers
            .entry(parser)
            .or_insert_with(|| ParserIndex(items_count))
    }

    pub(crate) fn parse_context_ident(&self) -> TokenStream {
        match &self.context_type {
            Some(ContextType::Concrete(ty)) => quote! {#ty},
            _ => quote! {CmdParserCtx},
        }
    }
}

#[cfg(test)]
pub(crate) struct MockCodegenContext {
    pub(crate) type_name: syn::Ident,
    pub(crate) generics: syn::Generics,
    pub(crate) vis_modifier: syn::Visibility,
}

#[cfg(test)]
impl Default for MockCodegenContext {
    fn default() -> Self {
        Self {
            vis_modifier: syn::Visibility::Inherited,
            type_name: format_ident!("MockType"),
            generics: syn::Generics::default(),
        }
    }
}

#[cfg(test)]
impl MockCodegenContext {
    pub(crate) fn context(&self) -> CodegenContext<'_> {
        CodegenContext {
            visibility_mod: &self.vis_modifier,
            type_name: &self.type_name,
            context_type: None,
            generics: &self.generics,
            parsers: LinkedHashMap::new(),
        }
    }
}