1use crate::experiment::CostPerformancePoint;
4use crate::recipes::RecipeResult;
5use std::collections::HashMap;
6
7#[derive(Debug)]
9pub struct CiCdBenchmarkRecipe {
10 pub(crate) name: String,
12 pub(crate) thresholds: HashMap<String, f64>,
14 results: Vec<CostPerformancePoint>,
16}
17
18impl CiCdBenchmarkRecipe {
19 pub fn new(name: impl Into<String>) -> Self {
21 Self { name: name.into(), thresholds: HashMap::new(), results: Vec::new() }
22 }
23
24 pub fn add_min_performance_threshold(&mut self, metric: impl Into<String>, min_value: f64) {
26 self.thresholds.insert(format!("min_{}", metric.into()), min_value);
27 }
28
29 pub fn add_max_cost_threshold(&mut self, max_cost: f64) {
31 self.thresholds.insert("max_cost".to_string(), max_cost);
32 }
33
34 pub fn add_result(&mut self, point: CostPerformancePoint) {
36 self.results.push(point);
37 }
38
39 pub fn check(&self) -> RecipeResult {
41 let mut result = RecipeResult::success(&self.name);
42 let mut all_passed = true;
43
44 for point in &self.results {
45 if let Some(&max_cost) = self.thresholds.get("max_cost") {
47 if point.cost > max_cost {
48 all_passed = false;
49 result = result
50 .with_metric(format!("{}_cost_exceeded", point.id), point.cost - max_cost);
51 }
52 }
53
54 for (key, &threshold) in &self.thresholds {
56 if let Some(metric_name) = key.strip_prefix("min_") {
57 if metric_name == "performance" && point.performance < threshold {
58 all_passed = false;
59 result = result.with_metric(
60 format!("{}_performance_below_threshold", point.id),
61 threshold - point.performance,
62 );
63 }
64 }
65 }
66 }
67
68 result = result.with_metric("all_checks_passed", if all_passed { 1.0 } else { 0.0 });
69
70 if !all_passed {
71 result.success = false;
72 result.error = Some("One or more threshold checks failed".to_string());
73 }
74
75 result
76 }
77}