use super::*;
pub(crate) fn rule_resource_rows(
checkpoint: &oxo_flow_core::executor::checkpoint::CheckpointState,
) -> Vec<serde_json::Value> {
let mut names: Vec<&String> = checkpoint.benchmarks.keys().collect();
names.sort_unstable();
names
.into_iter()
.map(|name| {
let b = &checkpoint.benchmarks[name];
let status = if checkpoint.completed_rules.contains(name) {
"completed"
} else if checkpoint.failed_rules.contains(name) {
if checkpoint.when_verdicts.get(name) == Some(&false) {
"skipped_by_when"
} else {
"failed"
}
} else {
"running"
};
serde_json::json!({
"rule": name,
"status": status,
"wall_time_secs": b.wall_time_secs,
"recorded_as": b.recorded_as,
"peak_rss_mb": b.max_memory_mb,
"cpu_seconds": b.cpu_seconds,
"retries": b.retries,
})
})
.collect()
}
#[derive(serde::Serialize, serde::Deserialize, Clone, Default)]
pub(crate) struct RefRecord {
pub(crate) fingerprint: String,
pub(crate) content_sig: Option<String>,
pub(crate) source_path: Option<String>,
}
pub(crate) fn source_content_sig(path: &std::path::Path) -> Option<String> {
let md = std::fs::metadata(path).ok()?;
if !md.is_file() {
return None;
}
let mut sig = format!("size:{}", md.len());
if let Some(hash) = oxo_flow_core::executor::checkpoint::content_hash_if_small(path, &md) {
sig.push_str(&format!("|hash:{hash}"));
}
Some(sig)
}
pub(crate) fn free_kilobytes(path: &std::path::Path) -> Option<u64> {
let out = std::process::Command::new("df")
.args(["-Pk", path.to_str()?])
.output()
.ok()?;
if !out.status.success() {
return None;
}
let stdout = String::from_utf8_lossy(&out.stdout);
let line = stdout.lines().next_back()?;
line.split_whitespace().nth(3)?.parse().ok()
}
pub(crate) fn age_ready_list(
mut ready: Vec<String>,
order_set: &std::collections::HashSet<String>,
submitted: &std::collections::HashSet<String>,
priority_map: &std::collections::HashMap<String, i32>,
waited: &std::collections::HashMap<String, i32>,
) -> Vec<String> {
ready.retain(|name| order_set.contains(name) && !submitted.contains(name));
ready.sort_by(|a, b| {
let pa = priority_map.get(a).copied().unwrap_or(0) + waited.get(a).copied().unwrap_or(0);
let pb = priority_map.get(b).copied().unwrap_or(0) + waited.get(b).copied().unwrap_or(0);
pb.cmp(&pa).then_with(|| a.cmp(b))
});
ready
}
pub(crate) fn suggest_jobs(
config: &WorkflowConfig,
groups: &[Vec<String>],
system_threads: u32,
) -> u32 {
let system_threads = system_threads.max(1);
let wave_capacity = |names: &[String]| -> u32 {
let max_threads = names
.iter()
.filter_map(|n| config.get_rule(n))
.map(|r| r.effective_threads())
.max()
.unwrap_or(1)
.max(1);
(system_threads / max_threads)
.min(names.len() as u32)
.max(1)
};
groups
.iter()
.map(|g| wave_capacity(g))
.max()
.unwrap_or(1)
.clamp(1, system_threads)
}