precept/
dispatch.rs

1use std::sync::OnceLock;
2
3use noop::NoopDispatch;
4
5use crate::catalog::CatalogEntry;
6
7pub mod noop;
8pub mod test;
9
10#[cfg(feature = "antithesis")]
11pub mod antithesis;
12
13pub enum Event {
14    RegisterEntry(&'static CatalogEntry),
15    EmitEntry {
16        entry: &'static CatalogEntry,
17        condition: bool,
18        details: serde_json::Value,
19    },
20    SetupComplete {
21        details: serde_json::Value,
22    },
23    Custom {
24        name: &'static str,
25        value: serde_json::Value,
26    },
27}
28
29pub trait Dispatch: Sync + Send {
30    fn emit(&self, event: Event);
31    fn random(&self) -> u64;
32}
33
34static DISPATCHER: OnceLock<&'static dyn Dispatch> = OnceLock::new();
35
36#[derive(Debug)]
37pub struct SetDispatchError;
38
39impl std::error::Error for SetDispatchError {}
40
41impl std::fmt::Display for SetDispatchError {
42    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
43        fmt.write_str("attempted to set the dispatcher after it was already set")
44    }
45}
46
47/// Set the DISPATCHER to the provided dispatcher instance. This function will
48/// fail if called multiple times.
49pub fn set_dispatcher(dispatcher: &'static dyn Dispatch) -> Result<(), SetDispatchError> {
50    DISPATCHER.set(dispatcher).map_err(|_| SetDispatchError)
51}
52
53/// Returns a reference to the dispatcher.
54///
55/// If a dispatcher has not been set, a no-op implementation is returned.
56pub fn dispatcher() -> &'static dyn Dispatch {
57    match DISPATCHER.get() {
58        Some(dispatch) => *dispatch,
59        None => {
60            static NOOP: NoopDispatch = NoopDispatch;
61            &NOOP
62        }
63    }
64}
65
66/// Generate a random u64 using the dispatcher
67#[inline]
68pub fn get_random() -> u64 {
69    dispatcher().random()
70}
71
72/// Emit an event using the dispatcher
73#[inline]
74pub fn emit(event: Event) {
75    dispatcher().emit(event);
76}