use alloc::boxed::Box;
use alloc::vec::Vec;
use core::cell::RefCell;
use sealed::sealed;
use wasefire_error::Error;
#[sealed(pub(crate))]
pub trait Rpc {
fn read(&self) -> Result<Option<Vec<u8>>, Error>;
fn write(&self, response: &[u8]) -> Result<(), Error>;
unsafe fn register(&self, func: extern "C" fn(*const u8), data: *const u8)
-> Result<(), Error>;
fn unregister(&self) -> Result<(), Error>;
}
pub trait Service: 'static {
fn process(&mut self, request: Vec<u8>) -> Vec<u8>;
}
impl<F: FnMut(Vec<u8>) -> Vec<u8> + 'static> Service for F {
fn process(&mut self, request: Vec<u8>) -> Vec<u8> {
self(request)
}
}
#[must_use]
pub struct Listener<'a, T: Rpc, S: Service> {
state: *const State<'a, T, S>,
}
struct State<'a, T: Rpc, S: Service> {
rpc: &'a T,
handler: RefCell<S>,
}
impl<'a, T: Rpc, S: Service> Listener<'a, T, S> {
pub fn new(rpc: &'a T, service: S) -> Self {
let state = Box::into_raw(Box::new(State { rpc, handler: RefCell::new(service) }));
unsafe { rpc.register(Self::call, state as *const u8) }.unwrap();
Listener { state }
}
pub fn stop(self) {
core::mem::drop(self);
}
pub fn leak(self) {
core::mem::forget(self);
}
extern "C" fn call(state: *const u8) {
let state = unsafe { &*(state as *const State<'a, T, S>) };
let request = match state.rpc.read().unwrap() {
Some(x) => x,
None => return, };
let response = state.handler.borrow_mut().process(request);
state.rpc.write(&response).unwrap();
}
}
impl<'a, T: Rpc, S: Service> Drop for Listener<'a, T, S> {
fn drop(&mut self) {
let state = unsafe { &*self.state };
state.rpc.unregister().unwrap();
drop(unsafe { Box::from_raw(self.state as *mut State<'a, T, S>) });
}
}