1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
use serde::{Deserialize, Serialize};
use crate::data::ApiDataResponse;
// #[cfg_attr(feature = "specta", derive(specta::Type))]
/// Represents all possible TorBox error codes returned from the API.
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
#[cfg_attr(feature = "specta", derive(specta::Type))]
pub enum ApiErrorCode {
/// Could not access internal database/memory store information, probably due to wrong ID.
DatabaseError,
/// The reason for the error is unknown. Usually there will be error data attached in the "data" key.
///
/// In these cases please report the request to `contact@torbox.app`.
UnknownError,
/// There are no provided credentials.
NoAuth,
/// The provided token is invalid.
BadToken,
/// There was an error verifying the given authentication.
AuthError,
/// The provided option is invalid.
InvalidOption,
/// The server tried redirecting, but it faulted.
RedirectError,
/// The server tried verifying your OAuth token, but it was not accepted by the provider.
OauthVerificationError,
/// You have hit an endpoint that doesn't exist.
EndpointNotFound,
/// The item you queried cannot be found.
ItemNotFound,
/// This feature is restricted to users of higher plans.
///
/// The user is recommended to upgrade their plan to use this endpoint.
PlanRestrictedFeature,
/// This item already exists.
DuplicateItem,
/// The RSS feed is invalid or not a well-formed XML.
BozoRssFeed,
/// There was an error with the Sellix API. Usually in the case of payments.
SellixError,
/// Client sent too much data to the API.
///
/// Please keep requests under 100MB in size.
TooMuchData,
/// This download is oversized for the user's plan.
///
/// The user is recommended to upgrade their plan to download this file.
///
/// - **Free** Plan Limit: `10737418240` bytes
/// - **Essential** Plan Limit: `214748364800` bytes
/// - **Standard** Plan Limit: `214748364800` bytes
/// - **Pro** Plan Limit: `536870912000` bytes
DownloadTooLarge,
/// The API is missing required information to process the request.
MissingRequiredOption,
/// Client sent too many options.
///
/// Usually this has to do with the API requiring only 1 option but the client sent more than the required.
TooManyOptions,
/// The torrent sent is not a valid torrent.
BozoTorrent,
/// There are no download servers available to handle this request.
///
/// This should never happen. If you receieve this error, please contact us at `contact@torbox.app`.
NoServersAvailableError,
/// User has hit the maximum monthly limit.
///
/// It is recommended user upgrade their account to be able to download more.
MonthlyLimit,
/// User is on download cooldown.
///
/// It is recommended user upgrade their account to be able to bypass this restriction.
CooldownLimit,
/// User has hit their max active download limit.
///
/// It is recommended user upgrade their account or purchase addons to bypass this restriction.
ActiveLimit,
/// There was an error interacting with the download on the download server.
///
/// It is recommdned to simply wait some time before trying again.
DownloadServerError,
/// The NZB sent is not a valid NZB file.
BozoNzb,
/// There was an error searching using the TorBox Search API.
SearchError,
/// The client is sending requests from the incorrect device.
InvalidDevice,
/// The request parameters sent does not allow for this request to complete.
DiffIssue,
/// The link given is inaccessible or has no online files.
LinkOffline,
/// This vendor account has been disabled.
///
/// Please contact support.
VendorDisabled,
/// The regex entered is invalid.
BozoRegex,
/// The confirmation code is invalid.
BadConfirmation,
/// The confirmation code has expired.
///
/// Request a new code.
ConfirmationExpired,
}
#[derive(Debug, thiserror::Error)]
pub enum ApiError {
#[error("Redirect error: {0}")]
RedirectError(String),
#[error("HTTP error: {0}")]
Transport(#[from] reqwest::Error),
#[error("JSON Error: {0}")]
SerdeError(#[from] serde_json::Error),
#[error("API returned success = false: {0:?}")]
Failure(ErrorValue),
#[error("Unexpected ApiDataResponse variant: {0:?}")]
Unexpected(ApiDataResponse),
#[error("Unexpected Payload variant")]
UnexpectedPayload,
#[error("UTF8 encoding/decoding error: {0:?}")]
Utf8(std::string::FromUtf8Error),
#[error("Unknown variant: {0:?}")]
Custom(String),
}
impl From<std::string::FromUtf8Error> for ApiError {
fn from(err: std::string::FromUtf8Error) -> Self {
ApiError::Utf8(err)
}
}
impl Serialize for ApiError {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl<'de> Deserialize<'de> for ApiError {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Ok(ApiError::Custom(s))
}
}
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "specta", derive(specta::Type))]
#[serde(untagged)]
pub enum ErrorValue {
Code(ApiErrorCode),
Bool(bool),
Message(String),
}