mingling_macros 0.1.2

Macros of the mingling library
Documentation
//! Chain Struct Macro Implementation
//!
//! This module provides the `pack!` macro for creating wrapper types
//! with automatic implementations of common traits.

use proc_macro::TokenStream;
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::{Ident, Result as SynResult, Token, Type};

enum PackInput {
    Explicit {
        group_name: Ident,
        type_name: Ident,
        inner_type: Type,
    },
    Default {
        type_name: Ident,
        inner_type: Type,
    },
}

impl Parse for PackInput {
    fn parse(input: ParseStream) -> SynResult<Self> {
        // Try to parse as explicit format first: GroupName, TypeName = InnerType
        let lookahead = input.lookahead1();

        if lookahead.peek(Ident) && input.peek2(Token![,]) {
            // Explicit format: GroupName, TypeName = InnerType
            let group_name = input.parse()?;
            input.parse::<Token![,]>()?;
            let type_name = input.parse()?;
            input.parse::<Token![=]>()?;
            let inner_type = input.parse()?;

            Ok(PackInput::Explicit {
                group_name,
                type_name,
                inner_type,
            })
        } else if lookahead.peek(Ident) && input.peek2(Token![=]) {
            // Default format: TypeName = InnerType
            let type_name = input.parse()?;
            input.parse::<Token![=]>()?;
            let inner_type = input.parse()?;

            Ok(PackInput::Default {
                type_name,
                inner_type,
            })
        } else {
            Err(lookahead.error())
        }
    }
}

pub fn pack(input: TokenStream) -> TokenStream {
    // Parse the input
    let pack_input = syn::parse_macro_input!(input as PackInput);

    // Determine if we're using default or explicit group
    let (group_name, type_name, inner_type, use_default) = match pack_input {
        PackInput::Explicit {
            group_name,
            type_name,
            inner_type,
        } => (group_name, type_name, inner_type, false),
        PackInput::Default {
            type_name,
            inner_type,
        } => (
            Ident::new("DefaultProgram", proc_macro2::Span::call_site()),
            type_name,
            inner_type,
            true,
        ),
    };

    // Generate the struct definition
    #[cfg(not(feature = "serde"))]
    let struct_def = quote! {
        #[derive(Debug)]
        pub struct #type_name {
            pub(crate) inner: #inner_type,
        }
    };

    #[cfg(feature = "serde")]
    let struct_def = quote! {
        #[derive(Debug, serde::Serialize)]
        pub struct #type_name {
            pub(crate) inner: #inner_type,
        }
    };

    // Generate the new() method
    let new_impl = quote! {
        impl #type_name {
            /// Creates a new instance of the wrapper type
            pub fn new(inner: #inner_type) -> Self {
                Self { inner }
            }
        }
    };

    // Generate From and Into implementations
    let from_into_impl = quote! {
        impl From<#inner_type> for #type_name {
            fn from(inner: #inner_type) -> Self {
                Self::new(inner)
            }
        }

        impl From<#type_name> for #inner_type {
            fn from(wrapper: #type_name) -> #inner_type {
                wrapper.inner
            }
        }
    };

    // Generate AsRef and AsMut implementations
    let as_ref_impl = quote! {
        impl ::std::convert::AsRef<#inner_type> for #type_name {
            fn as_ref(&self) -> &#inner_type {
                &self.inner
            }
        }

        impl ::std::convert::AsMut<#inner_type> for #type_name {
            fn as_mut(&mut self) -> &mut #inner_type {
                &mut self.inner
            }
        }
    };

    // Generate Deref and DerefMut implementations
    let deref_impl = quote! {
        impl ::std::ops::Deref for #type_name {
            type Target = #inner_type;

            fn deref(&self) -> &Self::Target {
                &self.inner
            }
        }

        impl ::std::ops::DerefMut for #type_name {
            fn deref_mut(&mut self) -> &mut Self::Target {
                &mut self.inner
            }
        }
    };

    // Check if the inner type implements Default by generating conditional code
    let default_impl = quote! {
        impl ::std::default::Default for #type_name
        where
            #inner_type: ::std::default::Default,
        {
            fn default() -> Self {
                Self::new(::std::default::Default::default())
            }
        }
    };

    let any_out_impl = quote! {
        impl Into<mingling::AnyOutput<#group_name>> for #type_name {
            fn into(self) -> mingling::AnyOutput<#group_name> {
                mingling::AnyOutput::new(self)
            }
        }

        impl Into<mingling::ChainProcess<#group_name>> for #type_name {
            fn into(self) -> mingling::ChainProcess<#group_name> {
                mingling::AnyOutput::new(self).route_chain()
            }
        }

        impl #type_name {
            /// Converts the wrapper type into a `ChainProcess` for chaining operations.
            pub fn to_chain(self) -> mingling::ChainProcess<#group_name> {
                mingling::AnyOutput::new(self).route_chain()
            }

            /// Converts the wrapper type into a `ChainProcess` for rendering operations.
            pub fn to_render(self) -> mingling::ChainProcess<#group_name> {
                mingling::AnyOutput::new(self).route_renderer()
            }
        }
    };

    let group_impl = quote! {
        impl ::mingling::Groupped<#group_name> for #type_name {
            fn member_id() -> #group_name {
                #group_name::#type_name
            }
        }
    };

    // Combine all implementations
    let expanded = if use_default {
        // For default case, use DefaultProgram
        quote! {
            #struct_def

            #new_impl
            #from_into_impl
            #as_ref_impl
            #deref_impl
            #default_impl

            impl Into<mingling::AnyOutput<DefaultProgram>> for #type_name {
                fn into(self) -> mingling::AnyOutput<DefaultProgram> {
                    mingling::AnyOutput::new(self)
                }
            }

            impl From<#type_name> for mingling::ChainProcess<DefaultProgram> {
                fn from(value: #type_name) -> Self {
                    mingling::AnyOutput::new(value).route_chain()
                }
            }

            impl #type_name {
                /// Converts the wrapper type into a `ChainProcess` for chaining operations.
                pub fn to_chain(self) -> mingling::ChainProcess<DefaultProgram> {
                    mingling::AnyOutput::new(self).route_chain()
                }

                /// Converts the wrapper type into a `ChainProcess` for rendering operations.
                pub fn to_render(self) -> mingling::ChainProcess<DefaultProgram> {
                    mingling::AnyOutput::new(self).route_renderer()
                }
            }

            impl ::mingling::Groupped<DefaultProgram> for #type_name {
                fn member_id() -> DefaultProgram {
                    DefaultProgram::#type_name
                }
            }
        }
    } else {
        // For explicit case, use the provided group_name
        quote! {
            #struct_def

            #new_impl
            #from_into_impl
            #as_ref_impl
            #deref_impl
            #default_impl

            #any_out_impl
            #group_impl
        }
    };

    expanded.into()
}