1use std::{
22 collections::HashMap,
23 fmt,
24};
25
26use crate::bucket::LifecycleRule;
27
28use serde::{Serialize, Deserialize};
29
30
31#[derive(Debug)]
36pub enum ValidationError {
37 BadUrl(String),
41 BadFormat(String),
45 MissingData(String),
49 OutOfBounds(String),
53 Incompatible(String),
57}
58
59impl std::error::Error for ValidationError {}
60
61impl fmt::Display for ValidationError {
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63 match self {
64 Self::BadUrl(s) => write!(f, "Error parsing URL: {}", s),
65 Self::BadFormat(s) => write!(f, "{}", s),
66 Self::MissingData(s) => write!(f, "{}", s),
67 Self::OutOfBounds(s) => write!(f, "{}", s),
68 Self::Incompatible(s) => write!(f, "{}", s),
69 }
70 }
71}
72
73impl From<url::ParseError> for ValidationError {
74 fn from(e: url::ParseError) -> Self {
75 Self::BadUrl(format!("{}", e))
76 }
77}
78
79#[cfg(feature = "with_surf")]
80impl From<http_types::Error> for ValidationError {
81 fn from(e: http_types::Error) -> Self {
82 Self::BadFormat(format!("{}", e))
83 }
84}
85
86#[cfg(feature = "with_hyper")]
87impl From<hyper::header::InvalidHeaderName> for ValidationError {
88 fn from(e: hyper::header::InvalidHeaderName) -> Self {
89 Self::BadFormat(format!("{}", e))
90 }
91}
92
93#[cfg(feature = "with_hyper")]
94impl From<hyper::header::InvalidHeaderValue> for ValidationError {
95 fn from(e: hyper::header::InvalidHeaderValue) -> Self {
96 Self::BadFormat(format!("{}", e))
97 }
98}
99
100#[cfg(feature = "with_isahc")]
101impl From<isahc::http::header::InvalidHeaderName> for ValidationError {
102 fn from(e: isahc::http::header::InvalidHeaderName) -> Self {
103 Self::BadFormat(format!("{}", e))
104 }
105}
106
107#[cfg(feature = "with_isahc")]
108impl From<isahc::http::header::InvalidHeaderValue> for ValidationError {
109 fn from(e: isahc::http::header::InvalidHeaderValue) -> Self {
110 Self::BadFormat(format!("{}", e))
111 }
112}
113
114#[derive(Debug)]
116pub struct BadData<T>
117 where T: std::fmt::Debug + std::fmt::Display,
118{
119 pub value: T,
120 pub(crate) msg: String,
121}
122
123impl<T> std::error::Error for BadData<T>
124 where T: std::fmt::Debug + std::fmt::Display,
125{}
126
127impl<T> fmt::Display for BadData<T>
128 where T: std::fmt::Debug + std::fmt::Display,
129{
130 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131 write!(f, "{}", self.msg)
132 }
133}
134
135#[derive(Debug)]
137pub struct BadHeaderName {
138 pub header: String,
140 pub invalid_char: char,
142}
143
144impl std::error::Error for BadHeaderName {}
145
146impl fmt::Display for BadHeaderName {
147 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148 write!(f, "Invalid character in header {}: {}",
149 self.header,
150 self.invalid_char
151 )
152 }
153}
154
155#[derive(Debug)]
157pub enum BucketValidationError {
158 BadNameLength(usize),
163 InvalidChar(char),
165}
166
167impl std::error::Error for BucketValidationError {}
168
169impl fmt::Display for BucketValidationError {
170 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
171 match self {
172 Self::BadNameLength(sz) => write!(f,
173 "Name must be between 6 and 50 characters, inclusive. Was {}",
174 sz
175 ),
176 Self::InvalidChar(ch) => write!(f, "Unexpected character: {}", ch),
177 }
178 }
179}
180
181pub type CorsRuleValidationError = BucketValidationError;
187
188#[derive(Debug)]
190pub enum FileNameValidationError {
191 BadLength(usize),
193 InvalidChar(char),
195}
196
197impl std::error::Error for FileNameValidationError {}
198
199impl fmt::Display for FileNameValidationError {
200 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201 match self {
202 Self::BadLength(sz) => write!(f,
203 "Name must be no more than 1024 bytes. Was {}", sz
204 ),
205 Self::InvalidChar(ch) => write!(f, "Illegal character: {}", ch),
206 }
207 }
208}
209
210#[derive(Debug)]
212pub enum LifecycleRuleValidationError {
213 TooManyRules(usize),
215 ConflictingRules(HashMap<String, Vec<LifecycleRule>>),
224}
225
226impl std::error::Error for LifecycleRuleValidationError {}
227
228impl fmt::Display for LifecycleRuleValidationError {
229 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
230 match self {
231 Self::TooManyRules(i) => write!(f, concat!(
232 "A bucket can have no more than 100 rules;",
233 "you have provided {}"), i
234 ),
235 Self::ConflictingRules(_) => (write!(f,
236 "Only one lifecycle rule can apply to any given set of files"
237 )),
238 }
239 }
240}
241
242#[derive(Debug)]
243pub struct MissingData {
244 pub field: &'static str,
245 msg: Option<String>,
246}
247
248impl MissingData {
249 pub fn new(missing_field: &'static str) -> Self {
250 Self {
251 field: missing_field,
252 msg: None,
253 }
254 }
255
256 pub fn with_message(mut self, message: impl Into<String>) -> Self {
257 self.msg = Some(message.into());
258 self
259 }
260}
261
262impl std::error::Error for MissingData {}
263
264impl fmt::Display for MissingData {
265 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
266 match &self.msg {
267 Some(msg) => write!(f, "{}", msg),
268 None => write!(f, "Missing data: {} is required", self.field),
269 }
270 }
271}
272
273#[derive(Debug)]
275pub enum Error<E>
276 where E: fmt::Debug + fmt::Display,
278{
279 Client(E),
281 IO(std::io::Error),
283 B2(B2Error),
285 Format(serde_json::Error),
287 Unauthorized(crate::account::Capability),
294 Validation(ValidationError),
296 MissingAuthorization,
299 NoRequest,
301}
302
303impl<E> std::error::Error for Error<E>
304 where E: fmt::Debug + fmt::Display,
305{}
306
307impl<E> fmt::Display for Error<E>
308 where E: fmt::Debug + fmt::Display,
309{
310 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
311 use fmt::Display;
312
313 match self {
314 Self::Client(e) => Display::fmt(&e, f),
315 Self::IO(e) => e.fmt(f),
316 Self::B2(e) => Display::fmt(&e, f),
317 Self::Format(e) => e.fmt(f),
318 Self::Unauthorized(c) => write!(f, "Missing capability: {:?}", c),
319 Self::Validation(e) => e.fmt(f),
320 Self::MissingAuthorization =>
321 write!(f, "An Authorization is required for that operation"),
322 Self::NoRequest => write!(f, "No request was created"),
323 }
324 }
325}
326
327impl<E> From<B2Error> for Error<E>
328 where E: fmt::Debug + fmt::Display,
329{
330 fn from(e: B2Error) -> Self {
331 Self::B2(e)
332 }
333}
334
335impl<E> From<std::io::Error> for Error<E>
336 where E: fmt::Debug + fmt::Display,
337{
338 fn from(e: std::io::Error) -> Self {
339 Self::IO(e)
340 }
341}
342
343impl<E> From<serde_json::Error> for Error<E>
344 where E: fmt::Debug + fmt::Display,
345{
346 fn from(e: serde_json::Error) -> Self {
347 Self::Format(e)
348 }
349}
350
351#[cfg(feature = "with_surf")]
352impl From<surf::Error> for Error<surf::Error> {
353 fn from(e: surf::Error) -> Self {
354 Self::Client(e)
355 }
356}
357
358#[cfg(feature = "with_hyper")]
359impl From<hyper::Error> for Error<hyper::Error> {
360 fn from(e: hyper::Error) -> Self {
361 Self::Client(e)
362 }
363}
364
365#[cfg(feature = "with_isahc")]
366impl From<isahc::Error> for Error<isahc::Error> {
367 fn from(e: isahc::Error) -> Self {
368 Self::Client(e)
369 }
370}
371
372#[cfg(feature = "with_isahc")]
373impl From<isahc::http::Error> for Error<isahc::Error> {
374 fn from(e: isahc::http::Error) -> Self {
375 Self::Client(e.into())
376 }
377}
378
379impl<E> From<ValidationError> for Error<E>
380 where E: fmt::Debug + fmt::Display,
381{
382 fn from(e: ValidationError) -> Self {
383 Self::Validation(e)
384 }
385}
386
387#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
393pub enum ErrorCode {
394 BadBucketId,
396 BadRequest, BucketMissingFileLock,
398 DuplicateBucketName,
399 FileNotPresent,
400 InvalidBucketId,
401 InvalidFileId,
402 NoSuchFile,
403 OutOfRange,
406 TooManyBuckets,
407
408 AccessDenied, BadAuthToken,
411 ExpiredAuthToken,
412 Unauthorized,
413 Unsupported,
414
415 CapExceeded,
417 StorageCapExceeded,
418 TransactionCapExceeded,
419
420 NotFound,
422
423 MethodNotAllowed,
425
426 RequestTimeout,
428
429 Conflict,
431
432 RangeNotSatisfiable,
434
435 InternalError,
437
438 ServiceUnavailable,
440
441 Unknown(String),
446}
447
448impl ErrorCode {
449 fn from_api_code<S: AsRef<str>>(code: S) -> Self {
451 match code.as_ref() {
452 "bad_bucket_id" => Self::BadBucketId,
453 "bad_request" => Self::BadRequest,
454 "bucket_missing_file_lock" => Self::BucketMissingFileLock,
455 "duplicate_bucket_name" => Self::DuplicateBucketName,
456 "file_not_present" => Self::FileNotPresent,
457 "invalid_bucket_id" => Self::InvalidBucketId,
458 "invalid_file_id" => Self::InvalidFileId,
459 "no_such_file" => Self::NoSuchFile,
460 "out_of_range" => Self::OutOfRange,
461 "too_many_buckets" => Self::TooManyBuckets,
462
463 "bad_auth_token" => Self::BadAuthToken,
464 "expired_auth_token" => Self::ExpiredAuthToken,
465 "unauthorized" => Self::Unauthorized,
466 "unsupported" => Self::Unsupported,
467
468 "access_denied" => Self::AccessDenied,
469 "cap_exceeded" => Self::CapExceeded,
470 "storage_cap_exceeded" => Self::StorageCapExceeded,
471 "transaction_cap_exceeded" => Self::TransactionCapExceeded,
472
473 "not_found" => Self::NotFound,
474
475 "method_not_allowed" => Self::MethodNotAllowed,
476
477 "request_timeout" => Self::RequestTimeout,
478
479 "range_not_satisfiable" => Self::RangeNotSatisfiable,
480
481 "conflict" => Self::Conflict,
482
483 "internal_error" => Self::InternalError,
484
485 "service_unavailable" => Self::ServiceUnavailable,
486
487 s => Self::Unknown(s.to_owned()),
488 }
489 }
490}
491
492#[derive(Debug, Deserialize)]
497pub struct B2Error {
498 status: u16,
500 #[serde(rename = "code")]
502 code_str: String,
503 message: String,
505}
506
507impl B2Error {
508 pub fn http_status(&self) -> u16 { self.status }
510
511 pub fn code(&self) -> ErrorCode {
512 ErrorCode::from_api_code(&self.code_str)
513 }
514}
515
516impl std::error::Error for B2Error {}
517
518impl fmt::Display for B2Error {
519 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
520 write!(f, "{}: {}", self.code_str, self.message)
521 }
522}