use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::fmt;
pub const JSONRPC_VERSION: &str = "2.0";
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(untagged)]
pub enum RequestId {
String(String),
Number(i64),
}
impl fmt::Display for RequestId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RequestId::String(s) => write!(f, "{}", s),
RequestId::Number(n) => write!(f, "{}", n),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonRpcRequest {
pub jsonrpc: String,
pub id: RequestId,
pub method: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub params: Option<Value>,
}
impl JsonRpcRequest {
pub fn new(id: RequestId, method: String, params: Option<Value>) -> Self {
Self {
jsonrpc: JSONRPC_VERSION.to_string(),
id,
method,
params,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ErrorCode {
ParseError = -32700,
InvalidRequest = -32600,
MethodNotFound = -32601,
InvalidParams = -32602,
InternalError = -32603,
ServerError = -32000,
}
#[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 {
pub fn new(code: ErrorCode, message: String, data: Option<Value>) -> Self {
Self {
code: code as i32,
message,
data,
}
}
pub fn parse_error() -> Self {
Self::new(ErrorCode::ParseError, "Parse error".to_string(), None)
}
pub fn invalid_request() -> Self {
Self::new(
ErrorCode::InvalidRequest,
"Invalid Request".to_string(),
None,
)
}
pub fn method_not_found(method: &str) -> Self {
Self::new(
ErrorCode::MethodNotFound,
format!("Method not found: {}", method),
None,
)
}
pub fn invalid_params(message: String) -> Self {
Self::new(ErrorCode::InvalidParams, message, None)
}
pub fn internal_error(message: String) -> Self {
Self::new(ErrorCode::InternalError, message, None)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonRpcResponse {
pub jsonrpc: String,
pub id: RequestId,
#[serde(skip_serializing_if = "Option::is_none")]
pub result: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<JsonRpcError>,
}
impl JsonRpcResponse {
pub fn success(id: RequestId, result: Value) -> Self {
Self {
jsonrpc: JSONRPC_VERSION.to_string(),
id,
result: Some(result),
error: None,
}
}
pub fn error(id: RequestId, error: JsonRpcError) -> Self {
Self {
jsonrpc: JSONRPC_VERSION.to_string(),
id,
result: None,
error: Some(error),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonRpcNotification {
pub jsonrpc: String,
pub method: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub params: Option<Value>,
}
impl JsonRpcNotification {
pub fn new(method: String, params: Option<Value>) -> Self {
Self {
jsonrpc: JSONRPC_VERSION.to_string(),
method,
params,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum JsonRpcMessage {
Request(JsonRpcRequest),
Response(JsonRpcResponse),
Notification(JsonRpcNotification),
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_request_serialization() {
let request = JsonRpcRequest::new(
RequestId::Number(1),
"test_method".to_string(),
Some(json!({"param": "value"})),
);
let json = serde_json::to_string(&request).unwrap();
assert!(json.contains(r#""jsonrpc":"2.0""#));
assert!(json.contains(r#""id":1"#));
assert!(json.contains(r#""method":"test_method""#));
assert!(json.contains(r#""params":{"param":"value"}"#));
}
#[test]
fn test_response_success_serialization() {
let response = JsonRpcResponse::success(
RequestId::String("abc".to_string()),
json!({"result": "success"}),
);
let json = serde_json::to_string(&response).unwrap();
assert!(json.contains(r#""jsonrpc":"2.0""#));
assert!(json.contains(r#""id":"abc""#));
assert!(json.contains(r#""result":{"result":"success"}"#));
assert!(!json.contains("error"));
}
#[test]
fn test_response_error_serialization() {
let response = JsonRpcResponse::error(
RequestId::Number(2),
JsonRpcError::method_not_found("unknown_method"),
);
let json = serde_json::to_string(&response).unwrap();
assert!(json.contains(r#""jsonrpc":"2.0""#));
assert!(json.contains(r#""id":2"#));
assert!(json.contains(r#""code":-32601"#));
assert!(json.contains("Method not found"));
assert!(!json.contains("result"));
}
#[test]
fn test_notification_serialization() {
let notification = JsonRpcNotification::new(
"status_update".to_string(),
Some(json!({"status": "ready"})),
);
let json = serde_json::to_string(¬ification).unwrap();
assert!(json.contains(r#""jsonrpc":"2.0""#));
assert!(json.contains(r#""method":"status_update""#));
assert!(json.contains(r#""params":{"status":"ready"}"#));
assert!(!json.contains("id"));
}
#[test]
fn test_message_deserialization() {
let request_json = r#"{"jsonrpc":"2.0","id":1,"method":"test","params":{}}"#;
let message: JsonRpcMessage = serde_json::from_str(request_json).unwrap();
assert!(matches!(message, JsonRpcMessage::Request(_)));
let response_json = r#"{"jsonrpc":"2.0","id":1,"result":{"status":"ok"}}"#;
let message: JsonRpcMessage = serde_json::from_str(response_json).unwrap();
assert!(matches!(message, JsonRpcMessage::Response(_)));
let notification_json = r#"{"jsonrpc":"2.0","method":"notify","params":null}"#;
let message: JsonRpcMessage = serde_json::from_str(notification_json).unwrap();
assert!(matches!(message, JsonRpcMessage::Notification(_)));
}
}