aft/protocol.rs
1use serde::{Deserialize, Serialize};
2
3/// Inbound request envelope.
4///
5/// Two-stage parse: deserialize this first to get `id` + `command`, then
6/// dispatch on `command` and pull specific params from the flattened `params`.
7#[derive(Debug, Deserialize)]
8pub struct RawRequest {
9 pub id: String,
10 pub command: String,
11 /// Optional LSP hints from the plugin (R031 forward compatibility).
12 #[serde(default)]
13 pub lsp_hints: Option<serde_json::Value>,
14 /// All remaining fields are captured here for per-command deserialization.
15 #[serde(flatten)]
16 pub params: serde_json::Value,
17}
18
19/// Outbound response envelope.
20///
21/// `data` is flattened into the top-level JSON object, so a response like
22/// `Response { id: "1", success: true, data: json!({"command": "pong"}) }`
23/// serializes to `{"id":"1","success":true,"command":"pong"}`.
24#[derive(Debug, Serialize)]
25pub struct Response {
26 pub id: String,
27 pub success: bool,
28 #[serde(flatten)]
29 pub data: serde_json::Value,
30}
31
32/// Parameters for the `echo` command.
33#[derive(Debug, Deserialize)]
34pub struct EchoParams {
35 pub message: String,
36}
37
38impl Response {
39 /// Build a success response with arbitrary data merged at the top level.
40 pub fn success(id: impl Into<String>, data: serde_json::Value) -> Self {
41 Response {
42 id: id.into(),
43 success: true,
44 data,
45 }
46 }
47
48 /// Build an error response with `code` and `message` fields.
49 pub fn error(id: impl Into<String>, code: &str, message: impl Into<String>) -> Self {
50 Response {
51 id: id.into(),
52 success: false,
53 data: serde_json::json!({
54 "code": code,
55 "message": message.into(),
56 }),
57 }
58 }
59
60 /// Build an error response with `code`, `message`, and additional structured data.
61 ///
62 /// The `extra` fields are merged into the top-level response alongside `code` and `message`.
63 pub fn error_with_data(
64 id: impl Into<String>,
65 code: &str,
66 message: impl Into<String>,
67 extra: serde_json::Value,
68 ) -> Self {
69 let mut data = serde_json::json!({
70 "code": code,
71 "message": message.into(),
72 });
73 if let (Some(base), Some(ext)) = (data.as_object_mut(), extra.as_object()) {
74 for (k, v) in ext {
75 base.insert(k.clone(), v.clone());
76 }
77 }
78 Response {
79 id: id.into(),
80 success: false,
81 data,
82 }
83 }
84}