use std::sync::{atomic::Ordering, Arc};
use tokio::sync::Mutex;
use crate::{
wapchost::{
errors, modulestate_async::ModuleStateAsync, traits::WebAssemblyEngineProviderAsync, Invocation, Result,
GLOBAL_MODULE_COUNT,
},
HostCallbackAsync,
};
#[must_use]
pub struct WapcHostAsync {
engine: Mutex<Box<dyn WebAssemblyEngineProviderAsync + Send>>,
state: Arc<ModuleStateAsync>,
}
impl std::fmt::Debug for WapcHostAsync {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("WapcHostAsync").field("state", &self.state).finish()
}
}
impl WapcHostAsync {
pub async fn new(
engine: Box<dyn WebAssemblyEngineProviderAsync + Send>,
host_callback: Option<Box<HostCallbackAsync>>,
) -> Result<Self> {
let id = GLOBAL_MODULE_COUNT.fetch_add(1, Ordering::SeqCst);
let state = Arc::new(ModuleStateAsync::new(host_callback, id));
let mh = WapcHostAsync {
engine: Mutex::new(engine),
state: state.clone(),
};
mh.initialize(state).await?;
Ok(mh)
}
async fn initialize(&self, state: Arc<ModuleStateAsync>) -> Result<()> {
match self.engine.lock().await.init(state).await {
Ok(_) => Ok(()),
Err(e) => Err(errors::Error::InitFailed(e.to_string())),
}
}
pub fn id(&self) -> u64 {
self.state.id
}
pub async 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().await = None;
*self.state.guest_request.write().await = Some(inv);
*self.state.guest_error.write().await = None;
*self.state.host_response.write().await = None;
*self.state.host_error.write().await = None;
}
let callresult = match self.engine.lock().await.call(op_len as i32, msg_len as i32).await {
Ok(c) => c,
Err(e) => {
return Err(errors::Error::GuestCallFailure(e.to_string()));
}
};
if callresult == 0 {
let lock = self.state.guest_error.read().await;
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 {
match self.state.guest_response.read().await.as_ref() {
Some(r) => Ok(r.clone()),
None => {
let lock = self.state.guest_error.read().await;
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())),
)
}
}
}
}
pub async fn replace_module(&self, module: &[u8]) -> Result<()> {
match self.engine.lock().await.replace(module).await {
Ok(_) => Ok(()),
Err(e) => Err(errors::Error::ReplacementFailed(e.to_string())),
}
}
}