dig_node_control_interface/
envelope.rs1use serde::{Deserialize, Serialize};
10use serde_json::Value;
11
12use crate::error::ControlError;
13
14#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
17#[serde(untagged)]
18pub enum RequestId {
19 Number(i64),
21 String(String),
23 Null,
25}
26
27impl From<i64> for RequestId {
28 fn from(n: i64) -> Self {
29 RequestId::Number(n)
30 }
31}
32
33impl From<&str> for RequestId {
34 fn from(s: &str) -> Self {
35 RequestId::String(s.to_string())
36 }
37}
38
39#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
44pub struct JsonRpcRequest {
45 pub jsonrpc: String,
47 pub id: RequestId,
49 pub method: String,
51 pub params: Value,
53}
54
55impl JsonRpcRequest {
56 pub fn new(id: RequestId, method: impl Into<String>, params: Value) -> JsonRpcRequest {
58 JsonRpcRequest {
59 jsonrpc: "2.0".to_string(),
60 id,
61 method: method.into(),
62 params,
63 }
64 }
65}
66
67#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
72pub struct JsonRpcResponse {
73 pub jsonrpc: String,
75 pub id: RequestId,
77 #[serde(skip_serializing_if = "Option::is_none", default)]
79 pub result: Option<Value>,
80 #[serde(skip_serializing_if = "Option::is_none", default)]
82 pub error: Option<ControlError>,
83}
84
85impl JsonRpcResponse {
86 pub fn success(id: RequestId, result: Value) -> JsonRpcResponse {
88 JsonRpcResponse {
89 jsonrpc: "2.0".to_string(),
90 id,
91 result: Some(result),
92 error: None,
93 }
94 }
95
96 pub fn error(id: RequestId, error: ControlError) -> JsonRpcResponse {
98 JsonRpcResponse {
99 jsonrpc: "2.0".to_string(),
100 id,
101 result: None,
102 error: Some(error),
103 }
104 }
105
106 pub fn into_result(self) -> Result<Value, ControlError> {
108 match (self.result, self.error) {
109 (Some(v), _) => Ok(v),
110 (None, Some(e)) => Err(e),
111 (None, None) => Err(ControlError::of(
112 crate::error::ControlErrorCode::ControlError,
113 "malformed JSON-RPC response: neither result nor error present",
114 )),
115 }
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122 use crate::error::ControlErrorCode;
123 use serde_json::json;
124
125 #[test]
126 fn request_serializes_to_the_canonical_shape() {
127 let req = JsonRpcRequest::new(1.into(), "control.status", json!({}));
128 let v = serde_json::to_value(&req).unwrap();
129 assert_eq!(
130 v,
131 json!({"jsonrpc":"2.0","id":1,"method":"control.status","params":{}})
132 );
133 }
134
135 #[test]
136 fn request_id_supports_number_string_and_null() {
137 assert_eq!(serde_json::to_value(RequestId::from(7)).unwrap(), json!(7));
138 assert_eq!(
139 serde_json::to_value(RequestId::from("abc")).unwrap(),
140 json!("abc")
141 );
142 assert_eq!(serde_json::to_value(RequestId::Null).unwrap(), json!(null));
143 assert_eq!(
144 serde_json::from_value::<RequestId>(json!("z")).unwrap(),
145 RequestId::String("z".into())
146 );
147 }
148
149 #[test]
150 fn success_response_omits_the_error_field() {
151 let resp = JsonRpcResponse::success(1.into(), json!({"ok":true}));
152 let v = serde_json::to_value(&resp).unwrap();
153 assert_eq!(v, json!({"jsonrpc":"2.0","id":1,"result":{"ok":true}}));
154 assert_eq!(resp.into_result().unwrap(), json!({"ok":true}));
155 }
156
157 #[test]
158 fn error_response_omits_the_result_field_and_unwraps_to_err() {
159 let resp = JsonRpcResponse::error(
160 1.into(),
161 ControlError::of(ControlErrorCode::Unauthorized, "no token"),
162 );
163 let v = serde_json::to_value(&resp).unwrap();
164 assert_eq!(v["error"]["data"]["code"], "UNAUTHORIZED");
165 assert!(v.get("result").is_none());
166 assert!(resp.into_result().is_err());
167 }
168
169 #[test]
170 fn a_response_with_neither_field_is_a_control_error() {
171 let resp = JsonRpcResponse {
172 jsonrpc: "2.0".into(),
173 id: RequestId::Number(1),
174 result: None,
175 error: None,
176 };
177 let err = resp.into_result().unwrap_err();
178 assert_eq!(err.code_enum(), Some(ControlErrorCode::ControlError));
179 }
180}