Skip to main content

hen/report/
ndjson.rs

1use serde_json::{json, Value};
2
3use crate::{automation, error::HenError};
4
5use super::{
6    artifacts::{request_failure_json, run_record_json},
7    common::{execution_trace_json, with_type},
8    json::{hen_diagnostic_json, hen_error_json, inspection_result_json},
9    BodyReportOptions,
10};
11
12pub fn run_outcome_ndjson(
13    outcome: &automation::RunOutcome,
14    body_options: BodyReportOptions,
15) -> String {
16    let mut lines = vec![json!({
17        "type": "run",
18        "collection": {
19            "path": outcome.collection.path.display().to_string(),
20            "name": outcome.collection.name,
21            "description": outcome.collection.description,
22            "availableEnvironments": outcome.collection.available_environments,
23            "selectedEnvironment": outcome.collection.selected_environment,
24        },
25        "plan": outcome.plan,
26        "selectedRequests": outcome.selected_requests,
27        "primaryTarget": outcome.primary_target,
28        "executionFailed": outcome.execution_failed,
29        "interrupted": outcome.interrupted.is_some(),
30        "interruptSignal": outcome.interrupted.map(|signal| signal.as_str()),
31        "recordCount": outcome.records.len(),
32        "failureCount": outcome.failures.len(),
33        "traceCount": outcome.trace.len(),
34    })];
35
36    lines.extend(
37        outcome
38            .records
39            .iter()
40            .map(|record| with_type(run_record_json(record, body_options), "record")),
41    );
42    lines.extend(
43        outcome
44            .failures
45            .iter()
46            .map(|failure| with_type(request_failure_json(failure, body_options), "failure")),
47    );
48    lines.extend(
49        outcome
50            .trace
51            .iter()
52            .map(|entry| with_type(execution_trace_json(entry), "trace")),
53    );
54
55    render_ndjson(lines)
56}
57
58pub fn verification_result_ndjson(result: &automation::VerificationResult) -> String {
59    verification_diagnostics_result_ndjson(&automation::VerificationDiagnosticsResult {
60        ok: true,
61        path: result.path.clone(),
62        summary: Some(result.summary.clone()),
63        required_inputs: result.required_inputs.clone(),
64        diagnostics: Vec::new(),
65        error: None,
66    })
67}
68
69pub fn verification_diagnostics_result_ndjson(
70    result: &automation::VerificationDiagnosticsResult,
71) -> String {
72    let mut lines = vec![json!({
73        "type": "verify",
74        "ok": result.ok,
75        "path": result.path.as_ref().map(|path| path.display().to_string()),
76        "name": result.summary.as_ref().map(|summary| summary.name.clone()),
77        "description": result.summary.as_ref().map(|summary| summary.description.clone()),
78        "availableEnvironments": result.summary.as_ref().map(|summary| summary.available_environments.clone()).unwrap_or_default(),
79        "requestCount": result.summary.as_ref().map(|summary| summary.requests.len()).unwrap_or(0),
80        "requiredInputCount": result.required_inputs.len(),
81        "diagnosticCount": result.diagnostics.len(),
82    })];
83
84    if let Some(summary) = result.summary.as_ref() {
85        lines.extend(summary.requests.iter().map(|request| {
86            json!({
87                "type": "request",
88                "index": request.index,
89                "description": request.description,
90                "method": request.method,
91                "url": request.url,
92                "protocol": request.protocol,
93                "protocolContext": request.protocol_context,
94            })
95        }));
96    }
97
98    lines.extend(result.required_inputs.iter().map(|prompt| {
99        json!({
100            "type": "requiredInput",
101            "name": prompt.name,
102            "default": prompt.default,
103        })
104    }));
105
106    lines.extend(
107        result
108            .diagnostics
109            .iter()
110            .map(|diagnostic| with_type(hen_diagnostic_json(diagnostic), "diagnostic")),
111    );
112
113    render_ndjson(lines)
114}
115
116pub fn inspection_result_ndjson(result: &automation::InspectionResult) -> String {
117    render_ndjson(vec![with_type(inspection_result_json(result), "inspect")])
118}
119
120pub fn hen_error_ndjson(error: &HenError) -> String {
121    render_ndjson(vec![with_type(hen_error_json(error), "error")])
122}
123
124fn render_ndjson(lines: Vec<Value>) -> String {
125    lines
126        .into_iter()
127        .map(|line| serde_json::to_string(&line).expect("ndjson line should serialize"))
128        .collect::<Vec<_>>()
129        .join("\n")
130}