1use crate::report::DetectionReport;
5use crate::sampler::RawSamples;
6use crate::stats::BOOTSTRAP_ITERATIONS;
7use anyhow::{Context, Result};
8use serde::Serialize;
9use std::io::Write;
10use std::path::Path;
11use std::time::{SystemTime, UNIX_EPOCH};
12
13pub fn write_csv(path: &Path, raw: &RawSamples) -> Result<()> {
14 let mut file = std::fs::File::create(path)
15 .with_context(|| format!("failed to create {}", path.display()))?;
16
17 writeln!(file, "class,elapsed_seconds")?;
18 for v in &raw.class_a {
19 writeln!(file, "a,{v:.9}")?;
20 }
21 for v in &raw.class_b {
22 writeln!(file, "b,{v:.9}")?;
23 }
24
25 Ok(())
26}
27
28#[derive(Serialize)]
32pub struct JsonReport {
33 pub target: String,
34 pub injection_point: String,
35 pub samples_per_class: usize,
36 pub jitter_ms: f64,
37 pub estimated_leak_us: f64,
38 pub significant: bool,
39 pub bootstrap_confidence: f64,
40 pub bootstrap_iterations: usize,
41 pub ci_low_us: f64,
42 pub ci_high_us: f64,
43 pub failed_requests: usize,
44 pub seed: u64,
45 pub timestamp_unix: u64,
46 pub sidecheck_version: String,
47}
48
49impl JsonReport {
50 pub fn from_detection(report: &DetectionReport) -> Self {
51 Self {
52 target: report.target.clone(),
53 injection_point: report.field.clone(),
54 samples_per_class: report.samples_per_class,
55 jitter_ms: report.jitter_seconds * 1000.0,
56 estimated_leak_us: report.result.estimated_leak * 1_000_000.0,
57 significant: report.result.is_significant(),
58 bootstrap_confidence: report.result.confidence,
59 bootstrap_iterations: BOOTSTRAP_ITERATIONS,
60 ci_low_us: report.result.ci_low * 1_000_000.0,
61 ci_high_us: report.result.ci_high * 1_000_000.0,
62 failed_requests: report.failures,
63 seed: report.seed,
64 timestamp_unix: SystemTime::now()
65 .duration_since(UNIX_EPOCH)
66 .map(|d| d.as_secs())
67 .unwrap_or(0),
68 sidecheck_version: report.sidecheck_version.clone(),
69 }
70 }
71}
72
73pub fn write_json(path: &Path, report: &DetectionReport) -> Result<()> {
74 let json_report = JsonReport::from_detection(report);
75 let text =
76 serde_json::to_string_pretty(&json_report).context("failed to serialize report to JSON")?;
77 std::fs::write(path, text).with_context(|| format!("failed to write {}", path.display()))?;
78 Ok(())
79}