use crate::ErrorCode;
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum RpcMessage {
Request(RpcRequest),
Notification(RpcNotification),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RpcRequest {
pub jsonrpc: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<Value>,
pub method: String,
#[serde(default)]
pub params: Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RpcNotification {
pub jsonrpc: String,
pub method: String,
#[serde(default)]
pub params: Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RpcResponse {
pub jsonrpc: String,
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<RpcError>,
}
impl RpcResponse {
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>, code: ErrorCode, message: impl Into<String>) -> Self {
Self {
jsonrpc: "2.0".to_string(),
id,
result: None,
error: Some(RpcError {
code: code.to_string(),
message: message.into(),
data: None,
}),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RpcError {
pub code: String,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<Value>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rpc_request_roundtrip() {
let req = RpcRequest {
jsonrpc: "2.0".to_string(),
id: Some(Value::String("1".to_string())),
method: "get_tools".to_string(),
params: serde_json::json!({}),
};
let json = serde_json::to_string(&req).unwrap();
let parsed: RpcRequest = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.id, Some(Value::String("1".to_string())));
assert_eq!(parsed.method, "get_tools");
}
#[test]
fn test_rpc_request_numeric_id() {
let json = r#"{"jsonrpc":"2.0","id":1,"method":"get_tools","params":{}}"#;
let parsed: RpcMessage = serde_json::from_str(json).unwrap();
match parsed {
RpcMessage::Request(req) => {
assert_eq!(req.id, Some(Value::Number(1.into())));
assert_eq!(req.method, "get_tools");
}
_ => panic!("expected Request"),
}
}
#[test]
fn test_rpc_response_success() {
let resp = RpcResponse::success(Some(Value::String("1".to_string())), serde_json::json!({"status": "ok"}));
assert_eq!(resp.id, Some(Value::String("1".to_string())));
assert!(resp.result.is_some());
assert!(resp.error.is_none());
}
#[test]
fn test_rpc_response_error() {
let resp = RpcResponse::error(
Some(Value::String("1".to_string())),
ErrorCode::MethodNotFound,
"method not found",
);
assert_eq!(resp.id, Some(Value::String("1".to_string())));
assert!(resp.result.is_none());
assert!(resp.error.is_some());
}
}