spatialrust_platform/
budget.rs1use crate::{PlatformError, PlatformResult};
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
7pub enum BudgetKind {
8 LatencyMicros,
10 LatencyMillis,
12 BytesCopied,
14 MemoryBytes,
16 AllocationCount,
18 ThreadCount,
20}
21
22#[derive(Clone, Debug, PartialEq, Eq)]
24pub struct PerformanceBudget {
25 pub id: String,
27 pub kind: BudgetKind,
29 pub ceiling: u64,
31}
32
33#[derive(Clone, Debug, PartialEq, Eq)]
35pub struct PerformanceSample {
36 pub budget_id: String,
38 pub observed: u64,
40}
41
42#[derive(Clone, Debug, Default, PartialEq, Eq)]
44pub struct PerformanceBudgetReport {
45 budgets: Vec<PerformanceBudget>,
46 samples: Vec<PerformanceSample>,
47}
48
49impl PerformanceBudgetReport {
50 #[must_use]
52 pub fn new() -> Self {
53 Self::default()
54 }
55
56 pub fn declare(&mut self, budget: PerformanceBudget) {
58 self.budgets.push(budget);
59 }
60
61 pub fn sample(&mut self, budget_id: impl Into<String>, observed: u64) {
63 self.samples.push(PerformanceSample { budget_id: budget_id.into(), observed });
64 }
65
66 #[must_use]
68 pub fn budgets(&self) -> &[PerformanceBudget] {
69 &self.budgets
70 }
71
72 #[must_use]
74 pub fn samples(&self) -> &[PerformanceSample] {
75 &self.samples
76 }
77
78 pub fn assert_within_budgets(&self) -> PlatformResult<()> {
80 for sample in &self.samples {
81 let Some(budget) = self.budgets.iter().find(|b| b.id == sample.budget_id) else {
82 return Err(PlatformError::InvalidConfiguration(format!(
83 "sample references unknown budget `{}`",
84 sample.budget_id
85 )));
86 };
87 if sample.observed > budget.ceiling {
88 return Err(PlatformError::BudgetExceeded {
89 budget_id: budget.id.clone(),
90 observed: sample.observed,
91 ceiling: budget.ceiling,
92 });
93 }
94 }
95 Ok(())
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::{BudgetKind, PerformanceBudget, PerformanceBudgetReport};
102
103 #[test]
104 fn rejects_over_ceiling() {
105 let mut report = PerformanceBudgetReport::new();
106 report.declare(PerformanceBudget {
107 id: "latency".into(),
108 kind: BudgetKind::LatencyMillis,
109 ceiling: 100,
110 });
111 report.sample("latency", 50);
112 assert!(report.assert_within_budgets().is_ok());
113 report.sample("latency", 101);
114 assert!(report.assert_within_budgets().is_err());
115 }
116}