use crate::analyze::LossFinding;
use parallax_core::{ConversionPolicy, MigrationId, RuntimeKind, SemanticLoss};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct MigrationTimings {
pub analyze_us: u64,
pub convert_us: u64,
pub capture_us: Option<u64>,
pub restore_us: Option<u64>,
pub total_us: u64,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct MigrationReport {
pub id: MigrationId,
pub source_runtime: RuntimeKind,
pub target_runtime: RuntimeKind,
pub policy: ConversionPolicy,
pub findings: Vec<LossFinding>,
pub worst_loss: SemanticLoss,
pub timings: MigrationTimings,
pub success: bool,
pub notes: Vec<String>,
}
impl MigrationReport {
pub fn format_human(&self) -> String {
let mut out = String::new();
out.push_str(&format!(
"Migration {} → {} ({})\n",
self.source_runtime,
self.target_runtime,
if self.success { "OK" } else { "FAILED" }
));
out.push_str(&format!("id: {}\n", self.id));
out.push_str(&format!("worst_loss: {}\n", self.worst_loss));
out.push_str("timings:\n");
if let Some(c) = self.timings.capture_us {
out.push_str(&format!(" capture: {c} µs\n"));
}
out.push_str(&format!(" analyze: {} µs\n", self.timings.analyze_us));
out.push_str(&format!(" convert: {} µs\n", self.timings.convert_us));
if let Some(r) = self.timings.restore_us {
out.push_str(&format!(" restore: {r} µs\n"));
}
out.push_str(&format!(" total: {} µs\n", self.timings.total_us));
if !self.findings.is_empty() {
out.push_str("\nfindings:\n");
for f in &self.findings {
out.push_str(&format!(" [{}] {} — {}\n", f.loss, f.path, f.message));
if let Some(s) = &f.suggestion {
out.push_str(&format!(" suggestion: {s}\n"));
}
}
}
for n in &self.notes {
out.push_str(&format!("note: {n}\n"));
}
out
}
}