Skip to main content

schema_sample/
schema_sample.rs

1//! Emit a sample `Report` and `MultiReport` as JSON for schema validation.
2//!
3//! Exercises every variant the schema is supposed to accept: all four
4//! `Verdict`s, every `Severity`, every `EvidenceData` variant, a check with
5//! tags, a check with no tags, a `FileRef` with and without a line range,
6//! a `MultiReport` aggregating two producers, and the empty-report case.
7//!
8//! Usage:
9//!
10//! ```text
11//! cargo run --example schema_sample > target/sample_report.json
12//! cargo run --example schema_sample -- multi > target/sample_multi.json
13//! ```
14//!
15//! The output JSON validates against `schema/report.schema.json`. CI runs
16//! the validator script in `scripts/validate_schema.py` against this output.
17
18use chrono::TimeZone;
19use dev_report::{CheckResult, Evidence, FileRef, MultiReport, Report, Severity};
20
21fn build_report(producer: &str) -> Report {
22    let frozen_start = chrono::Utc.with_ymd_and_hms(2026, 5, 11, 12, 0, 0).unwrap();
23    let frozen_end = chrono::Utc.with_ymd_and_hms(2026, 5, 11, 12, 0, 5).unwrap();
24
25    let mut r = Report::new("sample-subject", "0.9.3").with_producer(producer);
26    r.set_started_at(frozen_start);
27
28    // Pass with no detail, no severity, no tags, no evidence.
29    let mut c1 = CheckResult::pass("compile");
30    c1.at = frozen_start;
31    r.push(c1);
32
33    // Pass with a duration and a single numeric evidence.
34    let mut c2 = CheckResult::pass("bench::parse")
35        .with_duration_ms(7)
36        .with_tag("bench")
37        .with_evidence(Evidence::numeric("mean_ns", 1234.5))
38        .with_evidence(Evidence::numeric_int("iterations", 1_000_000));
39    c2.at = frozen_start;
40    r.push(c2);
41
42    // Warn with key-value evidence.
43    let mut c3 = CheckResult::warn("env::leaked", Severity::Warning)
44        .with_detail("RUST_LOG was set during test")
45        .with_evidence(Evidence::kv("env", [("CI", "true"), ("RUST_LOG", "debug")]));
46    c3.at = frozen_start;
47    r.push(c3);
48
49    // Fail with snippet + file_ref (no line range) + file_ref (with line range).
50    let mut c4 = CheckResult::fail("test::round_trip", Severity::Error)
51        .with_detail("expected 42, got 41")
52        .with_duration_ms(13)
53        .with_tags(["unit", "flaky"])
54        .with_evidence(Evidence::snippet(
55            "panic",
56            "assertion `left == right` failed",
57        ))
58        .with_evidence(Evidence::file_ref("source", "src/math.rs"))
59        .with_evidence(Evidence::file_ref_lines(
60            "call_site",
61            "tests/smoke.rs",
62            42,
63            47,
64        ));
65    c4.at = frozen_start;
66    r.push(c4);
67
68    // Fail with Critical severity.
69    let mut c5 = CheckResult::fail("integration::startup", Severity::Critical)
70        .with_detail("service refused to start");
71    c5.at = frozen_start;
72    r.push(c5);
73
74    // Warn with Info severity (lowest severity, still warn verdict).
75    let mut c6 = CheckResult::warn("style::trailing_ws", Severity::Info)
76        .with_detail("3 trailing-whitespace warnings");
77    c6.at = frozen_start;
78    r.push(c6);
79
80    // Skip with no severity.
81    let mut c7 = CheckResult::skip("integration::network").with_detail("no network in sandbox");
82    c7.at = frozen_start;
83    r.push(c7);
84
85    // Manually-constructed FileRef via Evidence::file_ref (covered above);
86    // also exercise a standalone FileRef with line_start but no line_end —
87    // valid per the schema (both are optional independently).
88    let mut c8 = CheckResult::pass("doc::link_check");
89    c8.at = frozen_start;
90    let standalone = FileRef::new("docs/index.md").with_line_range(10, 10);
91    c8 = c8.with_evidence(Evidence {
92        label: "anchor".into(),
93        data: dev_report::EvidenceData::FileRef(standalone),
94    });
95    r.push(c8);
96
97    r.set_finished_at(Some(frozen_end));
98    r
99}
100
101fn build_multi() -> MultiReport {
102    let frozen_start = chrono::Utc.with_ymd_and_hms(2026, 5, 11, 12, 0, 0).unwrap();
103    let frozen_end = chrono::Utc
104        .with_ymd_and_hms(2026, 5, 11, 12, 0, 10)
105        .unwrap();
106    let mut m = MultiReport::new("sample-subject", "0.9.3");
107    m.started_at = frozen_start;
108    m.push(build_report("dev-bench"));
109    m.push(build_report("dev-chaos"));
110    m.finished_at = Some(frozen_end);
111    m
112}
113
114fn main() {
115    let arg = std::env::args()
116        .nth(1)
117        .unwrap_or_else(|| "report".to_string());
118    let json = match arg.as_str() {
119        "multi" => build_multi().to_json().expect("serialize MultiReport"),
120        _ => build_report("dev-bench")
121            .to_json()
122            .expect("serialize Report"),
123    };
124    println!("{}", json);
125}