Skip to main content

eval_magic/pipeline/
io.rs

1//! Shared JSON read/write helpers for the pipeline stages.
2//!
3//! Every stage serializes artifacts the same way: pretty-printed, two-space
4//! indent, one trailing newline. `serde_json`'s `preserve_order` feature keeps
5//! object key order stable so artifacts diff cleanly across runs.
6
7use std::fs;
8use std::path::Path;
9use std::time::{SystemTime, UNIX_EPOCH};
10
11use chrono::{DateTime, SecondsFormat};
12use serde::Serialize;
13
14use crate::pipeline::error::PipelineError;
15
16/// Write `value` to `path` as pretty JSON with a trailing newline.
17pub fn write_json<T: Serialize>(path: &Path, value: &T) -> Result<(), PipelineError> {
18    let mut text = serde_json::to_string_pretty(value)?;
19    text.push('\n');
20    fs::write(path, text)?;
21    Ok(())
22}
23
24/// The current wall clock as `2026-06-08T12:00:00.000Z`, matching JS
25/// `new Date().toISOString()` — the `generated` stamp every report carries.
26/// chrono ships without its `clock` feature, so the instant comes from
27/// `std::time` and is formatted via chrono.
28pub fn now_iso8601() -> String {
29    let ms = SystemTime::now()
30        .duration_since(UNIX_EPOCH)
31        .unwrap_or_default()
32        .as_millis() as i64;
33    DateTime::from_timestamp_millis(ms)
34        .unwrap_or_default()
35        .to_rfc3339_opts(SecondsFormat::Millis, true)
36}