rustus 0.5.10

TUS protocol implementation written in Rust.
use std::io::{Error, ErrorKind};

use actix_web::{http::StatusCode, HttpResponse, HttpResponseBuilder, ResponseError};
use log::error;

pub type RustusResult<T> = Result<T, RustusError>;

#[derive(thiserror::Error, Debug)]
pub enum RustusError {
    #[error("Not found")]
    FileNotFound,
    #[error("File with id {0} already exists")]
    FileAlreadyExists(String),
    #[error("Given offset is incorrect.")]
    WrongOffset,
    #[error("Unknown error")]
    Unknown,
    #[error("File is frozen")]
    FrozenFile,
    #[error("Size already known")]
    SizeAlreadyKnown,
    #[error("Unable to serialize object")]
    UnableToSerialize(#[from] serde_json::Error),
    #[cfg(feature = "db_info_storage")]
    #[error("Database error: {0}")]
    DatabaseError(#[from] rbatis::error::Error),
    #[cfg(feature = "redis_info_storage")]
    #[error("Redis error: {0}")]
    RedisError(#[from] mobc_redis::redis::RedisError),
    #[cfg(feature = "redis_info_storage")]
    #[error("Redis error: {0}")]
    MobcError(#[from] mobc_redis::mobc::Error<mobc_redis::redis::RedisError>),
    #[error("Unable to get file information")]
    UnableToReadInfo,
    #[error("Unable to write file {0}")]
    UnableToWrite(String),
    #[error("Unable to remove file {0}")]
    UnableToRemove(String),
    #[error("Unable to prepare info storage. Reason: {0}")]
    UnableToPrepareInfoStorage(String),
    #[error("Unable to prepare storage. Reason: {0}")]
    UnableToPrepareStorage(String),
    #[error("Unknown extension: {0}")]
    UnknownExtension(String),
    #[cfg(feature = "http_notifier")]
    #[error("Http request failed: {0}")]
    HttpRequestError(#[from] reqwest::Error),
    #[error("Hook invocation failed. Reason: {0}")]
    HookError(String),
    #[error("Unable to configure logging: {0}")]
    LogConfigError(#[from] log::SetLoggerError),
    #[cfg(feature = "amqp_notifier")]
    #[error("AMQP error: {0}")]
    AMQPError(#[from] lapin::Error),
    #[cfg(feature = "amqp_notifier")]
    #[error("AMQP error: {0}")]
    AMQPPoolError(#[from] mobc_lapin::mobc::Error<lapin::Error>),
    #[error("Std error: {0}")]
    StdError(#[from] std::io::Error),
    #[error("Can't spawn task: {0}")]
    TokioSpawnError(#[from] tokio::task::JoinError),
    #[error("Unknown hashsum algorithm")]
    UnknownHashAlgorithm,
    #[error("Wrong checksum")]
    WrongChecksum,
    #[error("The header value is incorrect")]
    WrongHeaderValue,
    #[error("Metrics error: {0}")]
    PrometheusError(#[from] prometheus::Error),
    #[error("Blocking error: {0}")]
    BlockingError(#[from] actix_web::error::BlockingError),
    #[error("HTTP hook error. Returned status: {0}")]
    HTTPHookError(u16, String, Option<String>),
}

/// This conversion allows us to use `RustusError` in the `main` function.
#[cfg_attr(coverage, no_coverage)]
impl From<RustusError> for Error {
    fn from(err: RustusError) -> Self {
        Error::new(ErrorKind::Other, err)
    }
}

/// Trait to convert errors to http-responses.
#[cfg_attr(coverage, no_coverage)]
impl ResponseError for RustusError {
    fn error_response(&self) -> HttpResponse {
        error!("{}", self);
        match self {
            RustusError::HTTPHookError(_, proxy_response, content_type) => {
                HttpResponseBuilder::new(self.status_code())
                    .insert_header((
                        "Content-Type",
                        content_type
                            .clone()
                            .unwrap_or_else(|| "text/plain; charset=utf-8".into()),
                    ))
                    .body(proxy_response.clone())
            }
            _ => HttpResponseBuilder::new(self.status_code())
                .insert_header(("Content-Type", "text/html; charset=utf-8"))
                .body(format!("{}", self)),
        }
    }

    fn status_code(&self) -> StatusCode {
        match self {
            RustusError::FileNotFound => StatusCode::NOT_FOUND,
            RustusError::WrongOffset => StatusCode::CONFLICT,
            RustusError::FrozenFile
            | RustusError::SizeAlreadyKnown
            | RustusError::HookError(_)
            | RustusError::UnknownHashAlgorithm
            | RustusError::WrongHeaderValue => StatusCode::BAD_REQUEST,
            RustusError::WrongChecksum => StatusCode::EXPECTATION_FAILED,
            RustusError::HTTPHookError(status, _, _) => {
                StatusCode::from_u16(*status).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR)
            }
            _ => StatusCode::INTERNAL_SERVER_ERROR,
        }
    }
}