use crate::{
Constructor,
DispatchError,
FnOutput,
FnState,
MessageMut,
MessageRef,
};
use core::{
any::TypeId,
mem::ManuallyDrop,
};
use pro_env::{
Environment,
ReturnFlags,
};
use pro_primitives::Key;
use pro_storage::{
alloc,
alloc::ContractPhase,
traits::{
pull_spread_root,
push_spread_root,
},
};
#[doc(hidden)]
pub type Result<T> = core::result::Result<T, DispatchError>;
#[doc(hidden)]
pub trait MessageDispatcher {
type Type;
}
#[doc(hidden)]
pub trait ConstructorDispatcher {
type Type;
}
#[doc(hidden)]
pub trait Execute {
fn execute(self) -> Result<()>;
}
#[derive(Copy, Clone)]
#[doc(hidden)]
pub struct AcceptsPayments(pub bool);
impl From<AcceptsPayments> for bool {
#[inline]
fn from(accepts_payments: AcceptsPayments) -> Self {
accepts_payments.0
}
}
#[derive(Copy, Clone)]
#[doc(hidden)]
pub struct EnablesDynamicStorageAllocator(pub bool);
impl From<EnablesDynamicStorageAllocator> for bool {
#[inline]
fn from(enables_dynamic_storage_allocator: EnablesDynamicStorageAllocator) -> Self {
enables_dynamic_storage_allocator.0
}
}
#[inline]
#[doc(hidden)]
pub fn execute_message<E, M, F>(
accepts_payments: AcceptsPayments,
enables_dynamic_storage_allocator: EnablesDynamicStorageAllocator,
f: F,
) -> Result<()>
where
E: Environment,
M: MessageRef,
F: FnOnce(&<M as FnState>::State) -> <M as FnOutput>::Output,
{
let accepts_payments: bool = accepts_payments.into();
let enables_dynamic_storage_allocator: bool =
enables_dynamic_storage_allocator.into();
if !accepts_payments {
deny_payment::<E>()?;
}
if enables_dynamic_storage_allocator {
alloc::initialize(ContractPhase::Call);
}
let root_key = Key::from([0x00; 32]);
let state = ManuallyDrop::new(pull_spread_root::<<M as FnState>::State>(&root_key));
let result = f(&state);
if enables_dynamic_storage_allocator {
alloc::finalize();
}
if TypeId::of::<<M as FnOutput>::Output>() != TypeId::of::<()>() {
pro_env::return_value::<<M as FnOutput>::Output>(ReturnFlags::default(), &result)
}
Ok(())
}
#[inline]
#[doc(hidden)]
pub fn deny_payment<E>() -> Result<()>
where
E: Environment,
{
let transferred = pro_env::transferred_balance::<E>()
.expect("encountered error while querying transferred balance");
if transferred != <E as Environment>::Balance::from(0u32) {
return Err(DispatchError::PaidUnpayableMessage)
}
Ok(())
}
#[inline]
#[doc(hidden)]
pub fn execute_message_mut<E, M, F>(
accepts_payments: AcceptsPayments,
enables_dynamic_storage_allocator: EnablesDynamicStorageAllocator,
f: F,
) -> Result<()>
where
E: Environment,
M: MessageMut,
F: FnOnce(&mut <M as FnState>::State) -> <M as FnOutput>::Output,
{
let accepts_payments: bool = accepts_payments.into();
let enables_dynamic_storage_allocator: bool =
enables_dynamic_storage_allocator.into();
if !accepts_payments {
deny_payment::<E>()?;
}
if enables_dynamic_storage_allocator {
alloc::initialize(ContractPhase::Call);
}
let root_key = Key::from([0x00; 32]);
let mut state =
ManuallyDrop::new(pull_spread_root::<<M as FnState>::State>(&root_key));
let result = f(&mut state);
push_spread_root::<<M as FnState>::State>(&state, &root_key);
if enables_dynamic_storage_allocator {
alloc::finalize();
}
if TypeId::of::<<M as FnOutput>::Output>() != TypeId::of::<()>() {
pro_env::return_value::<<M as FnOutput>::Output>(ReturnFlags::default(), &result)
}
Ok(())
}
#[inline]
#[doc(hidden)]
pub fn execute_constructor<C, F>(
enables_dynamic_storage_allocator: EnablesDynamicStorageAllocator,
f: F,
) -> Result<()>
where
C: Constructor,
F: FnOnce() -> <C as FnState>::State,
{
let enables_dynamic_storage_allocator: bool =
enables_dynamic_storage_allocator.into();
if enables_dynamic_storage_allocator {
alloc::initialize(ContractPhase::Deploy);
}
let state = ManuallyDrop::new(f());
let root_key = Key::from([0x00; 32]);
push_spread_root::<<C as FnState>::State>(&state, &root_key);
if enables_dynamic_storage_allocator {
alloc::finalize();
}
Ok(())
}