use serde::{Deserialize, Serialize};
pub const JSONRPC_VERSION: &str = "2.0";
pub const CODE_PARSE_ERROR: i64 = -32700;
pub const CODE_INVALID_REQUEST: i64 = -32600;
pub const CODE_METHOD_NOT_FOUND: i64 = -32601;
pub const CODE_INVALID_PARAMS: i64 = -32602;
pub const CODE_INTERNAL_ERROR: i64 = -32603;
pub const CODE_STREAM_UNSUPPORTED: i64 = -32010;
pub const CODE_STREAM_REQUIRED: i64 = -32011;
#[derive(Debug, Clone, Deserialize)]
pub struct RpcRequest {
#[serde(default)]
pub jsonrpc: String,
#[serde(default)]
pub id: serde_json::Value,
pub method: String,
#[serde(default)]
pub params: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RpcResponse {
pub jsonrpc: String,
pub id: serde_json::Value,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub result: Option<serde_json::Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error: Option<RpcError>,
}
impl RpcResponse {
pub fn success(id: serde_json::Value, result: serde_json::Value) -> Self {
Self {
jsonrpc: JSONRPC_VERSION.to_string(),
id,
result: Some(result),
error: None,
}
}
pub fn failure(id: serde_json::Value, error: RpcError) -> Self {
Self {
jsonrpc: JSONRPC_VERSION.to_string(),
id,
result: None,
error: Some(error),
}
}
pub fn is_error(&self) -> bool {
self.error.is_some()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum StreamPhase {
Item,
End,
Error,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct RpcStreamFrame {
pub jsonrpc: String,
pub id: serde_json::Value,
pub stream: StreamPhase,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub result: Option<serde_json::Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error: Option<RpcError>,
}
impl RpcStreamFrame {
pub fn item(id: serde_json::Value, result: serde_json::Value) -> Self {
Self {
jsonrpc: JSONRPC_VERSION.to_string(),
id,
stream: StreamPhase::Item,
result: Some(result),
error: None,
}
}
pub fn end(id: serde_json::Value) -> Self {
Self {
jsonrpc: JSONRPC_VERSION.to_string(),
id,
stream: StreamPhase::End,
result: None,
error: None,
}
}
pub fn error(id: serde_json::Value, error: RpcError) -> Self {
Self {
jsonrpc: JSONRPC_VERSION.to_string(),
id,
stream: StreamPhase::Error,
result: None,
error: Some(error),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RpcError {
pub code: i64,
pub message: String,
}
impl RpcError {
pub fn new(code: i64, message: impl Into<String>) -> Self {
Self {
code,
message: message.into(),
}
}
pub fn invalid_params(message: impl Into<String>) -> Self {
Self::new(CODE_INVALID_PARAMS, message)
}
pub fn internal(message: impl Into<String>) -> Self {
Self::new(CODE_INTERNAL_ERROR, message)
}
pub fn method_not_found(method: &str, known: &[&str]) -> Self {
Self::new(
CODE_METHOD_NOT_FOUND,
format!("unknown method {method:?}; this listener serves {known:?}"),
)
}
pub fn stream_unsupported(method: &str, streaming: &[&str]) -> Self {
Self::new(
CODE_STREAM_UNSUPPORTED,
format!("method {method:?} does not stream; this listener streams {streaming:?}"),
)
}
pub fn stream_required(method: &str) -> Self {
Self::new(
CODE_STREAM_REQUIRED,
format!(
"method {method:?} answers only as a stream; resend with \"stream\": true \
and read frames until a terminal one"
),
)
}
}
impl std::fmt::Display for RpcError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[{}] {}", self.code, self.message)
}
}
impl std::error::Error for RpcError {}