use crate::RpcSend;
use serde::{ser::SerializeMap, Serialize, Serializer};
use serde_json::value::{to_raw_value, RawValue};
use std::borrow::Cow;
use std::fmt;
const INTERNAL_ERROR: Cow<'_, str> = Cow::Borrowed("Internal error");
#[derive(Debug, Clone)]
pub(crate) struct Response<'a, T, E> {
pub(crate) id: Box<RawValue>,
pub(crate) payload: &'a ResponsePayload<T, E>,
}
impl Response<'_, (), ()> {
#[allow(dead_code)] pub(crate) fn parse_error() -> Box<RawValue> {
Response::<(), ()> {
id: Default::default(), payload: &ResponsePayload::parse_error(),
}
.to_json()
}
pub(crate) fn invalid_params(id: Box<RawValue>) -> Box<RawValue> {
Response::<(), ()> {
id,
payload: &ResponsePayload::invalid_params(),
}
.to_json()
}
pub(crate) fn method_not_found(id: Box<RawValue>) -> Box<RawValue> {
Response::<(), ()> {
id,
payload: &ResponsePayload::method_not_found(),
}
.to_json()
}
pub(crate) fn serialization_failure(id: &RawValue) -> Box<RawValue> {
RawValue::from_string(format!(
r#"{{"jsonrpc":"2.0","id":{},"error":{{"code":-32700,"message":"response serialization error"}}}}"#,
id.get()
))
.expect("valid json")
}
}
impl<T, E> Response<'_, T, E>
where
T: Serialize,
E: Serialize,
{
pub(crate) fn to_json(&self) -> Box<RawValue> {
serde_json::value::to_raw_value(self).unwrap_or_else(|err| {
tracing::debug!(%err, id = %self.id, "failed to serialize response");
Response::serialization_failure(&self.id)
})
}
}
impl<T, E> Serialize for Response<'_, T, E>
where
T: Serialize,
E: Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(Some(3))?;
map.serialize_entry("jsonrpc", "2.0")?;
map.serialize_entry("id", &self.id)?;
match &self.payload {
ResponsePayload::Success(result) => {
map.serialize_entry("result", result)?;
}
ResponsePayload::Failure(error) => {
map.serialize_entry("error", error)?;
}
}
map.end()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ResponsePayload<Payload, ErrData> {
Success(Payload),
Failure(ErrorPayload<ErrData>),
}
impl<Payload, ErrData> ResponsePayload<Payload, ErrData> {
pub const fn parse_error() -> Self {
Self::Failure(ErrorPayload::parse_error())
}
pub const fn invalid_request() -> Self {
Self::Failure(ErrorPayload::invalid_request())
}
pub const fn method_not_found() -> Self {
Self::Failure(ErrorPayload::method_not_found())
}
pub const fn invalid_params() -> Self {
Self::Failure(ErrorPayload::invalid_params())
}
pub const fn internal_error() -> Self {
Self::Failure(ErrorPayload::internal_error())
}
pub const fn internal_error_message(message: Cow<'static, str>) -> Self {
Self::Failure(ErrorPayload::internal_error_message(message))
}
pub const fn internal_error_with_obj(data: ErrData) -> Self
where
ErrData: RpcSend,
{
Self::Failure(ErrorPayload::internal_error_with_obj(data))
}
pub const fn internal_error_with_message_and_obj(
message: Cow<'static, str>,
data: ErrData,
) -> Self
where
ErrData: RpcSend,
{
Self::Failure(ErrorPayload::internal_error_with_message_and_obj(
message, data,
))
}
pub const fn as_success(&self) -> Option<&Payload> {
match self {
Self::Success(payload) => Some(payload),
_ => None,
}
}
pub const fn as_error(&self) -> Option<&ErrorPayload<ErrData>> {
match self {
Self::Failure(payload) => Some(payload),
_ => None,
}
}
pub const fn is_success(&self) -> bool {
matches!(self, Self::Success(_))
}
pub const fn is_error(&self) -> bool {
matches!(self, Self::Failure(_))
}
}
#[derive(Clone, Debug, Serialize, PartialEq, Eq)]
pub struct ErrorPayload<ErrData = Box<RawValue>> {
pub code: i64,
pub message: Cow<'static, str>,
pub data: Option<ErrData>,
}
impl<E> ErrorPayload<E> {
pub const fn parse_error() -> Self {
Self {
code: -32700,
message: Cow::Borrowed("Parse error"),
data: None,
}
}
pub const fn invalid_request() -> Self {
Self {
code: -32600,
message: Cow::Borrowed("Invalid Request"),
data: None,
}
}
pub const fn method_not_found() -> Self {
Self {
code: -32601,
message: Cow::Borrowed("Method not found"),
data: None,
}
}
pub const fn invalid_params() -> Self {
Self {
code: -32602,
message: Cow::Borrowed("Invalid params"),
data: None,
}
}
pub const fn internal_error() -> Self {
Self {
code: -32603,
message: INTERNAL_ERROR,
data: None,
}
}
pub const fn internal_error_message(message: Cow<'static, str>) -> Self {
Self {
code: -32603,
message,
data: None,
}
}
pub const fn internal_error_with_obj(data: E) -> Self
where
E: RpcSend,
{
Self {
code: -32603,
message: INTERNAL_ERROR,
data: Some(data),
}
}
pub const fn internal_error_with_message_and_obj(message: Cow<'static, str>, data: E) -> Self
where
E: RpcSend,
{
Self {
code: -32603,
message,
data: Some(data),
}
}
pub fn is_retry_err(&self) -> bool {
if self.code == 429 {
return true;
}
if self.code == -32005 {
return true;
}
if self.code == -32016 && self.message.contains("rate limit") {
return true;
}
if self.code == -32012 && self.message.contains("credits") {
return true;
}
if self.code == -32007 && self.message.contains("request limit reached") {
return true;
}
match self.message.as_ref() {
"header not found" => true,
"daily request count exceeded, request rate limited" => true,
msg => {
msg.contains("rate limit")
|| msg.contains("rate exceeded")
|| msg.contains("too many requests")
|| msg.contains("credits limited")
|| msg.contains("request limit")
}
}
}
}
impl<T> From<T> for ErrorPayload<T>
where
T: std::error::Error + RpcSend,
{
fn from(value: T) -> Self {
Self {
code: -32603,
message: INTERNAL_ERROR,
data: Some(value),
}
}
}
impl<E> ErrorPayload<E>
where
E: RpcSend,
{
pub fn serialize_payload(&self) -> serde_json::Result<ErrorPayload> {
Ok(ErrorPayload {
code: self.code,
message: self.message.clone(),
data: match self.data.as_ref() {
Some(data) => Some(to_raw_value(data)?),
None => None,
},
})
}
}
impl<ErrData: fmt::Display> fmt::Display for ErrorPayload<ErrData> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"error code {}: {}{}",
self.code,
self.message,
self.data
.as_ref()
.map(|data| format!(", data: {}", data))
.unwrap_or_default()
)
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::test_utils::assert_rv_eq;
#[test]
fn ser_failure() {
let id = RawValue::from_string("1".to_string()).unwrap();
let res = Response::<(), ()>::serialization_failure(&id);
assert_rv_eq(
&res,
r#"{"jsonrpc":"2.0","id":1,"error":{"code":-32700,"message":"response serialization error"}}"#,
);
}
}