use std::collections::BTreeMap;
use boa_cat::Value;
use boa_cat::env::Env;
use boa_cat::fuel::Fuel;
use boa_cat::heap::Heap;
use boa_cat::outcome::{EvalResult, Outcome};
use boa_cat::value::{Cell, NativeFn, Object};
#[derive(Debug, Default, Clone)]
pub struct HostCommands {
commands: Vec<(String, NativeFn)>,
}
impl HostCommands {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with(self, name: impl Into<String>, callback: NativeFn) -> Self {
let extended: Vec<(String, NativeFn)> = self
.commands
.into_iter()
.chain(std::iter::once((name.into(), callback)))
.collect();
Self { commands: extended }
}
#[must_use]
pub fn install(&self, env: Env, heap: Heap) -> (Env, Heap) {
let properties: BTreeMap<String, Value> = self
.commands
.iter()
.map(|(name, callback)| (name.clone(), Value::Native(*callback)))
.chain(std::iter::once((
"invoke".to_owned(),
Value::Native(invoke_impl),
)))
.collect();
let (tauri_id, heap) = heap.alloc_object(Object::from_properties(properties));
let (cell_id, heap) = heap.alloc_cell(Cell::new(Value::Object(tauri_id), false));
std::iter::once(("__TAURI__", cell_id)).fold((env, heap), |(env, heap), (name, cell)| {
(env.extend_cell(name, cell), heap)
})
}
}
#[allow(clippy::needless_pass_by_value)]
fn invoke_impl(args: Vec<Value>, this: Value, heap: Heap, fuel: Fuel) -> EvalResult {
let command_name = args
.first()
.and_then(|first| match first {
Value::String(s) => Some(s.clone()),
Value::Undefined
| Value::Null
| Value::Boolean(_)
| Value::Number(_)
| Value::Object(_)
| Value::Function(_)
| Value::Native(_) => None,
})
.unwrap_or_default();
let this_id = match &this {
Value::Object(id) => Some(*id),
Value::Undefined
| Value::Null
| Value::Boolean(_)
| Value::Number(_)
| Value::String(_)
| Value::Function(_)
| Value::Native(_) => None,
};
let command_fn = this_id
.and_then(|id| heap.object(id))
.and_then(|obj| obj.get(&command_name).cloned())
.and_then(|value| match value {
Value::Native(f) => Some(f),
Value::Undefined
| Value::Null
| Value::Boolean(_)
| Value::Number(_)
| Value::String(_)
| Value::Object(_)
| Value::Function(_) => None,
});
let forwarded: Vec<Value> = args.into_iter().skip(1).collect();
command_fn.map_or(
Ok((Outcome::Normal(Value::Null), heap.clone(), fuel)),
|callback| callback(forwarded, this, heap, fuel),
)
}