Skip to main content

csskit_lsp/jsonrpc/
response.rs

1use serde::{Deserialize, Deserializer, Serialize, de::Error};
2use serde_json::{Value, json, to_value};
3
4use super::{ErrorCode, Id};
5
6/// A Response Message sent as a result of a request
7///
8/// As defined in [JSON-RPC](https://www.jsonrpc.org/specification#response_object) and [LSP](https://microsoft.github.io/language-server-protocol/specifications/specification-3-16/#responseMessage).
9///
10/// Can either be [`Ok`] (the response had a `result`, and did not have attached `error` information),
11/// or as [`Err`] (the response had no `result`, instead having attached `error` information).
12///
13/// Both [`Ok`] and [`Err`] have an [`Id`] which matches the [`Id`] of the [`super::Request`] object.
14#[derive(Debug, Clone, PartialEq, Eq, Hash)]
15pub enum Response {
16	/// [`Ok`]s also have an optional payload (serialised as a serde [`Value`]) which may contain more data about the
17	/// response, such as computed values.
18	Ok(Id, Value),
19	/// [`Err`]s also have an [`ErrorCode`] to determine which error occurred, an informatinal [`String`], and may contain
20	/// additional data (serialised as serde [`Value`]).
21	Err(Id, ErrorCode, String, Value),
22}
23
24impl Serialize for Response {
25	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
26	where
27		S: serde::Serializer,
28	{
29		#[derive(Serialize)]
30		struct Res<'a> {
31			id: &'a Id,
32			#[serde(skip_serializing_if = "Option::is_none")]
33			result: Option<&'a Value>,
34			#[serde(skip_serializing_if = "Option::is_none")]
35			error: Option<Value>,
36		}
37		if let Response::Ok(id, value) = self {
38			Res { id, result: Some(value), error: None }.serialize(serializer)
39		} else if let Response::Err(id, code, message, value) = self {
40			Res {
41				id,
42				result: None,
43				error: Some(json!({
44					"code": code, "message": message, "data": value
45				})),
46			}
47			.serialize(serializer)
48		} else {
49			unreachable!()
50		}
51	}
52}
53
54impl<'de> Deserialize<'de> for Response {
55	fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
56	where
57		D: Deserializer<'de>,
58	{
59		#[derive(Deserialize)]
60		struct Err {
61			code: i32,
62			message: String,
63			#[serde(default = "serde_json::Value::default")]
64			data: Value,
65		}
66
67		#[derive(Deserialize)]
68		struct Res {
69			id: Id,
70			result: Option<Value>,
71			error: Option<Err>,
72		}
73
74		let res = Res::deserialize(deserializer)?;
75		if let Some(result) = res.result {
76			if res.error.is_some() {
77				Err(Error::duplicate_field("error"))?
78			}
79			Ok(Response::Ok(res.id, result))
80		} else if let Some(error) = res.error {
81			if res.result.is_some() {
82				Err(Error::duplicate_field("result"))?
83			}
84			Ok(Response::Err(res.id, error.code.into(), error.message, error.data))
85		} else {
86			Err(Error::missing_field("result"))?
87		}
88	}
89}
90
91impl Response {
92	pub fn new_ok<T>(id: Id, result: T) -> Response
93	where
94		T: Serialize,
95	{
96		Response::Ok(id, to_value(result).unwrap())
97	}
98
99	pub fn new_err<T>(id: Id, error_code: ErrorCode, message: String, value: T) -> Response
100	where
101		T: Serialize,
102	{
103		Response::Err(id, error_code, message, to_value(value).unwrap())
104	}
105}
106
107#[cfg(test)]
108mod tests {
109	use serde_json::{from_str, json, to_string};
110
111	use super::*;
112
113	#[test]
114	fn test_response_serialize_ok_omits_error() {
115		let response = Response::Ok(1.into(), json!({"capabilities": {}}));
116		let serialized = to_string(&response).unwrap();
117		assert!(serialized.contains("\"result\""), "should contain result");
118		assert!(!serialized.contains("\"error\""), "should not contain error key");
119		assert_eq!(serialized, r#"{"id":1,"result":{"capabilities":{}}}"#);
120	}
121
122	#[test]
123	fn test_response_serialize_err_omits_result() {
124		let response = Response::Err(1.into(), ErrorCode::ParseError, "Parse error".into(), Value::Null);
125		let serialized = to_string(&response).unwrap();
126		assert!(serialized.contains("\"error\""), "should contain error");
127		assert!(!serialized.contains("\"result\""), "should not contain result key");
128	}
129
130	#[test]
131	fn test_response_deserialize() {
132		assert_eq!(from_str::<Response>(r#"{"id":3, "result":7}"#).unwrap(), Response::Ok(3.into(), json!(7)));
133		assert_eq!(
134			from_str::<Response>(r#"{"id":4, "result":["a", "b"]}"#).unwrap(),
135			Response::Ok(4.into(), json!(["a", "b"]))
136		);
137		assert_eq!(
138			from_str::<Response>(r#"{"id":"a", "error":{"code": -32700, "message": "Parse error"}}"#).unwrap(),
139			Response::Err("a".into(), ErrorCode::ParseError, "Parse error".into(), Value::Null)
140		);
141		assert_eq!(
142			from_str::<Response>(
143				r#"{"id":"foo", "error":{"code": -32600, "message": "Invalid Request", "data": ["foo"]}}"#
144			)
145			.unwrap(),
146			Response::Err("foo".into(), ErrorCode::InvalidRequest, "Invalid Request".into(), json!(["foo"]))
147		);
148	}
149
150	#[test]
151	fn test_response_deserialize_error() {
152		// Missing result/error
153		assert!(from_str::<Response>(r#"{"id":3}"#).is_err());
154
155		// Missing error Code/Message
156		assert!(from_str::<Response>(r#"{"id":3, "error":{}}"#).is_err());
157
158		// Missing error Message
159		assert!(from_str::<Response>(r#"{"id":3, "error":{"code":0}}"#).is_err());
160
161		// Missing error Code
162		assert!(from_str::<Response>(r#"{"id":3, "error":{"message":""}}"#).is_err());
163
164		// Both error/result present
165		assert!(from_str::<Response>(r#"{"id":3, "error":{"code":0, "message": ""}, "result":7}"#).is_err());
166		assert!(from_str::<Response>(r#"{"id":3, "result":7, "error":{"code":0, "message": ""}}"#).is_err());
167	}
168}