use core::time::Duration;
use emit_core::{
and::And,
ctxt::Ctxt,
emitter::Emitter,
empty::Empty,
filter::Filter,
runtime::{AmbientRuntime, AmbientSlot, InternalCtxt, InternalEmitter, InternalFilter},
};
use crate::platform::{self, Platform};
pub fn setup() -> Setup {
Setup::default()
}
pub use platform::{DefaultClock, DefaultCtxt, DefaultRng};
pub type DefaultEmitter = Empty;
pub type DefaultFilter = Empty;
#[must_use = "call `.init()` to finish setup"]
pub struct Setup<TEmitter = DefaultEmitter, TFilter = DefaultFilter, TCtxt = DefaultCtxt> {
emitter: TEmitter,
filter: TFilter,
ctxt: TCtxt,
platform: Platform,
}
impl Default for Setup {
fn default() -> Self {
Self::new()
}
}
impl Setup {
pub fn new() -> Self {
Setup {
emitter: Default::default(),
filter: Default::default(),
ctxt: Default::default(),
platform: Default::default(),
}
}
}
impl<TEmitter: Emitter, TFilter: Filter, TCtxt: Ctxt> Setup<TEmitter, TFilter, TCtxt> {
pub fn emit_to<UEmitter: Emitter>(self, emitter: UEmitter) -> Setup<UEmitter, TFilter, TCtxt> {
Setup {
emitter,
filter: self.filter,
ctxt: self.ctxt,
platform: self.platform,
}
}
pub fn and_emit_to<UEmitter: Emitter>(
self,
emitter: UEmitter,
) -> Setup<And<TEmitter, UEmitter>, TFilter, TCtxt> {
Setup {
emitter: self.emitter.and_to(emitter),
filter: self.filter,
ctxt: self.ctxt,
platform: self.platform,
}
}
pub fn map_emitter<UEmitter: Emitter>(
self,
map: impl FnOnce(TEmitter) -> UEmitter,
) -> Setup<UEmitter, TFilter, TCtxt> {
Setup {
emitter: map(self.emitter),
filter: self.filter,
ctxt: self.ctxt,
platform: self.platform,
}
}
pub fn emit_when<UFilter: Filter>(self, filter: UFilter) -> Setup<TEmitter, UFilter, TCtxt> {
Setup {
emitter: self.emitter,
filter,
ctxt: self.ctxt,
platform: self.platform,
}
}
pub fn and_emit_when<UFilter: Filter>(
self,
filter: UFilter,
) -> Setup<TEmitter, And<TFilter, UFilter>, TCtxt> {
Setup {
emitter: self.emitter,
filter: self.filter.and_when(filter),
ctxt: self.ctxt,
platform: self.platform,
}
}
pub fn with_ctxt<UCtxt: Ctxt>(self, ctxt: UCtxt) -> Setup<TEmitter, TFilter, UCtxt> {
Setup {
emitter: self.emitter,
filter: self.filter,
ctxt,
platform: self.platform,
}
}
pub fn map_ctxt<UCtxt: Ctxt>(
self,
map: impl FnOnce(TCtxt) -> UCtxt,
) -> Setup<TEmitter, TFilter, UCtxt> {
Setup {
emitter: self.emitter,
filter: self.filter,
ctxt: map(self.ctxt),
platform: self.platform,
}
}
}
impl<
TEmitter: Emitter + Send + Sync + 'static,
TFilter: Filter + Send + Sync + 'static,
TCtxt: Ctxt + Send + Sync + 'static,
> Setup<TEmitter, TFilter, TCtxt>
where
TCtxt::Frame: Send + 'static,
{
#[must_use = "call `flush_on_drop` or call `blocking_flush` at the end of `main` to ensure events are flushed."]
#[cfg(feature = "implicit_rt")]
pub fn init(self) -> Init<'static, TEmitter, TCtxt> {
self.init_slot(emit_core::runtime::shared_slot())
}
#[must_use = "call `flush_on_drop` or call `blocking_flush` at the end of `main` to ensure events are flushed."]
#[cfg(feature = "implicit_rt")]
pub fn try_init(self) -> Option<Init<'static, TEmitter, TCtxt>> {
self.try_init_slot(emit_core::runtime::shared_slot())
}
#[must_use = "call `flush_on_drop` or call `blocking_flush` at the end of `main` to ensure events are flushed."]
pub fn init_slot<'a>(self, slot: &'a AmbientSlot) -> Init<'a, TEmitter, TCtxt> {
self.try_init_slot(slot).expect("already initialized")
}
#[must_use = "call `flush_on_drop` or call `blocking_flush` at the end of `main` to ensure events are flushed."]
pub fn try_init_slot<'a>(self, slot: &'a AmbientSlot) -> Option<Init<'a, TEmitter, TCtxt>> {
let ambient = slot.init(
emit_core::runtime::Runtime::new()
.with_emitter(self.emitter)
.with_filter(self.filter)
.with_ctxt(self.ctxt)
.with_clock(self.platform.clock)
.with_rng(self.platform.rng),
)?;
Some(Init {
rt: slot.get(),
emitter: *ambient.emitter(),
ctxt: *ambient.ctxt(),
})
}
}
impl<
TEmitter: InternalEmitter + Send + Sync + 'static,
TFilter: InternalFilter + Send + Sync + 'static,
TCtxt: InternalCtxt + Send + Sync + 'static,
> Setup<TEmitter, TFilter, TCtxt>
where
TCtxt::Frame: Send + 'static,
{
#[must_use = "call `flush_on_drop` or call `blocking_flush` at the end of `main` (after flushing the main runtime) to ensure events are flushed."]
#[cfg(feature = "implicit_internal_rt")]
pub fn init_internal(self) -> Init<'static, TEmitter, TCtxt> {
self.try_init_internal().expect("already initialized")
}
#[must_use = "call `flush_on_drop` or call `blocking_flush` at the end of `main` (after flushing the main runtime) to ensure events are flushed."]
#[cfg(feature = "implicit_internal_rt")]
pub fn try_init_internal(self) -> Option<Init<'static, TEmitter, TCtxt>> {
let slot = emit_core::runtime::internal_slot();
let ambient = slot.init(
emit_core::runtime::Runtime::new()
.with_emitter(self.emitter)
.with_filter(self.filter)
.with_ctxt(self.ctxt)
.with_clock(self.platform.clock)
.with_rng(self.platform.rng),
)?;
Some(Init {
rt: slot.get(),
emitter: *ambient.emitter(),
ctxt: *ambient.ctxt(),
})
}
}
pub struct Init<'a, TEmitter: Emitter + ?Sized = DefaultEmitter, TCtxt: Ctxt + ?Sized = DefaultCtxt>
{
rt: &'a AmbientRuntime<'a>,
emitter: &'a TEmitter,
ctxt: &'a TCtxt,
}
impl<'a, TEmitter: Emitter + ?Sized, TCtxt: Ctxt + ?Sized> Init<'a, TEmitter, TCtxt> {
pub fn emitter(&self) -> &'a TEmitter {
self.emitter
}
pub fn ctxt(&self) -> &'a TCtxt {
self.ctxt
}
pub fn get(&self) -> &'a AmbientRuntime<'a> {
self.rt
}
pub fn blocking_flush(&self, timeout: Duration) -> bool {
self.emitter.blocking_flush(timeout)
}
pub fn flush_on_drop(self, timeout: Duration) -> InitGuard<'a, TEmitter, TCtxt> {
InitGuard {
inner: self,
timeout,
}
}
}
pub struct InitGuard<
'a,
TEmitter: Emitter + ?Sized = DefaultEmitter,
TCtxt: Ctxt + ?Sized = DefaultCtxt,
> {
inner: Init<'a, TEmitter, TCtxt>,
timeout: Duration,
}
impl<'a, TEmitter: Emitter + ?Sized, TCtxt: Ctxt + ?Sized> InitGuard<'a, TEmitter, TCtxt> {
pub fn inner(&self) -> &Init<'a, TEmitter, TCtxt> {
&self.inner
}
}
impl<'a, TEmitter: Emitter + ?Sized, TCtxt: Ctxt + ?Sized> Drop for InitGuard<'a, TEmitter, TCtxt> {
fn drop(&mut self) {
self.inner.blocking_flush(self.timeout);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn try_init() {
let slot = AmbientSlot::new();
assert!(setup().try_init_slot(&slot).is_some());
assert!(setup().try_init_slot(&slot).is_none());
}
}