Skip to main content

batuta/recipes/
cicd.rs

1//! CI/CD integration recipe for cost-performance benchmarking.
2
3use crate::experiment::CostPerformancePoint;
4use crate::recipes::RecipeResult;
5use std::collections::HashMap;
6
7/// CI/CD integration recipe for cost-performance benchmarking
8#[derive(Debug)]
9pub struct CiCdBenchmarkRecipe {
10    /// Benchmark name
11    pub(crate) name: String,
12    /// Threshold checks
13    pub(crate) thresholds: HashMap<String, f64>,
14    /// Results from runs
15    results: Vec<CostPerformancePoint>,
16}
17
18impl CiCdBenchmarkRecipe {
19    /// Create a new CI/CD benchmark recipe
20    pub fn new(name: impl Into<String>) -> Self {
21        Self { name: name.into(), thresholds: HashMap::new(), results: Vec::new() }
22    }
23
24    /// Add a performance threshold (fails if below)
25    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    /// Add a cost threshold (fails if above)
30    pub fn add_max_cost_threshold(&mut self, max_cost: f64) {
31        self.thresholds.insert("max_cost".to_string(), max_cost);
32    }
33
34    /// Add a result
35    pub fn add_result(&mut self, point: CostPerformancePoint) {
36        self.results.push(point);
37    }
38
39    /// Check thresholds and return CI/CD result
40    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            // Check cost threshold
46            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            // Check performance thresholds
55            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}