1use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct Request {
19 pub id: u64,
20 pub verb: String,
21 #[serde(default)]
22 pub args: serde_json::Value,
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct Response {
28 pub id: u64,
29 #[serde(flatten)]
30 pub outcome: ResponseOutcome,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
49#[serde(untagged)]
50pub enum ResponseOutcome {
51 Result { result: serde_json::Value },
52 Error { error: WireError },
53 Event { event: serde_json::Value },
54 End { end: EndMarker },
55}
56
57#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
61pub struct EndMarker {}
62
63#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct WireError {
65 pub kind: WireErrorKind,
66 pub message: String,
67}
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
72#[serde(rename_all = "snake_case")]
73pub enum WireErrorKind {
74 UnknownVerb,
75 BadArgs,
76 Internal,
77 NotImplemented,
79}
80
81pub fn encode_line<T: Serialize>(value: &T) -> Result<Vec<u8>, serde_json::Error> {
88 let mut buf = serde_json::to_vec(value)?;
89 buf.push(b'\n');
90 Ok(buf)
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96
97 #[test]
98 fn request_round_trips_through_json_with_args() {
99 let req =
100 Request { id: 42, verb: "stats".to_string(), args: serde_json::json!({ "scope": "all" }) };
101 let encoded = serde_json::to_string(&req).expect("serialize");
102 let decoded: Request = serde_json::from_str(&encoded).expect("deserialize");
103 assert_eq!(decoded.id, 42);
104 assert_eq!(decoded.verb, "stats");
105 assert_eq!(decoded.args, serde_json::json!({ "scope": "all" }));
106 }
107
108 #[test]
109 fn request_default_args_are_null() {
110 let raw = r#"{"id":1,"verb":"ping"}"#;
112 let req: Request = serde_json::from_str(raw).expect("deserialize");
113 assert!(req.args.is_null());
114 }
115
116 #[test]
117 fn response_result_serializes_with_flat_result_key() {
118 let resp = Response {
119 id: 7,
120 outcome: ResponseOutcome::Result { result: serde_json::json!({ "pong": true }) },
121 };
122 let value = serde_json::to_value(&resp).expect("to_value");
123 assert_eq!(value["id"], 7);
124 assert_eq!(value["result"], serde_json::json!({ "pong": true }));
125 assert!(value.get("error").is_none(), "result frame must not carry error key");
126 assert!(value.get("outcome").is_none(), "must flatten — no nested outcome key");
127 }
128
129 #[test]
130 fn response_error_serializes_with_flat_error_key() {
131 let resp = Response {
132 id: 3,
133 outcome: ResponseOutcome::Error {
134 error: WireError { kind: WireErrorKind::UnknownVerb, message: "no such verb".to_string() },
135 },
136 };
137 let value = serde_json::to_value(&resp).expect("to_value");
138 assert_eq!(value["id"], 3);
139 assert_eq!(value["error"]["kind"], "unknown_verb");
140 assert_eq!(value["error"]["message"], "no such verb");
141 assert!(value.get("result").is_none());
142 }
143
144 #[test]
145 fn unknown_verb_kind_round_trips_via_snake_case() {
146 for kind in [
147 WireErrorKind::UnknownVerb,
148 WireErrorKind::BadArgs,
149 WireErrorKind::Internal,
150 WireErrorKind::NotImplemented,
151 ] {
152 let s = serde_json::to_string(&kind).expect("serialize kind");
153 let back: WireErrorKind = serde_json::from_str(&s).expect("deserialize kind");
154 assert_eq!(kind, back);
155 }
156 assert_eq!(serde_json::to_string(&WireErrorKind::UnknownVerb).unwrap(), "\"unknown_verb\"");
157 assert_eq!(serde_json::to_string(&WireErrorKind::BadArgs).unwrap(), "\"bad_args\"");
158 assert_eq!(
159 serde_json::to_string(&WireErrorKind::NotImplemented).unwrap(),
160 "\"not_implemented\""
161 );
162 }
163
164 #[test]
165 fn response_event_outcome_serializes_with_event_key() {
166 let resp = Response {
167 id: 9,
168 outcome: ResponseOutcome::Event { event: serde_json::json!({ "kind": "trajectory" }) },
169 };
170 let value = serde_json::to_value(&resp).expect("to_value");
171 assert_eq!(value["id"], 9);
172 assert_eq!(value["event"]["kind"], "trajectory");
173 assert!(value.get("result").is_none());
174 assert!(value.get("error").is_none());
175 assert!(value.get("end").is_none());
176 }
177
178 #[test]
179 fn response_end_outcome_serializes_as_empty_end_object() {
180 let resp = Response { id: 4, outcome: ResponseOutcome::End { end: EndMarker {} } };
181 let value = serde_json::to_value(&resp).expect("to_value");
182 assert_eq!(value["id"], 4);
183 assert_eq!(value["end"], serde_json::json!({}));
184 assert!(value.get("event").is_none());
185 }
186
187 #[test]
188 fn response_event_round_trips_through_json() {
189 let frames = vec![
193 Response { id: 1, outcome: ResponseOutcome::Result { result: serde_json::json!(42) } },
194 Response { id: 2, outcome: ResponseOutcome::Event { event: serde_json::json!("hi") } },
195 Response { id: 3, outcome: ResponseOutcome::End { end: EndMarker {} } },
196 ];
197 for f in frames {
198 let s = serde_json::to_string(&f).expect("serialize");
199 let back: Response = serde_json::from_str(&s).expect("deserialize");
200 assert_eq!(back.id, f.id);
201 match (&f.outcome, &back.outcome) {
202 (ResponseOutcome::Result { .. }, ResponseOutcome::Result { .. })
203 | (ResponseOutcome::Event { .. }, ResponseOutcome::Event { .. })
204 | (ResponseOutcome::End { .. }, ResponseOutcome::End { .. }) => {}
205 other => panic!("variant changed: {other:?}"),
206 }
207 }
208 }
209
210 #[test]
211 fn encode_line_appends_newline() {
212 let req = Request { id: 1, verb: "ping".to_string(), args: serde_json::Value::Null };
213 let bytes = encode_line(&req).expect("encode");
214 assert_eq!(*bytes.last().expect("non-empty"), b'\n');
215 let body = &bytes[..bytes.len() - 1];
217 let decoded: Request = serde_json::from_slice(body).expect("decode body");
218 assert_eq!(decoded.verb, "ping");
219 }
220}