1#[cfg(feature = "serializable_callbacks")]
2use linkme::distributed_slice;
3
4pub use paste;
5use serde::{Deserialize, Serialize};
6use std::borrow::Cow;
7
8pub struct AnyAction(pub Box<dyn std::any::Any>);
9
10#[cfg(feature = "serializable_callbacks")]
11#[distributed_slice]
12pub static CALLBACKS: [(&str, fn(&str, Box<dyn std::any::Any>) -> AnyAction)];
13
14#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
15pub struct Callback<T> {
16 #[serde(skip, default = "default_fun_ptr")]
17 fun_ptr: Option<fn(T) -> AnyAction>,
18 pub fun_name: Cow<'static, str>,
19}
20
21fn default_fun_ptr<T>() -> Option<fn(T) -> AnyAction> {
22 None
23}
24
25impl<T: 'static> Callback<T> {
26 pub fn new(name: &'static str, fun_ptr: fn(T) -> AnyAction) -> Self {
27 Self {
28 fun_ptr: Some(fun_ptr),
29 fun_name: Cow::Borrowed(name),
30 }
31 }
32
33 pub fn call<Action>(&self, args: T) -> Action
34 where
35 Action: From<AnyAction>,
36 {
37 if let Some(fun) = self.fun_ptr {
38 return fun(args).into();
39 }
40
41 #[cfg(not(feature = "serializable_callbacks"))]
42 unimplemented!();
43
44 #[cfg(feature = "serializable_callbacks")]
45 {
46 for (name, fun) in CALLBACKS {
48 if name == &self.fun_name {
49 return fun(std::any::type_name::<T>(), Box::new(args)).into();
50 }
51 }
52
53 panic!("callback function {} not found", self.fun_name)
54 }
55 }
56}
57
58#[macro_export]
59macro_rules! _callback {
60 ($callback_name:ident, $action_ty:ty, $arg:tt, $arg_type:ty, $body:expr) => {{
61 use $crate::{AnyAction, Callback};
62
63 #[cfg(feature = "serializable_callbacks")]
64 use {$crate::CALLBACKS, linkme::distributed_slice};
65
66 redux::paste::paste! {
67 #[allow(unused)] fn convert_impl($arg: $arg_type) -> AnyAction {
69 let action: $action_ty = ($body).into();
70 AnyAction(Box::new(action))
71 }
72
73 fn $callback_name(call_type: &str, args: Box<dyn std::any::Any>) -> AnyAction {
74 #[cfg(feature = "serializable_callbacks")]
75 {
76 #[distributed_slice(CALLBACKS)]
77 static CALLBACK_DESERIALIZE: (&str, fn(&str, Box<dyn std::any::Any>) -> AnyAction) = (
78 stringify!($callback_name),
79 $callback_name,
80 );
81 }
82
83 let $arg = *args.downcast::<$arg_type>()
84 .expect(&format!(
85 "Invalid argument type: {}, expected: {}",
86 call_type,
87 stringify!($arg_type)));
88
89 convert_impl($arg)
90 }
91 }
92
93 Callback::new(stringify!($callback_name), convert_impl)
94 }};
95}
96
97#[macro_export]
113macro_rules! callback {
114 ($callback_name:ident(($($var:ident : $typ:ty),+)) -> $action_ty:ty $body:block) => {
115 $crate::_callback!($callback_name, $action_ty, ($($var),+), ($($typ),+), $body)
116 };
117 ($callback_name:ident($var:ident : $typ:ty) -> $action_ty:ty $body:block) => {
118 $crate::_callback!($callback_name, $action_ty, $var, $typ, $body)
119 };
120}