use core::{fmt, ops::ControlFlow};
use crate::{
extent::{Extent, ToExtent},
path::Path,
props::{ErasedProps, Props},
template::{Render, Template},
timestamp::Timestamp,
};
#[derive(Clone)]
pub struct Event<'a, P> {
mdl: Path<'a>,
tpl: Template<'a>,
extent: Option<Extent>,
props: P,
}
impl<'a, P> Event<'a, P> {
pub fn new(
mdl: impl Into<Path<'a>>,
tpl: impl Into<Template<'a>>,
extent: impl ToExtent,
props: P,
) -> Self {
Event {
mdl: mdl.into(),
tpl: tpl.into(),
extent: extent.to_extent(),
props,
}
}
pub fn mdl(&self) -> &Path<'a> {
&self.mdl
}
pub fn with_mdl(mut self, mdl: impl Into<Path<'a>>) -> Self {
self.mdl = mdl.into();
self
}
pub fn extent(&self) -> Option<&Extent> {
self.extent.as_ref()
}
pub fn with_extent(mut self, extent: impl ToExtent) -> Self {
self.extent = extent.to_extent();
self
}
pub fn ts(&self) -> Option<&Timestamp> {
self.extent.as_ref().map(|extent| extent.as_point())
}
pub fn ts_start(&self) -> Option<&Timestamp> {
self.extent
.as_ref()
.and_then(|extent| extent.as_range())
.map(|span| &span.start)
}
pub fn tpl(&self) -> &Template<'a> {
&self.tpl
}
pub fn with_tpl(mut self, tpl: impl Into<Template<'a>>) -> Self {
self.tpl = tpl.into();
self
}
pub fn props(&self) -> &P {
&self.props
}
pub fn props_mut(&mut self) -> &mut P {
&mut self.props
}
pub fn with_props<U>(self, props: U) -> Event<'a, U> {
Event {
mdl: self.mdl,
extent: self.extent,
tpl: self.tpl,
props,
}
}
pub fn map_props<U>(self, map: impl FnOnce(P) -> U) -> Event<'a, U> {
Event {
mdl: self.mdl,
extent: self.extent,
tpl: self.tpl,
props: map(self.props),
}
}
}
impl<'a, P: Props> Event<'a, P> {
pub fn msg(&self) -> Render<'_, &P> {
self.tpl.render(&self.props)
}
pub fn by_ref<'b>(&'b self) -> Event<'b, &'b P> {
Event {
mdl: self.mdl.by_ref(),
extent: self.extent.clone(),
tpl: self.tpl.by_ref(),
props: &self.props,
}
}
pub fn erase<'b>(&'b self) -> Event<'b, &'b dyn ErasedProps> {
Event {
mdl: self.mdl.by_ref(),
extent: self.extent.clone(),
tpl: self.tpl.by_ref(),
props: &self.props,
}
}
}
impl<'a, P: Props> fmt::Debug for Event<'a, P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
struct AsDebug<T>(T);
impl<T: Props> fmt::Debug for AsDebug<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut f = f.debug_map();
let _ = self.0.for_each(|k, v| {
f.key(&k.get());
f.value(&v);
ControlFlow::Continue(())
});
f.finish()
}
}
let mut f = f.debug_struct("Event");
f.field("mdl", &self.mdl);
f.field("tpl", &self.tpl);
f.field("extent", &self.extent);
f.field("props", &AsDebug(&self.props));
f.finish()
}
}
pub trait ToEvent {
type Props<'a>: Props
where
Self: 'a;
fn to_event<'a>(&'a self) -> Event<'a, Self::Props<'a>>;
}
impl<'a, T: ToEvent + ?Sized> ToEvent for &'a T {
type Props<'b>
= T::Props<'b>
where
Self: 'b;
fn to_event<'b>(&'b self) -> Event<'b, Self::Props<'b>> {
(**self).to_event()
}
}
impl<'a, P: Props> ToEvent for Event<'a, P> {
type Props<'b>
= &'b P
where
Self: 'b;
fn to_event<'b>(&'b self) -> Event<'b, Self::Props<'b>> {
self.by_ref()
}
}
#[cfg(feature = "alloc")]
mod alloc_support {
use super::*;
use crate::props::OwnedProps;
impl<'a, P: Props> Event<'a, P> {
pub fn to_owned(&self) -> Event<'static, OwnedProps> {
Event {
mdl: self.mdl.to_owned(),
extent: self.extent.clone(),
tpl: self.tpl.to_owned(),
props: OwnedProps::collect_owned(&self.props),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn owned_event_is_send_sync() {
fn check<T: Send + Sync>() {}
check::<Event<'static, OwnedProps>>();
}
}
}
#[cfg(test)]
mod tests {
use crate::{str::Str, value::Value};
use super::*;
#[test]
fn event_new() {
let evt = Event::new(
Path::new_raw("module"),
Template::literal("An event"),
Extent::range(Timestamp::MIN..Timestamp::MAX),
[
("a", Value::from(true)),
("b", Value::from(1)),
("c", Value::from("string")),
],
);
fn assert(evt: &Event<impl Props>) {
assert_eq!(Path::new_raw("module"), evt.mdl());
assert_eq!(
Timestamp::MIN..Timestamp::MAX,
evt.extent().unwrap().as_range().unwrap().clone()
);
assert_eq!("An event", evt.tpl().as_literal().unwrap());
assert_eq!(true, evt.props().pull::<bool, _>("a").unwrap());
assert_eq!(1, evt.props().pull::<i32, _>("b").unwrap());
assert_eq!("string", evt.props().pull::<Str, _>("c").unwrap());
}
assert(&evt);
assert(&evt.by_ref());
assert(&evt.erase());
#[cfg(feature = "alloc")]
{
assert(&evt.to_owned());
}
}
}