use super::config::Config;
use super::contract::{ConfiguredContract, ContractSrc, Method};
use super::entrypoint::*;
use proc_macro2::TokenStream;
use quote::quote;
use syn::parse::Result;
pub fn generate_trait_method(
item: &syn::ItemImpl,
config: &Config,
) -> Result<syn::ImplItem> {
let registrations = build_registrations(item, config)?;
Ok(syn::parse_quote! {
#[doc(hidden)]
fn __mock(contract: &mut ::thruput_sdk::MockContract) where Self: Sized {
#registrations
}
})
}
pub fn generate_bare_method(
item: &syn::ItemImpl,
config: &Config,
) -> Result<TokenStream> {
let registrations = build_registrations(item, config)?;
let self_ty = &item.self_ty;
Ok(quote! {
impl #self_ty {
#[doc(hidden)]
pub fn __mock(contract: &mut ::thruput_sdk::MockContract) {
#registrations
}
}
})
}
pub fn generate_impl_registry_fragment(
item: &syn::ItemImpl,
_config: &Config,
) -> Result<TokenStream> {
let self_ty = &item.self_ty;
let register_path = if let Some((trait_path, _)) = &item.trait_ {
quote! { <#self_ty as #trait_path>::__mock }
} else {
quote! { #self_ty::__mock }
};
let stateless = item.items.iter().all(|impl_item| match impl_item {
syn::ImplItem::Fn(f) => f.sig.receiver().is_none(),
_ => true,
});
let self_type_key = if stateless {
quote! { None }
} else {
quote! { Some(stringify!(#self_ty)) }
};
Ok(quote! {
const _: () = {
fn register(contract: &mut ::thruput_sdk::MockContract) {
#register_path(contract);
}
#[::thruput_sdk::__linkme::distributed_slice(::thruput_sdk::mock_registry::CONTRACT_MOCK_FRAGMENTS)]
#[linkme(crate = ::thruput_sdk::__linkme)]
static FRAGMENT: ::thruput_sdk::mock_registry::ContractMockFragment =
::thruput_sdk::mock_registry::ContractMockFragment {
package_name: env!("CARGO_PKG_NAME"),
self_type_key: #self_type_key,
register,
};
};
})
}
pub fn generate_free_function_registry_fragment(
item: &syn::ItemFn,
config: &Config,
) -> Result<TokenStream> {
let contract = ConfiguredContract::new(config.clone(), item);
let effective_interface_name =
super::contract::effective_interface_name(&contract);
let exports_mod_ident = if config.exports.is_some() {
config.internal_exports_mod_ident(&effective_interface_name)
} else {
super::exports::free_function_exports_mod_ident(
&effective_interface_name,
&item.sig.ident,
)
};
let registration =
generate_method_qualified(&contract, item, &exports_mod_ident)?;
Ok(quote! {
const _: () = {
fn register(contract: &mut ::thruput_sdk::MockContract) {
#registration
}
#[::thruput_sdk::__linkme::distributed_slice(::thruput_sdk::mock_registry::CONTRACT_MOCK_FRAGMENTS)]
#[linkme(crate = ::thruput_sdk::__linkme)]
static FRAGMENT: ::thruput_sdk::mock_registry::ContractMockFragment =
::thruput_sdk::mock_registry::ContractMockFragment {
package_name: env!("CARGO_PKG_NAME"),
self_type_key: None,
register,
};
};
})
}
fn build_registrations(
item: &syn::ItemImpl,
config: &Config,
) -> Result<TokenStream> {
let contract = ConfiguredContract::new(config.clone(), item);
let effective_interface_name =
super::contract::effective_interface_name(&contract);
let exports_mod_ident =
config.internal_exports_mod_ident(&effective_interface_name);
let mut registrations = TokenStream::new();
for method in contract.methods_sorted() {
registrations.extend(generate_method_qualified(
&contract,
&method,
&exports_mod_ident,
)?);
}
Ok(registrations)
}
fn generate_method_qualified(
src: &impl ContractSrc,
method: &impl Method,
exports_mod: &syn::Ident,
) -> Result<TokenStream> {
let sig = method.sig();
let ident = &sig.ident;
let internal_name = src.make_internal_name(ident);
let id = method_id_tokens(src, sig);
let entrypoint =
quote!(::thruput_sdk::contract::ContractEntrypointID::from_u64(#id));
Ok(quote! {
contract.register_method(#entrypoint, #exports_mod::#internal_name);
})
}