atruct 0.3.2

macros for anonymous structs in Rust
Documentation
use proc_macro2::{Ident, TokenStream};
use quote::{quote, format_ident, ToTokens};
use syn::{ImplItem, parse2, Error, ItemFn, Attribute};
use crate::internals::Interpret;
use super::{Return, WithReturn};


pub(crate) struct WithReturnStreams {
    pub impl_block_stream: TokenStream,
    pub structs_stream:    TokenStream,
}

impl Interpret<Result<WithReturnStreams, Error>> for WithReturn {
    fn interpret(self) -> Result<WithReturnStreams, Error> {
        let impl_block = self.0;
        let mut impl_block_stream = TokenStream::new();
        let mut structs_stream = TokenStream::new();
        
        for attr in impl_block.attrs {
            attr.to_tokens(&mut impl_block_stream)
        }
        if let Some(_unsafe) = impl_block.unsafety {
            _unsafe.to_tokens(&mut impl_block_stream)
        }
        impl_block.impl_token.to_tokens(&mut impl_block_stream);
        impl_block.generics.to_tokens(&mut impl_block_stream);
        if let Some((bang, path, _for)) = impl_block.trait_ {
            if let Some(_bang) = bang {
                _bang.to_tokens(&mut impl_block_stream)
            }
            path.to_tokens(&mut impl_block_stream);
            _for.to_tokens(&mut impl_block_stream);
        }
        impl_block.self_ty.to_tokens(&mut impl_block_stream);
        

        let mut impl_items = TokenStream::new();
        let return_attr_ident = format_ident!("Return");

        for item in impl_block.items {
            match item {
                ImplItem::Method(mut function) => {
                    if let Some(return_attr) = extract_attr_of_ident(
                        &return_attr_ident, &mut function.attrs
                    ) {
                        let ReturnStreams {
                            struct_stream,
                            function_stream
                        } = Return {
                            fields: return_attr.parse_args()?,
                            target: parse2(ItemFn {
                                attrs: function.attrs,
                                vis:   function.vis,
                                sig:   function.sig,
                                block: Box::new(function.block),
                            }.into_token_stream())?,
                        }.interpret();
                        
                        struct_stream.to_tokens(&mut structs_stream);
                        function_stream.to_tokens(&mut impl_items);
                    } else {
                        function.to_tokens(&mut impl_items)
                    }
                }
                other => other.to_tokens(&mut impl_items)
            }
        }

        impl_block_stream.extend(quote!(
            { #impl_items }
        ));

        Ok(WithReturnStreams { impl_block_stream, structs_stream })
    }
}


pub(crate) struct ReturnStreams {
    pub struct_stream:   TokenStream,
    pub function_stream: TokenStream,
}

impl Interpret<ReturnStreams> for Return {
    fn interpret(self) -> ReturnStreams {
        let struct_name = camel_cased(&self.target.name);

        let struct_stream = {
            let mut return_fields = TokenStream::new();
            for field in self.fields {
                let (name, typexp) = (field.name, field.typexp);
                return_fields.extend(quote!(
                    #name: #typexp,
                ))
            }

            quote!(
                struct #struct_name {
                    #return_fields
                }
            )
        };

        let function_stream = {
            let taget = self.target;

            let args = taget.args.to_token_stream();
            let body = taget.body;

            let mut signature = TokenStream::new();
            for attr in taget.attrs {
                attr.to_tokens(&mut signature)
            }
            taget.vis.to_tokens(&mut signature);
            if let Some(_async) = taget._async {
                _async.to_tokens(&mut signature)
            }
            if let Some(_unsafe) = taget._unsafe {
                _unsafe.to_tokens(&mut signature)
            }
            taget._fn.to_tokens(&mut signature);
            taget.name.to_tokens(&mut signature);
            taget.generics.to_tokens(&mut signature);

            quote!(
                #signature(#args) -> #struct_name {
                    type Return = #struct_name;
                    #body
                }
            )
        };

        ReturnStreams { struct_stream, function_stream }
    }
}


fn camel_cased(function_name: &Ident) -> Ident {
    let mut struct_name = String::new();
    let mut is_head_of_word = true;
    for ch in function_name.to_string().chars() {
        if ch == '_' { is_head_of_word = true } else {
            struct_name.push(
                if is_head_of_word {is_head_of_word = false; ch.to_ascii_uppercase()} else {ch}
            )
        }
    }
    format_ident!("{struct_name}")
}

fn extract_attr_of_ident(target: &Ident, vec: &mut Vec<Attribute>) -> Option<Attribute> {
    for i in 0..vec.len() {
        if vec[i].path.get_ident() == Some(target) {
            return Some(vec.remove(i))
        }
    }
    None
}