1use std::{any::Any, fmt};
2
3use fxhash::FxHashMap;
4use linkme::distributed_slice;
5use metrics::Label;
6use serde::{Deserialize, Serialize};
7use smallbox::{smallbox, SmallBox};
8
9use crate::dumping;
10
11pub trait Message: fmt::Debug + Clone + Any + Send + Serialize + for<'de> Deserialize<'de> {
12 #[doc(hidden)]
13 const VTABLE: &'static MessageVTable;
14
15 #[doc(hidden)]
18 fn _touch(&self);
19}
20
21pub trait Request: Message {
22 type Response: fmt::Debug + Clone + Send + Serialize;
23
24 #[doc(hidden)]
25 type Wrapper: Message + Into<Self::Response> + From<Self::Response>;
26}
27
28pub struct AnyMessage {
29 vtable: &'static MessageVTable,
30 data: SmallBox<dyn Any + Send, [usize; 23]>,
31}
32
33impl AnyMessage {
34 #[inline]
35 pub fn new<M: Message>(message: M) -> Self {
36 message._touch();
37
38 AnyMessage {
39 vtable: M::VTABLE,
40 data: smallbox!(message),
41 }
42 }
43
44 #[inline]
45 pub fn name(&self) -> &'static str {
46 self.vtable.name
47 }
48
49 #[inline]
50 pub fn protocol(&self) -> &'static str {
51 self.vtable.protocol
52 }
53
54 #[inline]
55 #[doc(hidden)]
56 pub fn labels(&self) -> &'static [Label] {
57 self.vtable.labels
58 }
59
60 #[inline]
61 #[doc(hidden)]
62 pub fn dumping_allowed(&self) -> bool {
63 self.vtable.dumping_allowed
64 }
65
66 #[inline]
67 pub fn is<M: Message>(&self) -> bool {
68 self.data.is::<M>()
69 }
70
71 #[inline]
72 pub fn downcast_ref<M: Message>(&self) -> Option<&M> {
73 self.data.downcast_ref::<M>().map(|message| {
74 message._touch();
75 message
76 })
77 }
78
79 #[inline]
80 pub fn downcast<M: Message>(self) -> Result<M, AnyMessage> {
81 if !self.is::<M>() {
82 return Err(self);
83 }
84
85 let message = self
86 .data
87 .downcast::<M>()
88 .expect("cannot downcast")
89 .into_inner();
90
91 message._touch();
92 Ok(message)
93 }
94
95 #[inline]
96 #[doc(hidden)]
97 pub fn erase(&self) -> dumping::ErasedMessage {
98 (self.vtable.erase)(self)
99 }
100}
101
102impl Clone for AnyMessage {
103 #[inline]
104 fn clone(&self) -> Self {
105 (self.vtable.clone)(self)
106 }
107}
108
109impl fmt::Debug for AnyMessage {
110 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111 (self.vtable.debug)(self, f)
112 }
113}
114
115#[derive(Clone)]
119pub struct MessageVTable {
120 pub name: &'static str,
122 pub protocol: &'static str,
125 pub labels: &'static [Label],
126 pub dumping_allowed: bool, pub clone: fn(&AnyMessage) -> AnyMessage,
128 pub debug: fn(&AnyMessage, &mut fmt::Formatter<'_>) -> fmt::Result,
129 pub erase: fn(&AnyMessage) -> dumping::ErasedMessage,
130}
131
132#[distributed_slice]
133pub static MESSAGE_LIST: [&'static MessageVTable] = [..];
134
135thread_local! {
136 static MESSAGE_BY_NAME: FxHashMap<(&'static str, &'static str), &'static MessageVTable> = {
137 MESSAGE_LIST.iter()
138 .map(|vtable| ((vtable.protocol, vtable.name), *vtable))
139 .collect()
140 };
141}
142
143pub(crate) fn init() {
144 MESSAGE_BY_NAME.with(|_| ());
145}