pict-rs/src/error.rs

300 lines
9.7 KiB
Rust
Raw Normal View History

2023-09-05 02:51:27 +00:00
use std::sync::Arc;
2021-06-19 19:39:41 +00:00
use actix_web::{http::StatusCode, HttpResponse, ResponseError};
2022-03-29 01:47:46 +00:00
use color_eyre::Report;
2021-09-14 01:22:42 +00:00
2023-09-02 01:50:10 +00:00
use crate::error_code::ErrorCode;
2021-09-14 01:22:42 +00:00
pub(crate) struct Error {
2022-03-29 01:47:46 +00:00
inner: color_eyre::Report,
2023-09-05 02:51:27 +00:00
debug: Arc<str>,
display: Arc<str>,
2022-03-29 01:47:46 +00:00
}
impl Error {
fn kind(&self) -> Option<&UploadError> {
self.inner.downcast_ref()
}
2023-07-17 02:51:14 +00:00
pub(crate) fn root_cause(&self) -> &(dyn std::error::Error + 'static) {
self.inner.root_cause()
}
2023-09-02 01:50:10 +00:00
pub(crate) fn error_code(&self) -> ErrorCode {
self.kind()
.map(|e| e.error_code())
.unwrap_or(ErrorCode::UNKNOWN_ERROR)
}
2023-09-05 02:51:27 +00:00
pub(crate) fn is_disconnected(&self) -> bool {
self.kind().map(|e| e.is_disconnected()).unwrap_or(false)
}
2021-09-14 01:22:42 +00:00
}
impl std::fmt::Debug for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2023-09-05 02:51:27 +00:00
f.write_str(&self.debug)
2021-09-14 01:22:42 +00:00
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2023-09-05 02:51:27 +00:00
f.write_str(&self.display)
2021-09-14 01:22:42 +00:00
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
2023-11-13 23:24:50 +00:00
Some(self.inner.as_ref())
2021-09-14 01:22:42 +00:00
}
}
impl<T> From<T> for Error
where
UploadError: From<T>,
{
2023-11-13 23:24:50 +00:00
#[track_caller]
2021-09-14 01:22:42 +00:00
fn from(error: T) -> Self {
2023-09-05 02:51:27 +00:00
let inner = Report::from(UploadError::from(error));
let debug = Arc::from(format!("{inner:?}"));
let display = Arc::from(format!("{inner}"));
2021-09-14 01:22:42 +00:00
Error {
2023-09-05 02:51:27 +00:00
inner,
debug,
display,
2021-09-14 01:22:42 +00:00
}
}
}
2020-06-06 21:41:17 +00:00
#[derive(Debug, thiserror::Error)]
2020-06-11 16:46:00 +00:00
pub(crate) enum UploadError {
2023-06-23 16:20:55 +00:00
#[error("Couldn't upload file")]
2021-09-14 01:50:51 +00:00
Upload(#[from] actix_form_data::Error),
2020-06-06 21:41:17 +00:00
2022-03-26 21:49:23 +00:00
#[error("Error in DB")]
Repo(#[from] crate::repo::RepoError),
2022-03-26 21:49:23 +00:00
#[error("Error in old repo")]
OldRepo(#[from] crate::repo_04::RepoError),
2020-06-06 21:41:17 +00:00
2022-03-26 21:49:23 +00:00
#[error("Error interacting with filesystem")]
2020-06-07 00:29:15 +00:00
Io(#[from] std::io::Error),
2023-07-13 22:42:21 +00:00
#[error("Error validating upload")]
Validation(#[from] crate::ingest::ValidationError),
2023-07-13 22:42:21 +00:00
#[error("Error in store")]
Store(#[source] crate::store::StoreError),
2023-07-10 20:29:41 +00:00
#[error("Error in ffmpeg")]
Ffmpeg(#[from] crate::ffmpeg::FfMpegError),
#[error("Error in imagemagick")]
Magick(#[from] crate::magick::MagickError),
#[error("Error in exiftool")]
Exiftool(#[from] crate::exiftool::ExifError),
#[error("Error in process")]
Process(#[from] crate::process::ProcessError),
2023-07-21 21:58:31 +00:00
#[error("Error building reqwest client")]
BuildClient(#[source] reqwest::Error),
#[error("Error making request")]
RequestMiddleware(#[from] reqwest_middleware::Error),
#[error("Error in request response")]
Request(#[from] reqwest::Error),
#[error("Invalid job popped from job queue: {1}")]
InvalidJob(#[source] serde_json::Error, String),
#[error("Error parsing upload query")]
InvalidUploadQuery(#[source] actix_web::error::QueryPayloadError),
2023-07-17 19:24:49 +00:00
#[error("pict-rs is in read-only mode")]
ReadOnly,
2021-10-21 02:36:18 +00:00
#[error("Provided process path is invalid")]
ParsePath,
#[error("Failed to acquire the semaphore")]
Semaphore,
2020-06-06 21:41:17 +00:00
#[error("Panic in blocking operation")]
Canceled,
#[error("No files present in upload")]
NoFiles,
#[error("Media has not been proxied")]
MissingProxy,
2020-06-06 22:43:33 +00:00
#[error("Requested a file that doesn't exist")]
MissingAlias,
#[error("Requested a file that pict-rs lost track of")]
MissingIdentifier,
2020-06-07 00:29:15 +00:00
#[error("Provided token did not match expected token")]
InvalidToken,
2020-06-07 01:44:26 +00:00
#[error("Process endpoint was called with invalid extension")]
UnsupportedProcessExtension,
#[error("Unable to download image, bad response {0}")]
Download(actix_web::http::StatusCode),
#[error("Tried to save an image with an already-taken name")]
DuplicateAlias,
2020-06-11 16:46:00 +00:00
2023-09-02 01:50:10 +00:00
#[error("Failed to serialize job")]
PushJob(#[source] serde_json::Error),
#[error("Range header not satisfiable")]
Range,
2022-03-26 21:49:23 +00:00
#[error("Hit limit")]
Limit(#[from] crate::stream::LimitError),
#[error("Response timeout")]
Timeout(#[from] crate::stream::TimeoutError),
2023-09-06 01:45:07 +00:00
#[error("Client took too long to send request")]
AggregateTimeout,
#[error("Timed out while waiting for media processing")]
ProcessTimeout,
2023-09-06 01:45:07 +00:00
#[error("Failed external validation")]
FailedExternalValidation,
#[cfg(feature = "random-errors")]
#[error("Randomly generated error for testing purposes")]
RandomError,
}
2023-09-02 01:50:10 +00:00
impl UploadError {
const fn error_code(&self) -> ErrorCode {
match self {
Self::Upload(actix_form_data::Error::FileSize) => ErrorCode::VALIDATE_FILE_SIZE,
2023-09-02 01:50:10 +00:00
Self::Upload(_) => ErrorCode::FILE_UPLOAD_ERROR,
Self::Repo(e) => e.error_code(),
Self::OldRepo(_) => ErrorCode::OLD_REPO_ERROR,
Self::Io(_) => ErrorCode::IO_ERROR,
Self::Validation(e) => e.error_code(),
Self::Store(e) => e.error_code(),
Self::Ffmpeg(e) => e.error_code(),
Self::Magick(e) => e.error_code(),
Self::Exiftool(e) => e.error_code(),
Self::Process(e) => e.error_code(),
2023-09-02 01:50:10 +00:00
Self::BuildClient(_) | Self::RequestMiddleware(_) | Self::Request(_) => {
ErrorCode::HTTP_CLIENT_ERROR
}
Self::Download(_) => ErrorCode::DOWNLOAD_FILE_ERROR,
Self::ReadOnly => ErrorCode::READ_ONLY,
Self::ParsePath => ErrorCode::INVALID_PROCESS_PATH,
Self::Semaphore => ErrorCode::PROCESS_SEMAPHORE_CLOSED,
Self::Canceled => ErrorCode::PANIC,
Self::NoFiles => ErrorCode::VALIDATE_NO_FILES,
Self::MissingProxy => ErrorCode::PROXY_NOT_FOUND,
2023-09-02 01:50:10 +00:00
Self::MissingAlias => ErrorCode::ALIAS_NOT_FOUND,
Self::MissingIdentifier => ErrorCode::LOST_FILE,
Self::InvalidToken => ErrorCode::INVALID_DELETE_TOKEN,
Self::UnsupportedProcessExtension => ErrorCode::INVALID_FILE_EXTENSION,
Self::DuplicateAlias => ErrorCode::DUPLICATE_ALIAS,
2023-09-03 22:11:34 +00:00
Self::PushJob(_) => ErrorCode::PUSH_JOB,
2023-09-02 01:50:10 +00:00
Self::Range => ErrorCode::RANGE_NOT_SATISFIABLE,
Self::Limit(_) => ErrorCode::VALIDATE_FILE_SIZE,
Self::Timeout(_) | Self::AggregateTimeout => ErrorCode::STREAM_TOO_SLOW,
Self::ProcessTimeout => ErrorCode::COMMAND_TIMEOUT,
2023-09-06 01:45:07 +00:00
Self::FailedExternalValidation => ErrorCode::FAILED_EXTERNAL_VALIDATION,
Self::InvalidJob(_, _) => ErrorCode::INVALID_JOB,
Self::InvalidUploadQuery(_) => ErrorCode::INVALID_UPLOAD_QUERY,
#[cfg(feature = "random-errors")]
Self::RandomError => ErrorCode::RANDOM_ERROR,
2023-09-02 01:50:10 +00:00
}
}
2023-09-05 02:51:27 +00:00
const fn is_disconnected(&self) -> bool {
match self {
Self::Repo(e) => e.is_disconnected(),
Self::Store(s) => s.is_disconnected(),
_ => false,
}
}
2023-09-02 01:50:10 +00:00
}
2021-02-10 22:57:42 +00:00
impl From<actix_web::error::BlockingError> for UploadError {
fn from(_: actix_web::error::BlockingError) -> Self {
UploadError::Canceled
2020-06-06 21:41:17 +00:00
}
}
impl From<tokio::sync::AcquireError> for UploadError {
fn from(_: tokio::sync::AcquireError) -> Self {
UploadError::Semaphore
}
}
impl From<crate::store::StoreError> for UploadError {
fn from(value: crate::store::StoreError) -> Self {
match value {
crate::store::StoreError::Repo(repo_error) => Self::Repo(repo_error),
e => Self::Store(e),
}
}
}
2021-09-14 01:22:42 +00:00
impl ResponseError for Error {
2020-06-06 21:41:17 +00:00
fn status_code(&self) -> StatusCode {
2022-03-29 01:47:46 +00:00
match self.kind() {
Some(UploadError::Upload(actix_form_data::Error::FileSize))
| Some(UploadError::Validation(crate::ingest::ValidationError::Filesize)) => {
StatusCode::PAYLOAD_TOO_LARGE
}
2022-03-29 01:47:46 +00:00
Some(
UploadError::DuplicateAlias
| UploadError::Limit(_)
| UploadError::NoFiles
2022-03-29 16:04:56 +00:00
| UploadError::Upload(_)
| UploadError::Store(crate::store::StoreError::Repo(
crate::repo::RepoError::AlreadyClaimed,
))
| UploadError::Repo(crate::repo::RepoError::AlreadyClaimed)
2023-07-13 22:42:21 +00:00
| UploadError::Validation(_)
| UploadError::InvalidUploadQuery(_)
| UploadError::UnsupportedProcessExtension
2023-09-06 01:45:07 +00:00
| UploadError::ReadOnly
| UploadError::FailedExternalValidation
| UploadError::AggregateTimeout,
2022-03-29 01:47:46 +00:00
) => StatusCode::BAD_REQUEST,
2023-07-10 20:29:41 +00:00
Some(UploadError::Magick(e)) if e.is_client_error() => StatusCode::BAD_REQUEST,
Some(UploadError::Ffmpeg(e)) if e.is_client_error() => StatusCode::BAD_REQUEST,
Some(UploadError::Exiftool(e)) if e.is_client_error() => StatusCode::BAD_REQUEST,
Some(UploadError::Process(e)) if e.is_client_error() => StatusCode::BAD_REQUEST,
Some(UploadError::MissingProxy | UploadError::MissingAlias) => StatusCode::NOT_FOUND,
2023-07-10 20:29:41 +00:00
Some(UploadError::Ffmpeg(e)) if e.is_not_found() => StatusCode::NOT_FOUND,
2022-03-29 01:47:46 +00:00
Some(UploadError::InvalidToken) => StatusCode::FORBIDDEN,
Some(UploadError::Range) => StatusCode::RANGE_NOT_SATISFIABLE,
2020-06-06 21:41:17 +00:00
_ => StatusCode::INTERNAL_SERVER_ERROR,
}
}
2021-06-19 19:39:41 +00:00
fn error_response(&self) -> HttpResponse {
2023-07-17 02:51:14 +00:00
HttpResponse::build(self.status_code())
.content_type("application/json")
.body(
2023-09-02 01:50:10 +00:00
serde_json::to_string(&serde_json::json!({
"msg": self.root_cause().to_string(),
"code": self.error_code()
}))
.unwrap_or_else(|_| {
r#"{"msg":"Request failed","code":"unknown-error"}"#.to_string()
}),
2023-07-17 02:51:14 +00:00
)
2020-06-06 21:41:17 +00:00
}
}