1use std::{borrow::Cow, fmt, sync::Arc, time::SystemTime};
2
3use erased_serde::Serialize as ErasedSerialize;
4use serde::Serialize;
5use smallbox::{smallbox, SmallBox};
6
7use elfo_macros::message;
8
9use super::{extract_name::extract_name, sequence_no::SequenceNo};
10use crate::{actor::ActorMeta, envelope, scope, thread::ThreadId, trace_id::TraceId};
11
12#[doc(hidden)]
15#[stability::unstable]
16pub struct Dump {
17 pub meta: Arc<ActorMeta>,
18 pub sequence_no: SequenceNo,
19 pub timestamp: Timestamp,
20 pub trace_id: TraceId,
21 pub thread_id: ThreadId,
22 pub direction: Direction,
23 pub message_name: MessageName,
24 pub message_protocol: &'static str,
25 pub message_kind: MessageKind,
26 pub message: ErasedMessage,
27}
28
29#[doc(hidden)]
30#[stability::unstable]
31pub type ErasedMessage = SmallBox<dyn ErasedSerialize + Send, [usize; 24]>;
32
33assert_impl_all!(Dump: Send);
34assert_eq_size!(Dump, [u8; 320]);
35
36impl Dump {
37 #[stability::unstable]
38 pub fn builder() -> DumpBuilder {
39 DumpBuilder {
40 timestamp: None,
41 direction: Direction::Out,
42 message_name: None,
43 message_protocol: "",
44 message_kind: MessageKind::Regular,
45 }
46 }
47
48 pub(crate) fn message<M: crate::Message>(
49 message: M,
50 kind: &envelope::MessageKind,
51 direction: Direction,
52 ) -> Self {
53 Self::builder()
54 .direction(direction)
55 .message_name(M::VTABLE.name)
56 .message_protocol(M::VTABLE.protocol)
57 .message_kind(MessageKind::from_message_kind(kind))
58 .finish(message)
59 }
60}
61
62#[stability::unstable]
65pub struct DumpBuilder {
66 timestamp: Option<Timestamp>,
67 direction: Direction,
68 message_name: Option<MessageName>,
69 message_protocol: &'static str,
70 message_kind: MessageKind,
71}
72
73impl DumpBuilder {
74 #[stability::unstable]
75 pub fn timestamp(&mut self, timestamp: impl Into<Timestamp>) -> &mut Self {
76 self.timestamp = Some(timestamp.into());
77 self
78 }
79
80 #[stability::unstable]
81 pub fn direction(&mut self, direction: Direction) -> &mut Self {
82 self.direction = direction;
83 self
84 }
85
86 #[stability::unstable]
87 pub fn message_name(&mut self, name: impl Into<MessageName>) -> &mut Self {
88 self.message_name = Some(name.into());
89 self
90 }
91
92 #[stability::unstable]
93 pub fn message_protocol(&mut self, protocol: &'static str) -> &mut Self {
94 self.message_protocol = protocol;
95 self
96 }
97
98 #[stability::unstable]
99 pub fn message_kind(&mut self, kind: MessageKind) -> &mut Self {
100 self.message_kind = kind;
101 self
102 }
103
104 #[stability::unstable]
105 pub fn finish<M>(&mut self, message: M) -> Dump
106 where
107 M: Serialize + Send + 'static,
108 {
109 if self.message_name.is_none() {
110 self.message_name = Some(extract_name(&message));
112 }
113
114 self.do_finish(smallbox!(message))
115 }
116
117 pub(crate) fn do_finish(&mut self, message: ErasedMessage) -> Dump {
118 let (meta, trace_id, sequence_no) = scope::with(|scope| {
119 (
120 scope.meta().clone(),
121 scope.trace_id(),
122 scope.dumping().next_sequence_no(),
123 )
124 });
125
126 Dump {
127 meta,
128 sequence_no,
129 timestamp: self.timestamp.unwrap_or_else(Timestamp::now),
130 trace_id,
131 thread_id: crate::thread::id(),
132 direction: self.direction,
133 message_name: self.message_name.take().unwrap_or_default(),
134 message_protocol: self.message_protocol,
135 message_kind: self.message_kind,
136 message,
137 }
138 }
139}
140
141#[message(part, elfo = crate)]
146#[derive(Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
147#[stability::unstable]
148pub struct Timestamp(u64);
149
150impl Timestamp {
151 #[cfg(not(test))]
152 #[inline]
153 #[stability::unstable]
154 pub fn now() -> Self {
155 SystemTime::now().into()
156 }
157
158 #[cfg(test)]
159 pub fn now() -> Self {
160 Self(42)
161 }
162
163 #[inline]
164 pub fn from_nanos(ns: u64) -> Self {
165 Self(ns)
166 }
167}
168
169impl From<SystemTime> for Timestamp {
170 fn from(sys_time: SystemTime) -> Self {
171 let ns = sys_time
172 .duration_since(SystemTime::UNIX_EPOCH)
173 .unwrap_or_default()
174 .as_nanos() as u64;
175
176 Self(ns)
177 }
178}
179
180#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
183#[stability::unstable]
184pub enum Direction {
185 In,
186 Out,
187}
188
189#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
192#[stability::unstable]
193pub struct MessageName(&'static str, Option<&'static str>);
194
195impl<'a> PartialEq<&'a str> for MessageName {
196 fn eq(&self, s: &&'a str) -> bool {
197 if let Some(variant) = self.1 {
198 s.split_once("::")
199 .map_or(false, |(n, v)| n == self.0 && v == variant)
200 } else {
201 self.0 == *s
202 }
203 }
204}
205
206impl PartialEq<MessageName> for &'_ str {
207 fn eq(&self, s: &MessageName) -> bool {
208 s == self
209 }
210}
211
212impl fmt::Display for MessageName {
213 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214 f.write_str(self.0)?;
215
216 if let Some(variant) = self.1 {
217 f.write_str("::")?;
218 f.write_str(variant)?;
219 }
220
221 Ok(())
222 }
223}
224
225impl From<&'static str> for MessageName {
226 #[inline]
227 fn from(struct_name: &'static str) -> Self {
228 Self(struct_name, None)
229 }
230}
231
232impl From<(&'static str, &'static str)> for MessageName {
233 #[inline]
234 fn from((enum_name, variant): (&'static str, &'static str)) -> Self {
235 Self(enum_name, Some(variant))
236 }
237}
238
239impl From<MessageName> for Cow<'static, str> {
241 #[inline]
242 fn from(name: MessageName) -> Self {
243 Self::from(&name)
244 }
245}
246
247impl From<&MessageName> for Cow<'static, str> {
248 #[inline]
249 fn from(name: &MessageName) -> Self {
250 match name.1 {
251 Some(variant) => {
252 let mut string = String::with_capacity(name.0.len() + 2 + variant.len());
253 string.push_str(name.0);
254 string.push_str("::");
255 string.push_str(variant);
256 Self::Owned(string)
257 }
258 None => Self::Borrowed(name.0),
259 }
260 }
261}
262
263impl MessageName {
264 #[doc(hidden)]
265 #[stability::unstable]
266 pub fn to_str<'a>(&self, buffer: &'a mut String) -> &'a str {
267 if let Some(variant) = self.1 {
268 buffer.clear();
269 buffer.push_str(self.0);
270 buffer.push_str("::");
271 buffer.push_str(variant);
272 &buffer[..]
273 } else {
274 self.0
275 }
276 }
277}
278
279#[derive(Debug, Clone, Copy, PartialEq, Eq)]
282#[stability::unstable]
283pub enum MessageKind {
284 Regular,
285 Request(u64),
286 Response(u64),
287}
288
289impl MessageKind {
290 pub(crate) fn from_message_kind(kind: &crate::envelope::MessageKind) -> Self {
291 use slotmap::Key;
292
293 use crate::envelope::MessageKind as MK;
294
295 match kind {
296 MK::Regular { .. } => Self::Regular,
297 MK::RequestAny(token) | MK::RequestAll(token) => {
298 Self::Request(token.request_id.data().as_ffi())
299 }
300 MK::Response { request_id, .. } => Self::Response(request_id.data().as_ffi()),
301 }
302 }
303}
304
305#[test]
306fn message_name() {
307 assert_eq!(MessageName::from("A"), "A");
308 assert_ne!(MessageName::from("A"), "AB");
309 assert_eq!(MessageName::from(("A", "B")), "A::B");
310 assert_ne!(MessageName::from(("A", "B")), "AB::B");
311 assert_eq!("A", MessageName::from("A"), "A");
312
313 assert_eq!(Cow::from(MessageName::from("A")), "A");
314 assert_eq!(Cow::from(MessageName::from(("A", "B"))), "A::B");
315
316 let mut buf = String::new();
317 assert_eq!(MessageName::from("A").to_str(&mut buf), "A");
318 assert_eq!(MessageName::from(("A", "B")).to_str(&mut buf), "A::B");
319
320 assert_eq!(MessageName::from("A").to_string(), "A");
321 assert_eq!(MessageName::from(("A", "B")).to_string(), "A::B");
322}