cuprate_json_rpc/error/
code.rs

1//! Error codes.
2
3//---------------------------------------------------------------------------------------------------- Use
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5
6use crate::error::constants::{
7    INTERNAL_ERROR, INVALID_PARAMS, INVALID_REQUEST, METHOD_NOT_FOUND, PARSE_ERROR, SERVER_ERROR,
8};
9
10//---------------------------------------------------------------------------------------------------- ErrorCode
11/// [Error object code](https://www.jsonrpc.org/specification#error_object).
12///
13/// This `enum` encapsulates JSON-RPC 2.0's error codes
14/// found in [`ErrorObject`](crate::error::ErrorObject).
15///
16/// It associates the code integer ([`i32`]) with its defined message.
17///
18/// # Application defined errors
19/// The custom error codes past `-32099` (`-31000, -31001`, ...)
20/// defined in JSON-RPC 2.0 are not supported by this enum because:
21///
22/// 1. The `(i32, &'static str)` required makes the enum more than 3x larger
23/// 2. It is not used by Cuprate/Monero[^1]
24///
25/// [^1]: Defined errors used by Monero (also excludes the last defined error `-32000 to -32099 Server error`): <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/contrib/epee/include/net/http_server_handlers_map2.h#L150>
26///
27/// # Display
28/// ```rust
29/// use cuprate_json_rpc::error::ErrorCode;
30/// use serde_json::{to_value, from_value, Value};
31///
32/// for e in [
33///     ErrorCode::ParseError,
34///     ErrorCode::InvalidRequest,
35///     ErrorCode::MethodNotFound,
36///     ErrorCode::InvalidParams,
37///     ErrorCode::InternalError,
38///     ErrorCode::ServerError(0),
39/// ] {
40///     // The formatting is `$CODE: $MSG`.
41///     let expected_fmt = format!("{}: {}", e.code(), e.msg());
42///     assert_eq!(expected_fmt, format!("{e}"));
43/// }
44/// ```
45///
46/// # (De)serialization
47/// This type gets (de)serialized as the associated `i32`, for example:
48/// ```rust
49/// use cuprate_json_rpc::error::ErrorCode;
50/// use serde_json::{to_value, from_value, Value};
51///
52/// for e in [
53///     ErrorCode::ParseError,
54///     ErrorCode::InvalidRequest,
55///     ErrorCode::MethodNotFound,
56///     ErrorCode::InvalidParams,
57///     ErrorCode::InternalError,
58///     ErrorCode::ServerError(0),
59///     ErrorCode::ServerError(1),
60///     ErrorCode::ServerError(2),
61/// ] {
62///     // Gets serialized into a JSON integer.
63///     let value = to_value(&e).unwrap();
64///     assert_eq!(value, Value::Number(e.code().into()));
65///
66///     // Expects a JSON integer when deserializing.
67///     assert_eq!(e, from_value(value).unwrap());
68/// }
69/// ```
70///
71/// ```rust,should_panic
72/// # use cuprate_json_rpc::error::ErrorCode;
73/// # use serde_json::from_value;
74/// // A JSON string that contains an integer won't work.
75/// from_value::<ErrorCode>("-32700".into()).unwrap();
76/// ```
77#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, thiserror::Error)]
78pub enum ErrorCode {
79    #[error("{}: {}", PARSE_ERROR.0, PARSE_ERROR.1)]
80    /// Invalid JSON was received by the server.
81    ///
82    /// An error occurred on the server while parsing the JSON text.
83    ParseError,
84
85    #[error("{}: {}", INVALID_REQUEST.0, INVALID_REQUEST.1)]
86    /// The JSON sent is not a valid Request object.
87    InvalidRequest,
88
89    #[error("{}: {}", METHOD_NOT_FOUND.0, METHOD_NOT_FOUND.1)]
90    /// The method does not exist / is not available.
91    MethodNotFound,
92
93    #[error("{}: {}", INVALID_PARAMS.0, INVALID_PARAMS.1)]
94    /// Invalid method parameters.
95    InvalidParams,
96
97    #[error("{}: {}", INTERNAL_ERROR.0, INTERNAL_ERROR.1)]
98    /// Internal JSON-RPC error.
99    InternalError,
100
101    #[error("{0}: {SERVER_ERROR}")]
102    /// Reserved for implementation-defined server-errors.
103    ServerError(i32),
104}
105
106impl ErrorCode {
107    /// Creates [`Self`] from a [`i32`] code.
108    ///
109    /// [`From<i32>`] is the same as this function.
110    ///
111    /// ```rust
112    /// use cuprate_json_rpc::error::{
113    ///     ErrorCode,
114    ///     INTERNAL_ERROR, INVALID_PARAMS, INVALID_REQUEST, METHOD_NOT_FOUND, PARSE_ERROR,
115    /// };
116    ///
117    /// assert_eq!(ErrorCode::from_code(PARSE_ERROR.0),      ErrorCode::ParseError);
118    /// assert_eq!(ErrorCode::from_code(INVALID_REQUEST.0),  ErrorCode::InvalidRequest);
119    /// assert_eq!(ErrorCode::from_code(METHOD_NOT_FOUND.0), ErrorCode::MethodNotFound);
120    /// assert_eq!(ErrorCode::from_code(INVALID_PARAMS.0),   ErrorCode::InvalidParams);
121    /// assert_eq!(ErrorCode::from_code(INTERNAL_ERROR.0),   ErrorCode::InternalError);
122    ///
123    /// // Non-defined code inputs will default to a custom `ServerError`.
124    /// assert_eq!(ErrorCode::from_code(0), ErrorCode::ServerError(0));
125    /// assert_eq!(ErrorCode::from_code(1), ErrorCode::ServerError(1));
126    /// assert_eq!(ErrorCode::from_code(2), ErrorCode::ServerError(2));
127    /// ```
128    pub const fn from_code(code: i32) -> Self {
129        // FIXME: you cannot `match` on tuple fields
130        // so use `if` (seems to compile to the same
131        // assembly as matching directly on `i32`s).
132        if code == PARSE_ERROR.0 {
133            Self::ParseError
134        } else if code == INVALID_REQUEST.0 {
135            Self::InvalidRequest
136        } else if code == METHOD_NOT_FOUND.0 {
137            Self::MethodNotFound
138        } else if code == INVALID_PARAMS.0 {
139            Self::InvalidParams
140        } else if code == INTERNAL_ERROR.0 {
141            Self::InternalError
142        } else {
143            Self::ServerError(code)
144        }
145    }
146
147    /// Returns `self`'s [`i32`] code representation.
148    ///
149    /// ```rust
150    /// use cuprate_json_rpc::error::{
151    ///     ErrorCode,
152    ///     INTERNAL_ERROR, INVALID_PARAMS, INVALID_REQUEST, METHOD_NOT_FOUND, PARSE_ERROR,
153    /// };
154    ///
155    /// assert_eq!(ErrorCode::ParseError.code(),     PARSE_ERROR.0);
156    /// assert_eq!(ErrorCode::InvalidRequest.code(), INVALID_REQUEST.0);
157    /// assert_eq!(ErrorCode::MethodNotFound.code(), METHOD_NOT_FOUND.0);
158    /// assert_eq!(ErrorCode::InvalidParams.code(),  INVALID_PARAMS.0);
159    /// assert_eq!(ErrorCode::InternalError.code(),  INTERNAL_ERROR.0);
160    /// assert_eq!(ErrorCode::ServerError(0).code(), 0);
161    /// assert_eq!(ErrorCode::ServerError(1).code(), 1);
162    /// ```
163    pub const fn code(&self) -> i32 {
164        match self {
165            Self::ParseError => PARSE_ERROR.0,
166            Self::InvalidRequest => INVALID_REQUEST.0,
167            Self::MethodNotFound => METHOD_NOT_FOUND.0,
168            Self::InvalidParams => INVALID_PARAMS.0,
169            Self::InternalError => INTERNAL_ERROR.0,
170            Self::ServerError(code) => *code,
171        }
172    }
173
174    /// Returns `self`'s human readable [`str`] message.
175    ///
176    /// ```rust
177    /// use cuprate_json_rpc::error::{
178    ///     ErrorCode,
179    ///     INTERNAL_ERROR, INVALID_PARAMS, INVALID_REQUEST, METHOD_NOT_FOUND, PARSE_ERROR, SERVER_ERROR,
180    /// };
181    ///
182    /// assert_eq!(ErrorCode::ParseError.msg(),     PARSE_ERROR.1);
183    /// assert_eq!(ErrorCode::InvalidRequest.msg(), INVALID_REQUEST.1);
184    /// assert_eq!(ErrorCode::MethodNotFound.msg(), METHOD_NOT_FOUND.1);
185    /// assert_eq!(ErrorCode::InvalidParams.msg(),  INVALID_PARAMS.1);
186    /// assert_eq!(ErrorCode::InternalError.msg(),  INTERNAL_ERROR.1);
187    /// assert_eq!(ErrorCode::ServerError(0).msg(), SERVER_ERROR);
188    /// ```
189    pub const fn msg(&self) -> &'static str {
190        match self {
191            Self::ParseError => PARSE_ERROR.1,
192            Self::InvalidRequest => INVALID_REQUEST.1,
193            Self::MethodNotFound => METHOD_NOT_FOUND.1,
194            Self::InvalidParams => INVALID_PARAMS.1,
195            Self::InternalError => INTERNAL_ERROR.1,
196            Self::ServerError(_) => SERVER_ERROR,
197        }
198    }
199}
200
201//---------------------------------------------------------------------------------------------------- Trait impl
202impl<N: Into<i32>> From<N> for ErrorCode {
203    fn from(code: N) -> Self {
204        Self::from_code(code.into())
205    }
206}
207
208//---------------------------------------------------------------------------------------------------- Serde impl
209impl<'a> Deserialize<'a> for ErrorCode {
210    fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> {
211        Ok(Self::from_code(Deserialize::deserialize(deserializer)?))
212    }
213}
214
215impl Serialize for ErrorCode {
216    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
217        serializer.serialize_i32(self.code())
218    }
219}