use std::{
collections::HashMap,
fmt,
};
use crate::bucket::LifecycleRule;
use serde::{Serialize, Deserialize};
#[derive(Debug)]
pub enum ValidationError {
BadUrl(String),
BadFormat(String),
MissingData(String),
OutOfBounds(String),
Incompatible(String),
}
impl std::error::Error for ValidationError {}
impl fmt::Display for ValidationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::BadUrl(s) => write!(f, "Error parsing URL: {}", s),
Self::BadFormat(s) => write!(f, "{}", s),
Self::MissingData(s) => write!(f, "{}", s),
Self::OutOfBounds(s) => write!(f, "{}", s),
Self::Incompatible(s) => write!(f, "{}", s),
}
}
}
impl From<url::ParseError> for ValidationError {
fn from(e: url::ParseError) -> Self {
Self::BadUrl(format!("{}", e))
}
}
#[cfg(feature = "with_surf")]
impl From<http_types::Error> for ValidationError {
fn from(e: http_types::Error) -> Self {
Self::BadFormat(format!("{}", e))
}
}
#[cfg(feature = "with_hyper")]
impl From<hyper::header::InvalidHeaderName> for ValidationError {
fn from(e: hyper::header::InvalidHeaderName) -> Self {
Self::BadFormat(format!("{}", e))
}
}
#[cfg(feature = "with_hyper")]
impl From<hyper::header::InvalidHeaderValue> for ValidationError {
fn from(e: hyper::header::InvalidHeaderValue) -> Self {
Self::BadFormat(format!("{}", e))
}
}
#[cfg(feature = "with_isahc")]
impl From<isahc::http::header::InvalidHeaderName> for ValidationError {
fn from(e: isahc::http::header::InvalidHeaderName) -> Self {
Self::BadFormat(format!("{}", e))
}
}
#[cfg(feature = "with_isahc")]
impl From<isahc::http::header::InvalidHeaderValue> for ValidationError {
fn from(e: isahc::http::header::InvalidHeaderValue) -> Self {
Self::BadFormat(format!("{}", e))
}
}
#[derive(Debug)]
pub struct BadData<T>
where T: std::fmt::Debug + std::fmt::Display,
{
pub value: T,
pub(crate) msg: String,
}
impl<T> std::error::Error for BadData<T>
where T: std::fmt::Debug + std::fmt::Display,
{}
impl<T> fmt::Display for BadData<T>
where T: std::fmt::Debug + std::fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.msg)
}
}
#[derive(Debug)]
pub struct BadHeaderName {
pub header: String,
pub invalid_char: char,
}
impl std::error::Error for BadHeaderName {}
impl fmt::Display for BadHeaderName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Invalid character in header {}: {}",
self.header,
self.invalid_char
)
}
}
#[derive(Debug)]
pub enum BucketValidationError {
BadNameLength(usize),
InvalidChar(char),
}
impl std::error::Error for BucketValidationError {}
impl fmt::Display for BucketValidationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::BadNameLength(sz) => write!(f,
"Name must be between 6 and 50 characters, inclusive. Was {}",
sz
),
Self::InvalidChar(ch) => write!(f, "Unexpected character: {}", ch),
}
}
}
pub type CorsRuleValidationError = BucketValidationError;
#[derive(Debug)]
pub enum FileNameValidationError {
BadLength(usize),
InvalidChar(char),
}
impl std::error::Error for FileNameValidationError {}
impl fmt::Display for FileNameValidationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::BadLength(sz) => write!(f,
"Name must be no more than 1024 bytes. Was {}", sz
),
Self::InvalidChar(ch) => write!(f, "Illegal character: {}", ch),
}
}
}
#[derive(Debug)]
pub enum LifecycleRuleValidationError {
TooManyRules(usize),
ConflictingRules(HashMap<String, Vec<LifecycleRule>>),
}
impl std::error::Error for LifecycleRuleValidationError {}
impl fmt::Display for LifecycleRuleValidationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::TooManyRules(i) => write!(f, concat!(
"A bucket can have no more than 100 rules;",
"you have provided {}"), i
),
Self::ConflictingRules(_) => (write!(f,
"Only one lifecycle rule can apply to any given set of files"
)),
}
}
}
#[derive(Debug)]
pub struct MissingData {
pub field: &'static str,
msg: Option<String>,
}
impl MissingData {
pub fn new(missing_field: &'static str) -> Self {
Self {
field: missing_field,
msg: None,
}
}
pub fn with_message(mut self, message: impl Into<String>) -> Self {
self.msg = Some(message.into());
self
}
}
impl std::error::Error for MissingData {}
impl fmt::Display for MissingData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.msg {
Some(msg) => write!(f, "{}", msg),
None => write!(f, "Missing data: {} is required", self.field),
}
}
}
#[derive(Debug)]
pub enum Error<E>
where E: fmt::Debug + fmt::Display,
{
Client(E),
IO(std::io::Error),
B2(B2Error),
Format(serde_json::Error),
Unauthorized(crate::account::Capability),
Validation(ValidationError),
MissingAuthorization,
NoRequest,
}
impl<E> std::error::Error for Error<E>
where E: fmt::Debug + fmt::Display,
{}
impl<E> fmt::Display for Error<E>
where E: fmt::Debug + fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use fmt::Display;
match self {
Self::Client(e) => Display::fmt(&e, f),
Self::IO(e) => e.fmt(f),
Self::B2(e) => Display::fmt(&e, f),
Self::Format(e) => e.fmt(f),
Self::Unauthorized(c) => write!(f, "Missing capability: {:?}", c),
Self::Validation(e) => e.fmt(f),
Self::MissingAuthorization =>
write!(f, "An Authorization is required for that operation"),
Self::NoRequest => write!(f, "No request was created"),
}
}
}
impl<E> From<B2Error> for Error<E>
where E: fmt::Debug + fmt::Display,
{
fn from(e: B2Error) -> Self {
Self::B2(e)
}
}
impl<E> From<std::io::Error> for Error<E>
where E: fmt::Debug + fmt::Display,
{
fn from(e: std::io::Error) -> Self {
Self::IO(e)
}
}
impl<E> From<serde_json::Error> for Error<E>
where E: fmt::Debug + fmt::Display,
{
fn from(e: serde_json::Error) -> Self {
Self::Format(e)
}
}
#[cfg(feature = "with_surf")]
impl From<surf::Error> for Error<surf::Error> {
fn from(e: surf::Error) -> Self {
Self::Client(e)
}
}
#[cfg(feature = "with_hyper")]
impl From<hyper::Error> for Error<hyper::Error> {
fn from(e: hyper::Error) -> Self {
Self::Client(e)
}
}
#[cfg(feature = "with_isahc")]
impl From<isahc::Error> for Error<isahc::Error> {
fn from(e: isahc::Error) -> Self {
Self::Client(e)
}
}
#[cfg(feature = "with_isahc")]
impl From<isahc::http::Error> for Error<isahc::Error> {
fn from(e: isahc::http::Error) -> Self {
Self::Client(e.into())
}
}
impl<E> From<ValidationError> for Error<E>
where E: fmt::Debug + fmt::Display,
{
fn from(e: ValidationError) -> Self {
Self::Validation(e)
}
}
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum ErrorCode {
BadBucketId,
BadRequest, BucketMissingFileLock,
DuplicateBucketName,
FileNotPresent,
InvalidBucketId,
InvalidFileId,
NoSuchFile,
OutOfRange,
TooManyBuckets,
AccessDenied, BadAuthToken,
ExpiredAuthToken,
Unauthorized,
Unsupported,
CapExceeded,
StorageCapExceeded,
TransactionCapExceeded,
NotFound,
MethodNotAllowed,
RequestTimeout,
Conflict,
RangeNotSatisfiable,
InternalError,
ServiceUnavailable,
Unknown(String),
}
impl ErrorCode {
fn from_api_code<S: AsRef<str>>(code: S) -> Self {
match code.as_ref() {
"bad_bucket_id" => Self::BadBucketId,
"bad_request" => Self::BadRequest,
"bucket_missing_file_lock" => Self::BucketMissingFileLock,
"duplicate_bucket_name" => Self::DuplicateBucketName,
"file_not_present" => Self::FileNotPresent,
"invalid_bucket_id" => Self::InvalidBucketId,
"invalid_file_id" => Self::InvalidFileId,
"no_such_file" => Self::NoSuchFile,
"out_of_range" => Self::OutOfRange,
"too_many_buckets" => Self::TooManyBuckets,
"bad_auth_token" => Self::BadAuthToken,
"expired_auth_token" => Self::ExpiredAuthToken,
"unauthorized" => Self::Unauthorized,
"unsupported" => Self::Unsupported,
"access_denied" => Self::AccessDenied,
"cap_exceeded" => Self::CapExceeded,
"storage_cap_exceeded" => Self::StorageCapExceeded,
"transaction_cap_exceeded" => Self::TransactionCapExceeded,
"not_found" => Self::NotFound,
"method_not_allowed" => Self::MethodNotAllowed,
"request_timeout" => Self::RequestTimeout,
"range_not_satisfiable" => Self::RangeNotSatisfiable,
"conflict" => Self::Conflict,
"internal_error" => Self::InternalError,
"service_unavailable" => Self::ServiceUnavailable,
s => Self::Unknown(s.to_owned()),
}
}
}
#[derive(Debug, Deserialize)]
pub struct B2Error {
status: u16,
#[serde(rename = "code")]
code_str: String,
message: String,
}
impl B2Error {
pub fn http_status(&self) -> u16 { self.status }
pub fn code(&self) -> ErrorCode {
ErrorCode::from_api_code(&self.code_str)
}
}
impl std::error::Error for B2Error {}
impl fmt::Display for B2Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {}", self.code_str, self.message)
}
}