Skip to main content

harn_vm/
jsonrpc.rs

1//! Shared JSON-RPC 2.0 message construction helpers.
2
3/// Build a JSON-RPC 2.0 request object.
4#[inline]
5pub fn request(
6    id: impl Into<serde_json::Value>,
7    method: &str,
8    params: serde_json::Value,
9) -> serde_json::Value {
10    serde_json::json!({
11        "jsonrpc": "2.0",
12        "id": id.into(),
13        "method": method,
14        "params": params,
15    })
16}
17
18/// Build a JSON-RPC 2.0 notification (no id).
19#[inline]
20pub fn notification(method: &str, params: serde_json::Value) -> serde_json::Value {
21    serde_json::json!({
22        "jsonrpc": "2.0",
23        "method": method,
24        "params": params,
25    })
26}
27
28/// Build a JSON-RPC 2.0 success response.
29#[inline]
30pub fn response(id: impl Into<serde_json::Value>, result: serde_json::Value) -> serde_json::Value {
31    serde_json::json!({
32        "jsonrpc": "2.0",
33        "id": id.into(),
34        "result": result,
35    })
36}
37
38/// Build a JSON-RPC 2.0 error response.
39#[inline]
40pub fn error_response(
41    id: impl Into<serde_json::Value>,
42    code: i64,
43    message: &str,
44) -> serde_json::Value {
45    serde_json::json!({
46        "jsonrpc": "2.0",
47        "id": id.into(),
48        "error": { "code": code, "message": message },
49    })
50}
51
52/// Build a JSON-RPC 2.0 error response with additional data.
53#[inline]
54pub fn error_response_with_data(
55    id: impl Into<serde_json::Value>,
56    code: i64,
57    message: &str,
58    data: serde_json::Value,
59) -> serde_json::Value {
60    serde_json::json!({
61        "jsonrpc": "2.0",
62        "id": id.into(),
63        "error": { "code": code, "message": message, "data": data },
64    })
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    #[test]
72    fn request_has_all_fields() {
73        let req = request(1, "foo/bar", serde_json::json!({"key": "val"}));
74        assert_eq!(req["jsonrpc"], "2.0");
75        assert_eq!(req["id"], 1);
76        assert_eq!(req["method"], "foo/bar");
77        assert_eq!(req["params"]["key"], "val");
78    }
79
80    #[test]
81    fn notification_has_no_id() {
82        let notif = notification("update", serde_json::json!({}));
83        assert_eq!(notif["jsonrpc"], "2.0");
84        assert_eq!(notif["method"], "update");
85        assert!(notif.get("id").is_none());
86    }
87
88    #[test]
89    fn response_wraps_result() {
90        let resp = response(42, serde_json::json!({"data": true}));
91        assert_eq!(resp["id"], 42);
92        assert_eq!(resp["result"]["data"], true);
93    }
94
95    #[test]
96    fn error_response_wraps_error() {
97        let resp = error_response(1, -32600, "Invalid Request");
98        assert_eq!(resp["error"]["code"], -32600);
99        assert_eq!(resp["error"]["message"], "Invalid Request");
100    }
101
102    #[test]
103    fn error_response_with_data_includes_data() {
104        let resp =
105            error_response_with_data(1, -32602, "Bad params", serde_json::json!({"field": "x"}));
106        assert_eq!(resp["error"]["data"]["field"], "x");
107    }
108
109    #[test]
110    fn request_accepts_string_id() {
111        let req = request("abc-123", "test", serde_json::json!(null));
112        assert_eq!(req["id"], "abc-123");
113    }
114}