use boa_engine::{Context, JsResult, JsValue};
use crate::events::HtmlEvent;
use crate::tui::graph_api::RenderCmd;
use super::convert::{FromJs, HostError, IntoJs, IntoJsError, JsObjectBuilder};
pub struct HostCtx<'a> {
args: &'a [JsValue],
event: Option<&'a HtmlEvent>,
render: &'a mut Vec<RenderCmd>,
context: &'a mut Context,
}
impl<'a> HostCtx<'a> {
pub(crate) fn new(
args: &'a [JsValue],
event: Option<&'a HtmlEvent>,
render: &'a mut Vec<RenderCmd>,
context: &'a mut Context,
) -> Self {
Self {
args,
event,
render,
context,
}
}
pub fn arg<T: FromJs>(&mut self, i: usize) -> Result<Option<T>, HostError> {
let Some(v) = self.args.get(i) else {
return Ok(None);
};
if v.is_undefined() || v.is_null() {
return Ok(None);
}
T::from_js(v, self.context)
.map(Some)
.map_err(HostError::from)
}
pub fn arg_required<T: FromJs>(&mut self, i: usize) -> Result<T, HostError> {
self.arg(i)?
.ok_or_else(|| HostError::type_error(format!("missing argument #{i}")))
}
pub fn event(&self) -> Option<&'a HtmlEvent> {
self.event
}
pub fn render(&mut self) -> &mut Vec<RenderCmd> {
self.render
}
pub fn object(&mut self) -> JsObjectBuilder {
JsObjectBuilder::new()
}
#[doc(hidden)]
pub fn raw_args(&self) -> &'a [JsValue] {
self.args
}
#[doc(hidden)]
pub fn context_mut(&mut self) -> &mut Context {
&mut *self.context
}
}
pub fn host_fn<F, T>(mut f: F) -> HostFn
where
F: for<'a> FnMut(&mut HostCtx<'a>) -> Result<T, HostError> + Send + 'static,
T: IntoJs,
{
Box::new(move |ctx: &mut HostCtx| match f(ctx) {
Ok(v) => v.into_js(ctx.context_mut()),
Err(e) => Err(e.into_js_error()),
})
}
pub type HostFn = Box<dyn FnMut(&mut HostCtx) -> JsResult<JsValue> + Send>;