use std::collections::HashMap;
use super::state::ServiceEnergy;
#[must_use]
#[allow(clippy::implicit_hasher)]
pub(crate) fn build_chassis_services(
service_mappings: &HashMap<String, String>,
) -> HashMap<String, Vec<String>> {
let mut out: HashMap<String, Vec<String>> = HashMap::new();
for (svc, chassis_id) in service_mappings {
out.entry(chassis_id.clone()).or_default().push(svc.clone());
}
out
}
#[allow(clippy::implicit_hasher)]
pub(crate) fn apply_chassis_scrape(
next: &mut HashMap<String, ServiceEnergy>,
chassis_services: &[String],
chassis_watts: f64,
scrape_interval_secs: f64,
op_deltas: &HashMap<String, u64>,
now_ms: u64,
) -> bool {
if !chassis_watts.is_finite()
|| chassis_watts <= 0.0
|| !scrape_interval_secs.is_finite()
|| scrape_interval_secs <= 0.0
|| chassis_services.is_empty()
{
return false;
}
let mut contributors: Vec<(&str, u64)> = Vec::with_capacity(chassis_services.len());
let mut total_ops: u64 = 0;
for svc in chassis_services {
let ops = op_deltas.get(svc.as_str()).copied().unwrap_or(0);
if ops > 0 {
contributors.push((svc.as_str(), ops));
total_ops = total_ops.saturating_add(ops);
}
}
if total_ops == 0 {
return false;
}
let chassis_kwh = (chassis_watts * scrape_interval_secs) / 3_600_000.0;
let energy_per_op = chassis_kwh / total_ops as f64;
if !energy_per_op.is_finite() || energy_per_op <= 0.0 {
return false;
}
let row = ServiceEnergy {
energy_per_op_kwh: energy_per_op,
last_update_ms: now_ms,
};
for (svc, _) in &contributors {
if let Some(slot) = next.get_mut(*svc) {
*slot = row;
} else {
next.insert((*svc).to_string(), row);
}
}
!contributors.is_empty()
}