use std::fmt;
use serde::de::Deserializer;
use serde::ser::Serializer;
use serde::{Deserialize, Serialize};
use serde_json::value::RawValue;
use std::borrow::{Borrow, Cow as StdCow};
use thiserror::Error;
pub type ErrorObjectOwned = ErrorObject<'static>;
#[derive(Debug, Deserialize, Serialize, Clone, thiserror::Error)]
#[serde(deny_unknown_fields)]
#[error("{self:?}")]
pub struct ErrorObject<'a> {
code: ErrorCode,
message: StdCow<'a, str>,
#[serde(skip_serializing_if = "Option::is_none")]
data: Option<StdCow<'a, RawValue>>,
}
impl<'a> ErrorObject<'a> {
pub fn code(&self) -> i32 {
self.code.code()
}
pub fn message(&self) -> &str {
self.message.borrow()
}
pub fn data(&self) -> Option<&RawValue> {
self.data.as_ref().map(|d| d.borrow())
}
pub fn owned<S: Serialize>(code: i32, message: impl Into<String>, data: Option<S>) -> ErrorObject<'static> {
let data = data.and_then(|d| serde_json::value::to_raw_value(&d).ok());
ErrorObject { code: code.into(), message: message.into().into(), data: data.map(StdCow::Owned) }
}
pub fn borrowed(code: i32, message: &'a str, data: Option<&'a RawValue>) -> ErrorObject<'a> {
ErrorObject { code: code.into(), message: StdCow::Borrowed(message), data: data.map(StdCow::Borrowed) }
}
pub fn into_owned(self) -> ErrorObject<'static> {
ErrorObject {
code: self.code,
message: StdCow::Owned(self.message.into_owned()),
data: self.data.map(|d| StdCow::Owned(d.into_owned())),
}
}
pub fn borrow(&'a self) -> ErrorObject<'a> {
ErrorObject {
code: self.code,
message: StdCow::Borrowed(self.message.borrow()),
data: self.data.as_ref().map(|d| StdCow::Borrowed(d.borrow())),
}
}
}
impl<'a> PartialEq for ErrorObject<'a> {
fn eq(&self, other: &Self) -> bool {
let this_raw = self.data.as_ref().map(|r| r.get());
let other_raw = other.data.as_ref().map(|r| r.get());
self.code == other.code && self.message == other.message && this_raw == other_raw
}
}
impl<'a> From<ErrorCode> for ErrorObject<'a> {
fn from(code: ErrorCode) -> Self {
Self { code, message: code.message().into(), data: None }
}
}
pub const PARSE_ERROR_CODE: i32 = -32700;
pub const INVALID_REQUEST_CODE: i32 = -32600;
pub const METHOD_NOT_FOUND_CODE: i32 = -32601;
pub const INVALID_PARAMS_CODE: i32 = -32602;
pub const INTERNAL_ERROR_CODE: i32 = -32603;
pub const CALL_EXECUTION_FAILED_CODE: i32 = -32000;
pub const UNKNOWN_ERROR_CODE: i32 = -32001;
pub const BATCHES_NOT_SUPPORTED_CODE: i32 = -32005;
pub const TOO_MANY_SUBSCRIPTIONS_CODE: i32 = -32006;
pub const OVERSIZED_REQUEST_CODE: i32 = -32007;
pub const OVERSIZED_RESPONSE_CODE: i32 = -32008;
pub const SERVER_IS_BUSY_CODE: i32 = -32009;
pub const TOO_BIG_BATCH_REQUEST_CODE: i32 = -32010;
pub const TOO_BIG_BATCH_RESPONSE_CODE: i32 = -32011;
pub const PARSE_ERROR_MSG: &str = "Parse error";
pub const OVERSIZED_REQUEST_MSG: &str = "Request is too big";
pub const OVERSIZED_RESPONSE_MSG: &str = "Response is too big";
pub const INTERNAL_ERROR_MSG: &str = "Internal error";
pub const INVALID_PARAMS_MSG: &str = "Invalid params";
pub const INVALID_REQUEST_MSG: &str = "Invalid request";
pub const METHOD_NOT_FOUND_MSG: &str = "Method not found";
pub const SERVER_IS_BUSY_MSG: &str = "Server is busy, try again later";
pub const SERVER_ERROR_MSG: &str = "Server error";
pub const BATCHES_NOT_SUPPORTED_MSG: &str = "Batched requests are not supported by this server";
pub const TOO_MANY_SUBSCRIPTIONS_MSG: &str = "Too many subscriptions on the connection";
pub const TOO_BIG_BATCH_REQUEST_MSG: &str = "The batch request was too large";
pub const TOO_BIG_BATCH_RESPONSE_MSG: &str = "The batch response was too large";
#[derive(Error, Debug, PartialEq, Eq, Copy, Clone)]
pub enum ErrorCode {
ParseError,
OversizedRequest,
InvalidRequest,
MethodNotFound,
ServerIsBusy,
InvalidParams,
InternalError,
ServerError(i32),
}
impl ErrorCode {
pub const fn code(&self) -> i32 {
use ErrorCode::*;
match *self {
ParseError => PARSE_ERROR_CODE,
OversizedRequest => OVERSIZED_REQUEST_CODE,
InvalidRequest => INVALID_REQUEST_CODE,
MethodNotFound => METHOD_NOT_FOUND_CODE,
ServerIsBusy => SERVER_IS_BUSY_CODE,
InvalidParams => INVALID_PARAMS_CODE,
InternalError => INTERNAL_ERROR_CODE,
ServerError(code) => code,
}
}
pub const fn message(&self) -> &'static str {
use ErrorCode::*;
match self {
ParseError => PARSE_ERROR_MSG,
OversizedRequest => OVERSIZED_REQUEST_MSG,
InvalidRequest => INVALID_REQUEST_MSG,
MethodNotFound => METHOD_NOT_FOUND_MSG,
ServerIsBusy => SERVER_IS_BUSY_MSG,
InvalidParams => INVALID_PARAMS_MSG,
InternalError => INTERNAL_ERROR_MSG,
ServerError(_) => SERVER_ERROR_MSG,
}
}
}
impl fmt::Display for ErrorCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {}", self.code(), self.message())
}
}
impl From<i32> for ErrorCode {
fn from(code: i32) -> Self {
use ErrorCode::*;
match code {
PARSE_ERROR_CODE => ParseError,
OVERSIZED_REQUEST_CODE => OversizedRequest,
INVALID_REQUEST_CODE => InvalidRequest,
METHOD_NOT_FOUND_CODE => MethodNotFound,
INVALID_PARAMS_CODE => InvalidParams,
INTERNAL_ERROR_CODE => InternalError,
code => ServerError(code),
}
}
}
impl<'a> serde::Deserialize<'a> for ErrorCode {
fn deserialize<D>(deserializer: D) -> Result<ErrorCode, D::Error>
where
D: Deserializer<'a>,
{
let code: i32 = Deserialize::deserialize(deserializer)?;
Ok(ErrorCode::from(code))
}
}
impl serde::Serialize for ErrorCode {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_i32(self.code())
}
}
pub fn reject_too_many_subscriptions(limit: u32) -> ErrorObjectOwned {
ErrorObjectOwned::owned(
TOO_MANY_SUBSCRIPTIONS_CODE,
TOO_MANY_SUBSCRIPTIONS_MSG,
Some(format!("Exceeded max limit of {limit}")),
)
}
pub fn reject_too_big_request(limit: u32) -> ErrorObjectOwned {
ErrorObjectOwned::owned(
OVERSIZED_REQUEST_CODE,
OVERSIZED_REQUEST_MSG,
Some(format!("Exceeded max limit of {limit}")),
)
}
pub fn reject_too_big_batch_request(limit: usize) -> ErrorObjectOwned {
ErrorObjectOwned::owned(
TOO_BIG_BATCH_REQUEST_CODE,
TOO_BIG_BATCH_REQUEST_MSG,
Some(format!("Exceeded max limit of {limit}")),
)
}
pub fn reject_too_big_batch_response(limit: usize) -> ErrorObjectOwned {
ErrorObjectOwned::owned(
TOO_BIG_BATCH_RESPONSE_CODE,
TOO_BIG_BATCH_RESPONSE_MSG,
Some(format!("Exceeded max limit of {limit}")),
)
}
#[cfg(test)]
mod tests {
use super::{ErrorCode, ErrorObject};
#[test]
fn deserialize_works() {
let ser = r#"{"code":-32700,"message":"Parse error"}"#;
let exp: ErrorObject = ErrorCode::ParseError.into();
let err: ErrorObject = serde_json::from_str(ser).unwrap();
assert_eq!(exp, err);
}
#[test]
fn deserialize_with_optional_data() {
let ser = r#"{"code":-32700,"message":"Parse error", "data":"vegan"}"#;
let data = serde_json::value::to_raw_value(&"vegan").unwrap();
let exp = ErrorObject::owned(ErrorCode::ParseError.code(), "Parse error", Some(data));
let err: ErrorObject = serde_json::from_str(ser).unwrap();
assert_eq!(exp, err);
}
#[test]
fn deserialized_error_with_quoted_str() {
let raw = r#"{
"code": 1002,
"message": "desc: \"Could not decode `ChargeAssetTxPayment::asset_id`\" } })",
"data": "\\\"validate_transaction\\\""
}"#;
let err: ErrorObject = serde_json::from_str(raw).unwrap();
let data = serde_json::value::to_raw_value(&"\\\"validate_transaction\\\"").unwrap();
let exp = ErrorObject::borrowed(
1002,
"desc: \"Could not decode `ChargeAssetTxPayment::asset_id`\" } })",
Some(&*data),
);
assert_eq!(err, exp);
}
#[test]
fn serialize_works() {
let exp = r#"{"code":-32603,"message":"Internal error"}"#;
let err: ErrorObject = ErrorCode::InternalError.into();
let ser = serde_json::to_string(&err).unwrap();
assert_eq!(exp, ser);
}
#[test]
fn serialize_optional_data_works() {
let exp = r#"{"code":-32699,"message":"food","data":"not vegan"}"#;
let data = serde_json::value::to_raw_value(&"not vegan").unwrap();
let ser = serde_json::to_string(&ErrorObject::owned(-32699, "food", Some(data))).unwrap();
assert_eq!(exp, ser);
}
}