// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. use crate::BufVec; use crate::ErrBox; use crate::ZeroCopyBuf; use futures::Future; use futures::FutureExt; use indexmap::IndexMap; use serde_json::json; use serde_json::Value; use std::collections::HashMap; use std::iter::once; use std::ops::Deref; use std::ops::DerefMut; use std::pin::Pin; use std::rc::Rc; pub type OpAsyncFuture = Pin>>>; pub type OpFn = dyn Fn(Rc, BufVec) -> Op + 'static; pub type OpId = usize; pub enum Op { Sync(Box<[u8]>), Async(OpAsyncFuture), /// AsyncUnref is the variation of Async, which doesn't block the program /// exiting. AsyncUnref(OpAsyncFuture), NotFound, } pub trait OpRouter { fn route_op(self: Rc, op_id: OpId, bufs: BufVec) -> Op; } pub trait OpRegistry: OpRouter + 'static { fn get_op_catalog(self: Rc) -> HashMap; fn register_op(&self, name: &str, op_fn: F) -> OpId where F: Fn(Rc, BufVec) -> Op + 'static; fn register_op_json_sync(self: &Rc, name: &str, op_fn: F) -> OpId where F: Fn(&Self, Value, &mut [ZeroCopyBuf]) -> Result + 'static, { let base_op_fn = move |state: Rc, mut bufs: BufVec| -> Op { let result = serde_json::from_slice(&bufs[0]) .map_err(ErrBox::from) .and_then(|args| op_fn(&state, args, &mut bufs[1..])); let buf = state.json_serialize_op_result(None, result); Op::Sync(buf) }; self.register_op(name, base_op_fn) } fn register_op_json_async(self: &Rc, name: &str, op_fn: F) -> OpId where F: Fn(Rc, Value, BufVec) -> R + 'static, R: Future> + 'static, { let try_dispatch_op = move |state: Rc, bufs: BufVec| -> Result { let args: Value = serde_json::from_slice(&bufs[0])?; let promise_id = args .get("promiseId") .and_then(Value::as_u64) .ok_or_else(|| ErrBox::type_error("missing or invalid `promiseId`"))?; let bufs = bufs[1..].into(); let fut = op_fn(state.clone(), args, bufs).map(move |result| { state.json_serialize_op_result(Some(promise_id), result) }); Ok(Op::Async(Box::pin(fut))) }; let base_op_fn = move |state: Rc, bufs: BufVec| -> Op { match try_dispatch_op(state.clone(), bufs) { Ok(op) => op, Err(err) => Op::Sync(state.json_serialize_op_result(None, Err(err))), } }; self.register_op(name, base_op_fn) } fn json_serialize_op_result( &self, promise_id: Option, result: Result, ) -> Box<[u8]> { let value = match result { Ok(v) => json!({ "ok": v, "promiseId": promise_id }), Err(err) => json!({ "promiseId": promise_id , "err": { "className": self.get_error_class_name(&err), "message": err.to_string(), } }), }; serde_json::to_vec(&value).unwrap().into_boxed_slice() } fn get_error_class_name(&self, _err: &ErrBox) -> &'static str { "Error" } } /// Collection for storing registered ops. The special 'get_op_catalog' /// op with OpId `0` is automatically added when the OpTable is created. pub struct OpTable(IndexMap>>); impl OpTable { pub fn get_op_catalog(&self) -> HashMap { self.keys().cloned().zip(0..).collect() } fn op_get_op_catalog(state: Rc, _bufs: BufVec) -> Op { let ops = state.get_op_catalog(); let buf = serde_json::to_vec(&ops).map(Into::into).unwrap(); Op::Sync(buf) } } impl Default for OpTable { fn default() -> Self { Self( once(("ops".to_owned(), Rc::new(Self::op_get_op_catalog) as _)).collect(), ) } } impl Deref for OpTable { type Target = IndexMap>>; fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for OpTable { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } }