use std::{
cell::RefCell,
sync::{atomic::Ordering, Arc},
};
use crate::wapchost::{
errors, modulestate::ModuleState, traits::WebAssemblyEngineProvider, HostCallback, Invocation, Result,
GLOBAL_MODULE_COUNT,
};
#[must_use]
pub struct WapcHost {
engine: RefCell<Box<dyn WebAssemblyEngineProvider>>,
state: Arc<ModuleState>,
}
impl std::fmt::Debug for WapcHost {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("WapcHost").field("state", &self.state).finish()
}
}
impl WapcHost {
pub fn new(engine: Box<dyn WebAssemblyEngineProvider>, host_callback: Option<Box<HostCallback>>) -> Result<Self> {
let id = GLOBAL_MODULE_COUNT.fetch_add(1, Ordering::SeqCst);
let state = Arc::new(ModuleState::new(host_callback, id));
let mh = WapcHost {
engine: RefCell::new(engine),
state: state.clone(),
};
mh.initialize(state)?;
Ok(mh)
}
fn initialize(&self, state: Arc<ModuleState>) -> Result<()> {
match self.engine.borrow_mut().init(state) {
Ok(_) => Ok(()),
Err(e) => Err(errors::Error::InitFailed(e.to_string())),
}
}
pub fn id(&self) -> u64 {
self.state.id
}
pub fn call(&self, op: &str, payload: &[u8]) -> Result<Vec<u8>> {
let inv = Invocation::new(op, payload.to_vec());
let op_len = inv.operation.len();
let msg_len = inv.msg.len();
{
*self.state.guest_response.write() = None;
*self.state.guest_request.write() = Some(inv);
*self.state.guest_error.write() = None;
*self.state.host_response.write() = None;
*self.state.host_error.write() = None;
}
let callresult = match self.engine.borrow_mut().call(op_len as i32, msg_len as i32) {
Ok(c) => c,
Err(e) => {
return Err(errors::Error::GuestCallFailure(e.to_string()));
}
};
if callresult == 0 {
let lock = self.state.guest_error.read();
lock.as_ref().map_or_else(
|| {
Err(errors::Error::GuestCallFailure(
"No error message set for call failure".to_owned(),
))
},
|s| Err(errors::Error::GuestCallFailure(s.clone())),
)
} else {
self.state.guest_response.read().as_ref().map_or_else(
|| {
let lock = self.state.guest_error.read();
lock.as_ref().map_or_else(
|| {
Err(errors::Error::GuestCallFailure(
"No error message OR response set for call success".to_owned(),
))
},
|s| Err(errors::Error::GuestCallFailure(s.clone())),
)
},
|e| Ok(e.clone()),
)
}
}
pub fn replace_module(&self, module: &[u8]) -> Result<()> {
match self.engine.borrow_mut().replace(module) {
Ok(_) => Ok(()),
Err(e) => Err(errors::Error::ReplacementFailed(e.to_string())),
}
}
}