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}