use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[serde(untagged)]
pub enum RequestId {
String(String),
Number(i64),
Null,
}
impl From<String> for RequestId {
fn from(s: String) -> Self {
RequestId::String(s)
}
}
impl From<i64> for RequestId {
fn from(n: i64) -> Self {
RequestId::Number(n)
}
}
impl From<RequestId> for Value {
fn from(id: RequestId) -> Self {
match id {
RequestId::String(s) => Value::String(s),
RequestId::Number(n) => Value::Number(n.into()),
RequestId::Null => Value::Null,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonRpcRequest {
pub jsonrpc: String,
pub method: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub params: Option<Value>,
pub id: Value,
}
impl JsonRpcRequest {
pub fn new(method: impl Into<String>, params: Option<Value>, id: Value) -> Self {
Self {
jsonrpc: "2.0".to_string(),
method: method.into(),
params,
id,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonRpcResponse {
pub jsonrpc: String,
pub result: Value,
pub id: Value,
}
impl JsonRpcResponse {
pub fn new(result: Value, id: Value) -> Self {
Self {
jsonrpc: "2.0".to_string(),
result,
id,
}
}
}
#[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>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonRpcErrorResponse {
pub jsonrpc: String,
pub error: JsonRpcError,
pub id: Value,
}
impl JsonRpcErrorResponse {
pub fn new(error: JsonRpcError, id: Value) -> Self {
Self {
jsonrpc: "2.0".to_string(),
error,
id,
}
}
}
#[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: impl Into<String>, params: Option<Value>) -> Self {
Self {
jsonrpc: "2.0".to_string(),
method: method.into(),
params,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(transparent)]
pub struct JsonRpcBatch(pub Vec<JsonRpcMessage>);
pub const JSON_RPC_VERSION: &str = "2.0";
#[allow(dead_code)]
pub mod error_codes {
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 const UNAUTHORIZED: i32 = -32000;
pub const FORBIDDEN: i32 = -32001;
pub const NOT_FOUND: i32 = -32002;
pub const RATE_LIMITED: i32 = -32003;
pub const TIMEOUT: i32 = -32004;
pub const PLUGIN_ERROR: i32 = -32005;
pub const ACL_DENIED: i32 = -32006;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum JsonRpcMessage {
Request(JsonRpcRequest),
Notification(JsonRpcNotification),
Response(JsonRpcResponse),
ErrorResponse(JsonRpcErrorResponse),
}
impl JsonRpcMessage {
pub fn expects_response(&self) -> bool {
matches!(self, JsonRpcMessage::Request(_))
}
pub fn id(&self) -> Option<&Value> {
match self {
JsonRpcMessage::Request(req) => Some(&req.id),
JsonRpcMessage::Response(resp) => Some(&resp.id),
JsonRpcMessage::ErrorResponse(err) => Some(&err.id),
JsonRpcMessage::Notification(_) => None,
}
}
pub fn method(&self) -> Option<&str> {
match self {
JsonRpcMessage::Request(req) => Some(&req.method),
JsonRpcMessage::Notification(notif) => Some(¬if.method),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_request_serialization() {
let req = JsonRpcRequest::new(
"network.getInterfaces",
Some(json!({"filter": "up"})),
json!(1),
);
let serialized = serde_json::to_string(&req).unwrap();
assert!(serialized.contains("\"jsonrpc\":\"2.0\""));
assert!(serialized.contains("\"method\":\"network.getInterfaces\""));
}
#[test]
fn test_response_serialization() {
let resp = JsonRpcResponse::new(json!({"status": "ok"}), json!(1));
let serialized = serde_json::to_string(&resp).unwrap();
assert!(serialized.contains("\"jsonrpc\":\"2.0\""));
assert!(serialized.contains("\"result\""));
}
#[test]
fn test_error_response() {
let error = JsonRpcError {
code: error_codes::METHOD_NOT_FOUND,
message: "Method not found".to_string(),
data: None,
};
let resp = JsonRpcErrorResponse::new(error, json!(1));
let serialized = serde_json::to_string(&resp).unwrap();
assert!(serialized.contains("\"error\""));
assert!(serialized.contains("-32601"));
}
}