browser_automation_cli/
envelope.rs1use serde::Serialize;
25use serde_json::Value;
26
27use crate::error::CliError;
28use crate::output;
29
30#[derive(Debug, Serialize)]
32pub struct SuccessEnvelope {
33 pub schema_version: u32,
35 pub ok: bool,
37 pub data: Value,
39}
40
41#[derive(Debug, Serialize)]
43pub struct ErrorBody {
44 pub kind: String,
46 pub message: String,
48 pub exit_code: u8,
50 #[serde(skip_serializing_if = "Option::is_none")]
52 pub suggestion: Option<String>,
53}
54
55#[derive(Debug, Serialize)]
57pub struct ErrorEnvelope {
58 pub schema_version: u32,
60 pub ok: bool,
62 pub error: ErrorBody,
64 #[serde(skip_serializing_if = "Option::is_none")]
66 pub data: Option<Value>,
67}
68
69pub fn print_success_json(data: Value) -> Result<(), CliError> {
71 let env = SuccessEnvelope {
72 schema_version: 1,
73 ok: true,
74 data,
75 };
76 output::write_json_line_ser(&env)
77}
78
79pub fn print_error_json(err: &CliError) -> Result<(), CliError> {
81 print_error_json_with_data(err, err.data().cloned())
82}
83
84pub fn print_error_json_with_data(err: &CliError, data: Option<Value>) -> Result<(), CliError> {
86 let env = ErrorEnvelope {
87 schema_version: 1,
88 ok: false,
89 error: ErrorBody {
90 kind: err.kind().as_str().to_string(),
91 message: err.message().to_string(),
92 exit_code: err.exit_code(),
93 suggestion: err.suggestion().map(|s| s.to_string()),
94 },
95 data,
96 };
97 output::write_json_line_ser(&env)
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103 use crate::error::ErrorKind;
104 use serde_json::json;
105
106 #[test]
107 fn success_envelope_roundtrip_shape() {
108 let env = SuccessEnvelope {
109 schema_version: 1,
110 ok: true,
111 data: json!({"x": 1}),
112 };
113 let s = crate::json_util::to_compact_string(&env).unwrap();
114 let v: Value = crate::json_util::from_str(&s).unwrap();
115 assert_eq!(v["schema_version"], 1);
116 assert_eq!(v["ok"], true);
117 assert_eq!(v["data"]["x"], 1);
118 assert!(!s.contains('\n'));
119 }
120
121 #[test]
122 fn error_envelope_omits_empty_suggestion() {
123 let err = CliError::new(ErrorKind::Data, "bad");
124 let env = ErrorEnvelope {
125 schema_version: 1,
126 ok: false,
127 error: ErrorBody {
128 kind: err.kind().as_str().to_string(),
129 message: err.message().to_string(),
130 exit_code: err.exit_code(),
131 suggestion: None,
132 },
133 data: None,
134 };
135 let s = crate::json_util::to_compact_string(&env).unwrap();
136 let v: Value = crate::json_util::from_str(&s).unwrap();
137 assert!(v.get("data").is_none());
138 assert!(v["error"].get("suggestion").is_none());
139 }
140}