Skip to main content

elfo_core/dumping/
dump.rs

1use std::{borrow::Cow, fmt, sync::Arc};
2
3use erased_serde::Serialize as ErasedSerialize;
4use serde::Serialize;
5use smallbox::{smallbox, SmallBox};
6
7use elfo_utils::time::SystemTime;
8
9use super::{extract_name::extract_name, sequence_no::SequenceNo};
10use crate::{actor::ActorMeta, envelope, scope, thread::ThreadId, tracing::TraceId, Message};
11
12// === Dump ===
13
14#[doc(hidden)]
15#[instability::unstable]
16pub struct Dump {
17    pub meta: Arc<ActorMeta>,
18    pub sequence_no: SequenceNo,
19    pub timestamp: SystemTime,
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#[instability::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    #[instability::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(
49        message: &impl Message,
50        kind: &envelope::MessageKind,
51        direction: Direction,
52    ) -> Self {
53        Self::builder()
54            .direction(direction)
55            .message_name(message.name())
56            .message_protocol(message.protocol())
57            .message_kind(MessageKind::from_message_kind(kind))
58            .do_finish(message._erase())
59    }
60}
61
62// === DumpBuilder ===
63
64#[instability::unstable]
65pub struct DumpBuilder {
66    timestamp: Option<SystemTime>,
67    direction: Direction,
68    message_name: Option<MessageName>,
69    message_protocol: &'static str,
70    message_kind: MessageKind,
71}
72
73impl DumpBuilder {
74    #[instability::unstable]
75    pub fn timestamp(&mut self, timestamp: impl Into<SystemTime>) -> &mut Self {
76        self.timestamp = Some(timestamp.into());
77        self
78    }
79
80    #[instability::unstable]
81    pub fn direction(&mut self, direction: Direction) -> &mut Self {
82        self.direction = direction;
83        self
84    }
85
86    #[instability::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    #[instability::unstable]
93    pub fn message_protocol(&mut self, protocol: &'static str) -> &mut Self {
94        self.message_protocol = protocol;
95        self
96    }
97
98    #[instability::unstable]
99    pub fn message_kind(&mut self, kind: MessageKind) -> &mut Self {
100        self.message_kind = kind;
101        self
102    }
103
104    #[instability::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    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(SystemTime::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// === Direction ===
142
143#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
144#[instability::unstable]
145pub enum Direction {
146    In,
147    Out,
148}
149
150// === MessageName ===
151
152#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
153#[instability::unstable]
154pub struct MessageName(&'static str, Option<&'static str>);
155
156impl<'a> PartialEq<&'a str> for MessageName {
157    fn eq(&self, s: &&'a str) -> bool {
158        if let Some(variant) = self.1 {
159            s.split_once("::")
160                .is_some_and(|(n, v)| n == self.0 && v == variant)
161        } else {
162            self.0 == *s
163        }
164    }
165}
166
167impl PartialEq<MessageName> for &'_ str {
168    fn eq(&self, s: &MessageName) -> bool {
169        s == self
170    }
171}
172
173impl fmt::Display for MessageName {
174    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
175        f.write_str(self.0)?;
176
177        if let Some(variant) = self.1 {
178            f.write_str("::")?;
179            f.write_str(variant)?;
180        }
181
182        Ok(())
183    }
184}
185
186impl From<&'static str> for MessageName {
187    #[inline]
188    fn from(struct_name: &'static str) -> Self {
189        Self(struct_name, None)
190    }
191}
192
193impl From<(&'static str, &'static str)> for MessageName {
194    #[inline]
195    fn from((enum_name, variant): (&'static str, &'static str)) -> Self {
196        Self(enum_name, Some(variant))
197    }
198}
199
200/// Useful for metrics.
201impl From<MessageName> for Cow<'static, str> {
202    #[inline]
203    fn from(name: MessageName) -> Self {
204        Self::from(&name)
205    }
206}
207
208impl From<&MessageName> for Cow<'static, str> {
209    #[inline]
210    fn from(name: &MessageName) -> Self {
211        match name.1 {
212            Some(variant) => {
213                let mut string = String::with_capacity(name.0.len() + 2 + variant.len());
214                string.push_str(name.0);
215                string.push_str("::");
216                string.push_str(variant);
217                Self::Owned(string)
218            }
219            None => Self::Borrowed(name.0),
220        }
221    }
222}
223
224impl MessageName {
225    #[doc(hidden)]
226    #[instability::unstable]
227    pub fn to_str<'a>(&self, buffer: &'a mut String) -> &'a str {
228        if let Some(variant) = self.1 {
229            buffer.clear();
230            buffer.push_str(self.0);
231            buffer.push_str("::");
232            buffer.push_str(variant);
233            &buffer[..]
234        } else {
235            self.0
236        }
237    }
238}
239
240// === MessageKind ===
241
242#[derive(Debug, Clone, Copy, PartialEq, Eq)]
243#[instability::unstable]
244pub enum MessageKind {
245    Regular,
246    Request(u64),
247    Response(u64),
248}
249
250impl MessageKind {
251    pub(crate) fn from_message_kind(kind: &crate::envelope::MessageKind) -> Self {
252        use slotmap::Key;
253
254        use crate::envelope::MessageKind as MK;
255
256        match kind {
257            MK::Regular { .. } => Self::Regular,
258            MK::RequestAny(token) | MK::RequestAll(token) => {
259                Self::Request(token.request_id().to_ffi())
260            }
261            MK::Response { request_id, .. } => Self::Response(request_id.data().as_ffi()),
262        }
263    }
264}
265
266#[test]
267fn message_name() {
268    assert_eq!(MessageName::from("A"), "A");
269    assert_ne!(MessageName::from("A"), "AB");
270    assert_eq!(MessageName::from(("A", "B")), "A::B");
271    assert_ne!(MessageName::from(("A", "B")), "AB::B");
272    assert_eq!("A", MessageName::from("A"), "A");
273
274    assert_eq!(Cow::from(MessageName::from("A")), "A");
275    assert_eq!(Cow::from(MessageName::from(("A", "B"))), "A::B");
276
277    let mut buf = String::new();
278    assert_eq!(MessageName::from("A").to_str(&mut buf), "A");
279    assert_eq!(MessageName::from(("A", "B")).to_str(&mut buf), "A::B");
280
281    assert_eq!(MessageName::from("A").to_string(), "A");
282    assert_eq!(MessageName::from(("A", "B")).to_string(), "A::B");
283}