use crate::score::prom_parser::{
find_label_block_end, parse_next_label, unescape_prometheus_value,
};
#[derive(Debug, Clone, PartialEq)]
pub struct ProcessPower {
pub exe: String,
pub cmdline: String,
pub power_microwatts: f64,
}
#[must_use]
pub fn parse_scaphandre_metrics(body: &str) -> Vec<ProcessPower> {
const METRIC_NAME: &str = "scaph_process_power_consumption_microwatts";
let mut out = Vec::new();
for line in body.lines() {
let line = line.trim();
if line.is_empty() || line.starts_with('#') {
continue;
}
let Some(rest) = line.strip_prefix(METRIC_NAME) else {
continue;
};
let (labels_str, value_str) = match rest.as_bytes().first() {
Some(b'{') => {
match find_label_block_end(rest) {
Some(end) => (&rest[1..end], rest[end + 1..].trim_start()),
None => continue, }
}
Some(b' ') => ("", rest.trim_start()),
_ => continue, };
let value_token = value_str.split_whitespace().next().unwrap_or("");
let Ok(value) = value_token.parse::<f64>() else {
continue;
};
let (exe, cmdline) = extract_exe_and_cmdline(labels_str);
let Some(exe) = exe else {
continue;
};
out.push(ProcessPower {
exe,
cmdline,
power_microwatts: value,
});
}
out
}
fn extract_exe_and_cmdline(labels: &str) -> (Option<String>, String) {
let bytes = labels.as_bytes();
let mut i = 0;
let mut exe: Option<String> = None;
let mut cmdline: Option<String> = None;
while i < bytes.len() {
let Some(parsed) = parse_next_label(labels, bytes, i) else {
break;
};
let materialize = || {
if parsed.needs_unescape {
unescape_prometheus_value(parsed.value)
} else {
parsed.value.to_string()
}
};
match parsed.name.trim() {
"exe" if exe.is_none() => exe = Some(materialize()),
"cmdline" if cmdline.is_none() => cmdline = Some(materialize()),
_ => {}
}
if exe.is_some() && cmdline.is_some() {
break;
}
i = parsed.next_index;
}
(exe, cmdline.unwrap_or_default())
}