use std::any;
use std::collections::HashMap;
use std::pin::Pin;
use std::sync::Arc;
use futures::Sink;
use futures::future::BoxFuture;
use tor_error::internal;
use void::Void;
#[cfg(feature = "describe-methods")]
pub(crate) mod description;
#[cfg(not(feature = "describe-methods"))]
#[macro_export]
#[doc(hidden)]
macro_rules! register_delegation_note {
{ $from_type:ty, $to_type:ty } => {
}
}
use crate::{Context, DynMethod, Object, RpcError, SendUpdateError};
#[doc(hidden)]
pub type RpcValue = Box<dyn erased_serde::Serialize + Send + 'static>;
#[doc(hidden)]
pub type RpcResult = Result<RpcValue, RpcError>;
#[doc(hidden)]
pub type RpcSendResult = Result<RpcValue, SendUpdateError>;
pub type RpcResultFuture = BoxFuture<'static, RpcResult>;
pub type BoxedUpdateSink = Pin<Box<dyn Sink<RpcValue, Error = SendUpdateError> + Send>>;
pub type UpdateSink<U> = Pin<Box<dyn Sink<U, Error = SendUpdateError> + Send + 'static>>;
type SpecialResultFuture = BoxFuture<'static, Box<dyn any::Any>>;
pub trait Invocable: Send + Sync + 'static {
fn object_type(&self) -> any::TypeId;
fn method_type(&self) -> any::TypeId;
fn object_and_method_type_names(&self) -> (&'static str, &'static str);
fn describe_invocable(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let (object_name, method_name) = self.object_and_method_type_names();
let rpc_method_name = crate::method::method_info_by_typeid(self.method_type())
.map(|mi| mi.method_name)
.unwrap_or("???");
write!(
f,
"Invocable({} ({}) for {})",
method_name, rpc_method_name, object_name,
)
}
fn invoke_special(
&self,
obj: Arc<dyn Object>,
method: Box<dyn DynMethod>,
ctx: Arc<dyn Context>,
) -> Result<SpecialResultFuture, InvokeError>;
}
pub trait RpcInvocable: Invocable {
fn invoke(
&self,
obj: Arc<dyn Object>,
method: Box<dyn DynMethod>,
ctx: Arc<dyn Context>,
sink: BoxedUpdateSink,
) -> Result<RpcResultFuture, InvokeError>;
}
macro_rules! declare_invocable_impl {
{
$( update_gen: $update_gen:ident,
update_arg: { $sink:ident: $update_arg:ty } ,
update_arg_where: { $($update_arg_where:tt)+ } ,
sink_fn: $sink_fn:expr
)?
} => {
impl<M, OBJ, Fut, S, E, $($update_gen)?> Invocable
for fn(Arc<OBJ>, Box<M>, Arc<dyn Context + 'static> $(, $update_arg )? ) -> Fut
where
M: crate::Method,
OBJ: Object,
S: 'static,
E: 'static,
Fut: futures::Future<Output = Result<S,E>> + Send + 'static,
$( M::Update: From<$update_gen>, )?
$( $($update_arg_where)+ )?
{
fn object_type(&self) -> any::TypeId {
any::TypeId::of::<OBJ>()
}
fn method_type(&self) -> any::TypeId {
any::TypeId::of::<M>()
}
fn object_and_method_type_names(&self) -> (&'static str, &'static str) {
(
any::type_name::<OBJ>(),
any::type_name::<M>(),
)
}
fn invoke_special(
&self,
obj: Arc<dyn Object>,
method: Box<dyn DynMethod>,
ctx: Arc<dyn Context>,
) -> Result<SpecialResultFuture, $crate::InvokeError> {
use futures::FutureExt;
#[allow(unused)]
use {tor_async_utils::SinkExt as _, futures::SinkExt as _};
let Ok(obj) = obj.downcast_arc::<OBJ>() else {
return Err(InvokeError::Bug($crate::internal!("Wrong object type")));
};
let Ok(method) = method.downcast::<M>() else {
return Err(InvokeError::Bug($crate::internal!("Wrong method type")));
};
$(
let $sink = Box::pin(futures::sink::drain().sink_err_into());
)?
Ok(
(self)(obj, method, ctx $(, $sink )? )
.map(|r| Box::new(r) as Box<dyn any::Any>)
.boxed()
)
}
}
impl<M, OBJ, Fut, S, E, $($update_gen)?> RpcInvocable
for fn(Arc<OBJ>, Box<M>, Arc<dyn Context + 'static> $(, $update_arg )? ) -> Fut
where
M: crate::RpcMethod,
M::Output: serde::Serialize,
S: 'static,
E: 'static,
OBJ: Object,
Fut: futures::Future<Output = Result<S, E>> + Send + 'static,
M::Output: From<S>,
RpcError: From<E>,
$( M::Update: From<$update_gen>, )?
$( $($update_arg_where)+ )?
{
fn invoke(
&self,
obj: Arc<dyn Object>,
method: Box<dyn DynMethod>,
ctx: Arc<dyn Context>,
#[allow(unused)]
sink: BoxedUpdateSink,
) -> Result<RpcResultFuture, $crate::InvokeError> {
use futures::FutureExt;
#[allow(unused)]
use tor_async_utils::SinkExt as _;
let Ok(obj) = obj.downcast_arc::<OBJ>() else {
return Err(InvokeError::Bug($crate::internal!("Wrong object type")));
};
let Ok(method) = method.downcast::<M>() else {
return Err(InvokeError::Bug($crate::internal!("Wrong method type")));
};
$(
#[allow(clippy::redundant_closure_call)]
let $sink = {
($sink_fn)(sink)
};
)?
Ok(
(self)(obj, method, ctx $(, $sink)? )
.map(|r| {
let r: RpcResult = match r {
Ok(v) => Ok(Box::new(M::Output::from(v))),
Err(e) => Err(RpcError::from(e)),
};
r
})
.boxed()
)
}
}
}
}
declare_invocable_impl! {}
declare_invocable_impl! {
update_gen: U,
update_arg: { sink: UpdateSink<U> },
update_arg_where: {
U: 'static + Send,
M::Update: serde::Serialize
},
sink_fn: |sink:BoxedUpdateSink| Box::pin(
sink.with_fn(|update: U| RpcSendResult::Ok(
Box::new(M::Update::from(update))
)
))
}
#[allow(clippy::exhaustive_structs)]
#[derive(Clone, Copy)]
#[must_use]
pub struct InvokerEnt {
#[doc(hidden)]
pub invoker: &'static dyn Invocable,
#[doc(hidden)]
pub rpc_invoker: Option<&'static dyn RpcInvocable>,
#[doc(hidden)]
pub file: &'static str,
#[doc(hidden)]
pub line: u32,
#[doc(hidden)]
pub function: &'static str,
}
impl InvokerEnt {
fn same_decl(&self, other: &Self) -> bool {
self.file == other.file && self.line == other.line && self.function == other.function
}
}
#[macro_export]
macro_rules! invoker_ent {
{ $func:expr } => {
$crate::invoker_ent!{ @@impl
func: ($func),
rpc_invoker:
(Some($crate::invocable_func_as_dyn_invocable!($func, $crate::dispatch::RpcInvocable))),
}
};
{ @special $func:expr } => {
$crate::invoker_ent!{ @@impl
func: ($func),
rpc_invoker: (None),
}
};
{ @@impl
func: ($func:expr),
rpc_invoker: ($rpc_invoker:expr),
} => {
$crate::dispatch::InvokerEnt {
invoker: $crate::invocable_func_as_dyn_invocable!($func, $crate::dispatch::Invocable),
rpc_invoker: $rpc_invoker,
file: file!(),
line: line!(),
function: stringify!($func)
}
};
}
#[macro_export]
macro_rules! invoker_ent_list {
{ $($(@$tag:ident)* $func:expr),* $(,)? } => {
vec![
$(
$crate::invoker_ent!($(@$tag)* $func)
),*
]
}
}
impl std::fmt::Debug for InvokerEnt {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.invoker.describe_invocable(f)
}
}
inventory::collect!(InvokerEnt);
#[macro_export]
macro_rules! static_rpc_invoke_fn {
{
$( $(@$tag:ident)* $func:expr; )*
} => {$crate::paste::paste!{ $(
$crate::inventory::submit!{
$crate::invoker_ent!($(@$tag)* $func)
}
)* }};
}
#[doc(hidden)]
#[macro_export]
macro_rules! invocable_func_as_dyn_invocable { { $f:expr, $trait:path } => { {
let f = &($f as _);
if let Some(v) = None {
let _: [_; 2] = [*f, $crate::dispatch::obtain_fn_type_for($f, v)];
}
f as &'static dyn $trait
} } }
#[doc(hidden)]
pub trait FnTypeOfFnTrait<X> {
type FnType;
}
#[doc(hidden)]
macro_rules! impl_fn_type_of_fn_trait { { $($arg:ident)* } => {
impl<Func, Ret, $($arg),*> FnTypeOfFnTrait<(Ret, $($arg),*)> for Func
where Func: Fn($($arg),*) -> Ret {
type FnType = fn($($arg),*) -> Ret;
}
} }
impl_fn_type_of_fn_trait!();
impl_fn_type_of_fn_trait!(A);
impl_fn_type_of_fn_trait!(A B);
impl_fn_type_of_fn_trait!(A B C);
impl_fn_type_of_fn_trait!(A B C D);
impl_fn_type_of_fn_trait!(A B C D E);
impl_fn_type_of_fn_trait!(A B C D E F);
#[doc(hidden)]
pub const fn obtain_fn_type_for<X, F: FnTypeOfFnTrait<X>>(_: F, v: Void) -> F::FnType {
match v {}
}
#[derive(Eq, PartialEq, Clone, Debug, Hash)]
struct FuncType {
obj_id: any::TypeId,
method_id: any::TypeId,
}
#[derive(Debug, Clone)]
pub struct DispatchTable {
map: HashMap<FuncType, InvokerEnt>,
}
impl DispatchTable {
pub fn from_inventory() -> Self {
let mut this = Self {
map: HashMap::new(),
};
for ent in inventory::iter::<InvokerEnt>() {
let old_val = this.insert_inner(*ent);
if old_val.is_some() {
panic!("Tried to insert duplicate entry for {:?}", ent);
}
}
this
}
fn insert_inner(&mut self, ent: InvokerEnt) -> Option<InvokerEnt> {
self.map.insert(
FuncType {
obj_id: ent.invoker.object_type(),
method_id: ent.invoker.method_type(),
},
ent,
)
}
pub fn insert(&mut self, ent: InvokerEnt) {
if let Some(old_ent) = self.insert_inner(ent) {
assert!(old_ent.same_decl(&ent));
}
}
pub fn extend<I>(&mut self, ents: I)
where
I: IntoIterator<Item = InvokerEnt>,
{
ents.into_iter().for_each(|e| self.insert(e));
}
fn resolve_entry(
&self,
mut obj: Arc<dyn Object>,
method_id: std::any::TypeId,
) -> Result<(Arc<dyn Object>, &InvokerEnt), InvokeError> {
loop {
let obj_id = {
let dyn_obj: &dyn Object = obj.as_ref();
dyn_obj.type_id()
};
let func_type = FuncType { obj_id, method_id };
if let Some(ent) = self.map.get(&func_type) {
return Ok((obj, ent));
} else if let Some(delegation) = obj.delegate() {
obj = delegation;
} else {
return Err(InvokeError::NoImpl);
}
}
}
pub(crate) fn resolve_rpc_invoker(
&self,
obj: Arc<dyn Object>,
method: &dyn DynMethod,
) -> Result<(Arc<dyn Object>, &'static dyn RpcInvocable), InvokeError> {
let (obj, invoker_ent) = self.resolve_entry(obj, method.type_id())?;
let rpc_invoker = invoker_ent.rpc_invoker.ok_or_else(|| {
InvokeError::Bug(internal!(
"Somehow tried to call a special method as an RPC method."
))
})?;
Ok((obj, rpc_invoker))
}
pub(crate) fn resolve_special_invoker<M: crate::Method>(
&self,
obj: Arc<dyn Object>,
) -> Result<(Arc<dyn Object>, &'static dyn Invocable), InvokeError> {
let (obj, invoker_ent) = self.resolve_entry(obj, std::any::TypeId::of::<M>())?;
Ok((obj, invoker_ent.invoker))
}
}
#[derive(Debug, Clone, thiserror::Error)]
#[non_exhaustive]
pub enum InvokeError {
#[error("No implementation for provided object and method types.")]
NoImpl,
#[error("Called invoke_without_dispatch on a regular RPC method")]
NoDispatchBypass,
#[error("Internal error")]
Bug(#[from] tor_error::Bug),
}
impl From<InvokeError> for RpcError {
fn from(err: InvokeError) -> Self {
use crate::RpcErrorKind as EK;
let kind = match &err {
InvokeError::NoImpl => EK::MethodNotImpl,
InvokeError::NoDispatchBypass => EK::InternalError,
InvokeError::Bug(_) => EK::InternalError,
};
RpcError::new(err.to_string(), kind)
}
}
#[cfg(test)]
pub(crate) mod test {
#![allow(clippy::bool_assert_comparison)]
#![allow(clippy::clone_on_copy)]
#![allow(clippy::dbg_macro)]
#![allow(clippy::mixed_attributes_style)]
#![allow(clippy::print_stderr)]
#![allow(clippy::print_stdout)]
#![allow(clippy::single_char_pattern)]
#![allow(clippy::unwrap_used)]
#![allow(clippy::unchecked_time_subtraction)]
#![allow(clippy::useless_vec)]
#![allow(clippy::needless_pass_by_value)]
use crate::{DispatchTable, InvokeError, Method, NoUpdates, method::RpcMethod, templates::*};
use derive_deftly::Deftly;
use futures::SinkExt;
use futures_await_test::async_test;
use std::sync::{Arc, RwLock};
use super::UpdateSink;
#[derive(Clone, Deftly)]
#[derive_deftly(Object)]
pub(crate) struct Swan;
#[derive(Clone, Deftly)]
#[derive_deftly(Object)]
pub(crate) struct Wombat;
#[derive(Clone, Deftly)]
#[derive_deftly(Object)]
pub(crate) struct Sheep;
#[derive(Clone, Deftly)]
#[derive_deftly(Object)]
pub(crate) struct Brick;
#[derive(Debug, serde::Deserialize, Deftly)]
#[derive_deftly(DynMethod)]
#[deftly(rpc(method_name = "x-test:getname"))]
pub(crate) struct GetName;
#[derive(Debug, serde::Deserialize, Deftly)]
#[derive_deftly(DynMethod)]
#[deftly(rpc(method_name = "x-test:getkids"))]
pub(crate) struct GetKids;
impl RpcMethod for GetName {
type Output = Outcome;
type Update = NoUpdates;
}
impl RpcMethod for GetKids {
type Output = Outcome;
type Update = String;
}
#[derive(serde::Serialize)]
pub(crate) struct Outcome {
pub(crate) v: String,
}
async fn getname_swan(
_obj: Arc<Swan>,
_method: Box<GetName>,
_ctx: Arc<dyn crate::Context>,
) -> Result<Outcome, crate::RpcError> {
Ok(Outcome {
v: "swan".to_string(),
})
}
async fn getname_sheep(
_obj: Arc<Sheep>,
_method: Box<GetName>,
_ctx: Arc<dyn crate::Context>,
) -> Result<Outcome, crate::RpcError> {
Ok(Outcome {
v: "sheep".to_string(),
})
}
async fn getname_wombat(
_obj: Arc<Wombat>,
_method: Box<GetName>,
_ctx: Arc<dyn crate::Context>,
) -> Result<Outcome, crate::RpcError> {
Ok(Outcome {
v: "wombat".to_string(),
})
}
async fn getname_brick(
_obj: Arc<Brick>,
_method: Box<GetName>,
_ctx: Arc<dyn crate::Context>,
) -> Result<Outcome, crate::RpcError> {
Ok(Outcome {
v: "brick".to_string(),
})
}
async fn getkids_swan(
_obj: Arc<Swan>,
_method: Box<GetKids>,
_ctx: Arc<dyn crate::Context>,
) -> Result<Outcome, crate::RpcError> {
Ok(Outcome {
v: "cygnets".to_string(),
})
}
async fn getkids_sheep(
_obj: Arc<Sheep>,
_method: Box<GetKids>,
_ctx: Arc<dyn crate::Context>,
) -> Result<Outcome, crate::RpcError> {
Ok(Outcome {
v: "lambs".to_string(),
})
}
async fn getkids_wombat(
_obj: Arc<Wombat>,
_method: Box<GetKids>,
_ctx: Arc<dyn crate::Context>,
mut sink: UpdateSink<String>,
) -> Result<Outcome, crate::RpcError> {
let _ignore = sink.send("brb, burrowing".to_string()).await;
Ok(Outcome {
v: "joeys".to_string(),
})
}
static_rpc_invoke_fn! {
getname_swan;
getname_sheep;
getname_wombat;
getname_brick;
getkids_swan;
getkids_sheep;
getkids_wombat;
}
pub(crate) struct Ctx {
table: Arc<RwLock<DispatchTable>>,
}
impl From<DispatchTable> for Ctx {
fn from(table: DispatchTable) -> Self {
Self {
table: Arc::new(RwLock::new(table)),
}
}
}
impl crate::Context for Ctx {
fn lookup_object(
&self,
_id: &crate::ObjectId,
) -> Result<std::sync::Arc<dyn crate::Object>, crate::LookupError> {
todo!()
}
fn register_owned(&self, _object: Arc<dyn crate::Object>) -> crate::ObjectId {
todo!()
}
fn release_owned(&self, _object: &crate::ObjectId) -> Result<(), crate::LookupError> {
todo!()
}
fn dispatch_table(&self) -> &Arc<RwLock<crate::DispatchTable>> {
&self.table
}
}
#[derive(Deftly, Clone)]
#[derive_deftly(Object)]
struct GenericObj<T, U>
where
T: Send + Sync + 'static + Clone + ToString,
U: Send + Sync + 'static + Clone + ToString,
{
name: T,
kids: U,
}
async fn getname_generic<T, U>(
obj: Arc<GenericObj<T, U>>,
_method: Box<GetName>,
_ctx: Arc<dyn crate::Context>,
) -> Result<Outcome, crate::RpcError>
where
T: Send + Sync + 'static + Clone + ToString,
U: Send + Sync + 'static + Clone + ToString,
{
Ok(Outcome {
v: obj.name.to_string(),
})
}
async fn getkids_generic<T, U>(
obj: Arc<GenericObj<T, U>>,
_method: Box<GetKids>,
_ctx: Arc<dyn crate::Context>,
) -> Result<Outcome, crate::RpcError>
where
T: Send + Sync + 'static + Clone + ToString,
U: Send + Sync + 'static + Clone + ToString,
{
Ok(Outcome {
v: obj.kids.to_string(),
})
}
static_rpc_invoke_fn! {
getname_generic::<u32,u32>;
getname_generic::<&'static str, &'static str>;
getkids_generic::<u32,u32>;
getkids_generic::<&'static str, &'static str>;
}
impl<T, U> GenericObj<T, U>
where
T: Send + Sync + 'static + Clone + ToString,
U: Send + Sync + 'static + Clone + ToString,
{
fn install_rpc_functions(table: &mut super::DispatchTable) {
table.insert(invoker_ent!(getname_generic::<T, U>));
table.insert(invoker_ent!(getkids_generic::<T, U>));
}
}
#[derive(Clone, Deftly)]
#[derive_deftly(Object)]
#[deftly(rpc(
delegate_with = "|this: &Self| this.contents.clone()",
delegate_type = "dyn crate::Object"
))]
struct CatCarrier {
contents: Option<Arc<dyn crate::Object>>,
}
#[async_test]
async fn try_invoke() {
use super::*;
fn invoke_helper<O: Object, M: Method>(
ctx: &Arc<dyn Context>,
obj: O,
method: M,
) -> Result<RpcResultFuture, InvokeError> {
let animal: Arc<dyn crate::Object> = Arc::new(obj);
let request: Box<dyn DynMethod> = Box::new(method);
let discard = Box::pin(futures::sink::drain().sink_err_into());
crate::invoke_rpc_method(
Arc::clone(ctx),
&crate::ObjectId::from("AnimalIdent"),
animal,
request,
discard,
)
}
async fn invoke_ok<O: crate::Object, M: crate::Method>(
table: &Arc<dyn Context>,
obj: O,
method: M,
) -> String {
let res = invoke_helper(table, obj, method).unwrap().await.unwrap();
serde_json::to_string(&res).unwrap()
}
async fn sentence<O: crate::Object + Clone>(table: &Arc<dyn Context>, obj: O) -> String {
format!(
"Hello I am a friendly {} and these are my lovely {}.",
invoke_ok(table, obj.clone(), GetName).await,
invoke_ok(table, obj, GetKids).await
)
}
let table: Arc<dyn Context> = Arc::new(Ctx::from(DispatchTable::from_inventory()));
assert_eq!(
sentence(&table, Swan).await,
r#"Hello I am a friendly {"v":"swan"} and these are my lovely {"v":"cygnets"}."#
);
assert_eq!(
sentence(&table, Sheep).await,
r#"Hello I am a friendly {"v":"sheep"} and these are my lovely {"v":"lambs"}."#
);
assert_eq!(
sentence(&table, Wombat).await,
r#"Hello I am a friendly {"v":"wombat"} and these are my lovely {"v":"joeys"}."#
);
assert!(matches!(
invoke_helper(&table, Brick, GetKids),
Err(InvokeError::NoImpl)
));
let obj1 = GenericObj {
name: "nuncle",
kids: "niblings",
};
let obj2 = GenericObj {
name: 1337_u32,
kids: 271828_u32,
};
assert_eq!(
sentence(&table, obj1).await,
r#"Hello I am a friendly {"v":"nuncle"} and these are my lovely {"v":"niblings"}."#
);
assert_eq!(
sentence(&table, obj2).await,
r#"Hello I am a friendly {"v":"1337"} and these are my lovely {"v":"271828"}."#
);
let obj3 = GenericObj {
name: 13371337_u64,
kids: 2718281828_u64,
};
assert!(matches!(
invoke_helper(&table, obj3.clone(), GetKids),
Err(InvokeError::NoImpl)
));
{
let mut tab = table.dispatch_table().write().unwrap();
GenericObj::<u64, u64>::install_rpc_functions(&mut tab);
}
assert_eq!(
sentence(&table, obj3).await,
r#"Hello I am a friendly {"v":"13371337"} and these are my lovely {"v":"2718281828"}."#
);
let carrier_1 = CatCarrier {
contents: Some(Arc::new(Wombat)),
};
let carrier_2 = CatCarrier {
contents: Some(Arc::new(Swan)),
};
let carrier_3 = CatCarrier {
contents: Some(Arc::new(Brick)),
};
let carrier_4 = CatCarrier { contents: None };
assert_eq!(
sentence(&table, carrier_1).await,
r#"Hello I am a friendly {"v":"wombat"} and these are my lovely {"v":"joeys"}."#
);
assert_eq!(
sentence(&table, carrier_2).await,
r#"Hello I am a friendly {"v":"swan"} and these are my lovely {"v":"cygnets"}."#
);
assert!(matches!(
invoke_helper(&table, carrier_3, GetKids),
Err(InvokeError::NoImpl)
));
assert!(matches!(
invoke_helper(&table, carrier_4, GetKids),
Err(InvokeError::NoImpl)
));
}
#[derive(Debug)]
struct MyObject {}
#[derive(Debug, Deftly)]
#[derive_deftly(DynMethod)]
#[deftly(rpc(no_method_name))]
struct SpecialOnly {}
impl Method for SpecialOnly {
type Output = Result<MyObject, MyObject>; type Update = crate::NoUpdates;
}
async fn specialonly_swan(
_obj: Arc<Swan>,
_method: Box<SpecialOnly>,
_ctx: Arc<dyn crate::Context>,
) -> Result<MyObject, MyObject> {
Ok(MyObject {})
}
static_rpc_invoke_fn! { @special specialonly_swan; }
#[async_test]
async fn try_invoke_special() {
let table = crate::DispatchTable::from_inventory();
let ctx: Arc<dyn crate::Context> = Arc::new(Ctx::from(table));
let res: Outcome =
crate::invoke_special_method(Arc::clone(&ctx), Arc::new(Swan), Box::new(GetKids))
.await
.unwrap()
.unwrap();
assert_eq!(res.v, "cygnets");
let _an_obj: MyObject = crate::invoke_special_method(
Arc::clone(&ctx),
Arc::new(Swan),
Box::new(SpecialOnly {}),
)
.await
.unwrap()
.unwrap();
}
#[test]
fn invoke_poorly() {
fn is_internal_invoke_err<T>(val: Result<T, InvokeError>) -> bool {
matches!(val, Err(InvokeError::Bug(_)))
}
let ctx: Arc<dyn crate::Context> = Arc::new(Ctx::from(DispatchTable::from_inventory()));
let discard = || Box::pin(futures::sink::drain().sink_err_into());
let table = DispatchTable::from_inventory();
let (_swan, ent) = table.resolve_rpc_invoker(Arc::new(Swan), &GetKids).unwrap();
let bug = ent.invoke(
Arc::new(Swan),
Box::new(GetName),
Arc::clone(&ctx),
discard(),
);
assert!(is_internal_invoke_err(bug));
let bug = ent.invoke(
Arc::new(Wombat),
Box::new(GetKids),
Arc::clone(&ctx),
discard(),
);
assert!(is_internal_invoke_err(bug));
let bug = ent.invoke_special(Arc::new(Swan), Box::new(GetName), Arc::clone(&ctx));
assert!(is_internal_invoke_err(bug));
let bug = ent.invoke_special(Arc::new(Wombat), Box::new(GetKids), Arc::clone(&ctx));
assert!(is_internal_invoke_err(bug));
}
#[test]
fn invoker_ents() {
let ent1 = invoker_ent!(@special specialonly_swan);
let ent1b = invoker_ent!(@special specialonly_swan); let ent2 = invoker_ent!(getname_generic::<String, String>);
let ent2b = invoker_ent!(getname_generic::<String, String>);
assert_eq!(ent1.same_decl(&ent1), true);
assert_eq!(ent1.same_decl(&ent1b), false);
assert_eq!(ent1.same_decl(&ent2), false);
assert_eq!(ent2.same_decl(&ent2), true);
assert_eq!(ent2.same_decl(&ent2b), false);
let re = regex::Regex::new(
r#"^Invocable\(.*GetName \(x-test:getname\) for .*GenericObj.*String.*String"#,
)
.unwrap();
let debug_fmt = format!("{:?}", &ent2);
dbg!(&debug_fmt);
assert!(re.is_match(&debug_fmt));
}
#[test]
fn redundant_invoker_ents() {
let ent = invoker_ent!(getname_generic::<String, String>);
let mut table = DispatchTable::from_inventory();
assert_eq!(ent.same_decl(&ent.clone()), true);
table.insert(ent.clone());
table.insert(ent);
}
#[test]
#[should_panic]
fn conflicting_invoker_ents() {
let ent = invoker_ent!(getname_generic::<String, String>);
let ent2 = invoker_ent!(getname_generic::<String, String>);
let mut table = DispatchTable::from_inventory();
table.insert(ent);
table.insert(ent2);
}
}