use std::collections::{BTreeMap, BTreeSet};
use std::marker::PhantomData;
use std::sync::Arc;
use async_trait::async_trait;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use super::stream::{RpcOutcome, RpcStreamMethod, typed_stream_method};
use super::wire::{
CODE_INVALID_REQUEST, CODE_PARSE_ERROR, JSONRPC_VERSION, RpcError, RpcRequest, RpcResponse,
};
#[derive(Deserialize)]
struct StreamOptIn {
#[serde(default)]
stream: bool,
}
#[derive(Deserialize)]
struct MethodName {
method: String,
}
#[async_trait]
pub trait RpcMethod: Send + Sync + 'static {
async fn call(&self, params: serde_json::Value) -> Result<serde_json::Value, RpcError>;
}
#[async_trait]
pub trait RpcFallback: Send + Sync + 'static {
async fn call(
&self,
method: &str,
params: serde_json::Value,
) -> Result<serde_json::Value, RpcError>;
}
struct Typed<Req, Resp, F> {
call: F,
_types: PhantomData<fn(Req) -> Resp>,
}
#[async_trait]
impl<Req, Resp, F, Fut> RpcMethod for Typed<Req, Resp, F>
where
Req: DeserializeOwned + Send + 'static,
Resp: Serialize + Send + 'static,
F: Fn(Req) -> Fut + Send + Sync + 'static,
Fut: std::future::Future<Output = Result<Resp, RpcError>> + Send + 'static,
{
async fn call(&self, params: serde_json::Value) -> Result<serde_json::Value, RpcError> {
let request: Req = serde_json::from_value(params)
.map_err(|e| RpcError::invalid_params(format!("params do not decode: {e}")))?;
let response = (self.call)(request).await?;
serde_json::to_value(&response)
.map_err(|e| RpcError::internal(format!("serialize response: {e}")))
}
}
pub fn typed_method<Req, Resp, F, Fut>(call: F) -> impl RpcMethod
where
Req: DeserializeOwned + Send + 'static,
Resp: Serialize + Send + 'static,
F: Fn(Req) -> Fut + Send + Sync + 'static,
Fut: std::future::Future<Output = Result<Resp, RpcError>> + Send + 'static,
{
Typed::<Req, Resp, F> {
call,
_types: PhantomData,
}
}
#[derive(Default)]
pub struct RpcRouter {
methods: BTreeMap<String, Arc<dyn RpcMethod>>,
streams: BTreeMap<String, Arc<dyn RpcStreamMethod>>,
fallback: Option<Arc<dyn RpcFallback>>,
liveness: BTreeSet<String>,
}
impl std::fmt::Debug for RpcRouter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RpcRouter")
.field("methods", &self.method_names().collect::<Vec<_>>())
.field("streams", &self.stream_names().collect::<Vec<_>>())
.field("liveness", &self.liveness_names().collect::<Vec<_>>())
.field("fallback", &self.fallback.is_some())
.finish()
}
}
impl RpcRouter {
pub fn new() -> Self {
Self::default()
}
pub fn method(mut self, name: impl Into<String>, handler: impl RpcMethod) -> Self {
self.methods.insert(name.into(), Arc::new(handler));
self
}
pub fn typed<Req, Resp, F, Fut>(self, name: impl Into<String>, call: F) -> Self
where
Req: DeserializeOwned + Send + 'static,
Resp: Serialize + Send + 'static,
F: Fn(Req) -> Fut + Send + Sync + 'static,
Fut: std::future::Future<Output = Result<Resp, RpcError>> + Send + 'static,
{
self.method(name, typed_method::<Req, Resp, F, Fut>(call))
}
pub fn mark_liveness(mut self, name: impl Into<String>) -> Self {
self.liveness.insert(name.into());
self
}
pub fn typed_liveness<Req, Resp, F, Fut>(self, name: impl Into<String>, call: F) -> Self
where
Req: DeserializeOwned + Send + 'static,
Resp: Serialize + Send + 'static,
F: Fn(Req) -> Fut + Send + Sync + 'static,
Fut: std::future::Future<Output = Result<Resp, RpcError>> + Send + 'static,
{
let name = name.into();
self.typed::<Req, Resp, F, Fut>(name.clone(), call)
.mark_liveness(name)
}
pub fn liveness_names(&self) -> impl Iterator<Item = &str> {
self.liveness.iter().map(String::as_str)
}
pub(super) fn frame_is_liveness(&self, frame: &[u8]) -> bool {
if self.liveness.is_empty() {
return false;
}
serde_json::from_slice::<MethodName>(frame)
.is_ok_and(|named| self.liveness.contains(&named.method))
}
pub fn stream_method(mut self, name: impl Into<String>, handler: impl RpcStreamMethod) -> Self {
self.streams.insert(name.into(), Arc::new(handler));
self
}
pub fn typed_stream<Req, F, Fut>(self, name: impl Into<String>, call: F) -> Self
where
Req: DeserializeOwned + Send + 'static,
F: Fn(Req) -> Fut + Send + Sync + 'static,
Fut: std::future::Future<Output = Result<super::RpcStreamItems, RpcError>> + Send + 'static,
{
self.stream_method(name, typed_stream_method::<Req, F, Fut>(call))
}
pub fn fallback(mut self, fallback: impl RpcFallback) -> Self {
self.fallback = Some(Arc::new(fallback));
self
}
pub fn method_names(&self) -> impl Iterator<Item = &str> {
self.methods.keys().map(String::as_str)
}
pub fn stream_names(&self) -> impl Iterator<Item = &str> {
self.streams.keys().map(String::as_str)
}
pub async fn dispatch(&self, frame: &[u8]) -> RpcResponse {
let request = match self.envelope(frame) {
Ok(request) => request,
Err(response) => return response,
};
if self.streams.contains_key(&request.method) {
let method = request.method.clone();
return RpcResponse::failure(request.id, RpcError::stream_required(&method));
}
self.call_unary(request).await
}
pub async fn dispatch_streaming(&self, frame: &[u8]) -> RpcOutcome {
let request = match self.envelope(frame) {
Ok(request) => request,
Err(response) => return RpcOutcome::Single(response),
};
if self.streams.is_empty() {
return RpcOutcome::Single(self.call_unary(request).await);
}
let wants_stream = serde_json::from_slice::<StreamOptIn>(frame)
.map(|opt_in| opt_in.stream)
.unwrap_or(false);
match (wants_stream, self.streams.get(&request.method)) {
(true, Some(handler)) => {
let handler = Arc::clone(handler);
match handler.call(request.params).await {
Ok(items) => RpcOutcome::Stream {
id: request.id,
items,
},
Err(error) => RpcOutcome::refused(request.id, error),
}
}
(true, None) => {
let streaming: Vec<&str> = self.stream_names().collect();
RpcOutcome::refused(
request.id,
RpcError::stream_unsupported(&request.method, &streaming),
)
}
(false, Some(_)) => {
let method = request.method.clone();
RpcOutcome::Single(RpcResponse::failure(
request.id,
RpcError::stream_required(&method),
))
}
(false, None) => RpcOutcome::Single(self.call_unary(request).await),
}
}
fn envelope(&self, frame: &[u8]) -> Result<RpcRequest, RpcResponse> {
let request: RpcRequest = match serde_json::from_slice(frame) {
Ok(r) => r,
Err(e) => {
return Err(RpcResponse::failure(
serde_json::Value::Null,
RpcError::new(CODE_PARSE_ERROR, format!("unparseable request frame: {e}")),
));
}
};
if request.jsonrpc != JSONRPC_VERSION {
return Err(RpcResponse::failure(
request.id,
RpcError::new(
CODE_INVALID_REQUEST,
format!(
"unsupported jsonrpc version {:?}; this listener speaks {JSONRPC_VERSION}",
request.jsonrpc
),
),
));
}
Ok(request)
}
async fn call_unary(&self, request: RpcRequest) -> RpcResponse {
let Some(handler) = self.methods.get(&request.method) else {
return self.dispatch_unregistered(request).await;
};
let handler = Arc::clone(handler);
match handler.call(request.params).await {
Ok(result) => RpcResponse::success(request.id, result),
Err(error) => RpcResponse::failure(request.id, error),
}
}
async fn dispatch_unregistered(&self, request: RpcRequest) -> RpcResponse {
let Some(fallback) = self.fallback.as_ref() else {
let known: Vec<&str> = self.method_names().collect();
return RpcResponse::failure(
request.id,
RpcError::method_not_found(&request.method, &known),
);
};
let fallback = Arc::clone(fallback);
let RpcRequest {
id, method, params, ..
} = request;
match fallback.call(&method, params).await {
Ok(result) => RpcResponse::success(id, result),
Err(error) => RpcResponse::failure(id, error),
}
}
}