cuprate_json_rpc/error/
object.rs

1//! Error object.
2
3//---------------------------------------------------------------------------------------------------- Use
4use std::{borrow::Cow, error::Error, fmt::Display};
5
6use serde::{Deserialize, Serialize};
7use serde_json::value::Value;
8
9use crate::error::{
10    constants::{
11        INTERNAL_ERROR, INVALID_PARAMS, INVALID_REQUEST, METHOD_NOT_FOUND, PARSE_ERROR,
12        SERVER_ERROR,
13    },
14    ErrorCode,
15};
16
17//---------------------------------------------------------------------------------------------------- ErrorObject
18/// [The error object](https://www.jsonrpc.org/specification).
19///
20/// This is the object sent back in a [`Response`](crate::Response)
21/// if the method call errored.
22///
23/// # Display
24/// ```rust
25/// use cuprate_json_rpc::error::ErrorObject;
26///
27/// // The format is `$CODE: $MESSAGE`.
28/// // If a message was not passed during construction,
29/// // the error code's message will be used.
30/// assert_eq!(format!("{}", ErrorObject::parse_error()),      "-32700: Parse error");
31/// assert_eq!(format!("{}", ErrorObject::invalid_request()),  "-32600: Invalid Request");
32/// assert_eq!(format!("{}", ErrorObject::method_not_found()), "-32601: Method not found");
33/// assert_eq!(format!("{}", ErrorObject::invalid_params()),   "-32602: Invalid params");
34/// assert_eq!(format!("{}", ErrorObject::internal_error()),   "-32603: Internal error");
35/// assert_eq!(format!("{}", ErrorObject::server_error(0)),    "0: Server error");
36///
37/// // Set a custom message.
38/// let mut e = ErrorObject::server_error(1);
39/// e.message = "hello".into();
40/// assert_eq!(format!("{e}"), "1: hello");
41/// ```
42#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
43pub struct ErrorObject {
44    /// The error code.
45    pub code: ErrorCode,
46
47    /// A custom message for this error, distinct from [`ErrorCode::msg`].
48    ///
49    /// A JSON `string` value.
50    ///
51    /// This is a `Cow<'static, str>` to support both 0-allocation for
52    /// `const` string ID's commonly found in programs, as well as support
53    /// for runtime [`String`]'s.
54    pub message: Cow<'static, str>,
55
56    /// Optional data associated with the error.
57    ///
58    /// # `None` vs `Some(Value::Null)`
59    /// This field will be completely omitted during serialization if [`None`],
60    /// however if it is `Some(Value::Null)`, it will be serialized as `"data": null`.
61    #[serde(skip_serializing_if = "Option::is_none")]
62    pub data: Option<Value>,
63}
64
65impl ErrorObject {
66    /// Creates a new error, deriving the message from the code.
67    ///
68    /// Same as `ErrorObject::from(ErrorCode)`.
69    ///
70    /// ```rust
71    /// use std::borrow::Cow;
72    /// use cuprate_json_rpc::error::{ErrorCode, ErrorObject};
73    ///
74    /// for code in [
75    ///     ErrorCode::ParseError,
76    ///     ErrorCode::InvalidRequest,
77    ///     ErrorCode::MethodNotFound,
78    ///     ErrorCode::InvalidParams,
79    ///     ErrorCode::InternalError,
80    ///     ErrorCode::ServerError(0),
81    /// ] {
82    ///     let object = ErrorObject::from_code(code);
83    ///     assert_eq!(object, ErrorObject {
84    ///         code,
85    ///         message: Cow::Borrowed(code.msg()),
86    ///         data: None,
87    ///     });
88    ///
89    /// }
90    /// ```
91    pub const fn from_code(code: ErrorCode) -> Self {
92        Self {
93            code,
94            message: Cow::Borrowed(code.msg()),
95            data: None,
96        }
97    }
98
99    /// Creates a new error using [`PARSE_ERROR`].
100    ///
101    /// ```rust
102    /// use std::borrow::Cow;
103    /// use cuprate_json_rpc::error::{ErrorCode, ErrorObject};
104    ///
105    /// let code = ErrorCode::ParseError;
106    /// let object = ErrorObject::parse_error();
107    /// assert_eq!(object, ErrorObject {
108    ///     code,
109    ///     message: Cow::Borrowed(code.msg()),
110    ///     data: None,
111    /// });
112    /// ```
113    pub const fn parse_error() -> Self {
114        Self {
115            code: ErrorCode::ParseError,
116            message: Cow::Borrowed(PARSE_ERROR.1),
117            data: None,
118        }
119    }
120
121    /// Creates a new error using [`INVALID_REQUEST`].
122    ///
123    /// ```rust
124    /// use std::borrow::Cow;
125    /// use cuprate_json_rpc::error::{ErrorCode, ErrorObject};
126    ///
127    /// let code = ErrorCode::InvalidRequest;
128    /// let object = ErrorObject::invalid_request();
129    /// assert_eq!(object, ErrorObject {
130    ///     code,
131    ///     message: Cow::Borrowed(code.msg()),
132    ///     data: None,
133    /// });
134    /// ```
135    pub const fn invalid_request() -> Self {
136        Self {
137            code: ErrorCode::InvalidRequest,
138            message: Cow::Borrowed(INVALID_REQUEST.1),
139            data: None,
140        }
141    }
142
143    /// Creates a new error using [`METHOD_NOT_FOUND`].
144    ///
145    /// ```rust
146    /// use std::borrow::Cow;
147    /// use cuprate_json_rpc::error::{ErrorCode, ErrorObject};
148    ///
149    /// let code = ErrorCode::MethodNotFound;
150    /// let object = ErrorObject::method_not_found();
151    /// assert_eq!(object, ErrorObject {
152    ///     code,
153    ///     message: Cow::Borrowed(code.msg()),
154    ///     data: None,
155    /// });
156    /// ```
157    pub const fn method_not_found() -> Self {
158        Self {
159            code: ErrorCode::MethodNotFound,
160            message: Cow::Borrowed(METHOD_NOT_FOUND.1),
161            data: None,
162        }
163    }
164
165    /// Creates a new error using [`INVALID_PARAMS`].
166    ///
167    /// ```rust
168    /// use std::borrow::Cow;
169    /// use cuprate_json_rpc::error::{ErrorCode, ErrorObject};
170    ///
171    /// let code = ErrorCode::InvalidParams;
172    /// let object = ErrorObject::invalid_params();
173    /// assert_eq!(object, ErrorObject {
174    ///     code,
175    ///     message: Cow::Borrowed(code.msg()),
176    ///     data: None,
177    /// });
178    /// ```
179    pub const fn invalid_params() -> Self {
180        Self {
181            code: ErrorCode::InvalidParams,
182            message: Cow::Borrowed(INVALID_PARAMS.1),
183            data: None,
184        }
185    }
186
187    /// Creates a new error using [`INTERNAL_ERROR`].
188    ///
189    ///
190    /// ```rust
191    /// use std::borrow::Cow;
192    /// use cuprate_json_rpc::error::{ErrorCode, ErrorObject};
193    ///
194    /// let code = ErrorCode::InternalError;
195    /// let object = ErrorObject::internal_error();
196    /// assert_eq!(object, ErrorObject {
197    ///     code,
198    ///     message: Cow::Borrowed(code.msg()),
199    ///     data: None,
200    /// });
201    /// ```
202    pub const fn internal_error() -> Self {
203        Self {
204            code: ErrorCode::InternalError,
205            message: Cow::Borrowed(INTERNAL_ERROR.1),
206            data: None,
207        }
208    }
209
210    /// Creates a new error using [`SERVER_ERROR`].
211    ///
212    /// You must provide the custom [`i32`] error code.
213    ///
214    /// ```rust
215    /// use std::borrow::Cow;
216    /// use cuprate_json_rpc::error::{ErrorCode, ErrorObject};
217    ///
218    /// let code = ErrorCode::ServerError(0);
219    /// let object = ErrorObject::server_error(0);
220    /// assert_eq!(object, ErrorObject {
221    ///     code,
222    ///     message: Cow::Borrowed(code.msg()),
223    ///     data: None,
224    /// });
225    /// ```
226    pub const fn server_error(error_code: i32) -> Self {
227        Self {
228            code: ErrorCode::ServerError(error_code),
229            message: Cow::Borrowed(SERVER_ERROR),
230            data: None,
231        }
232    }
233}
234
235//---------------------------------------------------------------------------------------------------- Trait impl
236impl From<ErrorCode> for ErrorObject {
237    fn from(code: ErrorCode) -> Self {
238        Self::from_code(code)
239    }
240}
241
242impl Display for ErrorObject {
243    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
244        // Using `self.code`'s formatting will write the
245        // message twice, so prefer the built-in message.
246        write!(f, "{}: {}", self.code.code(), self.message)
247    }
248}
249
250impl Error for ErrorObject {
251    fn source(&self) -> Option<&(dyn Error + 'static)> {
252        Some(&self.code)
253    }
254
255    fn description(&self) -> &str {
256        &self.message
257    }
258}