use std::path::{Path, PathBuf};
use super::hang_trace_classifier::{
classify_empty_on_success, classify_exit_code, classify_timeout_dump,
HangEmptyOnSuccessOutcome, HangExitOutcome, HangTimeoutOutcome, TraceDirListing,
};
use crate::error::{CliError, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum HangMode {
Timeout,
Success,
}
pub(crate) fn run(
trace_dir: &Path,
mode: HangMode,
world_size: usize,
exit_code: Option<i32>,
expected_exit_code: Option<i32>,
json: bool,
) -> Result<()> {
if !trace_dir.exists() {
return Err(CliError::FileNotFound(PathBuf::from(trace_dir)));
}
let entries = std::fs::read_dir(trace_dir)?
.filter_map(|e| e.ok())
.filter(|e| e.file_type().map(|t| t.is_file()).unwrap_or(false))
.map(|e| {
let name = e.file_name().to_string_lossy().to_string();
let size = e.metadata().map(|m| m.len()).unwrap_or(0);
(name, size)
})
.collect::<Vec<_>>();
let names: Vec<&str> = entries.iter().map(|e| e.0.as_str()).collect();
let sizes: Vec<u64> = entries.iter().map(|e| e.1).collect();
let listing = TraceDirListing { names, sizes };
let dir_outcome_t = if mode == HangMode::Timeout {
Some(classify_timeout_dump(&listing, world_size))
} else {
None
};
let dir_outcome_s = if mode == HangMode::Success {
Some(classify_empty_on_success(&listing))
} else {
None
};
let exit_outcome = match (exit_code, expected_exit_code) {
(Some(got), Some(expected)) => Some(classify_exit_code(got, expected)),
_ => None,
};
print_report(
trace_dir,
dir_outcome_t.as_ref(),
dir_outcome_s.as_ref(),
exit_outcome.as_ref(),
json,
);
if let Some(o) = &dir_outcome_t {
if !matches!(o, HangTimeoutOutcome::Ok { .. }) {
return Err(CliError::ValidationFailed(format!(
"hang-trace-lint timeout-dump gate rejected dir: {o:?}"
)));
}
}
if let Some(o) = &dir_outcome_s {
if !matches!(o, HangEmptyOnSuccessOutcome::Ok) {
return Err(CliError::ValidationFailed(format!(
"hang-trace-lint empty-on-success gate rejected dir: {o:?}"
)));
}
}
if let Some(o) = &exit_outcome {
if matches!(o, HangExitOutcome::ExitCodeMismatch { .. }) {
return Err(CliError::ValidationFailed(format!(
"hang-trace-lint exit-code gate rejected: {o:?}"
)));
}
}
Ok(())
}
fn print_report(
trace_dir: &Path,
timeout_outcome: Option<&HangTimeoutOutcome>,
success_outcome: Option<&HangEmptyOnSuccessOutcome>,
exit_outcome: Option<&HangExitOutcome>,
json: bool,
) {
if json {
let obj = serde_json::json!({
"trace_dir": trace_dir.display().to_string(),
"timeout_dump": timeout_outcome.map(|o| format!("{o:?}")),
"empty_on_success": success_outcome.map(|o| format!("{o:?}")),
"exit_code": exit_outcome.map(|o| format!("{o:?}")),
});
println!("{}", serde_json::to_string_pretty(&obj).unwrap_or_default());
return;
}
println!("hang-trace-lint report for {}", trace_dir.display());
if let Some(o) = timeout_outcome {
println!(" timeout_dump : {o:?}");
}
if let Some(o) = success_outcome {
println!(" empty_on_success: {o:?}");
}
if let Some(o) = exit_outcome {
println!(" exit_code : {o:?}");
}
}