1use std::any;
51use std::collections::HashMap;
52use std::pin::Pin;
53use std::sync::Arc;
54
55use futures::future::BoxFuture;
56use futures::Sink;
57
58use tor_error::internal;
59use void::Void;
60
61#[cfg(feature = "describe-methods")]
62pub(crate) mod description;
63
64#[cfg(not(feature = "describe-methods"))]
65#[macro_export]
66#[doc(hidden)]
67macro_rules! register_delegation_note {
68 { $from_type:ty, $to_type:ty } => {
69 }
70}
71
72use crate::{Context, DynMethod, Object, RpcError, SendUpdateError};
73
74#[doc(hidden)]
76pub type RpcValue = Box<dyn erased_serde::Serialize + Send + 'static>;
77
78#[doc(hidden)]
80pub type RpcResult = Result<RpcValue, RpcError>;
81
82#[doc(hidden)]
84pub type RpcSendResult = Result<RpcValue, SendUpdateError>;
85
86pub type RpcResultFuture = BoxFuture<'static, RpcResult>;
88
89pub type BoxedUpdateSink = Pin<Box<dyn Sink<RpcValue, Error = SendUpdateError> + Send>>;
91
92pub type UpdateSink<U> = Pin<Box<dyn Sink<U, Error = SendUpdateError> + Send + 'static>>;
99
100type SpecialResultFuture = BoxFuture<'static, Box<dyn any::Any>>;
103
104pub trait Invocable: Send + Sync + 'static {
112 fn object_type(&self) -> any::TypeId;
114 fn method_type(&self) -> any::TypeId;
116 fn object_and_method_type_names(&self) -> (&'static str, &'static str);
120 fn describe_invocable(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122 let (object_name, method_name) = self.object_and_method_type_names();
123 let rpc_method_name = crate::method::method_info_by_typeid(self.method_type())
124 .map(|mi| mi.method_name)
125 .unwrap_or("???");
126 write!(
127 f,
128 "Invocable({} ({}) for {})",
129 method_name, rpc_method_name, object_name,
130 )
131 }
132
133 fn invoke_special(
142 &self,
143 obj: Arc<dyn Object>,
144 method: Box<dyn DynMethod>,
145 ctx: Arc<dyn Context>,
146 ) -> Result<SpecialResultFuture, InvokeError>;
147}
148
149pub trait RpcInvocable: Invocable {
151 fn invoke(
156 &self,
157 obj: Arc<dyn Object>,
158 method: Box<dyn DynMethod>,
159 ctx: Arc<dyn Context>,
160 sink: BoxedUpdateSink,
161 ) -> Result<RpcResultFuture, InvokeError>;
162}
163
164macro_rules! declare_invocable_impl {
170 {
171 $( update_gen: $update_gen:ident,
174 update_arg: { $sink:ident: $update_arg:ty } ,
175 update_arg_where: { $($update_arg_where:tt)+ } ,
176 sink_fn: $sink_fn:expr
177 )?
178 } => {
179 impl<M, OBJ, Fut, S, E, $($update_gen)?> Invocable
180 for fn(Arc<OBJ>, Box<M>, Arc<dyn Context + 'static> $(, $update_arg )? ) -> Fut
181 where
182 M: crate::Method,
183 OBJ: Object,
184 S: 'static,
185 E: 'static,
186 Fut: futures::Future<Output = Result<S,E>> + Send + 'static,
187 $( M::Update: From<$update_gen>, )?
188 $( $($update_arg_where)+ )?
189 {
190 fn object_type(&self) -> any::TypeId {
191 any::TypeId::of::<OBJ>()
192 }
193
194 fn method_type(&self) -> any::TypeId {
195 any::TypeId::of::<M>()
196 }
197
198 fn object_and_method_type_names(&self) -> (&'static str, &'static str) {
199 (
200 any::type_name::<OBJ>(),
201 any::type_name::<M>(),
202 )
203 }
204
205 fn invoke_special(
206 &self,
207 obj: Arc<dyn Object>,
208 method: Box<dyn DynMethod>,
209 ctx: Arc<dyn Context>,
210 ) -> Result<SpecialResultFuture, $crate::InvokeError> {
211 use futures::FutureExt;
212 #[allow(unused)]
213 use {tor_async_utils::SinkExt as _, futures::SinkExt as _};
214
215 let Ok(obj) = obj.downcast_arc::<OBJ>() else {
216 return Err(InvokeError::Bug($crate::internal!("Wrong object type")));
217 };
218 let Ok(method) = method.downcast::<M>() else {
219 return Err(InvokeError::Bug($crate::internal!("Wrong method type")));
220 };
221
222 $(
223 let $sink = Box::pin(futures::sink::drain().sink_err_into());
224 )?
225
226 Ok(
227 (self)(obj, method, ctx $(, $sink )? )
228 .map(|r| Box::new(r) as Box<dyn any::Any>)
229 .boxed()
230 )
231 }
232 }
233
234 impl<M, OBJ, Fut, S, E, $($update_gen)?> RpcInvocable
235 for fn(Arc<OBJ>, Box<M>, Arc<dyn Context + 'static> $(, $update_arg )? ) -> Fut
236 where
237 M: crate::RpcMethod,
238 M::Output: serde::Serialize,
239 S: 'static,
240 E: 'static,
241 OBJ: Object,
242 Fut: futures::Future<Output = Result<S, E>> + Send + 'static,
243 M::Output: From<S>,
244 RpcError: From<E>,
245 $( M::Update: From<$update_gen>, )?
246 $( $($update_arg_where)+ )?
247 {
248 fn invoke(
249 &self,
250 obj: Arc<dyn Object>,
251 method: Box<dyn DynMethod>,
252 ctx: Arc<dyn Context>,
253 #[allow(unused)]
254 sink: BoxedUpdateSink,
255 ) -> Result<RpcResultFuture, $crate::InvokeError> {
256 use futures::FutureExt;
257 #[allow(unused)]
258 use tor_async_utils::SinkExt as _;
259 let Ok(obj) = obj.downcast_arc::<OBJ>() else {
260 return Err(InvokeError::Bug($crate::internal!("Wrong object type")));
261 };
262 let Ok(method) = method.downcast::<M>() else {
263 return Err(InvokeError::Bug($crate::internal!("Wrong method type")));
264 };
265 $(
266 #[allow(clippy::redundant_closure_call)]
267 let $sink = {
268 ($sink_fn)(sink)
269 };
270 )?
271
272 Ok(
273 (self)(obj, method, ctx $(, $sink)? )
274 .map(|r| {
275 let r: RpcResult = match r {
276 Ok(v) => Ok(Box::new(M::Output::from(v))),
277 Err(e) => Err(RpcError::from(e)),
278 };
279 r
280 })
281 .boxed()
282 )
283 }
284 }
285 }
286}
287
288declare_invocable_impl! {}
289
290declare_invocable_impl! {
291 update_gen: U,
292 update_arg: { sink: UpdateSink<U> },
293 update_arg_where: {
294 U: 'static + Send,
295 M::Update: serde::Serialize
296 },
297 sink_fn: |sink:BoxedUpdateSink| Box::pin(
298 sink.with_fn(|update: U| RpcSendResult::Ok(
299 Box::new(M::Update::from(update))
300 )
301 ))
302}
303
304#[allow(clippy::exhaustive_structs)]
308#[derive(Clone, Copy)]
309#[must_use]
310pub struct InvokerEnt {
311 #[doc(hidden)]
315 pub invoker: &'static (dyn Invocable),
316
317 #[doc(hidden)]
323 pub rpc_invoker: Option<&'static (dyn RpcInvocable)>,
324
325 #[doc(hidden)]
330 pub file: &'static str,
331 #[doc(hidden)]
332 pub line: u32,
333 #[doc(hidden)]
334 pub function: &'static str,
335}
336impl InvokerEnt {
337 fn same_decl(&self, other: &Self) -> bool {
346 self.file == other.file && self.line == other.line && self.function == other.function
347 }
348}
349
350#[macro_export]
368macro_rules! invoker_ent {
369 { $func:expr } => {
370 $crate::invoker_ent!{ @@impl
371 func: ($func),
372 rpc_invoker:
373 (Some($crate::invocable_func_as_dyn_invocable!($func, $crate::dispatch::RpcInvocable))),
374 }
375 };
376 { @special $func:expr } => {
377 $crate::invoker_ent!{ @@impl
378 func: ($func),
379 rpc_invoker: (None),
380 }
381 };
382 { @@impl
383 func: ($func:expr),
384 rpc_invoker: ($rpc_invoker:expr),
385 } => {
386 $crate::dispatch::InvokerEnt {
387 invoker: $crate::invocable_func_as_dyn_invocable!($func, $crate::dispatch::Invocable),
388 rpc_invoker: $rpc_invoker,
389 file: file!(),
390 line: line!(),
391 function: stringify!($func)
392 }
393 };
394}
395
396#[macro_export]
411macro_rules! invoker_ent_list {
412 { $($(@$tag:ident)* $func:expr),* $(,)? } => {
413 vec![
414 $(
415 $crate::invoker_ent!($(@$tag)* $func)
416 ),*
417 ]
418 }
419}
420
421impl std::fmt::Debug for InvokerEnt {
422 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
423 self.invoker.describe_invocable(f)
424 }
425}
426inventory::collect!(InvokerEnt);
427
428#[macro_export]
510macro_rules! static_rpc_invoke_fn {
511 {
512 $( $(@$tag:ident)* $func:expr; )*
513 } => {$crate::paste::paste!{ $(
514 $crate::inventory::submit!{
515 $crate::invoker_ent!($(@$tag)* $func)
516 }
517 )* }};
518}
519
520#[doc(hidden)]
525#[macro_export]
526macro_rules! invocable_func_as_dyn_invocable { { $f:expr, $trait:path } => { {
527 let f = &($f as _);
528 if let Some(v) = None {
535 let _: [_; 2] = [*f, $crate::dispatch::obtain_fn_type_for($f, v)];
546 }
547 f as &'static dyn $trait
550} } }
551
552#[doc(hidden)]
557pub trait FnTypeOfFnTrait<X> {
558 type FnType;
560}
561#[doc(hidden)]
563macro_rules! impl_fn_type_of_fn_trait { { $($arg:ident)* } => {
564 impl<Func, Ret, $($arg),*> FnTypeOfFnTrait<(Ret, $($arg),*)> for Func
565 where Func: Fn($($arg),*) -> Ret {
566 type FnType = fn($($arg),*) -> Ret;
567 }
568} }
569impl_fn_type_of_fn_trait!();
570impl_fn_type_of_fn_trait!(A);
571impl_fn_type_of_fn_trait!(A B);
572impl_fn_type_of_fn_trait!(A B C);
573impl_fn_type_of_fn_trait!(A B C D);
574impl_fn_type_of_fn_trait!(A B C D E);
575impl_fn_type_of_fn_trait!(A B C D E F);
576
577#[doc(hidden)]
589pub const fn obtain_fn_type_for<X, F: FnTypeOfFnTrait<X>>(_: F, v: Void) -> F::FnType {
590 match v {}
591}
592
593#[derive(Eq, PartialEq, Clone, Debug, Hash)]
595struct FuncType {
596 obj_id: any::TypeId,
598 method_id: any::TypeId,
600}
601
602#[derive(Debug, Clone)]
609pub struct DispatchTable {
610 map: HashMap<FuncType, InvokerEnt>,
613}
614
615impl DispatchTable {
616 pub fn from_inventory() -> Self {
623 let mut this = Self {
625 map: HashMap::new(),
626 };
627 for ent in inventory::iter::<InvokerEnt>() {
628 let old_val = this.insert_inner(*ent);
629 if old_val.is_some() {
630 panic!("Tried to insert duplicate entry for {:?}", ent);
631 }
632 }
633 this
634 }
635
636 fn insert_inner(&mut self, ent: InvokerEnt) -> Option<InvokerEnt> {
638 self.map.insert(
639 FuncType {
640 obj_id: ent.invoker.object_type(),
641 method_id: ent.invoker.method_type(),
642 },
643 ent,
644 )
645 }
646
647 pub fn insert(&mut self, ent: InvokerEnt) {
654 if let Some(old_ent) = self.insert_inner(ent) {
655 assert!(old_ent.same_decl(&ent));
657 }
658 }
659
660 pub fn extend<I>(&mut self, ents: I)
666 where
667 I: IntoIterator<Item = InvokerEnt>,
668 {
669 ents.into_iter().for_each(|e| self.insert(e));
670 }
671
672 fn resolve_entry(
678 &self,
679 mut obj: Arc<dyn Object>,
680 method_id: std::any::TypeId,
681 ) -> Result<(Arc<dyn Object>, &InvokerEnt), InvokeError> {
682 loop {
683 let obj_id = {
684 let dyn_obj: &dyn Object = obj.as_ref();
685 dyn_obj.type_id()
686 };
687 let func_type = FuncType { obj_id, method_id };
688 if let Some(ent) = self.map.get(&func_type) {
689 return Ok((obj, ent));
690 } else if let Some(delegation) = obj.delegate() {
691 obj = delegation;
692 } else {
693 return Err(InvokeError::NoImpl);
694 }
695 }
696 }
697
698 pub(crate) fn resolve_rpc_invoker(
704 &self,
705 obj: Arc<dyn Object>,
706 method: &dyn DynMethod,
707 ) -> Result<(Arc<dyn Object>, &'static dyn RpcInvocable), InvokeError> {
708 let (obj, invoker_ent) = self.resolve_entry(obj, method.type_id())?;
709 let rpc_invoker = invoker_ent.rpc_invoker.ok_or_else(|| {
710 InvokeError::Bug(internal!(
711 "Somehow tried to call a special method as an RPC method."
712 ))
713 })?;
714 Ok((obj, rpc_invoker))
715 }
716
717 pub(crate) fn resolve_special_invoker<M: crate::Method>(
723 &self,
724 obj: Arc<dyn Object>,
725 ) -> Result<(Arc<dyn Object>, &'static dyn Invocable), InvokeError> {
726 let (obj, invoker_ent) = self.resolve_entry(obj, std::any::TypeId::of::<M>())?;
727 Ok((obj, invoker_ent.invoker))
728 }
729}
730
731#[derive(Debug, Clone, thiserror::Error)]
733#[non_exhaustive]
734pub enum InvokeError {
735 #[error("No implementation for provided object and method types.")]
738 NoImpl,
739
740 #[error("Called invoke_without_dispatch on a regular RPC method")]
743 NoDispatchBypass,
744
745 #[error("Internal error")]
747 Bug(#[from] tor_error::Bug),
748}
749
750impl From<InvokeError> for RpcError {
751 fn from(err: InvokeError) -> Self {
752 use crate::RpcErrorKind as EK;
753 let kind = match &err {
754 InvokeError::NoImpl => EK::MethodNotImpl,
755 InvokeError::NoDispatchBypass => EK::InternalError,
756 InvokeError::Bug(_) => EK::InternalError,
757 };
758 RpcError::new(err.to_string(), kind)
759 }
760}
761
762#[cfg(test)]
763pub(crate) mod test {
764 #![allow(clippy::bool_assert_comparison)]
766 #![allow(clippy::clone_on_copy)]
767 #![allow(clippy::dbg_macro)]
768 #![allow(clippy::mixed_attributes_style)]
769 #![allow(clippy::print_stderr)]
770 #![allow(clippy::print_stdout)]
771 #![allow(clippy::single_char_pattern)]
772 #![allow(clippy::unwrap_used)]
773 #![allow(clippy::unchecked_duration_subtraction)]
774 #![allow(clippy::useless_vec)]
775 #![allow(clippy::needless_pass_by_value)]
776 use crate::{method::RpcMethod, templates::*, DispatchTable, InvokeError, Method, NoUpdates};
779 use derive_deftly::Deftly;
780 use futures::SinkExt;
781 use futures_await_test::async_test;
782 use std::sync::{Arc, RwLock};
783
784 use super::UpdateSink;
785
786 #[derive(Clone, Deftly)]
788 #[derive_deftly(Object)]
789 pub(crate) struct Swan;
790 #[derive(Clone, Deftly)]
791 #[derive_deftly(Object)]
792 pub(crate) struct Wombat;
793 #[derive(Clone, Deftly)]
794 #[derive_deftly(Object)]
795 pub(crate) struct Sheep;
796 #[derive(Clone, Deftly)]
797 #[derive_deftly(Object)]
798 pub(crate) struct Brick;
799
800 #[derive(Debug, serde::Deserialize, Deftly)]
802 #[derive_deftly(DynMethod)]
803 #[deftly(rpc(method_name = "x-test:getname"))]
804 pub(crate) struct GetName;
805
806 #[derive(Debug, serde::Deserialize, Deftly)]
807 #[derive_deftly(DynMethod)]
808 #[deftly(rpc(method_name = "x-test:getkids"))]
809 pub(crate) struct GetKids;
810
811 impl RpcMethod for GetName {
812 type Output = Outcome;
813 type Update = NoUpdates;
814 }
815 impl RpcMethod for GetKids {
816 type Output = Outcome;
817 type Update = String;
818 }
819
820 #[derive(serde::Serialize)]
821 pub(crate) struct Outcome {
822 pub(crate) v: String,
823 }
824
825 async fn getname_swan(
826 _obj: Arc<Swan>,
827 _method: Box<GetName>,
828 _ctx: Arc<dyn crate::Context>,
829 ) -> Result<Outcome, crate::RpcError> {
830 Ok(Outcome {
831 v: "swan".to_string(),
832 })
833 }
834 async fn getname_sheep(
835 _obj: Arc<Sheep>,
836 _method: Box<GetName>,
837 _ctx: Arc<dyn crate::Context>,
838 ) -> Result<Outcome, crate::RpcError> {
839 Ok(Outcome {
840 v: "sheep".to_string(),
841 })
842 }
843 async fn getname_wombat(
844 _obj: Arc<Wombat>,
845 _method: Box<GetName>,
846 _ctx: Arc<dyn crate::Context>,
847 ) -> Result<Outcome, crate::RpcError> {
848 Ok(Outcome {
849 v: "wombat".to_string(),
850 })
851 }
852 async fn getname_brick(
853 _obj: Arc<Brick>,
854 _method: Box<GetName>,
855 _ctx: Arc<dyn crate::Context>,
856 ) -> Result<Outcome, crate::RpcError> {
857 Ok(Outcome {
858 v: "brick".to_string(),
859 })
860 }
861 async fn getkids_swan(
862 _obj: Arc<Swan>,
863 _method: Box<GetKids>,
864 _ctx: Arc<dyn crate::Context>,
865 ) -> Result<Outcome, crate::RpcError> {
866 Ok(Outcome {
867 v: "cygnets".to_string(),
868 })
869 }
870 async fn getkids_sheep(
871 _obj: Arc<Sheep>,
872 _method: Box<GetKids>,
873 _ctx: Arc<dyn crate::Context>,
874 ) -> Result<Outcome, crate::RpcError> {
875 Ok(Outcome {
876 v: "lambs".to_string(),
877 })
878 }
879 async fn getkids_wombat(
880 _obj: Arc<Wombat>,
881 _method: Box<GetKids>,
882 _ctx: Arc<dyn crate::Context>,
883 mut sink: UpdateSink<String>,
884 ) -> Result<Outcome, crate::RpcError> {
885 let _ignore = sink.send("brb, burrowing".to_string()).await;
886 Ok(Outcome {
887 v: "joeys".to_string(),
888 })
889 }
890
891 static_rpc_invoke_fn! {
892 getname_swan;
893 getname_sheep;
894 getname_wombat;
895 getname_brick;
896
897 getkids_swan;
898 getkids_sheep;
899 getkids_wombat;
900 }
901
902 pub(crate) struct Ctx {
903 table: Arc<RwLock<DispatchTable>>,
904 }
905 impl From<DispatchTable> for Ctx {
906 fn from(table: DispatchTable) -> Self {
907 Self {
908 table: Arc::new(RwLock::new(table)),
909 }
910 }
911 }
912
913 impl crate::Context for Ctx {
914 fn lookup_object(
915 &self,
916 _id: &crate::ObjectId,
917 ) -> Result<std::sync::Arc<dyn crate::Object>, crate::LookupError> {
918 todo!()
919 }
920 fn register_owned(&self, _object: Arc<dyn crate::Object>) -> crate::ObjectId {
921 todo!()
922 }
923
924 fn release_owned(&self, _object: &crate::ObjectId) -> Result<(), crate::LookupError> {
925 todo!()
926 }
927
928 fn dispatch_table(&self) -> &Arc<RwLock<crate::DispatchTable>> {
929 &self.table
930 }
931 }
932
933 #[derive(Deftly, Clone)]
934 #[derive_deftly(Object)]
935 struct GenericObj<T, U>
936 where
937 T: Send + Sync + 'static + Clone + ToString,
938 U: Send + Sync + 'static + Clone + ToString,
939 {
940 name: T,
941 kids: U,
942 }
943
944 async fn getname_generic<T, U>(
945 obj: Arc<GenericObj<T, U>>,
946 _method: Box<GetName>,
947 _ctx: Arc<dyn crate::Context>,
948 ) -> Result<Outcome, crate::RpcError>
949 where
950 T: Send + Sync + 'static + Clone + ToString,
951 U: Send + Sync + 'static + Clone + ToString,
952 {
953 Ok(Outcome {
954 v: obj.name.to_string(),
955 })
956 }
957 async fn getkids_generic<T, U>(
958 obj: Arc<GenericObj<T, U>>,
959 _method: Box<GetKids>,
960 _ctx: Arc<dyn crate::Context>,
961 ) -> Result<Outcome, crate::RpcError>
962 where
963 T: Send + Sync + 'static + Clone + ToString,
964 U: Send + Sync + 'static + Clone + ToString,
965 {
966 Ok(Outcome {
967 v: obj.kids.to_string(),
968 })
969 }
970
971 static_rpc_invoke_fn! {
973 getname_generic::<u32,u32>;
974 getname_generic::<&'static str, &'static str>;
975 getkids_generic::<u32,u32>;
976 getkids_generic::<&'static str, &'static str>;
977 }
978
979 impl<T, U> GenericObj<T, U>
981 where
982 T: Send + Sync + 'static + Clone + ToString,
983 U: Send + Sync + 'static + Clone + ToString,
984 {
985 fn install_rpc_functions(table: &mut super::DispatchTable) {
986 table.insert(invoker_ent!(getname_generic::<T, U>));
987 table.insert(invoker_ent!(getkids_generic::<T, U>));
988 }
989 }
990
991 #[derive(Clone, Deftly)]
993 #[derive_deftly(Object)]
994 #[deftly(rpc(
995 delegate_with = "|this: &Self| this.contents.clone()",
996 delegate_type = "dyn crate::Object"
997 ))]
998 struct CatCarrier {
999 contents: Option<Arc<dyn crate::Object>>,
1000 }
1001
1002 #[async_test]
1003 async fn try_invoke() {
1004 use super::*;
1005 fn invoke_helper<O: Object, M: Method>(
1006 ctx: &Arc<dyn Context>,
1007 obj: O,
1008 method: M,
1009 ) -> Result<RpcResultFuture, InvokeError> {
1010 let animal: Arc<dyn crate::Object> = Arc::new(obj);
1011 let request: Box<dyn DynMethod> = Box::new(method);
1012 let discard = Box::pin(futures::sink::drain().sink_err_into());
1013 crate::invoke_rpc_method(
1014 Arc::clone(ctx),
1015 &crate::ObjectId::from("AnimalIdent"),
1016 animal,
1017 request,
1018 discard,
1019 )
1020 }
1021 async fn invoke_ok<O: crate::Object, M: crate::Method>(
1022 table: &Arc<dyn Context>,
1023 obj: O,
1024 method: M,
1025 ) -> String {
1026 let res = invoke_helper(table, obj, method).unwrap().await.unwrap();
1027 serde_json::to_string(&res).unwrap()
1028 }
1029 async fn sentence<O: crate::Object + Clone>(table: &Arc<dyn Context>, obj: O) -> String {
1030 format!(
1031 "Hello I am a friendly {} and these are my lovely {}.",
1032 invoke_ok(table, obj.clone(), GetName).await,
1033 invoke_ok(table, obj, GetKids).await
1034 )
1035 }
1036
1037 let table: Arc<dyn Context> = Arc::new(Ctx::from(DispatchTable::from_inventory()));
1038
1039 assert_eq!(
1040 sentence(&table, Swan).await,
1041 r#"Hello I am a friendly {"v":"swan"} and these are my lovely {"v":"cygnets"}."#
1042 );
1043 assert_eq!(
1044 sentence(&table, Sheep).await,
1045 r#"Hello I am a friendly {"v":"sheep"} and these are my lovely {"v":"lambs"}."#
1046 );
1047 assert_eq!(
1048 sentence(&table, Wombat).await,
1049 r#"Hello I am a friendly {"v":"wombat"} and these are my lovely {"v":"joeys"}."#
1050 );
1051
1052 assert!(matches!(
1053 invoke_helper(&table, Brick, GetKids),
1054 Err(InvokeError::NoImpl)
1055 ));
1056
1057 let obj1 = GenericObj {
1062 name: "nuncle",
1063 kids: "niblings",
1064 };
1065 let obj2 = GenericObj {
1066 name: 1337_u32,
1067 kids: 271828_u32,
1068 };
1069 assert_eq!(
1070 sentence(&table, obj1).await,
1071 r#"Hello I am a friendly {"v":"nuncle"} and these are my lovely {"v":"niblings"}."#
1072 );
1073 assert_eq!(
1074 sentence(&table, obj2).await,
1075 r#"Hello I am a friendly {"v":"1337"} and these are my lovely {"v":"271828"}."#
1076 );
1077
1078 let obj3 = GenericObj {
1079 name: 13371337_u64,
1080 kids: 2718281828_u64,
1081 };
1082 assert!(matches!(
1083 invoke_helper(&table, obj3.clone(), GetKids),
1084 Err(InvokeError::NoImpl)
1085 ));
1086 {
1087 let mut tab = table.dispatch_table().write().unwrap();
1088 GenericObj::<u64, u64>::install_rpc_functions(&mut tab);
1089 }
1090 assert_eq!(
1091 sentence(&table, obj3).await,
1092 r#"Hello I am a friendly {"v":"13371337"} and these are my lovely {"v":"2718281828"}."#
1093 );
1094
1095 let carrier_1 = CatCarrier {
1097 contents: Some(Arc::new(Wombat)),
1098 };
1099 let carrier_2 = CatCarrier {
1100 contents: Some(Arc::new(Swan)),
1101 };
1102 let carrier_3 = CatCarrier {
1103 contents: Some(Arc::new(Brick)),
1104 };
1105 let carrier_4 = CatCarrier { contents: None };
1106 assert_eq!(
1107 sentence(&table, carrier_1).await,
1108 r#"Hello I am a friendly {"v":"wombat"} and these are my lovely {"v":"joeys"}."#
1109 );
1110 assert_eq!(
1111 sentence(&table, carrier_2).await,
1112 r#"Hello I am a friendly {"v":"swan"} and these are my lovely {"v":"cygnets"}."#
1113 );
1114 assert!(matches!(
1115 invoke_helper(&table, carrier_3, GetKids),
1116 Err(InvokeError::NoImpl)
1117 ));
1118 assert!(matches!(
1119 invoke_helper(&table, carrier_4, GetKids),
1120 Err(InvokeError::NoImpl)
1121 ));
1122 }
1123
1124 #[derive(Debug)]
1126 struct MyObject {}
1127
1128 #[derive(Debug, Deftly)]
1129 #[derive_deftly(DynMethod)]
1130 #[deftly(rpc(no_method_name))]
1131 struct SpecialOnly {}
1132 impl Method for SpecialOnly {
1133 type Output = Result<MyObject, MyObject>; type Update = crate::NoUpdates;
1135 }
1136
1137 async fn specialonly_swan(
1138 _obj: Arc<Swan>,
1139 _method: Box<SpecialOnly>,
1140 _ctx: Arc<dyn crate::Context>,
1141 ) -> Result<MyObject, MyObject> {
1142 Ok(MyObject {})
1143 }
1144 static_rpc_invoke_fn! { @special specialonly_swan; }
1145
1146 #[async_test]
1147 async fn try_invoke_special() {
1148 let table = crate::DispatchTable::from_inventory();
1149 let ctx: Arc<dyn crate::Context> = Arc::new(Ctx::from(table));
1150
1151 let res: Outcome =
1152 crate::invoke_special_method(Arc::clone(&ctx), Arc::new(Swan), Box::new(GetKids))
1153 .await
1154 .unwrap()
1155 .unwrap();
1156
1157 assert_eq!(res.v, "cygnets");
1158
1159 let _an_obj: MyObject = crate::invoke_special_method(
1160 Arc::clone(&ctx),
1161 Arc::new(Swan),
1162 Box::new(SpecialOnly {}),
1163 )
1164 .await
1165 .unwrap()
1166 .unwrap();
1167 }
1168
1169 #[test]
1170 fn invoke_poorly() {
1171 fn is_internal_invoke_err<T>(val: Result<T, InvokeError>) -> bool {
1172 matches!(val, Err(InvokeError::Bug(_)))
1173 }
1174
1175 let ctx: Arc<dyn crate::Context> = Arc::new(Ctx::from(DispatchTable::from_inventory()));
1178 let discard = || Box::pin(futures::sink::drain().sink_err_into());
1179
1180 let table = DispatchTable::from_inventory();
1181 let (_swan, ent) = table.resolve_rpc_invoker(Arc::new(Swan), &GetKids).unwrap();
1182
1183 let bug = ent.invoke(
1185 Arc::new(Swan),
1186 Box::new(GetName),
1187 Arc::clone(&ctx),
1188 discard(),
1189 );
1190 assert!(is_internal_invoke_err(bug));
1191
1192 let bug = ent.invoke(
1194 Arc::new(Wombat),
1195 Box::new(GetKids),
1196 Arc::clone(&ctx),
1197 discard(),
1198 );
1199 assert!(is_internal_invoke_err(bug));
1200
1201 let bug = ent.invoke_special(Arc::new(Swan), Box::new(GetName), Arc::clone(&ctx));
1203 assert!(is_internal_invoke_err(bug));
1204 let bug = ent.invoke_special(Arc::new(Wombat), Box::new(GetKids), Arc::clone(&ctx));
1206 assert!(is_internal_invoke_err(bug));
1207 }
1208
1209 #[test]
1210 fn invoker_ents() {
1211 let ent1 = invoker_ent!(@special specialonly_swan);
1212 let ent1b = invoker_ent!(@special specialonly_swan); let ent2 = invoker_ent!(getname_generic::<String, String>);
1214 let ent2b = invoker_ent!(getname_generic::<String, String>);
1215
1216 assert_eq!(ent1.same_decl(&ent1), true);
1217 assert_eq!(ent1.same_decl(&ent1b), false);
1218 assert_eq!(ent1.same_decl(&ent2), false);
1219
1220 assert_eq!(ent2.same_decl(&ent2), true);
1221 assert_eq!(ent2.same_decl(&ent2b), false);
1222
1223 let re = regex::Regex::new(
1224 r#"^Invocable\(.*GetName \(x-test:getname\) for .*GenericObj.*String.*String"#,
1225 )
1226 .unwrap();
1227 let debug_fmt = format!("{:?}", &ent2);
1228 dbg!(&debug_fmt);
1229 assert!(re.is_match(&debug_fmt));
1230 }
1231
1232 #[test]
1233 fn redundant_invoker_ents() {
1234 let ent = invoker_ent!(getname_generic::<String, String>);
1235 let mut table = DispatchTable::from_inventory();
1236
1237 assert_eq!(ent.same_decl(&ent.clone()), true);
1238 table.insert(ent.clone());
1239 table.insert(ent);
1240 }
1241
1242 #[test]
1243 #[should_panic]
1244 fn conflicting_invoker_ents() {
1245 let ent = invoker_ent!(getname_generic::<String, String>);
1246 let ent2 = invoker_ent!(getname_generic::<String, String>);
1247 let mut table = DispatchTable::from_inventory();
1248 table.insert(ent);
1249 table.insert(ent2);
1250 }
1251}