use std::io;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use tokio::io::{AsyncBufReadExt, AsyncReadExt, AsyncWrite, AsyncWriteExt, BufReader};
use tokio::sync::Mutex;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[serde(untagged)]
pub enum RequestId {
Number(i64),
String(String),
}
#[non_exhaustive]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Message {
pub jsonrpc: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<RequestId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub method: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub params: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub result: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<JsonRpcError>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonRpcError {
pub code: i32,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<Value>,
}
impl JsonRpcError {
#[must_use]
pub fn new(code: i32, message: impl Into<String>) -> Self {
Self {
code,
message: message.into(),
data: None,
}
}
#[must_use]
pub fn with_data(mut self, data: Value) -> Self {
self.data = Some(data);
self
}
}
impl Message {
#[must_use]
pub fn request(id: RequestId, method: impl Into<String>, params: Value) -> Self {
Self {
jsonrpc: "2.0".to_owned(),
id: Some(id),
method: Some(method.into()),
params: Some(params),
result: None,
error: None,
}
}
#[must_use]
pub fn notification(method: impl Into<String>, params: Value) -> Self {
Self {
jsonrpc: "2.0".to_owned(),
id: None,
method: Some(method.into()),
params: Some(params),
result: None,
error: None,
}
}
#[must_use]
pub fn response(id: RequestId, result: Value) -> Self {
Self {
jsonrpc: "2.0".to_owned(),
id: Some(id),
method: None,
params: None,
result: Some(result),
error: None,
}
}
#[must_use]
pub fn response_error(id: RequestId, error: JsonRpcError) -> Self {
Self {
jsonrpc: "2.0".to_owned(),
id: Some(id),
method: None,
params: None,
result: None,
error: Some(error),
}
}
}
#[non_exhaustive]
#[derive(Debug, thiserror::Error)]
pub enum FramingError {
#[error(transparent)]
Io(#[from] io::Error),
#[error("malformed Content-Length header: {0}")]
BadHeader(String),
#[error("malformed JSON body: {0}")]
BadJson(#[from] serde_json::Error),
#[error("peer closed the stream")]
Closed,
}
pub async fn read_message<R>(reader: &mut BufReader<R>) -> Result<Message, FramingError>
where
R: AsyncReadExt + Unpin,
{
let mut content_length: Option<usize> = None;
let mut line = String::new();
loop {
line.clear();
let read = reader.read_line(&mut line).await?;
if read == 0 {
return Err(FramingError::Closed);
}
let trimmed = line.trim_end_matches(['\r', '\n']);
if trimmed.is_empty() {
break;
}
if let Some(value) = trimmed.strip_prefix("Content-Length:") {
content_length = Some(
value
.trim()
.parse()
.map_err(|_| FramingError::BadHeader(trimmed.to_owned()))?,
);
}
}
let length = content_length.ok_or_else(|| FramingError::BadHeader("missing".to_owned()))?;
let mut body = vec![0u8; length];
reader.read_exact(&mut body).await?;
let message: Message = serde_json::from_slice(&body)?;
Ok(message)
}
pub struct MessageWriter<W: AsyncWrite + Unpin + Send> {
inner: Mutex<W>,
}
impl<W: AsyncWrite + Unpin + Send> MessageWriter<W> {
pub fn new(writer: W) -> Self {
Self {
inner: Mutex::new(writer),
}
}
pub async fn write_message(&self, message: &Message) -> Result<(), FramingError> {
let body = serde_json::to_vec(message)?;
let mut guard = self.inner.lock().await;
let header = format!("Content-Length: {}\r\n\r\n", body.len());
guard.write_all(header.as_bytes()).await?;
guard.write_all(&body).await?;
guard.flush().await?;
Ok(())
}
}