use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum JsonRpcId {
String(String),
Number(i64),
Null,
}
impl From<&str> for JsonRpcId {
fn from(value: &str) -> Self {
Self::String(value.to_string())
}
}
impl From<String> for JsonRpcId {
fn from(value: String) -> Self {
Self::String(value)
}
}
impl From<i64> for JsonRpcId {
fn from(value: i64) -> Self {
Self::Number(value)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct JsonRpcRequest {
pub jsonrpc: String,
pub id: JsonRpcId,
pub method: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub params: Option<Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct JsonRpcNotification {
pub jsonrpc: String,
pub method: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub params: Option<Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct JsonRpcError {
pub code: i64,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct JsonRpcResponse {
pub jsonrpc: String,
pub id: JsonRpcId,
#[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, PartialEq)]
pub enum JsonRpcMessage {
Request(JsonRpcRequest),
Notification(JsonRpcNotification),
Response(JsonRpcResponse),
}
pub mod error_codes {
pub const PARSE_ERROR: i64 = -32700;
pub const INVALID_REQUEST: i64 = -32600;
pub const METHOD_NOT_FOUND: i64 = -32601;
pub const INVALID_PARAMS: i64 = -32602;
pub const INTERNAL_ERROR: i64 = -32603;
}
pub trait JsonRpcTransport {
fn start(&mut self) {}
fn stop(&mut self);
fn send(&mut self, response: JsonRpcResponse);
fn notify(&mut self, method: &str, params: Option<Value>);
fn send_result(&mut self, id: JsonRpcId, result: Value);
fn send_error(&mut self, id: JsonRpcId, code: i64, message: &str, data: Option<Value>);
}
#[derive(Debug, Default, Clone)]
pub struct LineBasedJsonRpcTransport {
pub pending: HashMap<JsonRpcIdKey, String>,
pub next_request_id: u64,
pub stopped: bool,
pub sent_lines: Vec<String>,
}
pub type JsonRpcIdKey = String;
impl LineBasedJsonRpcTransport {
pub fn new() -> Self {
Self {
next_request_id: 1,
..Self::default()
}
}
pub fn parse_line(&self, line: &str) -> Result<Option<JsonRpcMessage>, Box<JsonRpcResponse>> {
let trimmed = line.trim();
if trimmed.is_empty() {
return Ok(None);
}
let parsed: Value = match serde_json::from_str(trimmed) {
Ok(value) => value,
Err(_) => {
return Err(boxed_error_response(
JsonRpcId::Null,
error_codes::PARSE_ERROR,
"Parse error: invalid JSON",
None,
));
}
};
let Some(obj) = parsed.as_object() else {
return Err(boxed_error_response(
JsonRpcId::Null,
error_codes::INVALID_REQUEST,
"Invalid Request: not a valid JSON-RPC 2.0 message",
None,
));
};
if obj.get("jsonrpc").and_then(Value::as_str) != Some("2.0") {
return Err(boxed_error_response(
JsonRpcId::Null,
error_codes::INVALID_REQUEST,
"Invalid Request: not a valid JSON-RPC 2.0 message",
None,
));
}
if obj.get("method").and_then(Value::as_str).is_none()
&& obj.contains_key("id")
&& (obj.contains_key("result") || obj.contains_key("error"))
{
let response: JsonRpcResponse = serde_json::from_value(parsed).map_err(|_| {
boxed_error_response(
JsonRpcId::Null,
error_codes::INVALID_REQUEST,
"Invalid Request: not a valid JSON-RPC 2.0 response",
None,
)
})?;
return Ok(Some(JsonRpcMessage::Response(response)));
}
if obj.get("method").and_then(Value::as_str).is_none() {
return Err(boxed_error_response(
JsonRpcId::Null,
error_codes::INVALID_REQUEST,
"Invalid Request: not a valid JSON-RPC 2.0 message",
None,
));
}
if obj.contains_key("id") {
Ok(Some(JsonRpcMessage::Request(
serde_json::from_value(parsed).map_err(|_| {
boxed_error_response(
JsonRpcId::Null,
error_codes::INVALID_REQUEST,
"Invalid Request: not a valid JSON-RPC 2.0 request",
None,
)
})?,
)))
} else {
Ok(Some(JsonRpcMessage::Notification(
serde_json::from_value(parsed).map_err(|_| {
boxed_error_response(
JsonRpcId::Null,
error_codes::INVALID_REQUEST,
"Invalid Request: not a valid JSON-RPC 2.0 notification",
None,
)
})?,
)))
}
}
pub fn write_json(&mut self, value: &impl Serialize) {
if let Ok(mut line) = serde_json::to_string(value) {
line.push('\n');
self.sent_lines.push(line);
}
}
}
impl JsonRpcTransport for LineBasedJsonRpcTransport {
fn stop(&mut self) {
self.stopped = true;
self.pending.clear();
}
fn send(&mut self, response: JsonRpcResponse) {
self.write_json(&response);
}
fn notify(&mut self, method: &str, params: Option<Value>) {
self.write_json(&JsonRpcNotification {
jsonrpc: "2.0".to_string(),
method: method.to_string(),
params,
});
}
fn send_result(&mut self, id: JsonRpcId, result: Value) {
self.send(JsonRpcResponse {
jsonrpc: "2.0".to_string(),
id,
result: Some(result),
error: None,
});
}
fn send_error(&mut self, id: JsonRpcId, code: i64, message: &str, data: Option<Value>) {
self.send(error_response(id, code, message, data));
}
}
#[derive(Debug, Clone)]
pub struct StdioTransport {
inner: LineBasedJsonRpcTransport,
pub exit_on_close: bool,
}
impl Default for StdioTransport {
fn default() -> Self {
Self {
inner: LineBasedJsonRpcTransport::new(),
exit_on_close: true,
}
}
}
impl JsonRpcTransport for StdioTransport {
fn stop(&mut self) {
self.inner.stop();
}
fn send(&mut self, response: JsonRpcResponse) {
self.inner.send(response);
}
fn notify(&mut self, method: &str, params: Option<Value>) {
self.inner.notify(method, params);
}
fn send_result(&mut self, id: JsonRpcId, result: Value) {
self.inner.send_result(id, result);
}
fn send_error(&mut self, id: JsonRpcId, code: i64, message: &str, data: Option<Value>) {
self.inner.send_error(id, code, message, data);
}
}
#[derive(Debug, Clone)]
pub struct SocketTransport {
inner: LineBasedJsonRpcTransport,
pub prefix: String,
pub raw_lines: Vec<String>,
}
impl SocketTransport {
pub fn new(prefix: impl Into<String>) -> Self {
Self {
inner: LineBasedJsonRpcTransport::new(),
prefix: prefix.into(),
raw_lines: Vec::new(),
}
}
pub fn write_raw(&mut self, line: &str) {
let mut line = line.to_string();
if !line.ends_with('\n') {
line.push('\n');
}
self.raw_lines.push(line);
}
}
impl JsonRpcTransport for SocketTransport {
fn stop(&mut self) {
self.inner.stop();
}
fn send(&mut self, response: JsonRpcResponse) {
self.inner.send(response);
}
fn notify(&mut self, method: &str, params: Option<Value>) {
self.inner.notify(method, params);
}
fn send_result(&mut self, id: JsonRpcId, result: Value) {
self.inner.send_result(id, result);
}
fn send_error(&mut self, id: JsonRpcId, code: i64, message: &str, data: Option<Value>) {
self.inner.send_error(id, code, message, data);
}
}
pub fn error_response(
id: JsonRpcId,
code: i64,
message: &str,
data: Option<Value>,
) -> JsonRpcResponse {
JsonRpcResponse {
jsonrpc: "2.0".to_string(),
id,
result: None,
error: Some(JsonRpcError {
code,
message: message.to_string(),
data,
}),
}
}
fn boxed_error_response(
id: JsonRpcId,
code: i64,
message: &str,
data: Option<Value>,
) -> Box<JsonRpcResponse> {
Box::new(error_response(id, code, message, data))
}