use std::{borrow::Cow, fmt, sync::Arc, time::SystemTime};
use erased_serde::Serialize as ErasedSerialize;
use serde::Serialize;
use smallbox::{smallbox, SmallBox};
use elfo_macros::message;
use super::{extract_name::extract_name, sequence_no::SequenceNo};
use crate::{actor::ActorMeta, envelope, scope, thread::ThreadId, trace_id::TraceId};
#[doc(hidden)]
#[stability::unstable]
pub struct Dump {
pub meta: Arc<ActorMeta>,
pub sequence_no: SequenceNo,
pub timestamp: Timestamp,
pub trace_id: TraceId,
pub thread_id: ThreadId,
pub direction: Direction,
pub message_name: MessageName,
pub message_protocol: &'static str,
pub message_kind: MessageKind,
pub message: ErasedMessage,
}
#[doc(hidden)]
#[stability::unstable]
pub type ErasedMessage = SmallBox<dyn ErasedSerialize + Send, [usize; 24]>;
assert_impl_all!(Dump: Send);
assert_eq_size!(Dump, [u8; 320]);
impl Dump {
#[stability::unstable]
pub fn builder() -> DumpBuilder {
DumpBuilder {
timestamp: None,
direction: Direction::Out,
message_name: None,
message_protocol: "",
message_kind: MessageKind::Regular,
}
}
pub(crate) fn message<M: crate::Message>(
message: M,
kind: &envelope::MessageKind,
direction: Direction,
) -> Self {
Self::builder()
.direction(direction)
.message_name(M::VTABLE.name)
.message_protocol(M::VTABLE.protocol)
.message_kind(MessageKind::from_message_kind(kind))
.finish(message)
}
}
#[stability::unstable]
pub struct DumpBuilder {
timestamp: Option<Timestamp>,
direction: Direction,
message_name: Option<MessageName>,
message_protocol: &'static str,
message_kind: MessageKind,
}
impl DumpBuilder {
#[stability::unstable]
pub fn timestamp(&mut self, timestamp: impl Into<Timestamp>) -> &mut Self {
self.timestamp = Some(timestamp.into());
self
}
#[stability::unstable]
pub fn direction(&mut self, direction: Direction) -> &mut Self {
self.direction = direction;
self
}
#[stability::unstable]
pub fn message_name(&mut self, name: impl Into<MessageName>) -> &mut Self {
self.message_name = Some(name.into());
self
}
#[stability::unstable]
pub fn message_protocol(&mut self, protocol: &'static str) -> &mut Self {
self.message_protocol = protocol;
self
}
#[stability::unstable]
pub fn message_kind(&mut self, kind: MessageKind) -> &mut Self {
self.message_kind = kind;
self
}
#[stability::unstable]
pub fn finish<M>(&mut self, message: M) -> Dump
where
M: Serialize + Send + 'static,
{
if self.message_name.is_none() {
self.message_name = Some(extract_name(&message));
}
self.do_finish(smallbox!(message))
}
pub(crate) fn do_finish(&mut self, message: ErasedMessage) -> Dump {
let (meta, trace_id, sequence_no) = scope::with(|scope| {
(
scope.meta().clone(),
scope.trace_id(),
scope.dumping().next_sequence_no(),
)
});
Dump {
meta,
sequence_no,
timestamp: self.timestamp.unwrap_or_else(Timestamp::now),
trace_id,
thread_id: crate::thread::id(),
direction: self.direction,
message_name: self.message_name.take().unwrap_or_default(),
message_protocol: self.message_protocol,
message_kind: self.message_kind,
message,
}
}
}
#[message(part, elfo = crate)]
#[derive(Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[stability::unstable]
pub struct Timestamp(u64);
impl Timestamp {
#[cfg(not(test))]
#[inline]
#[stability::unstable]
pub fn now() -> Self {
SystemTime::now().into()
}
#[cfg(test)]
pub fn now() -> Self {
Self(42)
}
#[inline]
pub fn from_nanos(ns: u64) -> Self {
Self(ns)
}
}
impl From<SystemTime> for Timestamp {
fn from(sys_time: SystemTime) -> Self {
let ns = sys_time
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos() as u64;
Self(ns)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[stability::unstable]
pub enum Direction {
In,
Out,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
#[stability::unstable]
pub struct MessageName(&'static str, Option<&'static str>);
impl<'a> PartialEq<&'a str> for MessageName {
fn eq(&self, s: &&'a str) -> bool {
if let Some(variant) = self.1 {
s.split_once("::")
.map_or(false, |(n, v)| n == self.0 && v == variant)
} else {
self.0 == *s
}
}
}
impl PartialEq<MessageName> for &'_ str {
fn eq(&self, s: &MessageName) -> bool {
s == self
}
}
impl fmt::Display for MessageName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.0)?;
if let Some(variant) = self.1 {
f.write_str("::")?;
f.write_str(variant)?;
}
Ok(())
}
}
impl From<&'static str> for MessageName {
#[inline]
fn from(struct_name: &'static str) -> Self {
Self(struct_name, None)
}
}
impl From<(&'static str, &'static str)> for MessageName {
#[inline]
fn from((enum_name, variant): (&'static str, &'static str)) -> Self {
Self(enum_name, Some(variant))
}
}
impl From<MessageName> for Cow<'static, str> {
#[inline]
fn from(name: MessageName) -> Self {
Self::from(&name)
}
}
impl From<&MessageName> for Cow<'static, str> {
#[inline]
fn from(name: &MessageName) -> Self {
match name.1 {
Some(variant) => {
let mut string = String::with_capacity(name.0.len() + 2 + variant.len());
string.push_str(name.0);
string.push_str("::");
string.push_str(variant);
Self::Owned(string)
}
None => Self::Borrowed(name.0),
}
}
}
impl MessageName {
#[doc(hidden)]
#[stability::unstable]
pub fn to_str<'a>(&self, buffer: &'a mut String) -> &'a str {
if let Some(variant) = self.1 {
buffer.clear();
buffer.push_str(self.0);
buffer.push_str("::");
buffer.push_str(variant);
&buffer[..]
} else {
self.0
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[stability::unstable]
pub enum MessageKind {
Regular,
Request(u64),
Response(u64),
}
impl MessageKind {
pub(crate) fn from_message_kind(kind: &crate::envelope::MessageKind) -> Self {
use slotmap::Key;
use crate::envelope::MessageKind as MK;
match kind {
MK::Regular { .. } => Self::Regular,
MK::RequestAny(token) | MK::RequestAll(token) => {
Self::Request(token.request_id.data().as_ffi())
}
MK::Response { request_id, .. } => Self::Response(request_id.data().as_ffi()),
}
}
}
#[test]
fn message_name() {
assert_eq!(MessageName::from("A"), "A");
assert_ne!(MessageName::from("A"), "AB");
assert_eq!(MessageName::from(("A", "B")), "A::B");
assert_ne!(MessageName::from(("A", "B")), "AB::B");
assert_eq!("A", MessageName::from("A"), "A");
assert_eq!(Cow::from(MessageName::from("A")), "A");
assert_eq!(Cow::from(MessageName::from(("A", "B"))), "A::B");
let mut buf = String::new();
assert_eq!(MessageName::from("A").to_str(&mut buf), "A");
assert_eq!(MessageName::from(("A", "B")).to_str(&mut buf), "A::B");
assert_eq!(MessageName::from("A").to_string(), "A");
assert_eq!(MessageName::from(("A", "B")).to_string(), "A::B");
}