use std::{
any,
collections::{HashMap, HashSet},
sync::Arc,
};
use derive_deftly::define_derive_deftly;
use downcast_rs::Downcast;
use std::sync::LazyLock;
pub trait DynMethod: std::fmt::Debug + Send + Downcast {
fn invoke_without_dispatch(
&self,
ctx: Arc<dyn crate::Context>,
obj_id: &ObjectId,
) -> Result<crate::dispatch::RpcResultFuture, crate::InvokeError> {
let _ = ctx;
let _ = obj_id;
Err(crate::InvokeError::NoDispatchBypass)
}
fn is_cancellable(&self) -> bool {
true
}
}
downcast_rs::impl_downcast!(DynMethod);
#[typetag::deserialize(tag = "method", content = "params")]
pub trait DeserMethod: DynMethod {
fn upcast_box(self: Box<Self>) -> Box<dyn DynMethod>;
}
pub trait Method: DynMethod {
type Output: Send + 'static;
type Update: Send + 'static;
}
pub trait RpcMethod: DeserMethod {
type Output: Send + serde::Serialize + 'static;
type Update: Send + serde::Serialize + 'static;
}
impl<T: RpcMethod> Method for T {
type Output = Result<<T as RpcMethod>::Output, crate::RpcError>;
type Update = <T as RpcMethod>::Update;
}
#[derive(serde::Serialize)]
#[allow(clippy::exhaustive_enums)]
pub enum NoUpdates {}
#[doc(hidden)]
#[allow(clippy::exhaustive_structs)]
pub struct MethodInfo_ {
pub method_name: &'static str,
pub typeid: fn() -> any::TypeId,
pub output_name: fn() -> &'static str,
pub update_name: fn() -> &'static str,
}
inventory::collect!(MethodInfo_);
define_derive_deftly! {
export DynMethod:
#[allow(clippy::incompatible_msrv)] const _: () = {
${if not(tmeta(rpc(bypass_method_dispatch))) {
impl $crate::DynMethod for $ttype {
${if tmeta(rpc(no_cancel)) {
fn is_cancellable(&self) -> bool {
false
}
}}
}
} else if tmeta(rpc(no_method_name)) {
${error "no_method_name is incompatible with bypass_method_dispatch."}
}}
${select1 tmeta(rpc(method_name)) {
use $crate::typetag;
#[typetag::deserialize(name = ${tmeta(rpc(method_name)) as str})]
impl $crate::DeserMethod for $ttype {
fn upcast_box(self: Box<Self>) -> Box<dyn $crate::DynMethod> {
self as _
}
}
$crate::inventory::submit! {
$crate::MethodInfo_ {
method_name : ${tmeta(rpc(method_name)) as str},
typeid : std::any::TypeId::of::<$ttype>, output_name: std::any::type_name::<<$ttype as $crate::RpcMethod>::Output>,
update_name: std::any::type_name::<<$ttype as $crate::RpcMethod>::Update>,
}
}
} else if tmeta(rpc(no_method_name)) {
}}
};
}
pub use derive_deftly_template_DynMethod;
use crate::{InvalidRpcIdentifier, ObjectId, is_valid_rpc_identifier};
pub fn is_method_name(name: &str) -> bool {
static METHOD_NAMES: LazyLock<HashSet<&'static str>> =
LazyLock::new(|| iter_method_names().collect());
METHOD_NAMES.contains(name)
}
pub fn iter_method_names() -> impl Iterator<Item = &'static str> {
inventory::iter::<MethodInfo_>().map(|mi| mi.method_name)
}
pub(crate) fn method_info_by_typeid(typeid: any::TypeId) -> Option<&'static MethodInfo_> {
static METHOD_INFO_BY_TYPEID: LazyLock<HashMap<any::TypeId, &'static MethodInfo_>> =
LazyLock::new(|| {
inventory::iter::<MethodInfo_>()
.map(|mi| ((mi.typeid)(), mi))
.collect()
});
METHOD_INFO_BY_TYPEID.get(&typeid).copied()
}
pub fn check_method_names<'a>(
additional_namespaces: impl IntoIterator<Item = &'a str>,
) -> Vec<(&'static str, InvalidRpcIdentifier)> {
let mut recognized_namespaces: HashSet<&str> = additional_namespaces.into_iter().collect();
recognized_namespaces.extend(["arti", "rpc", "auth"]);
iter_method_names()
.filter_map(|name| {
is_valid_rpc_identifier(Some(&recognized_namespaces), name)
.err()
.map(|e| (name, e))
})
.collect()
}