use serde::{Deserialize, Serialize};
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;
#[derive(Debug, Deserialize)]
pub struct JsonRpcRequest {
#[serde(rename = "jsonrpc")]
pub version: String,
pub id: Option<serde_json::Value>,
pub method: String,
#[serde(default)]
pub params: Option<serde_json::Value>,
}
#[derive(Debug, Serialize)]
pub struct JsonRpcResponse {
pub jsonrpc: &'static str,
pub id: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub result: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<JsonRpcError>,
}
#[derive(Debug, Serialize)]
pub struct JsonRpcError {
pub code: i64,
pub message: String,
}
impl JsonRpcResponse {
#[must_use]
pub fn success(id: Option<serde_json::Value>, result: serde_json::Value) -> Self {
Self {
jsonrpc: "2.0",
id,
result: Some(result),
error: None,
}
}
#[must_use]
pub fn error(id: Option<serde_json::Value>, code: i64, message: impl Into<String>) -> Self {
Self {
jsonrpc: "2.0",
id,
result: None,
error: Some(JsonRpcError {
code,
message: message.into(),
}),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn deserialize_request_with_params() {
let json = r#"{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"add"}}"#;
let req: JsonRpcRequest = serde_json::from_str(json).unwrap();
assert_eq!(req.version, "2.0");
assert_eq!(req.id, Some(serde_json::json!(1)));
assert_eq!(req.method, "tools/call");
assert!(req.params.is_some());
}
#[test]
fn deserialize_request_without_params() {
let json = r#"{"jsonrpc":"2.0","id":2,"method":"tools/list"}"#;
let req: JsonRpcRequest = serde_json::from_str(json).unwrap();
assert!(req.params.is_none());
}
#[test]
fn deserialize_notification_without_id() {
let json = r#"{"jsonrpc":"2.0","method":"initialized"}"#;
let req: JsonRpcRequest = serde_json::from_str(json).unwrap();
assert!(req.id.is_none());
}
#[test]
fn serialize_success_response() {
let resp =
JsonRpcResponse::success(Some(serde_json::json!(1)), serde_json::json!({"ok": true}));
let json = serde_json::to_string(&resp).unwrap();
assert!(json.contains(r#""jsonrpc":"2.0""#));
assert!(json.contains(r#""result":{""#));
assert!(!json.contains("error"));
}
#[test]
fn serialize_error_response() {
let resp = JsonRpcResponse::error(Some(serde_json::json!(1)), PARSE_ERROR, "bad json");
let json = serde_json::to_string(&resp).unwrap();
assert!(json.contains(r#""code":-32700"#));
assert!(json.contains(r#""message":"bad json""#));
assert!(!json.contains("result"));
}
#[test]
fn serialize_error_omits_null_id() {
let resp = JsonRpcResponse::error(None, METHOD_NOT_FOUND, "no such method");
let json = serde_json::to_string(&resp).unwrap();
assert!(json.contains(r#""id":null"#));
}
#[test]
fn response_jsonrpc_field_is_static() {
let resp = JsonRpcResponse::success(None, serde_json::json!(null));
assert_eq!(resp.jsonrpc, "2.0");
}
}