enum-extract-macro 0.1.1

A derive macro for extracting enum variants into their own types.
Documentation
use heck::ToSnakeCase;
use proc_macro2::{Ident, Span};
use quote::ToTokens;

static DOCS_ERROR_TYPE: &'static str = "enum_extract_error::EnumExtractError";

pub struct FunctionDef {
    pub declaration: syn::Ident,
    pub docs: String,
}

impl ToTokens for FunctionDef {
    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
        self.declaration.to_tokens(tokens)
    }
}

impl FunctionDef {
    pub fn new_is_variant(enum_name: &Ident, variant_name: &Ident) -> Self {
        FunctionDef {
            docs: format!(
                "Returns true if this is a `{}::{}`, otherwise false",
                enum_name, variant_name,
            ),
            declaration: Ident::new(
                &format!("is_{}", variant_name).to_snake_case(),
                Span::call_site(),
            ),
        }
    }

    pub fn new_as_variant(enum_name: &Ident, variant_name: &Ident) -> Self {
        FunctionDef {
            docs: format!(
                "Returns references to the inner fields if this is a `{}::{}`, otherwise an [`{}`]",
                enum_name, variant_name, DOCS_ERROR_TYPE,
            ),
            declaration: Ident::new(
                &format!("as_{}", variant_name).to_snake_case(),
                Span::call_site(),
            ),
        }
    }

    pub fn new_extract_as_variant(enum_name: &Ident, variant_name: &Ident) -> Self {
        let alt_fn = Self::new_as_variant(enum_name, variant_name);

        FunctionDef {
            docs: format!(
                r#"
                Returns references to the inner fields if this is a `{enum_name}::{variant}`, otherwise panics.
    
                # Panics
    
                Panics when the value is not a `{enum_name}::{variant}`.
                Prefer using a `match` or [`{enum_name}::{alt_fn}`]; only use this if you *know* this is a `{enum_name}::{variant}`,
                or if panicking is ok, such as during testing.
                "#,
                enum_name = enum_name,
                variant = variant_name,
                alt_fn = alt_fn.declaration,
            ),
            declaration: Ident::new(
                &format!("extract_as_{}", variant_name).to_snake_case(),
                Span::call_site(),
            ),
        }
    }

    pub fn new_as_variant_mut(enum_name: &Ident, variant_name: &Ident) -> Self {
        FunctionDef {
            docs: format!(
                "Returns mutable references to the inner fields if this is a `{}::{}`, otherwise an [`{}`].",
                enum_name,
                variant_name,
                DOCS_ERROR_TYPE,
            ),
            declaration: Ident::new(
                &format!("as_{}_mut", variant_name).to_snake_case(),
                Span::call_site(),
            )
        }
    }

    pub fn new_extract_as_variant_mut(enum_name: &Ident, variant_name: &Ident) -> Self {
        let alt_fn = Self::new_as_variant_mut(enum_name, variant_name);

        FunctionDef {
            docs: format!(
                r#"
                Returns mutable references to the inner fields if this is a `{enum_name}::{variant}`, otherwise panics.
    
                # Panics
    
                Panics when the value is not a `{enum_name}::{variant}`.
                Prefer using a `match` or [`{enum_name}::{alt_fn}`]; only use this if you *know* this is a `{enum_name}::{variant}`,
                or if panicking is ok, such as during testing.
                "#,
                enum_name = enum_name,
                variant = variant_name,
                alt_fn = alt_fn.declaration,
            ),
            declaration: Ident::new(
                &format!("extract_as_{}_mut", variant_name).to_snake_case(),
                Span::call_site(),
            ),
        }
    }

    pub fn new_into_variant(enum_name: &Ident, variant_name: &Ident) -> Self {
        FunctionDef {
            docs: format!(
                "Returns the inner fields if this is a `{}::{}`, otherwise otherwise an [`{}`].",
                enum_name, variant_name, DOCS_ERROR_TYPE,
            ),
            declaration: Ident::new(
                &format!("into_{}", variant_name).to_snake_case(),
                Span::call_site(),
            ),
        }
    }

    pub fn new_extract_into_variant(enum_name: &Ident, variant_name: &Ident) -> Self {
        let alt_fn = Self::new_into_variant(enum_name, variant_name);

        FunctionDef {
            docs: format!(
                r#"
                Returns the inner fields if this is a `{enum_name}::{variant}`, otherwise panics.
                
                # Panics
    
                Panics when the value is not a `{enum_name}::{variant}`.
                Prefer using a `match` or [`{enum_name}::{alt_fn}`]; only use this if you *know* this is a `{enum_name}::{variant}`,
                or if panicking is ok, such as during testing.
                "#,
                enum_name = enum_name,
                variant = variant_name,
                alt_fn = alt_fn.declaration,
            ),
            declaration: Ident::new(
                &format!("extract_into_{}", variant_name).to_snake_case(),
                Span::call_site(),
            ),
        }
    }
}