#[doc = include_str!("../README.md")]
mod utils;
pub use utils::*;
use std::sync::Arc;
use async_trait::async_trait;
pub use nanorpc_derive::nanorpc_derive;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(untagged)]
pub enum JrpcId {
Number(i64),
String(String),
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct JrpcRequest {
pub jsonrpc: String,
pub method: String,
pub params: Vec<serde_json::Value>,
pub id: JrpcId,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct JrpcResponse {
pub jsonrpc: String,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub result: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub error: Option<JrpcError>,
pub id: JrpcId,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct JrpcError {
pub code: i64,
pub message: String,
pub data: serde_json::Value,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct ServerError {
pub code: u32,
pub message: String,
pub details: serde_json::Value,
}
#[async_trait]
pub trait RpcService: Sync + Send + 'static {
async fn respond(
&self,
method: &str,
params: Vec<serde_json::Value>,
) -> Option<Result<serde_json::Value, ServerError>>;
async fn respond_raw(&self, jrpc_req: JrpcRequest) -> JrpcResponse {
if jrpc_req.jsonrpc != "2.0" {
JrpcResponse {
id: jrpc_req.id,
jsonrpc: "2.0".into(),
result: None,
error: Some(JrpcError {
code: -32600,
message: "JSON-RPC version wrong".into(),
data: serde_json::Value::Null,
}),
}
} else if let Some(response) = self.respond(&jrpc_req.method, jrpc_req.params).await {
match response {
Ok(response) => JrpcResponse {
id: jrpc_req.id,
jsonrpc: "2.0".into(),
result: Some(response),
error: None,
},
Err(err) => JrpcResponse {
id: jrpc_req.id,
jsonrpc: "2.0".into(),
result: None,
error: Some(JrpcError {
code: -1,
message: err.message,
data: err.details,
}),
},
}
} else {
JrpcResponse {
id: jrpc_req.id,
jsonrpc: "2.0".into(),
result: None,
error: Some(JrpcError {
code: -32601,
message: "Method not found".into(),
data: serde_json::Value::Null,
}),
}
}
}
}
#[async_trait]
impl<T: RpcService + ?Sized> RpcService for Arc<T> {
async fn respond(
&self,
method: &str,
params: Vec<serde_json::Value>,
) -> Option<Result<serde_json::Value, ServerError>> {
self.as_ref().respond(method, params).await
}
}
#[async_trait]
pub trait RpcTransport: Sync + Send + 'static {
type Error: Sync + Send + 'static;
async fn call(
&self,
method: &str,
params: &[serde_json::Value],
) -> Result<Option<Result<serde_json::Value, ServerError>>, Self::Error> {
let reqid = format!("req-{}", fastrand::u64(..));
let req = JrpcRequest {
jsonrpc: "2.0".into(),
id: JrpcId::String(reqid),
method: method.into(),
params: params
.iter()
.map(|s| serde_json::to_value(s).unwrap())
.collect(),
};
let result = self.call_raw(req).await?;
if let Some(res) = result.result {
Ok(Some(Ok(res)))
} else if let Some(res) = result.error {
if res.code == -32600 {
Ok(None)
} else {
Ok(Some(Err(ServerError {
code: res.code as u32,
message: res.message,
details: res.data,
})))
}
} else {
Ok(Some(Ok(serde_json::Value::Null)))
}
}
async fn call_raw(&self, req: JrpcRequest) -> Result<JrpcResponse, Self::Error>;
}
#[async_trait]
impl<T: RpcTransport + ?Sized> RpcTransport for Arc<T> {
type Error = T::Error;
async fn call_raw(&self, req: JrpcRequest) -> Result<JrpcResponse, Self::Error> {
self.as_ref().call_raw(req).await
}
}
#[async_trait]
impl<T: RpcTransport + ?Sized> RpcTransport for Box<T> {
type Error = T::Error;
async fn call_raw(&self, req: JrpcRequest) -> Result<JrpcResponse, Self::Error> {
self.as_ref().call_raw(req).await
}
}
#[cfg(test)]
mod tests {
use crate::{self as nanorpc, ServerError};
use nanorpc::{nanorpc_derive, RpcService};
#[nanorpc_derive]
#[async_trait::async_trait]
pub trait MathProtocol {
async fn add(&self, x: f64, y: f64) -> f64;
async fn mult(&self, x: f64, y: f64) -> f64;
async fn maybe_fail(&self) -> Result<f64, f64>;
}
struct Mather;
#[async_trait::async_trait]
impl MathProtocol for Mather {
async fn add(&self, x: f64, y: f64) -> f64 {
x + y
}
async fn mult(&self, x: f64, y: f64) -> f64 {
x * y
}
async fn maybe_fail(&self) -> Result<f64, f64> {
Err(12345.0)
}
}
#[test]
fn test_notfound_macro() {
smol::future::block_on(async move {
let service = MathService(Mather);
assert_eq!(
service
.respond("!nonexistent!", serde_json::from_str("[]").unwrap())
.await,
None
);
});
}
#[test]
fn test_simple_macro() {
smol::future::block_on(async move {
let service = MathService(Mather);
assert_eq!(
service
.respond("maybe_fail", serde_json::from_str("[]").unwrap())
.await
.unwrap()
.unwrap_err(),
ServerError {
code: 1,
message: "12345".into(),
details: 12345.0f64.into()
}
);
assert_eq!(
service
.respond("add", serde_json::from_str("[1, 2]").unwrap())
.await
.unwrap()
.unwrap(),
serde_json::Value::from(3.0f64)
);
});
}
}