sails-macros-core 2.0.0

Implementations of procedural macros for the Sails framework
Documentation
use super::*;
use proc_macro2::TokenStream;
use quote::quote;

impl ServiceBuilder<'_> {
    pub(super) fn service_signature_impl(&self) -> TokenStream {
        let sails_path = self.sails_path;
        let service_type_path = self.type_path;
        let generics = &self.generics;
        let service_type_constraints = self.type_constraints.as_ref();
        let service_method_expo = self
            .service_handlers
            .iter()
            .filter(|fn_builder| fn_builder.has_ethabi_codec())
            .map(|fn_builder| fn_builder.sol_handler_signature(Some(service_type_path)));

        let combined_methods = if self.base_types.is_empty() {
            quote! {
                &[#( #service_method_expo, )*]
            }
        } else {
            let base_methods = self.base_types.iter().map(|base_type| {
                let path_wo_lifetimes = shared::remove_lifetimes(base_type);
                quote! {
                    <#path_wo_lifetimes as #sails_path::solidity::ServiceSignature>::METHODS
                }
            });
            quote! {
                #sails_path::const_concat_slices!(
                    <#sails_path::solidity::MethodExpo>,
                    &[#( #service_method_expo, )*],
                    #( #base_methods ),*
                )
            }
        };

        quote! {
            impl #generics #sails_path::solidity::ServiceSignature for #service_type_path #service_type_constraints {
                const METHODS: &'static [#sails_path::solidity::MethodExpo] = #combined_methods;
            }
        }
    }

    pub(super) fn try_handle_solidity_impl(&self) -> TokenStream {
        let impl_method = |is_async: bool| {
            let method_ident = Ident::new(
                if is_async {
                    "try_handle_solidity_async"
                } else {
                    "try_handle_solidity"
                },
                Span::call_site(),
            );
            let sails_path = self.sails_path;

            let method_sig = quote! {
                (
                    mut self,
                    interface_id: #sails_path::meta::InterfaceId,
                    entry_id: u16,
                    input: &[u8],
                ) -> Option<(#sails_path::Vec<u8>, u128, bool)>
            };

            let extra_imports = quote! {
                use #sails_path::gstd::CommandReply;
            };

            let service_type_path = self.type_path;

            let metadata_type = quote!(self::#service_type_path);

            let params = DispatchParams {
                is_async,
                method_name_ident: &method_ident,
                method_sig: &method_sig,
                extra_imports: &extra_imports,
                metadata_type: &metadata_type,
                codec: Codec::Ethabi,
            };

            self.generate_dispatch_impl(
                params,
                |fn_builder, _| self.generate_sol_decode_and_handle(fn_builder),
                |base_service_token, await_token, method_name| {
                    quote! {
                        #sails_path::gstd::services::Service::expose(#base_service_token, self.route_idx) . #method_name (interface_id, entry_id, input) #await_token
                    }
                }
            )
        };

        let sync_impl = impl_method(false);
        let async_impl = impl_method(true);

        quote! {
            #sync_impl
            #async_impl
        }
    }

    pub(super) fn exposure_emit_eth_impls(&self) -> Option<TokenStream> {
        let sails_path = self.sails_path;

        self.events_type.map(|events_type| {
            quote! {
                pub fn emit_eth_event(&self, event: #events_type) -> #sails_path::errors::Result<()>
                where
                    #events_type: #sails_path::EthEvent,
                {
                    use #sails_path::gstd::services::ExposureWithEvents;

                    self.emitter().emit_eth_event(event)
                }
            }
        })
    }

    fn generate_sol_decode_and_handle(&self, fn_builder: &FnBuilder) -> TokenStream {
        let sails_path = self.sails_path;
        let handler_ident = fn_builder.ident;
        let handler_params = fn_builder.params_idents();
        let sol_types = fn_builder.params_types().iter().map(|t| {
            quote! {
                << #t as #sails_path::alloy_sol_types::SolValue >::SolType as #sails_path::alloy_sol_types::SolType>::RustType
            }
        });
        let handler_params_into = fn_builder.params_idents().iter().map(|p| {
            quote! {
                #p.into()
            }
        });

        let (result_type, reply_with_value) = fn_builder.result_type_with_value();

        let await_token = fn_builder.is_async().then(|| quote!(.await));
        let unwrap_token = fn_builder.error_type.is_some().then(|| quote!(.unwrap()));

        let handle_token = if reply_with_value {
            quote! {
                let command_reply: CommandReply< #result_type > = self.#handler_ident(#(#handler_params_into),*)#await_token #unwrap_token.into();
                let (result, value) = command_reply.to_tuple();
            }
        } else {
            quote! {
                let result = self.#handler_ident(#(#handler_params_into),*)#await_token #unwrap_token;
                let value = 0u128;
            }
        };

        let payable_check = fn_builder.payable_check();

        quote! {
            let (__encode_reply, #(#handler_params,)*) : (bool, #(#sol_types,)*) = #sails_path::alloy_sol_types::SolValue::abi_decode_params(input).ok()?;
            #payable_check
            #handle_token
            let output = if __encode_reply {
                // encode MessageId and result if passed `encode_reply`
                let message_id = #sails_path::alloy_primitives::B256::new(#sails_path::gstd::Syscall::message_id().into_bytes());
                #sails_path::alloy_sol_types::SolValue::abi_encode_sequence(&(message_id, result,))
            } else {
                #sails_path::alloy_sol_types::SolValue::abi_encode_sequence(&(result,))
            };
            return Some((output, value, __encode_reply));
        }
    }
}