use serde::{Deserialize, Serialize};
use crate::brand::{NAME, PHASE, VERSION};
use crate::hardware::{EnvironmentSnapshot, HardwareInfo};
use crate::Result;
pub const REPRO_FORMAT: &str = "silicera-repro-pack";
pub const REPRO_VERSION: u32 = 1;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReproPack {
pub format: String,
pub version: u32,
pub product: String,
pub silicera_version: String,
pub phase: String,
pub experiment: String,
pub created_at: String,
pub fingerprint: Option<String>,
pub host_brand: String,
pub microarchitecture: Option<String>,
pub environment: EnvironmentSnapshot,
pub parameters: serde_json::Value,
pub random_seed: Option<u64>,
pub result_digest: Option<String>,
pub caveats: Vec<String>,
}
impl ReproPack {
pub fn capture(
info: &HardwareInfo,
experiment: impl Into<String>,
parameters: serde_json::Value,
random_seed: Option<u64>,
result_digest: Option<String>,
) -> Self {
let microarchitecture = info
.fingerprint
.as_ref()
.and_then(|f| f.value.split(':').nth(2).map(|s| s.to_ascii_lowercase()));
Self {
format: REPRO_FORMAT.into(),
version: REPRO_VERSION,
product: NAME.into(),
silicera_version: VERSION.into(),
phase: PHASE.into(),
experiment: experiment.into(),
created_at: chrono::Utc::now().to_rfc3339(),
fingerprint: info.fingerprint.as_ref().map(|f| f.value.clone()),
host_brand: info.brand.clone(),
microarchitecture,
environment: info.environment.clone(),
parameters,
random_seed,
result_digest,
caveats: vec![
"Repro packs identify measurement conditions; they are not authentication.".into(),
"Re-run on the same fingerprint class; do not assume cross-machine transfer.".into(),
"No user home paths or hardware serial numbers are included.".into(),
],
}
}
pub fn to_json_pretty(&self) -> Result<String> {
Ok(serde_json::to_string_pretty(self)?)
}
pub fn write_to(&self, path: &std::path::Path) -> Result<()> {
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)?;
}
std::fs::write(path, self.to_json_pretty()?)?;
Ok(())
}
}