turul_rpc_core/
response.rs1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4use crate::error::JsonRpcError;
5use crate::types::{JsonRpcVersion, RequestId};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9#[serde(untagged)]
10pub enum ResponseResult {
11 Success(Value),
13 Null,
15}
16
17impl ResponseResult {
18 pub fn success(value: Value) -> Self {
19 ResponseResult::Success(value)
20 }
21
22 pub fn null() -> Self {
23 ResponseResult::Null
24 }
25
26 pub fn is_null(&self) -> bool {
27 matches!(self, ResponseResult::Null)
28 }
29
30 pub fn as_value(&self) -> Option<&Value> {
31 match self {
32 ResponseResult::Success(value) => Some(value),
33 ResponseResult::Null => None,
34 }
35 }
36}
37
38impl From<Value> for ResponseResult {
39 fn from(value: Value) -> Self {
40 if value.is_null() {
41 ResponseResult::Null
42 } else {
43 ResponseResult::Success(value)
44 }
45 }
46}
47
48impl From<()> for ResponseResult {
49 fn from(_: ()) -> Self {
50 ResponseResult::Null
51 }
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct JsonRpcSuccessResponse {
58 #[serde(rename = "jsonrpc")]
59 pub version: JsonRpcVersion,
60 pub id: RequestId,
61 pub result: ResponseResult,
62}
63
64impl JsonRpcSuccessResponse {
65 pub fn new(id: RequestId, result: ResponseResult) -> Self {
66 Self {
67 version: JsonRpcVersion::V2_0,
68 id,
69 result,
70 }
71 }
72
73 pub fn success(id: RequestId, result: Value) -> Self {
74 Self::new(id, ResponseResult::Success(result))
75 }
76
77 pub fn null(id: RequestId) -> Self {
78 Self::new(id, ResponseResult::Null)
79 }
80}
81
82impl<T> From<(RequestId, T)> for JsonRpcSuccessResponse
83where
84 T: Into<ResponseResult>,
85{
86 fn from((id, result): (RequestId, T)) -> Self {
87 Self::new(id, result.into())
88 }
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize)]
93#[serde(untagged)]
94pub enum JsonRpcResponse {
95 Success(JsonRpcSuccessResponse),
97 Error(JsonRpcError),
99}
100
101impl JsonRpcResponse {
102 pub fn success(id: RequestId, result: ResponseResult) -> Self {
104 Self::Success(JsonRpcSuccessResponse::new(id, result))
105 }
106
107 pub fn error(error: JsonRpcError) -> Self {
109 Self::Error(error)
110 }
111
112 pub fn is_error(&self) -> bool {
114 matches!(self, JsonRpcResponse::Error(_))
115 }
116
117 pub fn id(&self) -> Option<&RequestId> {
119 match self {
120 JsonRpcResponse::Success(r) => Some(&r.id),
121 JsonRpcResponse::Error(e) => e.id.as_ref(),
122 }
123 }
124}
125
126impl From<JsonRpcSuccessResponse> for JsonRpcResponse {
127 fn from(r: JsonRpcSuccessResponse) -> Self {
128 Self::Success(r)
129 }
130}
131
132impl From<JsonRpcError> for JsonRpcResponse {
133 fn from(e: JsonRpcError) -> Self {
134 Self::Error(e)
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141 use crate::error::{JsonRpcError, JsonRpcErrorObject};
142 use serde_json::{Value, from_str, json, to_string};
143
144 #[test]
145 fn success_response_serializes_with_result_no_error() {
146 let r = JsonRpcSuccessResponse::success(RequestId::Number(1), json!({"ok": true}));
147 let v: Value = serde_json::to_value(&r).unwrap();
148 assert_eq!(v["jsonrpc"], "2.0");
149 assert_eq!(v["id"], 1);
150 assert!(v.get("result").is_some());
151 assert!(v.get("error").is_none());
152 }
153
154 #[test]
155 fn response_union_serializes_success_shape() {
156 let r = JsonRpcResponse::success(
157 RequestId::Number(1),
158 ResponseResult::Success(json!({"ok": true})),
159 );
160 let v: Value = serde_json::to_value(&r).unwrap();
161 assert!(v.get("result").is_some());
162 assert!(v.get("error").is_none());
163 assert!(!r.is_error());
164 }
165
166 #[test]
167 fn response_union_serializes_error_shape() {
168 let err = JsonRpcError::new(
169 Some(RequestId::Number(7)),
170 JsonRpcErrorObject {
171 code: -32601,
172 message: "Method not found".into(),
173 data: None,
174 },
175 );
176 let r = JsonRpcResponse::error(err);
177 let v: Value = serde_json::to_value(&r).unwrap();
178 assert!(v.get("error").is_some());
179 assert!(v.get("result").is_none());
180 assert!(r.is_error());
181 assert_eq!(r.id(), Some(&RequestId::Number(7)));
182 }
183
184 #[test]
185 fn response_union_deserializes_both_shapes() {
186 let ok: JsonRpcResponse = from_str(r#"{"jsonrpc":"2.0","id":1,"result":{"a":1}}"#).unwrap();
187 assert!(matches!(ok, JsonRpcResponse::Success(_)));
188 let err: JsonRpcResponse =
189 from_str(r#"{"jsonrpc":"2.0","id":1,"error":{"code":-32601,"message":"x"}}"#).unwrap();
190 assert!(matches!(err, JsonRpcResponse::Error(_)));
191 }
192
193 #[test]
194 fn success_response_round_trip_and_null() {
195 let r = JsonRpcSuccessResponse::null(RequestId::String("t".into()));
196 let s = to_string(&r).unwrap();
197 let parsed: JsonRpcSuccessResponse = from_str(&s).unwrap();
198 assert_eq!(parsed.id, RequestId::String("t".into()));
199 match parsed.result {
200 ResponseResult::Success(ref val) if val.is_null() => {}
201 ResponseResult::Null => {}
202 _ => panic!("expected null-ish result, got {:?}", parsed.result),
203 }
204 }
205
206 #[test]
207 fn success_response_from_tuple() {
208 let r: JsonRpcSuccessResponse = (RequestId::Number(1), json!({"x": true})).into();
209 assert_eq!(r.id, RequestId::Number(1));
210 }
211
212 #[test]
213 fn response_result_conversion() {
214 assert!(matches!(
215 ResponseResult::from(json!({"d": 42})),
216 ResponseResult::Success(_)
217 ));
218 assert!(matches!(
219 ResponseResult::from(json!(null)),
220 ResponseResult::Null
221 ));
222 assert!(matches!(ResponseResult::from(()), ResponseResult::Null));
223 }
224}