use super::*;
use proc_macro2::TokenStream;
use quote::quote;
impl ProgramBuilder {
pub fn program_signature_impl(&self) -> TokenStream {
let sails_path = self.sails_path();
let (program_type_path, _, _) = self.impl_type();
let (generics, program_type_constraints) = self.impl_constraints();
let program_ctors = self.program_ctors();
let program_ctor_sigs = program_ctors
.iter()
.filter(|fn_builder| fn_builder.has_ethabi_codec())
.map(|fn_builder| fn_builder.sol_handler_signature(None));
let service_ctors = self.service_ctors();
let service_ctor_sigs = service_ctors
.iter()
.filter(|fn_builder| fn_builder.has_ethabi_codec())
.map(|fn_builder| fn_builder.sol_service_signature());
let ethabi_service_ctors = service_ctors
.iter()
.filter(|fn_builder| fn_builder.has_ethabi_codec())
.collect::<Vec<_>>();
let methods_len_iter = ethabi_service_ctors.iter().map(|fn_builder| {
let service_type = &fn_builder.result_type;
quote!(<#service_type as #sails_path::solidity::ServiceSignature>::METHODS.len())
});
let methods_len = if ethabi_service_ctors.is_empty() {
quote! {0}
} else {
quote! {#(#methods_len_iter) + *}
};
quote! {
impl #generics #sails_path::solidity::ProgramSignature for #program_type_path #program_type_constraints {
const CTORS: &'static [#sails_path::solidity::MethodExpo] = &[
#( #program_ctor_sigs, )*
];
const SERVICES: &'static [#sails_path::solidity::ServiceExpo] = &[
#( #service_ctor_sigs, )*
];
const METHODS_LEN: usize = #methods_len;
}
}
}
pub fn program_const(&self) -> TokenStream {
let sails_path = self.sails_path();
let (program_type_path, ..) = self.impl_type();
quote! {
const __CTOR_SIGS: [[u8; 4]; <#program_type_path as #sails_path::solidity::ProgramSignature>::CTORS.len()]
= #sails_path::solidity::ConstProgramMeta::<#program_type_path>::ctor_sigs();
const _: () = #sails_path::solidity::assert_unique_selectors(&__CTOR_SIGS);
const __CTOR_CALLBACK_SIGS: [[u8; 4]; <#program_type_path as #sails_path::solidity::ProgramSignature>::CTORS.len()]
= #sails_path::solidity::ConstProgramMeta::<#program_type_path>::ctor_callback_sigs();
const __METHOD_SIGS: [[u8; 4]; <#program_type_path as #sails_path::solidity::ProgramSignature>::METHODS_LEN]
= #sails_path::solidity::ConstProgramMeta::<#program_type_path>::method_sigs();
const _: () = #sails_path::solidity::assert_unique_selectors(&__METHOD_SIGS);
const __METHOD_ROUTES: [(#sails_path::meta::InterfaceId, u16, u8); <#program_type_path as #sails_path::solidity::ProgramSignature>::METHODS_LEN]
= #sails_path::solidity::ConstProgramMeta::<#program_type_path>::method_routes();
const __CALLBACK_SIGS: [[u8; 4]; <#program_type_path as #sails_path::solidity::ProgramSignature>::METHODS_LEN]
= #sails_path::solidity::ConstProgramMeta::<#program_type_path>::callback_sigs();
}
}
pub fn match_ctor_impl(&self, program_ident: &Ident) -> TokenStream {
let (program_type_path, ..) = self.impl_type();
let program_ctors = self.program_ctors();
let ctor_branches = program_ctors
.iter()
.filter(|fn_builder| fn_builder.has_ethabi_codec())
.map(|fn_builder| fn_builder.sol_ctor_branch_impl(program_type_path, program_ident));
quote! {
fn match_ctor_solidity(entry_id: u16, input: &[u8]) -> Option<bool> {
match entry_id {
#( #ctor_branches )*
_ => None,
}
}
}
}
pub fn sol_init(&self, input_ident: &Ident) -> TokenStream {
let sails_path = self.sails_path();
let (program_type_path, ..) = self.impl_type();
quote! {
if let Some(input_sig) = #input_ident.get(..4)
&& let Ok(sig) = <[u8; 4]>::try_from(input_sig)
&& let Some(idx) = __CTOR_SIGS.iter().position(|s| s == &sig)
&& let Some(encode_reply) = match_ctor_solidity(
<#program_type_path as #sails_path::solidity::ProgramSignature>::CTORS[idx].1,
&#input_ident[4..],
)
{
if encode_reply {
let output = [__CTOR_CALLBACK_SIGS[idx].as_slice(), gstd::msg::id().into_bytes().as_slice()].concat();
gstd::msg::reply_bytes(output, 0).expect("Failed to send output");
}
return;
}
}
}
pub fn sol_main(&self, solidity_dispatchers: &[TokenStream]) -> TokenStream {
quote! {
if let Some(input_sig) = input.get(..4)
&& let Ok(sig) = <[u8; 4]>::try_from(input_sig)
&& let Some(idx) = __METHOD_SIGS.iter().position(|s| s == &sig)
{
let (interface_id, entry_id, route_idx) = __METHOD_ROUTES[idx];
#(#solidity_dispatchers)*
}
}
}
}
impl FnBuilder<'_> {
fn sol_service_signature(&self) -> TokenStream {
let sails_path = self.sails_path;
let route_idx = self.service_route_idx();
let service_name = self.route_camel_case();
let service_type = &self.result_type;
quote! {
(
#service_name,
#route_idx,
<#service_type as #sails_path::solidity::ServiceSignature>::METHODS,
)
}
}
pub(crate) fn sol_handler_signature(&self, service_path: Option<&TypePath>) -> TokenStream {
let sails_path = self.sails_path;
let entry_id = self.entry_id;
let handler_name = if service_path.is_some() {
&self.route
} else {
&self.route_camel_case()
};
let handler_types = self.params_types();
let (result_type, _) = self.result_type_with_value();
let intrface_id = if let Some(service_path) = service_path {
let path_wo_lifetimes = shared::remove_lifetimes(&service_path.path);
quote! {
<#path_wo_lifetimes as #sails_path::meta::Identifiable>::INTERFACE_ID
}
} else {
quote! {
#sails_path::meta::InterfaceId::zero()
}
};
let handler_types = quote! { bool, #(#handler_types,)* };
let callback_types = if service_path.is_some() {
quote! { #sails_path::alloy_primitives::B256, #result_type }
} else {
quote! { #sails_path::alloy_primitives::B256, }
};
quote! {
(
#intrface_id,
#entry_id,
#handler_name,
<<(#handler_types) as #sails_path::alloy_sol_types::SolValue>::SolType as #sails_path::alloy_sol_types::SolType>::SOL_NAME,
<<(#callback_types) as #sails_path::alloy_sol_types::SolValue>::SolType as #sails_path::alloy_sol_types::SolType>::SOL_NAME,
)
}
}
fn sol_ctor_branch_impl(
&self,
program_type_path: &TypePath,
program_ident: &Ident,
) -> TokenStream {
let sails_path = self.sails_path;
let entry_id = self.entry_id;
let handler_ident = self.ident;
let handler_params = self.params_idents();
let sol_types = self.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 = self.params_idents().iter().map(|p| {
quote! {
#p.into()
}
});
let unwrap_token = self.error_type.is_some().then(|| quote!(.unwrap()));
let ctor_invocation = if self.is_async() {
quote! {
gstd::message_loop(async move {
let program = #program_type_path :: #handler_ident (#(#handler_params_into),*).await #unwrap_token;
unsafe { #program_ident = Some(program) };
});
}
} else {
quote! {
let program = #program_type_path :: #handler_ident (#(#handler_params_into),*) #unwrap_token;
unsafe { #program_ident = Some(program) };
}
};
let payable_check = self.payable_check();
quote! {
#entry_id => {
let (__encode_reply, #(#handler_params,)*) : (bool, #(#sol_types,)*) = #sails_path::alloy_sol_types::SolValue::abi_decode_params(input).expect("Failed to decode request");
#payable_check
#ctor_invocation
Some(__encode_reply)
}
}
}
pub(crate) fn sol_service_invocation(&self) -> TokenStream2 {
let sails_path = self.sails_path;
let route_idx = self.service_route_idx();
let service_ctor_ident = self.ident;
let service_type = &self.result_type;
quote! {
if route_idx == #route_idx {
let mut service = program_ref.#service_ctor_ident();
let Some(is_async) = <#service_type as #sails_path::gstd::services::Service>::Exposure::check_asyncness(interface_id, entry_id) else {
gstd::unknown_input_panic("Unknown service method", &input);
};
if is_async {
gstd::message_loop(async move {
let (output, value, encode_reply) = service
.try_handle_solidity_async(interface_id, entry_id, &input[4..])
.await
.unwrap_or_else(|| {
gstd::unknown_input_panic("Unknown request", &input)
});
let output = if encode_reply {
let selector = __CALLBACK_SIGS[idx];
[selector.as_slice(), output.as_slice()].concat()
} else {
output
};
gstd::msg::reply_bytes(output, value).expect("Failed to send output");
});
} else {
let (output, value, encode_reply) = service
.try_handle_solidity(interface_id, entry_id, &input[4..])
.unwrap_or_else(|| {
gstd::unknown_input_panic("Unknown request", &input)
});
let output = if encode_reply {
let selector = __CALLBACK_SIGS[idx];
[selector.as_slice(), output.as_slice()].concat()
} else {
output
};
gstd::msg::reply_bytes(output, value).expect("Failed to send output");
}
return;
}
}
}
}