use {
core::fmt::{self, Debug, Display, Formatter},
fehler::{throw, throws},
serde::{Deserialize, Serialize},
serde_json::{Map, Number, Value},
std::{
collections::hash_map::{Entry, HashMap},
sync::atomic::{AtomicU64, Ordering},
},
};
#[allow(clippy::decimal_literal_representation)] const PREDEFINED_ERROR_MIN_CODE: i64 = -32768;
const PARSE_ERROR_CODE: i64 = -32700;
const INVALID_REQUEST_CODE: i64 = -32600;
const METHOD_NOT_FOUND_CODE: i64 = -32601;
const INVALID_PARAMS_CODE: i64 = -32602;
const INTERNAL_ERROR_CODE: i64 = -32603;
const SERVER_ERROR_MIN_CODE: i64 = -32099;
const PREDEFINED_ERROR_MAX_CODE: i64 = -32000;
const SERVER_ERROR_MAX_CODE: i64 = PREDEFINED_ERROR_MAX_CODE;
const METHOD_NOT_FOUND_MESSAGE: &str = "Method not found";
pub(crate) type ResultHandler<S, O> = fn(&mut S, Value) -> Option<Result<O, serde_json::Error>>;
pub(crate) type ErrorHandler<S, O> = fn(&mut S, ErrorObject) -> Option<O>;
pub(crate) type RequestHandler<S> = fn(&mut S, Params) -> Outcome;
pub(crate) type NotificationHandler<S> = fn(&mut S, Params);
pub(crate) type ResponseHandlers<S, O> = (ResultHandler<S, O>, ErrorHandler<S, O>);
pub(crate) type MethodHandlers<S> = (Option<RequestHandler<S>>, Option<NotificationHandler<S>>);
pub(crate) trait Request<S, O> {
const METHOD: &'static str;
#[throws(serde_json::Error)]
fn params(&self) -> Params;
fn response_handlers(&self) -> Option<ResponseHandlers<S, O>> {
None
}
fn method_handlers(&self) -> MethodHandlers<S> {
(None, None)
}
}
#[derive(Clone, Debug, Deserialize, parse_display::Display, PartialEq, Serialize)]
#[display("{kind}")]
pub(crate) struct Object {
jsonrpc: Version,
#[serde(flatten)]
kind: Kind,
}
impl From<Kind> for Object {
fn from(kind: Kind) -> Self {
Self {
jsonrpc: Version::V2_0,
kind,
}
}
}
impl From<RequestObject> for Object {
fn from(request: RequestObject) -> Self {
Self::from(Kind::from(request))
}
}
impl From<ResponseObject> for Object {
fn from(response: ResponseObject) -> Self {
Self::from(Kind::from(response))
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
enum Version {
#[serde(rename = "2.0")]
V2_0,
}
#[derive(Clone, Debug, Deserialize, parse_display::Display, PartialEq, Serialize)]
#[serde(untagged)]
pub(crate) enum Kind {
#[display("{0}")]
Request(RequestObject),
#[display("{0}")]
Response(ResponseObject),
}
impl From<RequestObject> for Kind {
fn from(request: RequestObject) -> Self {
Self::Request(request)
}
}
impl From<ResponseObject> for Kind {
fn from(response: ResponseObject) -> Self {
Self::Response(response)
}
}
impl From<Object> for Kind {
fn from(value: Object) -> Self {
value.kind
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub(crate) struct RequestObject {
method: String,
params: Params,
#[serde(skip_serializing_if = "Option::is_none")]
id: Option<Id>,
}
impl Display for RequestObject {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match &self.id {
Some(id) => format!("Request[{}]: '{}' w/ {}", id, self.method, self.params),
None => format!("Notification: '{}' w/ {}", self.method, self.params),
}
)
}
}
#[derive(Clone, Debug, Deserialize, parse_display::Display, PartialEq, Serialize)]
#[serde(untagged)]
pub enum Params {
None,
#[display("{0:?}")]
Array(Vec<Value>),
#[display("{0:?}")]
Object(Map<String, Value>),
}
impl From<Value> for Params {
fn from(value: Value) -> Self {
match value {
serde_json::Value::Null => Self::None,
serde_json::Value::Bool(_)
| serde_json::Value::Number(_)
| serde_json::Value::String(_) => Self::Array(vec![value]),
serde_json::Value::Array(seq) => Self::Array(seq),
serde_json::Value::Object(map) => Self::Object(map),
}
}
}
impl From<Params> for Value {
#[inline]
fn from(params: Params) -> Self {
match params {
Params::None => Self::Null,
Params::Array(array) => Self::Array(array),
Params::Object(map) => Self::Object(map),
}
}
}
#[derive(Clone, Debug, Deserialize, Eq, parse_display::Display, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
#[serde(untagged)]
pub(crate) enum Id {
#[display("NULL")]
Null,
#[display("{0}")]
Num(Number),
#[display("{0}")]
Str(String),
}
impl From<u64> for Id {
fn from(num_id: u64) -> Self {
Self::Num(num_id.into())
}
}
#[derive(Clone, Debug, Deserialize, parse_display::Display, PartialEq, Serialize)]
#[display("Response[{id}]: {outcome}")]
pub(crate) struct ResponseObject {
#[serde(flatten)]
outcome: Outcome,
id: Id,
}
#[derive(Clone, Debug, Deserialize, parse_display::Display, PartialEq, Serialize)]
#[serde(rename_all = "lowercase")]
pub(crate) enum Outcome {
#[display("Success {0}")]
Result(Value),
#[display("{0}")]
Error(ErrorObject),
}
impl Outcome {
pub(crate) fn unknown_request() -> Self {
Self::Error(ErrorObject {
code: METHOD_NOT_FOUND_CODE,
message: METHOD_NOT_FOUND_MESSAGE.to_string(),
data: None,
})
}
pub(crate) fn invalid_state() -> Self {
Self::Error(ErrorObject {
code: -127,
message: "Invalid state".to_string(),
data: None,
})
}
pub(crate) fn invalid_params(error: &serde_json::Error) -> Self {
Self::Error(ErrorObject {
code: INVALID_PARAMS_CODE,
message: "Invalid method parameter(s)".to_string(),
data: Some(Value::String(error.to_string())),
})
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, thiserror::Error)]
#[error("Error[{code}]: {message}")]
pub struct ErrorObject {
code: i64,
message: String,
data: Option<Value>,
}
pub(crate) struct Client<S, O> {
next_id: AtomicU64,
response_handlers: HashMap<u64, ResponseHandlers<S, O>>,
}
impl<S, O> Client<S, O> {
pub(crate) fn new() -> Self {
Self {
next_id: AtomicU64::new(1),
response_handlers: HashMap::new(),
}
}
#[throws(serde_json::Error)]
pub(crate) fn request<R: Request<S, O>>(&mut self, request: &R) -> RequestObject {
let request = RequestObject {
method: R::METHOD.to_string(),
params: request.params()?,
id: request.response_handlers().map(|handlers| {
loop {
let id_num = self.next_id.fetch_add(1, Ordering::Relaxed);
if let Entry::Vacant(entry) = self.response_handlers.entry(id_num) {
#[allow(unused_results)] {
entry.insert(handlers);
}
break Id::Num(id_num.into());
}
}
}),
};
log::trace!("{}", request);
request
}
#[throws(ProcessResponseError)]
pub(crate) fn process_response(
&mut self,
state: &mut S,
response: ResponseObject,
) -> Option<O> {
let mut response_handlers = None;
if let Id::Num(ref id) = response.id {
if let Some(id_u64) = id.as_u64() {
response_handlers = self.response_handlers.remove(&id_u64);
}
}
if let Some(handlers) = response_handlers {
match response.outcome {
Outcome::Result(value) => (handlers.0)(state, value).transpose()?,
Outcome::Error(ref error_object) => match Error::from(error_object.clone()) {
Error::Application(error) => (handlers.1)(state, error),
Error::Predefined(error) => throw!(error),
},
}
} else {
log::warn!("Received response with unrecognized id '{}'", response.id);
None
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum ProcessResponseError {
#[error(transparent)]
Predefined(#[from] PredefinedError),
#[error(transparent)]
Serialize(#[from] serde_json::Error),
}
#[derive(Debug, thiserror::Error)]
pub struct InsertRequestError {
method: String,
was_request: bool,
}
impl Display for InsertRequestError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"{} handler for `{}` already existed",
if self.was_request {
"request"
} else {
"notification"
},
self.method
)
}
}
#[throws(InsertRequestError)]
fn insert_request<S, O, R: Request<S, O>>(
request_handlers: &mut HashMap<&'static str, RequestHandler<S>>,
notification_handlers: &mut HashMap<&'static str, NotificationHandler<S>>,
request: &R,
) {
let (req, notification) = request.method_handlers();
if let Some(request_handler) = req {
if request_handlers
.insert(R::METHOD, request_handler)
.is_some()
{
throw!(InsertRequestError {
method: R::METHOD.to_string(),
was_request: true,
});
}
}
if let Some(notification_handler) = notification {
if notification_handlers
.insert(R::METHOD, notification_handler)
.is_some()
{
throw!(InsertRequestError {
method: R::METHOD.to_string(),
was_request: false,
});
}
}
}
pub(crate) struct Server<S> {
request_handlers: HashMap<&'static str, RequestHandler<S>>,
notification_handlers: HashMap<&'static str, NotificationHandler<S>>,
}
impl<S> Server<S> {
#[throws(InsertRequestError)]
pub(crate) fn new<O, R: Request<S, O>>(requests: Vec<R>) -> Self {
let mut request_handlers = HashMap::new();
let mut notification_handlers = HashMap::new();
for request in requests {
insert_request(&mut request_handlers, &mut notification_handlers, &request)?;
}
Self {
request_handlers,
notification_handlers,
}
}
pub(crate) fn process_request(
&mut self,
state: &mut S,
request: RequestObject,
) -> Option<ResponseObject> {
if let Some(id) = request.id {
let outcome =
if let Some(request_handler) = self.request_handlers.get(request.method.as_str()) {
(request_handler)(state, request.params)
} else {
log::warn!("Received request with unknown method: {}", request.method);
Outcome::unknown_request()
};
Some(ResponseObject { id, outcome })
} else if let Some(notification_handler) =
self.notification_handlers.get(request.method.as_str())
{
(notification_handler)(state, request.params);
None
} else {
log::warn!(
"Received notification with unknown method: {}",
request.method
);
None
}
}
}
#[derive(Debug, thiserror::Error, market::ConsumeFault)]
pub(crate) enum Error {
#[error(transparent)]
Predefined(PredefinedError),
#[error(transparent)]
Application(ErrorObject),
}
impl From<ErrorObject> for Error {
fn from(error: ErrorObject) -> Self {
match error.code {
PREDEFINED_ERROR_MIN_CODE..=PREDEFINED_ERROR_MAX_CODE => match error.code {
PARSE_ERROR_CODE => Self::Predefined(PredefinedError::Parse(error.data)),
INVALID_REQUEST_CODE => {
Self::Predefined(PredefinedError::InvalidRequest(error.data))
}
METHOD_NOT_FOUND_CODE => {
Self::Predefined(PredefinedError::MethodNotFound(error.data))
}
INVALID_PARAMS_CODE => Self::Predefined(PredefinedError::InvalidParams(error.data)),
INTERNAL_ERROR_CODE => Self::Predefined(PredefinedError::Internal(error.data)),
SERVER_ERROR_MIN_CODE..=SERVER_ERROR_MAX_CODE => {
Self::Predefined(PredefinedError::Server(error))
}
_ => Self::Predefined(PredefinedError::Reserved(error)),
},
_ => Self::Application(error),
}
}
}
#[derive(Debug, thiserror::Error, market::ConsumeFault)]
pub enum PredefinedError {
#[error("An error occurred while parsing the JSON text")]
Parse(Option<Value>),
#[error("JSON is not a valid Request object")]
InvalidRequest(Option<Value>),
#[error("The method does not exist/is not available")]
MethodNotFound(Option<Value>),
#[error("Invalid method parameter(s)")]
InvalidParams(Option<Value>),
#[error("Internal JSON-RPC error")]
Internal(Option<Value>),
#[error("Implementation-defined server error")]
Server(ErrorObject),
#[error("")]
Reserved(ErrorObject),
}