Skip to main content

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}