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}