use core::fmt;
#[cfg(feature = "alloc")]
mod attribution;
mod counts;
#[cfg(feature = "alloc")]
pub use attribution::{
AttributingFlowObserver, AttributingFlowObserverCreationError, FlowAttribution,
FlowAttributionFamily, FlowAttributionPhase,
};
pub use counts::{CountingFlowObserver, ExecutionCounts, TransitionCounts};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[must_use = "flow observations must be recorded or explicitly discarded"]
pub enum FlowObservation {
CircuitTurnBegan {
circuit: &'static str,
input: &'static str,
input_representation_bytes: usize,
},
CircuitTurnCompleted {
circuit: &'static str,
output: &'static str,
output_representation_bytes: usize,
},
PipeSendBegan {
participant: &'static str,
input: &'static str,
transport: &'static str,
input_representation_bytes: usize,
},
PipeSendCompleted {
participant: &'static str,
transport: &'static str,
outcome: &'static str,
output_representation_bytes: usize,
},
ParticipantStepBegan {
participant: &'static str,
input: &'static str,
input_representation_bytes: usize,
},
ParticipantStepCompleted {
participant: &'static str,
outcome: &'static str,
output_representation_bytes: usize,
},
SwitchboardRouteBegan {
switchboard: &'static str,
input: &'static str,
input_representation_bytes: usize,
},
SwitchboardRouteCompleted {
switchboard: &'static str,
output: &'static str,
output_representation_bytes: usize,
},
}
pub trait FlowObserver {
fn observe(&mut self, observation: FlowObservation);
}
impl<Observer: FlowObserver + ?Sized> FlowObserver for &mut Observer {
#[inline(always)]
fn observe(&mut self, observation: FlowObservation) {
(**self).observe(observation);
}
}
impl<First: FlowObserver, Second: FlowObserver> FlowObserver for (First, Second) {
fn observe(&mut self, observation: FlowObservation) {
self.0.observe(observation);
self.1.observe(observation);
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct NoopFlowObserver;
impl FlowObserver for NoopFlowObserver {
#[inline(always)]
fn observe(&mut self, _observation: FlowObservation) {}
}
impl fmt::Display for FlowObservation {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::CircuitTurnBegan {
circuit,
input,
input_representation_bytes,
} => write!(
formatter,
"circuit-turn-began circuit={circuit} input={input} input-representation-bytes={input_representation_bytes}"
),
Self::CircuitTurnCompleted {
circuit,
output,
output_representation_bytes,
} => write!(
formatter,
"circuit-turn-completed circuit={circuit} output={output} output-representation-bytes={output_representation_bytes}"
),
Self::PipeSendBegan {
participant,
input,
transport,
input_representation_bytes,
} => write!(
formatter,
"pipe-send-began participant={participant} input={input} transport={transport} input-representation-bytes={input_representation_bytes}"
),
Self::PipeSendCompleted {
participant,
transport,
outcome,
output_representation_bytes,
} => write!(
formatter,
"pipe-send-completed participant={participant} transport={transport} outcome={outcome} output-representation-bytes={output_representation_bytes}"
),
Self::ParticipantStepBegan {
participant,
input,
input_representation_bytes,
} => write!(
formatter,
"participant-step-began participant={participant} input={input} input-representation-bytes={input_representation_bytes}"
),
Self::ParticipantStepCompleted {
participant,
outcome,
output_representation_bytes,
} => write!(
formatter,
"participant-step-completed participant={participant} outcome={outcome} output-representation-bytes={output_representation_bytes}"
),
Self::SwitchboardRouteBegan {
switchboard,
input,
input_representation_bytes,
} => write!(
formatter,
"switchboard-route-began switchboard={switchboard} input={input} input-representation-bytes={input_representation_bytes}"
),
Self::SwitchboardRouteCompleted {
switchboard,
output,
output_representation_bytes,
} => write!(
formatter,
"switchboard-route-completed switchboard={switchboard} output={output} output-representation-bytes={output_representation_bytes}"
),
}
}
}
#[cfg(test)]
mod tests {
extern crate alloc;
use alloc::string::ToString as _;
use core::mem::size_of;
use super::*;
use crate::PipeFlow;
#[test]
fn every_observation_has_a_flat_display_form() {
let observations = [
FlowObservation::CircuitTurnBegan {
circuit: "circuit",
input: "input",
input_representation_bytes: 1,
},
FlowObservation::CircuitTurnCompleted {
circuit: "circuit",
output: "output",
output_representation_bytes: 2,
},
FlowObservation::PipeSendBegan {
participant: "machine",
input: "input",
transport: "inline",
input_representation_bytes: 3,
},
FlowObservation::PipeSendCompleted {
participant: "machine",
transport: "inline",
outcome: "output",
output_representation_bytes: 4,
},
FlowObservation::ParticipantStepBegan {
participant: "machine",
input: "input",
input_representation_bytes: 5,
},
FlowObservation::ParticipantStepCompleted {
participant: "machine",
outcome: "output",
output_representation_bytes: 6,
},
FlowObservation::SwitchboardRouteBegan {
switchboard: "switchboard",
input: "input",
input_representation_bytes: 7,
},
FlowObservation::SwitchboardRouteCompleted {
switchboard: "switchboard",
output: "output",
output_representation_bytes: 8,
},
];
let displayed = observations.map(|observation| observation.to_string());
assert_eq!(
[
"circuit-turn-began circuit=circuit input=input input-representation-bytes=1",
"circuit-turn-completed circuit=circuit output=output output-representation-bytes=2",
"pipe-send-began participant=machine input=input transport=inline input-representation-bytes=3",
"pipe-send-completed participant=machine transport=inline outcome=output output-representation-bytes=4",
"participant-step-began participant=machine input=input input-representation-bytes=5",
"participant-step-completed participant=machine outcome=output output-representation-bytes=6",
"switchboard-route-began switchboard=switchboard input=input input-representation-bytes=7",
"switchboard-route-completed switchboard=switchboard output=output output-representation-bytes=8",
],
displayed
);
}
#[test]
fn borrowed_and_noop_observers_accept_observations() {
let observation = FlowObservation::ParticipantStepCompleted {
participant: "machine",
outcome: "output",
output_representation_bytes: 0,
};
let mut noop = NoopFlowObserver;
let borrowed = &mut noop;
borrowed.observe(observation);
}
#[test]
fn observer_pairs_receive_the_same_observation() {
let observation = FlowObservation::ParticipantStepCompleted {
participant: "machine",
outcome: "output",
output_representation_bytes: 7,
};
let mut observers = (CountingFlowObserver::new(), CountingFlowObserver::new());
observers.observe(observation);
assert_eq!(observers.0.counts(), observers.1.counts());
}
#[test]
fn noop_flow_observation_carries_no_runtime_state() {
assert_eq!(size_of::<NoopFlowObserver>(), 0);
assert_eq!(size_of::<PipeFlow<NoopFlowObserver>>(), 0);
}
}