#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))]
mod store;
mod vm;
mod hypervisor;
use std::any::Any;
use std::cell::{Cell, RefCell};
use allocator_api2::alloc::Allocator;
use ixc::SchemaValue;
use ixc_message_api::{AccountID};
use ixc_core::{Context};
use ixc_core::account_api::{create_account_raw, ROOT_ACCOUNT};
use ixc_core::handler::{Handler, Service, Client, InitMessage, HandlerClient};
use ixc_core::resource::{InitializationError, ResourceScope, Resources};
use ixc_core::routing::{Router};
use crate::hypervisor::Hypervisor;
use ixc_message_api::code::{ErrorCode, SystemCode};
use ixc_message_api::handler::{HostBackend, RawHandler};
use ixc_message_api::header::{ContextInfo};
use ixc_message_api::packet::MessagePacket;
use ixc_schema::mem::MemoryManager;
use crate::store::{VersionedMultiStore};
use crate::vm::{NativeVM};
use ixc_core::result::ClientResult;
#[doc(hidden)]
pub use ixc_core::account_api::create_account;
use crate::default_account::{DefaultAccount, DefaultAccountCreate};
pub struct TestApp {
hypervisor: RefCell<Hypervisor<VersionedMultiStore>>,
native_vm: NativeVM,
mem: MemoryManager,
mock_id: Cell<u64>,
}
impl Default for TestApp {
fn default() -> Self {
let mut hypervisor: Hypervisor<VersionedMultiStore> = Default::default();
let native_vm = NativeVM::new();
hypervisor.register_vm("native", std::boxed::Box::new(native_vm.clone())).unwrap();
hypervisor.set_default_vm("native").unwrap();
let mem = MemoryManager::new();
let mut test_app = Self {
hypervisor: RefCell::new(hypervisor),
native_vm,
mem,
mock_id: Cell::new(0),
};
test_app.register_handler::<default_account::DefaultAccount>().unwrap();
test_app
}
}
impl TestApp {
pub fn register_handler<H: Handler>(&mut self) -> core::result::Result<(), InitializationError> {
let scope = ResourceScope::default();
unsafe { self.native_vm.register_handler(H::NAME, Box::new(H::new(&scope)?)); }
Ok(())
}
pub fn new_client_account(&self) -> ClientResult<AccountID> {
let mut ctx = self.client_context_for(ROOT_ACCOUNT);
let client = create_account::<DefaultAccount>(&mut ctx, DefaultAccountCreate{})?;
Ok(client.account_id())
}
pub fn new_client_context(&self) -> ClientResult<Context> {
let account_id = self.new_client_account()?;
Ok(self.client_context_for(account_id))
}
pub fn client_context_for(&self, account_id: AccountID) -> Context
{
unsafe {
let ctx = Context::new(ContextInfo{
account: account_id,
caller: account_id,
gas_limit: 0,
}, self);
ctx
}
}
pub fn add_mock(&self, ctx: &mut Context, mock: MockHandler) -> ClientResult<AccountID> {
let mock_id = self.mock_id.get();
self.mock_id.set(mock_id + 1);
let handler_id = format!("mock{}", mock_id);
self.native_vm.register_handler(&handler_id, std::boxed::Box::new(mock));
create_account_raw(ctx, &handler_id, &[])
}
pub fn exec_in<HC: HandlerClient, F, R>(&self, client: &HC, f: F) -> R
where
F: FnOnce(&HC::Handler, &mut Context) -> R,
{
let scope = ResourceScope::default();
let h = unsafe { HC::Handler::new(&scope) }.unwrap();
let mut ctx = self.client_context_for(client.account_id());
f(&h, &mut ctx)
}
}
impl HostBackend for TestApp {
fn invoke(&self, message_packet: &mut MessagePacket, allocator: &dyn Allocator) -> Result<(), ErrorCode> {
self.hypervisor.borrow_mut().invoke(message_packet, allocator)
}
}
pub struct MockHandler {
mocks: Vec<std::boxed::Box<dyn RawHandler>>,
}
impl MockHandler {
pub fn new() -> Self {
MockHandler {
mocks: Vec::new(),
}
}
pub fn add_handler<T: RawHandler + ?Sized + 'static>(&mut self, mock: std::boxed::Box<T>) {
self.mocks.push(Box::new(MockWrapper::<T>(mock)));
}
pub fn of<T: RawHandler + ?Sized + 'static>(mock: std::boxed::Box<T>) -> Self {
let mut mocks = MockHandler::new();
mocks.add_handler(Box::new(MockWrapper::<T>(mock)));
mocks
}
}
impl RawHandler for MockHandler {
fn handle(&self, message_packet: &mut MessagePacket, callbacks: &dyn HostBackend, allocator: &dyn Allocator) -> Result<(), ErrorCode> {
for mock in &self.mocks {
let res = mock.handle(message_packet, callbacks, allocator);
match res {
Err(ErrorCode::SystemCode(SystemCode::MessageNotHandled)) => continue,
_ => return res
}
}
Err(ErrorCode::SystemCode(SystemCode::MessageNotHandled))
}
}
struct MockWrapper<T: RawHandler + ?Sized>(std::boxed::Box<T>);
impl <T: RawHandler + ?Sized> RawHandler for MockWrapper<T> {
fn handle(&self, message_packet: &mut MessagePacket, callbacks: &dyn HostBackend, allocator: &dyn Allocator) -> Result<(), ErrorCode> {
self.0.handle(message_packet, callbacks, allocator)
}
}
#[ixc::handler(DefaultAccount)]
mod default_account {
use ixc::*;
#[derive(Resources)]
pub struct DefaultAccount{}
impl DefaultAccount {
#[on_create]
pub fn create(&self, ctx: &mut Context) -> Result<()> { Ok(()) }
}
}