use std::collections::HashMap;
use super::config::KeplerConfig;
use super::parser::KeplerSample;
use super::state::{KeplerState, ServiceEnergy};
#[must_use]
pub fn compute_energy_per_op_kwh(joules_delta: f64, ops: u64) -> Option<f64> {
if ops == 0 || !joules_delta.is_finite() || joules_delta < 0.0 {
return None;
}
let kwh = joules_delta / 3_600_000.0;
Some(kwh / ops as f64)
}
#[allow(clippy::implicit_hasher)]
pub fn joules_deltas(
samples: &[KeplerSample],
service_mappings: &HashMap<String, String>,
last_raw_joules: &mut HashMap<String, f64>,
) -> HashMap<String, f64> {
let by_label: HashMap<&str, f64> = samples
.iter()
.map(|s| (s.label_value.as_str(), s.joules_total))
.collect();
let mut out = HashMap::with_capacity(service_mappings.len());
for (service, label_value) in service_mappings {
let Some(¤t) = by_label.get(label_value.as_str()) else {
continue;
};
let previous = if let Some(slot) = last_raw_joules.get_mut(service) {
let prev = *slot;
*slot = current;
Some(prev)
} else {
last_raw_joules.insert(service.clone(), current);
None
};
if let Some(prev) = previous {
let delta = current - prev;
if delta > 0.0 && delta.is_finite() {
out.insert(service.clone(), delta);
}
}
}
out
}
#[allow(clippy::implicit_hasher)]
pub fn apply_scrape(
state: &KeplerState,
joules_deltas_map: &HashMap<String, f64>,
op_deltas: &HashMap<String, u64>,
now_ms: u64,
) {
let mut next = state.current_owned();
let mut any_change = false;
for (service, &joules_delta) in joules_deltas_map {
let Some(ops) = op_deltas.get(service).copied() else {
continue;
};
let Some(energy_per_op) = compute_energy_per_op_kwh(joules_delta, ops) else {
continue;
};
let row = ServiceEnergy {
energy_per_op_kwh: energy_per_op,
last_update_ms: now_ms,
};
if let Some(slot) = next.get_mut(service.as_str()) {
*slot = row;
} else {
next.insert(service.clone(), row);
}
any_change = true;
}
if any_change {
state.publish(next);
}
}
#[allow(clippy::implicit_hasher)]
pub fn process_scrape(
state: &KeplerState,
samples: &[KeplerSample],
op_deltas: &HashMap<String, u64>,
cfg: &KeplerConfig,
last_raw_joules: &mut HashMap<String, f64>,
now_ms: u64,
) {
let joules_deltas_map = joules_deltas(samples, &cfg.service_mappings, last_raw_joules);
apply_scrape(state, &joules_deltas_map, op_deltas, now_ms);
}