mkutils-macros 0.1.198

Utility methods, traits, and types.
Documentation
use crate::error::Error;
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use syn::{
    Error as SynError, Ident, ItemImpl, Path, Type, Visibility,
    ext::IdentExt,
    parse::{Parse, ParseStream},
};

enum EmptyType {
    Enum,
    UnitStruct,
    CStruct,
}

impl EmptyType {
    fn get_declaration(&self, ident: &Ident) -> TokenStream2 {
        match self {
            Self::Enum => quote::quote!(enum #ident {}),
            Self::UnitStruct => quote::quote!(struct #ident;),
            Self::CStruct => quote::quote!(struct #ident {}),
        }
    }
}

pub struct Empty {
    visibility: Visibility,
    empty_type: EmptyType,
}

impl Parse for Empty {
    fn parse(parse_stream: ParseStream) -> Result<Self, SynError> {
        let visibility = parse_stream.parse()?;
        let empty_type = if parse_stream.is_empty() {
            EmptyType::Enum
        } else {
            let ident = Ident::parse_any(parse_stream)?;

            match ident.to_string().as_str() {
                "enum" => EmptyType::Enum,
                "unit_struct" => EmptyType::UnitStruct,
                "c_struct" => EmptyType::CStruct,
                _ => {
                    return Err(Error::unexpected_value_multi(
                        &ident,
                        &["enum", "unit_struct", "c_struct"],
                    ));
                }
            }
        };
        let empty = Self { visibility, empty_type };

        Ok(empty)
    }
}

impl Empty {
    fn get_ident(item_impl: &ItemImpl) -> Result<&Ident, SynError> {
        let Type::Path(type_path) = item_impl.self_ty.as_ref() else {
            return Err(Error::unexpected_value(
                item_impl.self_ty.as_ref(),
                std::any::type_name::<Path>(),
            ));
        };
        let Some(segment) = type_path.path.segments.last() else {
            return Err(Error::empty_path(&type_path.path));
        };

        Ok(&segment.ident)
    }

    fn derive_impl(&self, item_impl: &ItemImpl) -> Result<TokenStream2, SynError> {
        let Self { visibility, empty_type } = self;
        let ident = Self::get_ident(item_impl)?;
        let declaration = empty_type.get_declaration(ident);
        let declaration_and_impl_token_stream = quote::quote! {
            #visibility #declaration

            #item_impl
        };

        Ok(declaration_and_impl_token_stream)
    }

    pub fn derive(attr_args_token_stream: TokenStream, input_token_stream: TokenStream) -> TokenStream {
        let empty = syn::parse_macro_input!(attr_args_token_stream as Self);
        let item_impl = syn::parse_macro_input!(input_token_stream);

        empty
            .derive_impl(&item_impl)
            .unwrap_or_else(SynError::into_compile_error)
            .into()
    }
}