use crate::v2::params::{Id, TwoPointZero}; use serde::de::{Deserializer, MapAccess, Visitor}; use serde::ser::{SerializeMap, Serializer}; use serde::{Deserialize, Serialize}; use serde_json::value::RawValue; use std::fmt; use thiserror::Error; /// [Failed JSON-RPC response object](https://www.jsonrpc.org/specification#response_object). #[derive(Serialize, Debug)] pub struct JsonRpcError<'a> { /// JSON-RPC version. pub jsonrpc: TwoPointZero, /// Error. pub error: ErrorCode, /// Request ID pub id: Option<&'a RawValue>, } /// [Failed JSON-RPC response object with allocations](https://www.jsonrpc.org/specification#response_object). #[derive(Error, Debug, Deserialize, PartialEq)] pub struct JsonRpcErrorAlloc { /// JSON-RPC version. pub jsonrpc: TwoPointZero, /// Error object. pub error: ErrorCode, /// Request ID. pub id: Id, } impl fmt::Display for JsonRpcErrorAlloc { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}: {}: {:?}", self.jsonrpc, self.error, self.id) } } /// Parse error code. pub const PARSE_ERROR_CODE: i32 = -32700; /// Internal error code. pub const INTERNAL_ERROR_CODE: i32 = -32603; /// Invalid params error code. pub const INVALID_PARAMS_CODE: i32 = -32602; /// Invalid request error code. pub const INVALID_REQUEST_CODE: i32 = -32600; /// Method not found error code. pub const METHOD_NOT_FOUND_CODE: i32 = -32601; /// Reserved for implementation-defined server-errors. pub const SERVER_ERROR_CODE_RANGE_START: i32 = -32000; /// Reserved for implementation-defined server-errors. pub const SERVER_ERROR_CODE_RANGE_END: i32 = 32099; /// Parse error message pub const PARSE_ERROR_MSG: &str = "Parse error"; /// Internal error message. pub const INTERNAL_ERROR_MSG: &str = "Internal error"; /// Invalid params error message. pub const INVALID_PARAMS_MSG: &str = "Invalid params"; /// Invalid request error message. pub const INVALID_REQUEST_MSG: &str = "Invalid request"; /// Method not found error message. pub const METHOD_NOT_FOUND_MSG: &str = "Method not found"; /// Reserved for implementation-defined server-errors. pub const SERVER_ERROR_MSG: &str = "Server error"; /// Application defined error which is not in the reserved space (-32000..=-32768) pub const APPLICATION_ERROR_MSG: &str = "Application error"; /// JSONRPC error code #[derive(Error, Debug, PartialEq, Copy, Clone)] pub enum ErrorCode { /// Invalid JSON was received by the server. /// An error occurred on the server while parsing the JSON text. ParseError, /// The JSON sent is not a valid Request object. InvalidRequest, /// The method does not exist / is not available. MethodNotFound, /// Invalid method parameter(s). InvalidParams, /// Internal JSON-RPC error. InternalError, /// Reserved for implementation-defined server-errors. ServerError(i32), /// Application defined error which is not in the reserved space (-32000..=-32768) ApplicationError(i32), } impl ErrorCode { /// Returns integer code value pub const fn code(&self) -> i32 { match *self { ErrorCode::ParseError => PARSE_ERROR_CODE, ErrorCode::InvalidRequest => INVALID_REQUEST_CODE, ErrorCode::MethodNotFound => METHOD_NOT_FOUND_CODE, ErrorCode::InvalidParams => INVALID_PARAMS_CODE, ErrorCode::InternalError => INTERNAL_ERROR_CODE, ErrorCode::ServerError(code) => code, ErrorCode::ApplicationError(code) => code, } } /// Returns the message for the given error code. pub const fn message(&self) -> &str { match self { ErrorCode::ParseError => PARSE_ERROR_MSG, ErrorCode::InvalidRequest => INVALID_REQUEST_MSG, ErrorCode::MethodNotFound => METHOD_NOT_FOUND_MSG, ErrorCode::InvalidParams => INVALID_PARAMS_MSG, ErrorCode::InternalError => INTERNAL_ERROR_MSG, ErrorCode::ServerError(_) => SERVER_ERROR_MSG, ErrorCode::ApplicationError(_) => APPLICATION_ERROR_MSG, } } } impl fmt::Display for ErrorCode { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}: {}", self.code(), self.message()) } } impl From for ErrorCode { fn from(code: i32) -> Self { match code { PARSE_ERROR_CODE => ErrorCode::ParseError, INVALID_REQUEST_CODE => ErrorCode::InvalidRequest, METHOD_NOT_FOUND_CODE => ErrorCode::MethodNotFound, INVALID_PARAMS_CODE => ErrorCode::InvalidParams, INTERNAL_ERROR_CODE => ErrorCode::InternalError, SERVER_ERROR_CODE_RANGE_START..=SERVER_ERROR_CODE_RANGE_END => ErrorCode::ServerError(code), code => ErrorCode::ApplicationError(code), } } } impl<'a> serde::Deserialize<'a> for ErrorCode { fn deserialize(deserializer: D) -> Result where D: Deserializer<'a>, { let code = deserializer.deserialize_map(ErrorCodeVisitor)?; Ok(code) } } impl serde::Serialize for ErrorCode { fn serialize(&self, serializer: S) -> Result where S: Serializer, { let mut map = serializer.serialize_map(Some(2))?; map.serialize_entry("code", &self.code())?; map.serialize_entry("message", self.message())?; map.end() } } struct ErrorCodeVisitor; impl<'de> Visitor<'de> for ErrorCodeVisitor { type Value = ErrorCode; // Format a message stating what data this Visitor expects to receive. fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("code") } fn visit_map(self, mut access: M) -> Result where M: MapAccess<'de>, { let mut maybe_code = None; while let Ok(Some((key, val))) = access.next_entry::<&str, i32>() { if key == "code" && maybe_code.is_none() { maybe_code = Some(val.into()) } } let code = maybe_code.ok_or_else(|| serde::de::Error::missing_field("code"))?; Ok(code) } } #[cfg(test)] mod tests { use super::{ErrorCode, Id, JsonRpcError, JsonRpcErrorAlloc, TwoPointZero}; #[test] fn deserialize_works() { let ser = r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error"},"id":null}"#; let err: JsonRpcErrorAlloc = serde_json::from_str(ser).unwrap(); assert_eq!(err.jsonrpc, TwoPointZero); assert_eq!(err.error, ErrorCode::ParseError); assert_eq!(err.id, Id::Null); } #[test] fn serialize_works() { let exp = r#"{"jsonrpc":"2.0","error":{"code":-32603,"message":"Internal error"},"id":1337}"#; let raw_id = serde_json::value::to_raw_value(&1337).unwrap(); let err = JsonRpcError { jsonrpc: TwoPointZero, error: ErrorCode::InternalError, id: Some(&*raw_id) }; let ser = serde_json::to_string(&err).unwrap(); assert_eq!(exp, ser); } }