use serde::{Deserialize, Serialize};
use super::AtpLoggerConfig;
pub const ATP_REPLAY_ARTIFACT_SCHEMA_ID: &str = "asupersync.atp.replay_artifacts.v1";
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ReplayArtifacts {
pub schema_id: String,
pub schema_version: u32,
pub session_id: String,
pub seed: u64,
pub replay_command: String,
pub trace_artifact: String,
pub qlog_artifact: String,
pub pathlog_artifact: String,
pub repairlog_artifact: String,
pub journal_digest_artifact: String,
pub proof_bundle_artifact: String,
pub environment_summary: ReplayEnvironment,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ReplayEnvironment {
pub os: String,
pub arch: String,
pub redaction_enabled: bool,
}
#[must_use]
pub fn generate(session_id: &str, seed: u64, config: &AtpLoggerConfig) -> ReplayArtifacts {
let safe_session = sanitize_identifier(session_id);
ReplayArtifacts {
schema_id: ATP_REPLAY_ARTIFACT_SCHEMA_ID.to_string(),
schema_version: 1,
session_id: safe_session.clone(),
seed,
replay_command: format!("atp replay --session {safe_session} --seed {seed}"),
trace_artifact: format!("{safe_session}/trace.jsonl"),
qlog_artifact: format!("{safe_session}/quic.qlog.json"),
pathlog_artifact: format!("{safe_session}/pathlog.json"),
repairlog_artifact: format!("{safe_session}/repairlog.json"),
journal_digest_artifact: format!("{safe_session}/journal.digest.json"),
proof_bundle_artifact: format!("{safe_session}/proof.bundle.json"),
environment_summary: ReplayEnvironment {
os: std::env::consts::OS.to_string(),
arch: std::env::consts::ARCH.to_string(),
redaction_enabled: config.redaction_enabled,
},
}
}
fn sanitize_identifier(value: &str) -> String {
value
.chars()
.map(|ch| {
if ch.is_ascii_alphanumeric() || matches!(ch, '-' | '_') {
ch
} else {
'_'
}
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn replay_artifacts_include_reproduction_context() {
let artifacts = generate("session:ATP-N6", 42, &AtpLoggerConfig::default());
assert_eq!(artifacts.schema_id, ATP_REPLAY_ARTIFACT_SCHEMA_ID);
assert_eq!(artifacts.schema_version, 1);
assert_eq!(artifacts.session_id, "session_ATP-N6");
assert_eq!(
artifacts.replay_command,
"atp replay --session session_ATP-N6 --seed 42"
);
assert_eq!(artifacts.trace_artifact, "session_ATP-N6/trace.jsonl");
assert_eq!(artifacts.qlog_artifact, "session_ATP-N6/quic.qlog.json");
assert_eq!(artifacts.pathlog_artifact, "session_ATP-N6/pathlog.json");
assert_eq!(
artifacts.repairlog_artifact,
"session_ATP-N6/repairlog.json"
);
assert_eq!(
artifacts.journal_digest_artifact,
"session_ATP-N6/journal.digest.json"
);
assert_eq!(
artifacts.proof_bundle_artifact,
"session_ATP-N6/proof.bundle.json"
);
}
}