elfo_core/dumping/
dump.rs

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// === Dump ===
13
14#[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// === DumpBuilder ===
63
64#[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            // If the simplest serializer fails, the actual serialization will fail too.
111            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// === Timestamp ===
142
143// TODO: move to `time`.
144/// Timestamp in nanos since Unix epoch.
145#[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// === Direction ===
181
182#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
183#[stability::unstable]
184pub enum Direction {
185    In,
186    Out,
187}
188
189// === MessageName ===
190
191#[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
239/// Useful for metrics.
240impl 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// === MessageKind ===
280
281#[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}