use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_json::Value;
use std::fmt;
use crate::types::RequestId;
pub const JSONRPC_VERSION: &str = "2.0";
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct JsonRpcVersion;
impl Serialize for JsonRpcVersion {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(JSONRPC_VERSION)
}
}
impl<'de> Deserialize<'de> for JsonRpcVersion {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let version = String::deserialize(deserializer)?;
if version == JSONRPC_VERSION {
Ok(JsonRpcVersion)
} else {
Err(serde::de::Error::custom(format!(
"Invalid JSON-RPC version: expected '{JSONRPC_VERSION}', got '{version}'"
)))
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonRpcRequest {
pub jsonrpc: JsonRpcVersion,
pub method: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub params: Option<Value>,
pub id: RequestId,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum JsonRpcResponsePayload {
Success {
result: Value,
},
Error {
error: JsonRpcError,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonRpcResponse {
pub jsonrpc: JsonRpcVersion,
#[serde(flatten)]
pub payload: JsonRpcResponsePayload,
pub id: ResponseId,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct ResponseId(pub Option<RequestId>);
impl ResponseId {
pub fn from_request(id: RequestId) -> Self {
Self(Some(id))
}
pub fn null() -> Self {
Self(None)
}
pub fn as_request_id(&self) -> Option<&RequestId> {
self.0.as_ref()
}
pub fn is_null(&self) -> bool {
self.0.is_none()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonRpcNotification {
pub jsonrpc: JsonRpcVersion,
pub method: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub params: Option<Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct JsonRpcError {
pub code: i32,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub data: Option<Value>,
}
impl JsonRpcError {
const SERVER_ERROR_RANGE: std::ops::RangeInclusive<i32> = -32099..=-32000;
const STANDARD_CODES: &'static [i32] = &[-32700, -32600, -32601, -32602, -32603];
pub fn new(code: i32, message: impl Into<String>) -> Self {
if !Self::is_valid_code(code) {
tracing::warn!(
code,
"JSON-RPC error code outside reserved server-error range -32099..=-32000 \
and not a standardized JSON-RPC 2.0 code; this risks colliding with \
future spec assignments"
);
}
Self {
code,
message: message.into(),
data: None,
}
}
pub fn with_validated_code(
code: i32,
message: impl Into<String>,
) -> Result<Self, &'static str> {
if !Self::is_valid_code(code) {
return Err(
"JSON-RPC error code must be a standardized code or in the -32099..=-32000 server-error range",
);
}
Ok(Self {
code,
message: message.into(),
data: None,
})
}
fn is_valid_code(code: i32) -> bool {
Self::SERVER_ERROR_RANGE.contains(&code) || Self::STANDARD_CODES.contains(&code)
}
pub fn with_data(code: i32, message: impl Into<String>, data: Value) -> Self {
if !Self::is_valid_code(code) {
tracing::warn!(
code,
"JSON-RPC error code outside reserved server-error range -32099..=-32000"
);
}
Self {
code,
message: message.into(),
data: Some(data),
}
}
pub fn parse_error() -> Self {
Self::new(-32700, "Parse error")
}
pub fn parse_error_with_details(details: impl Into<String>) -> Self {
Self::with_data(
-32700,
"Parse error",
serde_json::json!({ "details": details.into() }),
)
}
pub fn invalid_request() -> Self {
Self::new(-32600, "Invalid Request")
}
pub fn invalid_request_with_reason(reason: impl Into<String>) -> Self {
Self::with_data(
-32600,
"Invalid Request",
serde_json::json!({ "reason": reason.into() }),
)
}
pub fn method_not_found(method: &str) -> Self {
Self::new(-32601, format!("Method not found: {method}"))
}
pub fn invalid_params(details: &str) -> Self {
Self::new(-32602, format!("Invalid params: {details}"))
}
pub fn internal_error(details: &str) -> Self {
Self::new(-32603, format!("Internal error: {details}"))
}
pub fn is_parse_error(&self) -> bool {
self.code == -32700
}
pub fn is_invalid_request(&self) -> bool {
self.code == -32600
}
pub fn code(&self) -> i32 {
self.code
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum JsonRpcErrorCode {
ParseError,
InvalidRequest,
MethodNotFound,
InvalidParams,
InternalError,
ApplicationError(i32),
}
impl JsonRpcErrorCode {
pub fn code(&self) -> i32 {
match self {
Self::ParseError => -32700,
Self::InvalidRequest => -32600,
Self::MethodNotFound => -32601,
Self::InvalidParams => -32602,
Self::InternalError => -32603,
Self::ApplicationError(code) => *code,
}
}
pub fn message(&self) -> &'static str {
match self {
Self::ParseError => "Parse error",
Self::InvalidRequest => "Invalid Request",
Self::MethodNotFound => "Method not found",
Self::InvalidParams => "Invalid params",
Self::InternalError => "Internal error",
Self::ApplicationError(_) => "Application error",
}
}
}
impl fmt::Display for JsonRpcErrorCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} ({})", self.message(), self.code())
}
}
impl From<JsonRpcErrorCode> for JsonRpcError {
fn from(code: JsonRpcErrorCode) -> Self {
Self {
code: code.code(),
message: code.message().to_string(),
data: None,
}
}
}
impl From<i32> for JsonRpcErrorCode {
fn from(code: i32) -> Self {
match code {
-32700 => Self::ParseError,
-32600 => Self::InvalidRequest,
-32601 => Self::MethodNotFound,
-32602 => Self::InvalidParams,
-32603 => Self::InternalError,
other => Self::ApplicationError(other),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum JsonRpcMessage {
Request(JsonRpcRequest),
Response(JsonRpcResponse),
Notification(JsonRpcNotification),
}
impl JsonRpcRequest {
pub fn new(method: String, params: Option<Value>, id: RequestId) -> Self {
Self {
jsonrpc: JsonRpcVersion,
method,
params,
id,
}
}
pub fn without_params(method: String, id: RequestId) -> Self {
Self::new(method, None, id)
}
pub fn with_params<P: Serialize>(
method: String,
params: P,
id: RequestId,
) -> Result<Self, serde_json::Error> {
let params_value = serde_json::to_value(params)?;
Ok(Self::new(method, Some(params_value), id))
}
}
impl JsonRpcResponse {
pub fn success(result: Value, id: RequestId) -> Self {
Self {
jsonrpc: JsonRpcVersion,
payload: JsonRpcResponsePayload::Success { result },
id: ResponseId::from_request(id),
}
}
pub fn error_response(error: JsonRpcError, id: RequestId) -> Self {
Self {
jsonrpc: JsonRpcVersion,
payload: JsonRpcResponsePayload::Error { error },
id: ResponseId::from_request(id),
}
}
pub fn parse_error(message: Option<String>) -> Self {
let error = JsonRpcError {
code: JsonRpcErrorCode::ParseError.code(),
message: message.unwrap_or_else(|| JsonRpcErrorCode::ParseError.message().to_string()),
data: None,
};
Self {
jsonrpc: JsonRpcVersion,
payload: JsonRpcResponsePayload::Error { error },
id: ResponseId::null(),
}
}
pub fn is_success(&self) -> bool {
matches!(self.payload, JsonRpcResponsePayload::Success { .. })
}
pub fn is_error(&self) -> bool {
matches!(self.payload, JsonRpcResponsePayload::Error { .. })
}
pub fn result(&self) -> Option<&Value> {
match &self.payload {
JsonRpcResponsePayload::Success { result } => Some(result),
JsonRpcResponsePayload::Error { .. } => None,
}
}
pub fn error(&self) -> Option<&JsonRpcError> {
match &self.payload {
JsonRpcResponsePayload::Success { .. } => None,
JsonRpcResponsePayload::Error { error } => Some(error),
}
}
pub fn request_id(&self) -> Option<&RequestId> {
self.id.as_request_id()
}
pub fn is_parse_error(&self) -> bool {
self.id.is_null()
}
pub fn result_mut(&mut self) -> Option<&mut Value> {
match &mut self.payload {
JsonRpcResponsePayload::Success { result } => Some(result),
JsonRpcResponsePayload::Error { .. } => None,
}
}
pub fn error_mut(&mut self) -> Option<&mut JsonRpcError> {
match &mut self.payload {
JsonRpcResponsePayload::Success { .. } => None,
JsonRpcResponsePayload::Error { error } => Some(error),
}
}
pub fn set_result(&mut self, result: Value) {
self.payload = JsonRpcResponsePayload::Success { result };
}
pub fn set_error(&mut self, error: JsonRpcError) {
self.payload = JsonRpcResponsePayload::Error { error };
}
}
impl JsonRpcNotification {
pub fn new(method: String, params: Option<Value>) -> Self {
Self {
jsonrpc: JsonRpcVersion,
method,
params,
}
}
pub fn without_params(method: String) -> Self {
Self::new(method, None)
}
pub fn with_params<P: Serialize>(method: String, params: P) -> Result<Self, serde_json::Error> {
let params_value = serde_json::to_value(params)?;
Ok(Self::new(method, Some(params_value)))
}
}
pub mod utils {
use super::*;
pub fn parse_message(json: &str) -> Result<JsonRpcMessage, serde_json::Error> {
serde_json::from_str(json)
}
pub fn serialize_message(message: &JsonRpcMessage) -> Result<String, serde_json::Error> {
serde_json::to_string(message)
}
pub fn extract_method(json: &str) -> Option<String> {
if let Ok(value) = serde_json::from_str::<serde_json::Value>(json)
&& let Some(method) = value.get("method")
{
return method.as_str().map(String::from);
}
None
}
}
pub mod http {
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HttpJsonRpcRequest {
pub jsonrpc: String,
#[serde(default)]
pub id: Option<Value>,
pub method: String,
#[serde(default)]
pub params: Option<Value>,
}
impl HttpJsonRpcRequest {
pub fn is_valid(&self) -> bool {
self.jsonrpc == "2.0" && !self.method.is_empty()
}
pub fn is_notification(&self) -> bool {
self.id.is_none()
}
pub fn id_string(&self) -> Option<String> {
self.id.as_ref().map(|v| match v {
Value::String(s) => s.clone(),
Value::Number(n) => n.to_string(),
_ => v.to_string(),
})
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HttpJsonRpcResponse {
pub jsonrpc: String,
#[serde(default)]
pub id: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub result: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<super::JsonRpcError>,
}
impl HttpJsonRpcResponse {
pub fn success(id: Option<Value>, result: Value) -> Self {
Self {
jsonrpc: "2.0".to_string(),
id,
result: Some(result),
error: None,
}
}
pub fn error(id: Option<Value>, error: super::JsonRpcError) -> Self {
Self {
jsonrpc: "2.0".to_string(),
id,
result: None,
error: Some(error),
}
}
pub fn error_from_code(id: Option<Value>, code: i32, message: impl Into<String>) -> Self {
Self::error(id, super::JsonRpcError::new(code, message))
}
pub fn invalid_request(id: Option<Value>, reason: impl Into<String>) -> Self {
Self::error(id, super::JsonRpcError::invalid_request_with_reason(reason))
}
pub fn parse_error(details: Option<String>) -> Self {
Self::error(
None,
details
.map(super::JsonRpcError::parse_error_with_details)
.unwrap_or_else(super::JsonRpcError::parse_error),
)
}
pub fn internal_error(id: Option<Value>, details: &str) -> Self {
Self::error(id, super::JsonRpcError::internal_error(details))
}
pub fn method_not_found(id: Option<Value>, method: &str) -> Self {
Self::error(id, super::JsonRpcError::method_not_found(method))
}
pub fn is_error(&self) -> bool {
self.error.is_some()
}
pub fn is_success(&self) -> bool {
self.result.is_some() && self.error.is_none()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_http_request_parsing() {
let json = r#"{"jsonrpc":"2.0","method":"test","id":1,"params":{"key":"value"}}"#;
let request: HttpJsonRpcRequest = serde_json::from_str(json).unwrap();
assert!(request.is_valid());
assert!(!request.is_notification());
assert_eq!(request.method, "test");
}
#[test]
fn test_http_request_invalid_version() {
let json = r#"{"jsonrpc":"1.0","method":"test","id":1}"#;
let request: HttpJsonRpcRequest = serde_json::from_str(json).unwrap();
assert!(!request.is_valid());
}
#[test]
fn test_http_response_success() {
let response = HttpJsonRpcResponse::success(
Some(Value::Number(1.into())),
serde_json::json!({"result": "ok"}),
);
assert!(response.is_success());
assert!(!response.is_error());
}
#[test]
fn test_http_response_error() {
let response = HttpJsonRpcResponse::invalid_request(
Some(Value::String("req-1".into())),
"jsonrpc must be 2.0",
);
assert!(!response.is_success());
assert!(response.is_error());
}
#[test]
fn test_http_response_serialization() {
let response = HttpJsonRpcResponse::success(
Some(Value::Number(1.into())),
serde_json::json!({"data": "test"}),
);
let json = serde_json::to_string(&response).unwrap();
assert!(json.contains(r#""jsonrpc":"2.0""#));
assert!(json.contains(r#""result""#));
assert!(!json.contains(r#""error""#));
}
}
}
#[cfg(test)]
#[allow(deprecated)] mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_jsonrpc_version() {
let version = JsonRpcVersion;
let json = serde_json::to_string(&version).unwrap();
assert_eq!(json, "\"2.0\"");
let parsed: JsonRpcVersion = serde_json::from_str(&json).unwrap();
assert_eq!(parsed, version);
}
#[test]
fn test_request_creation() {
let request = JsonRpcRequest::new(
"test_method".to_string(),
Some(json!({"key": "value"})),
RequestId::String("test-id".to_string()),
);
assert_eq!(request.method, "test_method");
assert!(request.params.is_some());
}
#[test]
fn test_response_creation() {
let response = JsonRpcResponse::success(
json!({"result": "success"}),
RequestId::String("test-id".to_string()),
);
assert!(response.is_success());
assert!(!response.is_error());
assert!(response.result().is_some());
assert!(response.error().is_none());
assert!(!response.is_parse_error());
}
#[test]
fn test_error_response() {
let error = JsonRpcError::from(JsonRpcErrorCode::MethodNotFound);
let response =
JsonRpcResponse::error_response(error, RequestId::String("test-id".to_string()));
assert!(!response.is_success());
assert!(response.is_error());
assert!(response.result().is_none());
assert!(response.error().is_some());
assert!(!response.is_parse_error());
}
#[test]
fn test_parse_error_response() {
let response = JsonRpcResponse::parse_error(Some("Invalid JSON".to_string()));
assert!(!response.is_success());
assert!(response.is_error());
assert!(response.result().is_none());
assert!(response.error().is_some());
assert!(response.is_parse_error());
assert!(response.request_id().is_none());
let error = response.error().unwrap();
assert_eq!(error.code, JsonRpcErrorCode::ParseError.code());
assert_eq!(error.message, "Invalid JSON");
}
#[test]
fn test_notification() {
let notification = JsonRpcNotification::without_params("test_notification".to_string());
assert_eq!(notification.method, "test_notification");
assert!(notification.params.is_none());
}
#[test]
fn test_serialization() {
let request = JsonRpcRequest::new(
"test_method".to_string(),
Some(json!({"param": "value"})),
RequestId::String("123".to_string()),
);
let json = serde_json::to_string(&request).unwrap();
let parsed: JsonRpcRequest = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.method, request.method);
assert_eq!(parsed.params, request.params);
}
#[test]
fn test_utils() {
let json = r#"{"jsonrpc":"2.0","method":"test","id":"123"}"#;
assert_eq!(utils::extract_method(json), Some("test".to_string()));
}
#[test]
fn test_error_codes() {
let parse_error = JsonRpcErrorCode::ParseError;
assert_eq!(parse_error.code(), -32700);
assert_eq!(parse_error.message(), "Parse error");
let app_error = JsonRpcErrorCode::ApplicationError(-32001);
assert_eq!(app_error.code(), -32001);
}
}