use serde::{Deserialize, Serialize};
use serde_json::value::RawValue;
use serde_json::Value;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum RpcId {
Number(i64),
String(String),
Null,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RpcRequest {
pub jsonrpc: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<RpcId>,
pub method: String,
pub params: Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RpcResponse {
pub jsonrpc: String,
pub id: Option<RpcId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub result: Option<Box<RawValue>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<RpcError>,
#[serde(skip_serializing_if = "Option::is_none")]
pub partial: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RpcError {
pub code: i32,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<Value>,
}
pub const PARSE_ERROR: i32 = -32700;
pub const INVALID_REQUEST: i32 = -32600;
pub const METHOD_NOT_FOUND: i32 = -32601;
pub const INVALID_PARAMS: i32 = -32602;
pub const INTERNAL_ERROR: i32 = -32603;
pub fn ok(id: Option<RpcId>, result: Box<RawValue>) -> RpcResponse {
RpcResponse {
jsonrpc: "2.0".to_string(),
id,
result: Some(result),
error: None,
partial: None,
}
}
pub fn err(id: Option<RpcId>, code: i32, message: String, data: Option<Value>) -> RpcResponse {
RpcResponse {
jsonrpc: "2.0".to_string(),
id,
result: None,
error: Some(RpcError {
code,
message,
data,
}),
partial: None,
}
}
pub fn ok_partial(id: Option<RpcId>, result: Box<RawValue>) -> RpcResponse {
RpcResponse {
jsonrpc: "2.0".to_string(),
id,
result: Some(result),
error: None,
partial: Some(true),
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_rpc_id_serialization() {
let id = RpcId::Number(42);
let json = serde_json::to_string(&id).unwrap();
assert_eq!(json, "42");
let parsed: RpcId = serde_json::from_str(&json).unwrap();
assert_eq!(parsed, id);
let id = RpcId::String("abc-123".to_string());
let json = serde_json::to_string(&id).unwrap();
assert_eq!(json, r#""abc-123""#);
let parsed: RpcId = serde_json::from_str(&json).unwrap();
assert_eq!(parsed, id);
let id = RpcId::Null;
let json = serde_json::to_string(&id).unwrap();
assert_eq!(json, "null");
let parsed: RpcId = serde_json::from_str(&json).unwrap();
assert_eq!(parsed, id);
}
#[test]
fn test_request_serialization() {
let request = RpcRequest {
jsonrpc: "2.0".to_string(),
id: Some(RpcId::Number(1)),
method: "test.method".to_string(),
params: json!({"key": "value"}),
};
let json = serde_json::to_value(&request).unwrap();
assert_eq!(json["jsonrpc"], "2.0");
assert_eq!(json["id"], 1);
assert_eq!(json["method"], "test.method");
assert_eq!(json["params"]["key"], "value");
let parsed: RpcRequest = serde_json::from_value(json).unwrap();
assert_eq!(parsed.jsonrpc, "2.0");
assert_eq!(parsed.id, Some(RpcId::Number(1)));
assert_eq!(parsed.method, "test.method");
}
#[test]
fn test_response_ok() {
let raw = serde_json::value::to_raw_value(&json!({"status": "ok"})).unwrap();
let response = ok(Some(RpcId::Number(1)), raw);
assert_eq!(response.jsonrpc, "2.0");
assert_eq!(response.id, Some(RpcId::Number(1)));
assert!(response.result.is_some());
assert!(response.error.is_none());
let serialized = serde_json::to_string(&response).unwrap();
let json: serde_json::Value = serde_json::from_str(&serialized).unwrap();
assert_eq!(json["result"]["status"], "ok");
assert!(json.get("error").is_none());
}
#[test]
fn test_response_error() {
let response = err(
Some(RpcId::Number(2)),
METHOD_NOT_FOUND,
"Method not found".to_string(),
Some(json!({"method": "unknown.method"})),
);
assert_eq!(response.jsonrpc, "2.0");
assert_eq!(response.id, Some(RpcId::Number(2)));
assert!(response.result.is_none());
assert!(response.error.is_some());
let error = response.error.unwrap();
assert_eq!(error.code, METHOD_NOT_FOUND);
assert_eq!(error.message, "Method not found");
assert_eq!(error.data.unwrap()["method"], "unknown.method");
}
#[test]
fn test_notification_no_id() {
let request = RpcRequest {
jsonrpc: "2.0".to_string(),
id: None,
method: "notification".to_string(),
params: json!(null),
};
let json = serde_json::to_value(&request).unwrap();
assert!(json["id"].is_null() || !json.as_object().unwrap().contains_key("id"));
}
}