extern crate alloc;
use alloc::{string::String, sync::Arc, vec::Vec};
use crate::profiling::StageMetrics;
use crate::StageKind;
#[derive(Debug)]
pub struct StageEntry {
pub metrics: Arc<StageMetrics>,
pub name: Option<String>,
}
impl StageEntry {
fn new() -> Self {
Self {
metrics: Arc::new(StageMetrics::new()),
name: None,
}
}
}
#[derive(Debug, Default)]
pub struct RecordProfilingMetrics {
sources: Vec<StageEntry>,
taps: Vec<StageEntry>,
links: Vec<StageEntry>,
#[allow(dead_code)]
transforms: Vec<StageEntry>,
}
impl RecordProfilingMetrics {
pub fn new() -> Self {
Self::default()
}
pub fn is_empty(&self) -> bool {
self.sources.is_empty()
&& self.taps.is_empty()
&& self.links.is_empty()
&& self.transforms.is_empty()
}
pub fn push_source(&mut self) -> (usize, Arc<StageMetrics>) {
Self::push(&mut self.sources)
}
pub fn push_tap(&mut self) -> (usize, Arc<StageMetrics>) {
Self::push(&mut self.taps)
}
pub fn push_link(&mut self) -> (usize, Arc<StageMetrics>) {
Self::push(&mut self.links)
}
fn push(vec: &mut Vec<StageEntry>) -> (usize, Arc<StageMetrics>) {
let idx = vec.len();
let entry = StageEntry::new();
let metrics = entry.metrics.clone();
vec.push(entry);
(idx, metrics)
}
pub fn source(&self, idx: usize) -> Option<&StageEntry> {
self.sources.get(idx)
}
pub fn tap(&self, idx: usize) -> Option<&StageEntry> {
self.taps.get(idx)
}
pub fn link(&self, idx: usize) -> Option<&StageEntry> {
self.links.get(idx)
}
pub fn tap_count(&self) -> usize {
self.taps.len()
}
pub fn sources(&self) -> &[StageEntry] {
&self.sources
}
pub fn taps(&self) -> &[StageEntry] {
&self.taps
}
pub fn links(&self) -> &[StageEntry] {
&self.links
}
pub fn set_stage_name(&mut self, kind: StageKind, idx: usize, name: &str) {
let vec = match kind {
StageKind::Source => &mut self.sources,
StageKind::Tap => &mut self.taps,
StageKind::Link => &mut self.links,
StageKind::Transform => &mut self.transforms,
};
if let Some(entry) = vec.get_mut(idx) {
entry.name = Some(String::from(name));
}
}
pub fn reset_all(&self) {
for e in self
.sources
.iter()
.chain(&self.taps)
.chain(&self.links)
.chain(&self.transforms)
{
e.metrics.reset();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn push_assigns_sequential_indices() {
let mut m = RecordProfilingMetrics::new();
assert!(m.is_empty());
let (i0, _) = m.push_tap();
let (i1, _) = m.push_tap();
assert_eq!((i0, i1), (0, 1));
assert_eq!(m.tap_count(), 2);
assert!(!m.is_empty());
assert!(m.tap(0).is_some());
assert!(m.tap(2).is_none());
}
#[test]
fn set_stage_name() {
let mut m = RecordProfilingMetrics::new();
let _ = m.push_source();
m.set_stage_name(StageKind::Source, 0, "sensor_reader");
assert_eq!(m.source(0).unwrap().name.as_deref(), Some("sensor_reader"));
m.set_stage_name(StageKind::Source, 5, "ignored");
}
#[test]
fn reset_all_clears_metrics() {
let mut m = RecordProfilingMetrics::new();
let (_, src) = m.push_source();
let (_, tap) = m.push_tap();
src.record(10);
tap.record(20);
m.reset_all();
assert_eq!(src.call_count(), 0);
assert_eq!(tap.call_count(), 0);
}
}