Skip to main content

parse_rust_core/
error.rs

1//! Parse error codes.
2//!
3//! **Error codes are API.** Every variant carries the upstream numeric code from
4//! `src/Error.js`, and `spec/` asserts on these numbers directly. Never invent a code and
5//! never change one to a better-fitting one.
6//!
7//! Codes extracted from the `parse` npm SDK bundled with parse-server 9.10.1-alpha.6, which is
8//! the same table `src/Error.js` re-exports.
9
10use std::fmt;
11
12/// The upstream error code table. The discriminant *is* the wire value.
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14#[repr(i32)]
15#[non_exhaustive]
16pub enum ErrorCode {
17    OtherCause = -1,
18    InternalServerError = 1,
19    ConnectionFailed = 100,
20    ObjectNotFound = 101,
21    InvalidQuery = 102,
22    InvalidClassName = 103,
23    MissingObjectId = 104,
24    InvalidKeyName = 105,
25    InvalidPointer = 106,
26    InvalidJson = 107,
27    CommandUnavailable = 108,
28    NotInitialized = 109,
29    IncorrectType = 111,
30    InvalidChannelName = 112,
31    PushMisconfigured = 115,
32    ObjectTooLarge = 116,
33    OperationForbidden = 119,
34    CacheMiss = 120,
35    InvalidNestedKey = 121,
36    InvalidFileName = 122,
37    InvalidAcl = 123,
38    Timeout = 124,
39    InvalidEmailAddress = 125,
40    MissingContentType = 126,
41    MissingContentLength = 127,
42    InvalidContentLength = 128,
43    FileTooLarge = 129,
44    FileSaveError = 130,
45    DuplicateValue = 137,
46    InvalidRoleName = 139,
47    ExceededQuota = 140,
48    ScriptFailed = 141,
49    ValidationError = 142,
50    InvalidImageData = 143,
51    UnsavedFileError = 151,
52    InvalidPushTimeError = 152,
53    FileDeleteError = 153,
54    RequestLimitExceeded = 155,
55    DuplicateRequest = 159,
56    InvalidEventName = 160,
57    FileDeleteUnnamedError = 161,
58    InvalidValue = 162,
59    UsernameMissing = 200,
60    PasswordMissing = 201,
61    UsernameTaken = 202,
62    EmailTaken = 203,
63    EmailMissing = 204,
64    EmailNotFound = 205,
65    SessionMissing = 206,
66    MustCreateUserThroughSignup = 207,
67    AccountAlreadyLinked = 208,
68    InvalidSessionToken = 209,
69    MfaError = 210,
70    MfaTokenRequired = 211,
71    LinkedIdMissing = 250,
72    InvalidLinkedSession = 251,
73    UnsupportedService = 252,
74    InvalidSchemaOperation = 255,
75    AggregateError = 600,
76    FileReadError = 601,
77    XDomainRequest = 602,
78}
79
80impl ErrorCode {
81    /// The wire value. This is what goes in the `code` field of an error body.
82    pub fn as_i32(self) -> i32 {
83        self as i32
84    }
85}
86
87impl fmt::Display for ErrorCode {
88    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89        write!(f, "{}", self.as_i32())
90    }
91}
92
93/// A Parse error: a code plus a message, both wire-visible.
94///
95/// No `source` chaining and no automatic `From` conversions from I/O or driver errors. That is
96/// deliberate: mapping a storage failure onto a Parse code is a decision each adapter must make
97/// explicitly, because picking the wrong code is a wire-compatibility bug that no type system
98/// will catch.
99#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
100#[error("{code}: {message}")]
101pub struct ParseError {
102    pub code: ErrorCode,
103    pub message: String,
104}
105
106impl ParseError {
107    pub fn new(code: ErrorCode, message: impl Into<String>) -> Self {
108        Self {
109            code,
110            message: message.into(),
111        }
112    }
113}
114
115/// Convenience constructors for the codes used most often in the core.
116impl ParseError {
117    pub fn invalid_json(message: impl Into<String>) -> Self {
118        Self::new(ErrorCode::InvalidJson, message)
119    }
120    pub fn incorrect_type(message: impl Into<String>) -> Self {
121        Self::new(ErrorCode::IncorrectType, message)
122    }
123    pub fn invalid_key_name(message: impl Into<String>) -> Self {
124        Self::new(ErrorCode::InvalidKeyName, message)
125    }
126    pub fn invalid_acl(message: impl Into<String>) -> Self {
127        Self::new(ErrorCode::InvalidAcl, message)
128    }
129    pub fn invalid_query(message: impl Into<String>) -> Self {
130        Self::new(ErrorCode::InvalidQuery, message)
131    }
132}
133
134#[cfg(test)]
135mod tests {
136    use super::*;
137
138    #[test]
139    fn discriminants_match_upstream() {
140        // Spot-check the ones this repository's design documents lean on, plus the two that
141        // are easy to transpose.
142        assert_eq!(ErrorCode::OtherCause.as_i32(), -1);
143        assert_eq!(ErrorCode::InternalServerError.as_i32(), 1);
144        assert_eq!(ErrorCode::ObjectNotFound.as_i32(), 101);
145        assert_eq!(ErrorCode::InvalidQuery.as_i32(), 102);
146        assert_eq!(ErrorCode::IncorrectType.as_i32(), 111);
147        assert_eq!(ErrorCode::OperationForbidden.as_i32(), 119);
148        assert_eq!(ErrorCode::DuplicateValue.as_i32(), 137);
149        assert_eq!(ErrorCode::ScriptFailed.as_i32(), 141);
150        assert_eq!(ErrorCode::DuplicateRequest.as_i32(), 159);
151        assert_eq!(ErrorCode::InvalidSessionToken.as_i32(), 209);
152        assert_eq!(ErrorCode::InvalidSchemaOperation.as_i32(), 255);
153        // 110 and 113 do not exist upstream; there is no variant to assert, and adding one
154        // would be inventing a code.
155    }
156
157    #[test]
158    fn display_is_the_number() {
159        assert_eq!(ErrorCode::ObjectNotFound.to_string(), "101");
160    }
161}