1use std::io;
8
9use serde::{Deserialize, Serialize};
10use serde_json::Value;
11use tokio::io::{AsyncBufReadExt, AsyncReadExt, AsyncWrite, AsyncWriteExt, BufReader};
12use tokio::sync::Mutex;
13
14#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
16#[serde(untagged)]
17pub enum RequestId {
18 Number(i64),
20 String(String),
22}
23
24#[non_exhaustive]
26#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct Message {
28 pub jsonrpc: String,
30 #[serde(skip_serializing_if = "Option::is_none")]
32 pub id: Option<RequestId>,
33 #[serde(skip_serializing_if = "Option::is_none")]
35 pub method: Option<String>,
36 #[serde(skip_serializing_if = "Option::is_none")]
38 pub params: Option<Value>,
39 #[serde(skip_serializing_if = "Option::is_none")]
41 pub result: Option<Value>,
42 #[serde(skip_serializing_if = "Option::is_none")]
44 pub error: Option<JsonRpcError>,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct JsonRpcError {
50 pub code: i32,
52 pub message: String,
54 #[serde(skip_serializing_if = "Option::is_none")]
56 pub data: Option<Value>,
57}
58
59impl JsonRpcError {
60 #[must_use]
62 pub fn new(code: i32, message: impl Into<String>) -> Self {
63 Self {
64 code,
65 message: message.into(),
66 data: None,
67 }
68 }
69
70 #[must_use]
72 pub fn with_data(mut self, data: Value) -> Self {
73 self.data = Some(data);
74 self
75 }
76}
77
78impl Message {
79 #[must_use]
81 pub fn request(id: RequestId, method: impl Into<String>, params: Value) -> Self {
82 Self {
83 jsonrpc: "2.0".to_owned(),
84 id: Some(id),
85 method: Some(method.into()),
86 params: Some(params),
87 result: None,
88 error: None,
89 }
90 }
91
92 #[must_use]
94 pub fn notification(method: impl Into<String>, params: Value) -> Self {
95 Self {
96 jsonrpc: "2.0".to_owned(),
97 id: None,
98 method: Some(method.into()),
99 params: Some(params),
100 result: None,
101 error: None,
102 }
103 }
104
105 #[must_use]
107 pub fn response(id: RequestId, result: Value) -> Self {
108 Self {
109 jsonrpc: "2.0".to_owned(),
110 id: Some(id),
111 method: None,
112 params: None,
113 result: Some(result),
114 error: None,
115 }
116 }
117
118 #[must_use]
120 pub fn response_error(id: RequestId, error: JsonRpcError) -> Self {
121 Self {
122 jsonrpc: "2.0".to_owned(),
123 id: Some(id),
124 method: None,
125 params: None,
126 result: None,
127 error: Some(error),
128 }
129 }
130}
131
132#[non_exhaustive]
134#[derive(Debug, thiserror::Error)]
135pub enum FramingError {
136 #[error(transparent)]
138 Io(#[from] io::Error),
139 #[error("malformed Content-Length header: {0}")]
141 BadHeader(String),
142 #[error("malformed JSON body: {0}")]
144 BadJson(#[from] serde_json::Error),
145 #[error("peer closed the stream")]
147 Closed,
148}
149
150pub async fn read_message<R>(reader: &mut BufReader<R>) -> Result<Message, FramingError>
155where
156 R: AsyncReadExt + Unpin,
157{
158 let mut content_length: Option<usize> = None;
159 let mut line = String::new();
160 loop {
161 line.clear();
162 let read = reader.read_line(&mut line).await?;
163 if read == 0 {
164 return Err(FramingError::Closed);
165 }
166 let trimmed = line.trim_end_matches(['\r', '\n']);
167 if trimmed.is_empty() {
168 break;
169 }
170 if let Some(value) = trimmed.strip_prefix("Content-Length:") {
171 content_length = Some(
172 value
173 .trim()
174 .parse()
175 .map_err(|_| FramingError::BadHeader(trimmed.to_owned()))?,
176 );
177 }
178 }
179 let length = content_length.ok_or_else(|| FramingError::BadHeader("missing".to_owned()))?;
180 let mut body = vec![0u8; length];
181 reader.read_exact(&mut body).await?;
182 let message: Message = serde_json::from_slice(&body)?;
183 Ok(message)
184}
185
186pub struct MessageWriter<W: AsyncWrite + Unpin + Send> {
188 inner: Mutex<W>,
189}
190
191impl<W: AsyncWrite + Unpin + Send> MessageWriter<W> {
192 pub fn new(writer: W) -> Self {
194 Self {
195 inner: Mutex::new(writer),
196 }
197 }
198
199 pub async fn write_message(&self, message: &Message) -> Result<(), FramingError> {
205 let body = serde_json::to_vec(message)?;
206 let mut guard = self.inner.lock().await;
207 let header = format!("Content-Length: {}\r\n\r\n", body.len());
208 guard.write_all(header.as_bytes()).await?;
209 guard.write_all(&body).await?;
210 guard.flush().await?;
211 Ok(())
212 }
213}