struct-patch-derive 0.12.7

A library that helps you implement partial updates for your structs.
Documentation
use proc_macro2::{Ident, TokenStream};
use quote::quote;
use syn::{DeriveInput, Result};
use syn_serde::json;

pub(crate) struct Substrate {
    struct_name: Ident,
    fields: syn::Fields,
}

impl Substrate {
    /// Generate the token stream which provide expose for Substrate
    pub fn to_token_stream(&self) -> Result<TokenStream> {
        let Substrate {
            struct_name,
            fields,
        } = self;

        let mut field_idents = Vec::new();
        let mut field_tys = Vec::new();
        for field in fields.iter() {
            field_idents.push(field.ident.clone());
            field_tys.push(field.ty.clone());
        }

        let active_site = json::to_string(fields);

        let substrate_impl_inner = {
            #[cfg(feature = "unsafe")]
            {
                quote! {
                    #[automatically_derived]
                    impl #struct_name {
                        #[allow(clippy::too_many_arguments)]
                        /// Support `decouple` of Complex with private fields, a new function generated by Substrate macro
                        pub fn __substrate_new(
                            #( #field_idents: #field_tys, )*
                        ) -> #struct_name {
                            unsafe {
                                use std::mem::MaybeUninit;
                                let mut substrate = MaybeUninit::<#struct_name>::uninit();
                                #( std::ptr::write(
                                    std::ptr::addr_of_mut!((*substrate.as_mut_ptr()).#field_idents),
                                    #field_idents,
                                ); )*
                                substrate.assume_init()
                            }
                        }

                        #[allow(clippy::too_many_arguments)]
                        /// Support `bind` of Catalyst with private fields, an unpack function generated by Substrate macro
                        pub fn __substrate_unpack(
                            self
                        ) -> (
                            #( #field_tys, )*
                        ) {
                            let substrate = std::mem::ManuallyDrop::new(self);
                            unsafe {
                                (
                                    #( std::ptr::read(&substrate.#field_idents), )*
                                )
                            }
                        }
                    }
                }
            }
            #[cfg(not(feature = "unsafe"))]
            {
                quote! {
                    #[automatically_derived]
                    impl #struct_name {
                        #[allow(clippy::too_many_arguments)]
                        /// Support `decouple` of Complex with private fields, a new function generated by Substrate macro
                        pub fn __substrate_new(
                            #( #field_idents: #field_tys, )*
                        ) -> #struct_name {
                            #struct_name {
                                #( #field_idents, )*
                            }
                        }

                        #[allow(clippy::too_many_arguments)]
                        /// Support `bind` of Catalyst with private fields, an unpack function generated by Substrate macro
                        pub fn __substrate_unpack(
                            self
                        ) -> (
                            #( #field_tys, )*
                        ) {
                            (
                                #( self.#field_idents, )*
                            )
                        }
                    }
                }
            }
        };

        Ok(quote! {
            #[automatically_derived]
            impl struct_patch::traits::Substrate for #struct_name {
                fn expose_content() -> &'static str {
                    #active_site
                }
                fn expose() {
                    println!("cargo:rustc-env={}={}", stringify!(#struct_name), Self::expose_content());
                }
            }

            #substrate_impl_inner
        })
    }
    /// Parse the substrate struct
    pub fn from_ast(DeriveInput { ident, data, .. }: syn::DeriveInput) -> Result<Substrate> {
        let fields = if let syn::Data::Struct(syn::DataStruct { fields, .. }) = data {
            fields
        } else {
            return Err(syn::Error::new(
                ident.span(),
                "Substrate derive only use for struct",
            ));
        };

        Ok(Substrate {
            struct_name: ident,
            fields,
        })
    }
}