mirror of
https://git.asonix.dog/asonix/pict-rs
synced 2024-11-20 11:21:14 +00:00
Add error codes
This commit is contained in:
parent
3c09aad5e8
commit
ba3a23ed43
15 changed files with 380 additions and 46 deletions
68
src/error.rs
68
src/error.rs
|
@ -1,6 +1,8 @@
|
|||
use actix_web::{http::StatusCode, HttpResponse, ResponseError};
|
||||
use color_eyre::Report;
|
||||
|
||||
use crate::error_code::ErrorCode;
|
||||
|
||||
pub(crate) struct Error {
|
||||
inner: color_eyre::Report,
|
||||
}
|
||||
|
@ -13,6 +15,12 @@ impl Error {
|
|||
pub(crate) fn root_cause(&self) -> &(dyn std::error::Error + 'static) {
|
||||
self.inner.root_cause()
|
||||
}
|
||||
|
||||
pub(crate) fn error_code(&self) -> ErrorCode {
|
||||
self.kind()
|
||||
.map(|e| e.error_code())
|
||||
.unwrap_or(ErrorCode::UNKNOWN_ERROR)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Error {
|
||||
|
@ -55,21 +63,12 @@ pub(crate) enum UploadError {
|
|||
#[error("Error in old repo")]
|
||||
OldRepo(#[from] crate::repo_04::RepoError),
|
||||
|
||||
#[error("Error parsing string")]
|
||||
ParseString(#[from] std::string::FromUtf8Error),
|
||||
|
||||
#[error("Error interacting with filesystem")]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
#[error("Error validating upload")]
|
||||
Validation(#[from] crate::validate::ValidationError),
|
||||
|
||||
#[error("Error generating path")]
|
||||
PathGenerator(#[from] storage_path_generator::PathError),
|
||||
|
||||
#[error("Error stripping prefix")]
|
||||
StripPrefix(#[from] std::path::StripPrefixError),
|
||||
|
||||
#[error("Error in store")]
|
||||
Store(#[source] crate::store::StoreError),
|
||||
|
||||
|
@ -127,11 +126,8 @@ pub(crate) enum UploadError {
|
|||
#[error("Tried to save an image with an already-taken name")]
|
||||
DuplicateAlias,
|
||||
|
||||
#[error("Error in json")]
|
||||
Json(#[from] serde_json::Error),
|
||||
|
||||
#[error("Error in cbor")]
|
||||
Cbor(#[from] serde_cbor::Error),
|
||||
#[error("Failed to serialize job")]
|
||||
PushJob(#[source] serde_json::Error),
|
||||
|
||||
#[error("Range header not satisfiable")]
|
||||
Range,
|
||||
|
@ -143,6 +139,41 @@ pub(crate) enum UploadError {
|
|||
Timeout(#[from] crate::stream::TimeoutError),
|
||||
}
|
||||
|
||||
impl UploadError {
|
||||
const fn error_code(&self) -> ErrorCode {
|
||||
match self {
|
||||
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::BuildClient(_) | Self::RequestMiddleware(_) | Self::Request(_) => {
|
||||
ErrorCode::HTTP_CLIENT_ERROR
|
||||
}
|
||||
Self::Download(_) => ErrorCode::DOWNLOAD_FILE_ERROR,
|
||||
Self::ReadOnly => ErrorCode::READ_ONLY,
|
||||
Self::InvalidProcessExtension => ErrorCode::INVALID_FILE_EXTENSION,
|
||||
Self::ParsePath => ErrorCode::INVALID_PROCESS_PATH,
|
||||
Self::Semaphore => ErrorCode::PROCESS_SEMAPHORE_CLOSED,
|
||||
Self::Canceled => ErrorCode::PANIC,
|
||||
Self::NoFiles => ErrorCode::VALIDATE_NO_FILES,
|
||||
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,
|
||||
Self::PushJob(_) => todo!(),
|
||||
Self::Range => ErrorCode::RANGE_NOT_SATISFIABLE,
|
||||
Self::Limit(_) => ErrorCode::VALIDATE_FILE_SIZE,
|
||||
Self::Timeout(_) => ErrorCode::STREAM_TOO_SLOW,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<actix_web::error::BlockingError> for UploadError {
|
||||
fn from(_: actix_web::error::BlockingError) -> Self {
|
||||
UploadError::Canceled
|
||||
|
@ -196,8 +227,13 @@ impl ResponseError for Error {
|
|||
HttpResponse::build(self.status_code())
|
||||
.content_type("application/json")
|
||||
.body(
|
||||
serde_json::to_string(&serde_json::json!({ "msg": self.root_cause().to_string() }))
|
||||
.unwrap_or_else(|_| r#"{"msg":"Request failed"}"#.to_string()),
|
||||
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()
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
137
src/error_code.rs
Normal file
137
src/error_code.rs
Normal file
|
@ -0,0 +1,137 @@
|
|||
#[derive(Debug, serde::Serialize)]
|
||||
#[serde(transparent)]
|
||||
pub(crate) struct ErrorCode {
|
||||
code: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub(crate) struct OwnedErrorCode {
|
||||
code: String,
|
||||
}
|
||||
|
||||
impl ErrorCode {
|
||||
pub(crate) fn into_owned(self) -> OwnedErrorCode {
|
||||
OwnedErrorCode {
|
||||
code: self.code.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) const COMMAND_TIMEOUT: ErrorCode = ErrorCode {
|
||||
code: "command-timeout",
|
||||
};
|
||||
pub(crate) const COMMAND_ERROR: ErrorCode = ErrorCode {
|
||||
code: "command-error",
|
||||
};
|
||||
pub(crate) const COMMAND_FAILURE: ErrorCode = ErrorCode {
|
||||
code: "command-failure",
|
||||
};
|
||||
pub(crate) const OLD_REPO_ERROR: ErrorCode = ErrorCode {
|
||||
code: "old-repo-error",
|
||||
};
|
||||
pub(crate) const NOT_FOUND: ErrorCode = ErrorCode { code: "not-found" };
|
||||
pub(crate) const FILE_IO_ERROR: ErrorCode = ErrorCode {
|
||||
code: "file-io-error",
|
||||
};
|
||||
pub(crate) const PARSE_PATH_ERROR: ErrorCode = ErrorCode {
|
||||
code: "parse-path-error",
|
||||
};
|
||||
pub(crate) const FILE_EXISTS: ErrorCode = ErrorCode {
|
||||
code: "file-exists",
|
||||
};
|
||||
pub(crate) const FORMAT_FILE_ID_ERROR: ErrorCode = ErrorCode {
|
||||
code: "format-file-id-error",
|
||||
};
|
||||
pub(crate) const OBJECT_REQUEST_ERROR: ErrorCode = ErrorCode {
|
||||
code: "object-request-error",
|
||||
};
|
||||
pub(crate) const OBJECT_IO_ERROR: ErrorCode = ErrorCode {
|
||||
code: "object-io-error",
|
||||
};
|
||||
pub(crate) const PARSE_OBJECT_ID_ERROR: ErrorCode = ErrorCode {
|
||||
code: "parse-object-id-error",
|
||||
};
|
||||
pub(crate) const PANIC: ErrorCode = ErrorCode { code: "panic" };
|
||||
pub(crate) const ALREADY_CLAIMED: ErrorCode = ErrorCode {
|
||||
code: "already-claimed",
|
||||
};
|
||||
pub(crate) const SLED_ERROR: ErrorCode = ErrorCode { code: "sled-error" };
|
||||
pub(crate) const EXTRACT_DETAILS: ErrorCode = ErrorCode {
|
||||
code: "extract-details",
|
||||
};
|
||||
pub(crate) const EXTRACT_UPLOAD_RESULT: ErrorCode = ErrorCode {
|
||||
code: "extract-upload-result",
|
||||
};
|
||||
pub(crate) const CONFLICTED_RECORD: ErrorCode = ErrorCode {
|
||||
code: "conflicted-record",
|
||||
};
|
||||
pub(crate) const COMMAND_NOT_FOUND: ErrorCode = ErrorCode {
|
||||
code: "command-not-found",
|
||||
};
|
||||
pub(crate) const COMMAND_PERMISSION_DENIED: ErrorCode = ErrorCode {
|
||||
code: "command-permission-denied",
|
||||
};
|
||||
pub(crate) const FILE_UPLOAD_ERROR: ErrorCode = ErrorCode {
|
||||
code: "file-upload-error",
|
||||
};
|
||||
pub(crate) const IO_ERROR: ErrorCode = ErrorCode { code: "io-error" };
|
||||
pub(crate) const VALIDATE_WIDTH: ErrorCode = ErrorCode {
|
||||
code: "validate-width",
|
||||
};
|
||||
pub(crate) const VALIDATE_HEIGHT: ErrorCode = ErrorCode {
|
||||
code: "validate-height",
|
||||
};
|
||||
pub(crate) const VALIDATE_AREA: ErrorCode = ErrorCode {
|
||||
code: "validate-area",
|
||||
};
|
||||
pub(crate) const VALIDATE_FRAMES: ErrorCode = ErrorCode {
|
||||
code: "validate-frames",
|
||||
};
|
||||
pub(crate) const VALIDATE_FILE_EMPTY: ErrorCode = ErrorCode {
|
||||
code: "validate-file-empty",
|
||||
};
|
||||
pub(crate) const VALIDATE_FILE_SIZE: ErrorCode = ErrorCode {
|
||||
code: "validate-file-size",
|
||||
};
|
||||
pub(crate) const VIDEO_DISABLED: ErrorCode = ErrorCode {
|
||||
code: "video-disabled",
|
||||
};
|
||||
pub(crate) const HTTP_CLIENT_ERROR: ErrorCode = ErrorCode {
|
||||
code: "http-client-error",
|
||||
};
|
||||
pub(crate) const DOWNLOAD_FILE_ERROR: ErrorCode = ErrorCode {
|
||||
code: "download-file-error",
|
||||
};
|
||||
pub(crate) const READ_ONLY: ErrorCode = ErrorCode { code: "read-only" };
|
||||
pub(crate) const INVALID_FILE_EXTENSION: ErrorCode = ErrorCode {
|
||||
code: "invalid-file-extension",
|
||||
};
|
||||
pub(crate) const INVALID_PROCESS_PATH: ErrorCode = ErrorCode {
|
||||
code: "invalid-process-path",
|
||||
};
|
||||
pub(crate) const PROCESS_SEMAPHORE_CLOSED: ErrorCode = ErrorCode {
|
||||
code: "process-semaphore-closed",
|
||||
};
|
||||
pub(crate) const VALIDATE_NO_FILES: ErrorCode = ErrorCode {
|
||||
code: "validate-no-files",
|
||||
};
|
||||
pub(crate) const ALIAS_NOT_FOUND: ErrorCode = ErrorCode {
|
||||
code: "alias-not-found",
|
||||
};
|
||||
pub(crate) const LOST_FILE: ErrorCode = ErrorCode { code: "lost-file" };
|
||||
pub(crate) const INVALID_DELETE_TOKEN: ErrorCode = ErrorCode {
|
||||
code: "invalid-delete-token",
|
||||
};
|
||||
pub(crate) const DUPLICATE_ALIAS: ErrorCode = ErrorCode {
|
||||
code: "duplicate-alias",
|
||||
};
|
||||
pub(crate) const RANGE_NOT_SATISFIABLE: ErrorCode = ErrorCode {
|
||||
code: "range-not-satisfiable",
|
||||
};
|
||||
pub(crate) const STREAM_TOO_SLOW: ErrorCode = ErrorCode {
|
||||
code: "stream-too-slow",
|
||||
};
|
||||
pub(crate) const UNKNOWN_ERROR: ErrorCode = ErrorCode {
|
||||
code: "unknown-error",
|
||||
};
|
||||
}
|
|
@ -1,4 +1,7 @@
|
|||
use crate::process::{Process, ProcessError};
|
||||
use crate::{
|
||||
error_code::ErrorCode,
|
||||
process::{Process, ProcessError},
|
||||
};
|
||||
use actix_web::web::Bytes;
|
||||
use tokio::io::{AsyncRead, AsyncReadExt};
|
||||
|
||||
|
@ -24,6 +27,13 @@ impl From<ProcessError> for ExifError {
|
|||
}
|
||||
|
||||
impl ExifError {
|
||||
pub(crate) const fn error_code(&self) -> ErrorCode {
|
||||
match self {
|
||||
Self::Process(e) => e.error_code(),
|
||||
Self::Read(_) => ErrorCode::COMMAND_ERROR,
|
||||
Self::CommandFailed(_) => ErrorCode::COMMAND_FAILURE,
|
||||
}
|
||||
}
|
||||
pub(crate) fn is_client_error(&self) -> bool {
|
||||
// if exiftool bails we probably have bad input
|
||||
matches!(
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::{
|
||||
error_code::ErrorCode,
|
||||
formats::InternalVideoFormat,
|
||||
process::{Process, ProcessError},
|
||||
store::{Store, StoreError},
|
||||
|
@ -63,6 +64,24 @@ impl From<ProcessError> for FfMpegError {
|
|||
}
|
||||
|
||||
impl FfMpegError {
|
||||
pub(crate) const fn error_code(&self) -> ErrorCode {
|
||||
match self {
|
||||
Self::CommandFailed(_) => ErrorCode::COMMAND_FAILURE,
|
||||
Self::Store(s) => s.error_code(),
|
||||
Self::Process(e) => e.error_code(),
|
||||
Self::Read(_)
|
||||
| Self::Write(_)
|
||||
| Self::Json(_)
|
||||
| Self::CreateDir(_)
|
||||
| Self::ReadFile(_)
|
||||
| Self::OpenFile(_)
|
||||
| Self::CreateFile(_)
|
||||
| Self::CloseFile(_)
|
||||
| Self::RemoveFile(_)
|
||||
| Self::Path => ErrorCode::COMMAND_ERROR,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_client_error(&self) -> bool {
|
||||
// Failing validation or ffmpeg bailing probably means bad input
|
||||
matches!(
|
||||
|
|
|
@ -6,6 +6,7 @@ mod details;
|
|||
mod discover;
|
||||
mod either;
|
||||
mod error;
|
||||
mod error_code;
|
||||
mod exiftool;
|
||||
mod ffmpeg;
|
||||
mod file;
|
||||
|
@ -450,11 +451,11 @@ async fn claim_upload<S: Store + 'static>(
|
|||
}]
|
||||
})))
|
||||
}
|
||||
UploadResult::Failure { message } => Ok(HttpResponse::UnprocessableEntity().json(
|
||||
&serde_json::json!({
|
||||
UploadResult::Failure { message, code } => Ok(HttpResponse::UnprocessableEntity()
|
||||
.json(&serde_json::json!({
|
||||
"msg": message,
|
||||
}),
|
||||
)),
|
||||
"code": code,
|
||||
}))),
|
||||
}
|
||||
}
|
||||
Err(_) => Ok(HttpResponse::NoContent().finish()),
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::{
|
||||
error_code::ErrorCode,
|
||||
formats::ProcessableFormat,
|
||||
process::{Process, ProcessError},
|
||||
store::Store,
|
||||
|
@ -57,6 +58,24 @@ impl From<ProcessError> for MagickError {
|
|||
}
|
||||
|
||||
impl MagickError {
|
||||
pub(crate) const fn error_code(&self) -> ErrorCode {
|
||||
match self {
|
||||
Self::CommandFailed(_) => ErrorCode::COMMAND_FAILURE,
|
||||
Self::Store(e) => e.error_code(),
|
||||
Self::Process(e) => e.error_code(),
|
||||
Self::Json(_)
|
||||
| Self::Read(_)
|
||||
| Self::Write(_)
|
||||
| Self::CreateFile(_)
|
||||
| Self::CreateDir(_)
|
||||
| Self::CloseFile(_)
|
||||
| Self::RemoveFile(_)
|
||||
| Self::Discover(_)
|
||||
| Self::Empty
|
||||
| Self::Path => ErrorCode::COMMAND_ERROR,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_client_error(&self) -> bool {
|
||||
// Failing validation or imagemagick bailing probably means bad input
|
||||
matches!(
|
||||
|
|
|
@ -14,6 +14,8 @@ use tokio::{
|
|||
};
|
||||
use tracing::{Instrument, Span};
|
||||
|
||||
use crate::error_code::ErrorCode;
|
||||
|
||||
struct MetricsGuard {
|
||||
start: Instant,
|
||||
armed: bool,
|
||||
|
@ -100,6 +102,18 @@ pub(crate) enum ProcessError {
|
|||
Other(#[source] std::io::Error),
|
||||
}
|
||||
|
||||
impl ProcessError {
|
||||
pub(crate) const fn error_code(&self) -> ErrorCode {
|
||||
match self {
|
||||
Self::NotFound(_) => ErrorCode::COMMAND_NOT_FOUND,
|
||||
Self::PermissionDenied(_) => ErrorCode::COMMAND_PERMISSION_DENIED,
|
||||
Self::LimitReached | Self::Other(_) => ErrorCode::COMMAND_ERROR,
|
||||
Self::Timeout(_) => ErrorCode::COMMAND_TIMEOUT,
|
||||
Self::Status(_, _) => ErrorCode::COMMAND_FAILURE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Process {
|
||||
pub(crate) fn run(command: &str, args: &[&str], timeout: u64) -> Result<Self, ProcessError> {
|
||||
let res = tracing::trace_span!(parent: None, "Create command", %command)
|
||||
|
|
25
src/queue.rs
25
src/queue.rs
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
concurrent_processor::ProcessMap,
|
||||
config::Configuration,
|
||||
error::Error,
|
||||
error::{Error, UploadError},
|
||||
formats::InputProcessableFormat,
|
||||
repo::{Alias, DeleteToken, FullRepo, Hash, JobId, UploadId},
|
||||
serde_str::Serde,
|
||||
|
@ -94,13 +94,14 @@ pub(crate) async fn cleanup_alias(
|
|||
let job = serde_json::to_vec(&Cleanup::Alias {
|
||||
alias: Serde::new(alias),
|
||||
token: Serde::new(token),
|
||||
})?;
|
||||
})
|
||||
.map_err(UploadError::PushJob)?;
|
||||
repo.push(CLEANUP_QUEUE, job.into()).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn cleanup_hash(repo: &Arc<dyn FullRepo>, hash: Hash) -> Result<(), Error> {
|
||||
let job = serde_json::to_vec(&Cleanup::Hash { hash })?;
|
||||
let job = serde_json::to_vec(&Cleanup::Hash { hash }).map_err(UploadError::PushJob)?;
|
||||
repo.push(CLEANUP_QUEUE, job.into()).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -111,7 +112,8 @@ pub(crate) async fn cleanup_identifier<I: Identifier>(
|
|||
) -> Result<(), Error> {
|
||||
let job = serde_json::to_vec(&Cleanup::Identifier {
|
||||
identifier: Base64Bytes(identifier.to_bytes()?),
|
||||
})?;
|
||||
})
|
||||
.map_err(UploadError::PushJob)?;
|
||||
repo.push(CLEANUP_QUEUE, job.into()).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -121,25 +123,26 @@ async fn cleanup_variants(
|
|||
hash: Hash,
|
||||
variant: Option<String>,
|
||||
) -> Result<(), Error> {
|
||||
let job = serde_json::to_vec(&Cleanup::Variant { hash, variant })?;
|
||||
let job =
|
||||
serde_json::to_vec(&Cleanup::Variant { hash, variant }).map_err(UploadError::PushJob)?;
|
||||
repo.push(CLEANUP_QUEUE, job.into()).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn cleanup_outdated_proxies(repo: &Arc<dyn FullRepo>) -> Result<(), Error> {
|
||||
let job = serde_json::to_vec(&Cleanup::OutdatedProxies)?;
|
||||
let job = serde_json::to_vec(&Cleanup::OutdatedProxies).map_err(UploadError::PushJob)?;
|
||||
repo.push(CLEANUP_QUEUE, job.into()).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn cleanup_outdated_variants(repo: &Arc<dyn FullRepo>) -> Result<(), Error> {
|
||||
let job = serde_json::to_vec(&Cleanup::OutdatedVariants)?;
|
||||
let job = serde_json::to_vec(&Cleanup::OutdatedVariants).map_err(UploadError::PushJob)?;
|
||||
repo.push(CLEANUP_QUEUE, job.into()).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn cleanup_all_variants(repo: &Arc<dyn FullRepo>) -> Result<(), Error> {
|
||||
let job = serde_json::to_vec(&Cleanup::AllVariants)?;
|
||||
let job = serde_json::to_vec(&Cleanup::AllVariants).map_err(UploadError::PushJob)?;
|
||||
repo.push(CLEANUP_QUEUE, job.into()).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -154,7 +157,8 @@ pub(crate) async fn queue_ingest(
|
|||
identifier: Base64Bytes(identifier),
|
||||
declared_alias: declared_alias.map(Serde::new),
|
||||
upload_id: Serde::new(upload_id),
|
||||
})?;
|
||||
})
|
||||
.map_err(UploadError::PushJob)?;
|
||||
repo.push(PROCESS_QUEUE, job.into()).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -171,7 +175,8 @@ pub(crate) async fn queue_generate(
|
|||
source: Serde::new(source),
|
||||
process_path,
|
||||
process_args,
|
||||
})?;
|
||||
})
|
||||
.map_err(UploadError::PushJob)?;
|
||||
repo.push(PROCESS_QUEUE, job.into()).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -117,6 +117,7 @@ where
|
|||
|
||||
UploadResult::Failure {
|
||||
message: e.root_cause().to_string(),
|
||||
code: e.error_code().into_owned(),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
22
src/repo.rs
22
src/repo.rs
|
@ -1,6 +1,7 @@
|
|||
use crate::{
|
||||
config,
|
||||
details::Details,
|
||||
error_code::{ErrorCode, OwnedErrorCode},
|
||||
store::{Identifier, StoreError},
|
||||
stream::LocalBoxStream,
|
||||
};
|
||||
|
@ -52,9 +53,16 @@ pub(crate) struct UploadId {
|
|||
id: Uuid,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum UploadResult {
|
||||
Success { alias: Alias, token: DeleteToken },
|
||||
Failure { message: String },
|
||||
Success {
|
||||
alias: Alias,
|
||||
token: DeleteToken,
|
||||
},
|
||||
Failure {
|
||||
message: String,
|
||||
code: OwnedErrorCode,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
|
@ -69,6 +77,16 @@ pub(crate) enum RepoError {
|
|||
Canceled,
|
||||
}
|
||||
|
||||
impl RepoError {
|
||||
pub(crate) const fn error_code(&self) -> ErrorCode {
|
||||
match self {
|
||||
Self::SledError(e) => e.error_code(),
|
||||
Self::AlreadyClaimed => ErrorCode::ALREADY_CLAIMED,
|
||||
Self::Canceled => ErrorCode::PANIC,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
pub(crate) trait FullRepo:
|
||||
UploadRepo
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::{
|
||||
details::HumanDate,
|
||||
error_code::{ErrorCode, OwnedErrorCode},
|
||||
serde_str::Serde,
|
||||
store::StoreError,
|
||||
stream::{from_iterator, LocalBoxStream},
|
||||
|
@ -46,7 +47,10 @@ pub(crate) enum SledError {
|
|||
Sled(#[from] sled::Error),
|
||||
|
||||
#[error("Invalid details json")]
|
||||
Details(#[from] serde_json::Error),
|
||||
Details(serde_json::Error),
|
||||
|
||||
#[error("Invalid upload result json")]
|
||||
UploadResult(serde_json::Error),
|
||||
|
||||
#[error("Error parsing variant key")]
|
||||
VariantKey(#[from] VariantKeyError),
|
||||
|
@ -58,6 +62,18 @@ pub(crate) enum SledError {
|
|||
Conflict,
|
||||
}
|
||||
|
||||
impl SledError {
|
||||
pub(super) const fn error_code(&self) -> ErrorCode {
|
||||
match self {
|
||||
Self::Sled(_) | Self::VariantKey(_) => ErrorCode::SLED_ERROR,
|
||||
Self::Details(_) => ErrorCode::EXTRACT_DETAILS,
|
||||
Self::UploadResult(_) => ErrorCode::EXTRACT_UPLOAD_RESULT,
|
||||
Self::Panic => ErrorCode::PANIC,
|
||||
Self::Conflict => ErrorCode::CONFLICTED_RECORD,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct SledRepo {
|
||||
healthz_count: Arc<AtomicU64>,
|
||||
|
@ -442,6 +458,7 @@ enum InnerUploadResult {
|
|||
},
|
||||
Failure {
|
||||
message: String,
|
||||
code: OwnedErrorCode,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -452,7 +469,7 @@ impl From<UploadResult> for InnerUploadResult {
|
|||
alias: Serde::new(alias),
|
||||
token: Serde::new(token),
|
||||
},
|
||||
UploadResult::Failure { message } => InnerUploadResult::Failure { message },
|
||||
UploadResult::Failure { message, code } => InnerUploadResult::Failure { message, code },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -464,7 +481,7 @@ impl From<InnerUploadResult> for UploadResult {
|
|||
alias: Serde::into_inner(alias),
|
||||
token: Serde::into_inner(token),
|
||||
},
|
||||
InnerUploadResult::Failure { message } => UploadResult::Failure { message },
|
||||
InnerUploadResult::Failure { message, code } => UploadResult::Failure { message, code },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -538,7 +555,7 @@ impl UploadRepo for SledRepo {
|
|||
if let Some(bytes) = opt {
|
||||
if bytes != b"1" {
|
||||
let result: InnerUploadResult =
|
||||
serde_json::from_slice(&bytes).map_err(SledError::from)?;
|
||||
serde_json::from_slice(&bytes).map_err(SledError::UploadResult)?;
|
||||
return Ok(result.into());
|
||||
}
|
||||
} else {
|
||||
|
@ -553,7 +570,7 @@ impl UploadRepo for SledRepo {
|
|||
sled::Event::Insert { value, .. } => {
|
||||
if value != b"1" {
|
||||
let result: InnerUploadResult =
|
||||
serde_json::from_slice(&value).map_err(SledError::from)?;
|
||||
serde_json::from_slice(&value).map_err(SledError::UploadResult)?;
|
||||
return Ok(result.into());
|
||||
}
|
||||
}
|
||||
|
@ -576,7 +593,7 @@ impl UploadRepo for SledRepo {
|
|||
result: UploadResult,
|
||||
) -> Result<(), RepoError> {
|
||||
let result: InnerUploadResult = result.into();
|
||||
let result = serde_json::to_vec(&result).map_err(SledError::from)?;
|
||||
let result = serde_json::to_vec(&result).map_err(SledError::UploadResult)?;
|
||||
|
||||
b!(self.uploads, uploads.insert(upload_id.as_bytes(), result));
|
||||
|
||||
|
@ -940,7 +957,7 @@ impl DetailsRepo for SledRepo {
|
|||
) -> Result<(), StoreError> {
|
||||
let key = identifier.to_bytes()?;
|
||||
let details = serde_json::to_vec(&details.inner)
|
||||
.map_err(SledError::from)
|
||||
.map_err(SledError::Details)
|
||||
.map_err(RepoError::from)?;
|
||||
|
||||
b!(
|
||||
|
@ -959,7 +976,7 @@ impl DetailsRepo for SledRepo {
|
|||
|
||||
opt.map(|ivec| serde_json::from_slice(&ivec).map(|inner| Details { inner }))
|
||||
.transpose()
|
||||
.map_err(SledError::from)
|
||||
.map_err(SledError::Details)
|
||||
.map_err(RepoError::from)
|
||||
.map_err(StoreError::from)
|
||||
}
|
||||
|
|
11
src/store.rs
11
src/store.rs
|
@ -4,6 +4,8 @@ use futures_core::Stream;
|
|||
use std::{fmt::Debug, sync::Arc};
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
|
||||
use crate::error_code::ErrorCode;
|
||||
|
||||
pub(crate) mod file_store;
|
||||
pub(crate) mod object_store;
|
||||
|
||||
|
@ -29,6 +31,15 @@ pub(crate) enum StoreError {
|
|||
}
|
||||
|
||||
impl StoreError {
|
||||
pub(crate) const fn error_code(&self) -> ErrorCode {
|
||||
match self {
|
||||
Self::FileStore(e) => e.error_code(),
|
||||
Self::ObjectStore(e) => e.error_code(),
|
||||
Self::Repo(e) => e.error_code(),
|
||||
Self::Repo04(_) => ErrorCode::OLD_REPO_ERROR,
|
||||
Self::FileNotFound(_) | Self::ObjectNotFound(_) => ErrorCode::NOT_FOUND,
|
||||
}
|
||||
}
|
||||
pub(crate) const fn is_not_found(&self) -> bool {
|
||||
matches!(self, Self::FileNotFound(_)) || matches!(self, Self::ObjectNotFound(_))
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::{
|
||||
error_code::ErrorCode,
|
||||
file::File,
|
||||
repo::{Repo, SettingsRepo},
|
||||
store::Store,
|
||||
|
@ -32,16 +33,27 @@ pub(crate) enum FileError {
|
|||
#[error("Failed to generate path")]
|
||||
PathGenerator(#[from] storage_path_generator::PathError),
|
||||
|
||||
#[error("Error formatting file store identifier")]
|
||||
#[error("Error formatting file store ID")]
|
||||
IdError,
|
||||
|
||||
#[error("Mailformed file store identifier")]
|
||||
#[error("Malformed file store ID")]
|
||||
PrefixError,
|
||||
|
||||
#[error("Tried to save over existing file")]
|
||||
FileExists,
|
||||
}
|
||||
|
||||
impl FileError {
|
||||
pub(super) const fn error_code(&self) -> ErrorCode {
|
||||
match self {
|
||||
Self::Io(_) => ErrorCode::FILE_IO_ERROR,
|
||||
Self::PathGenerator(_) => ErrorCode::PARSE_PATH_ERROR,
|
||||
Self::FileExists => ErrorCode::FILE_EXISTS,
|
||||
Self::IdError | Self::PrefixError => ErrorCode::FORMAT_FILE_ID_ERROR,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct FileStore {
|
||||
path_gen: Generator,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::{
|
||||
bytes_stream::BytesStream,
|
||||
error_code::ErrorCode,
|
||||
repo::{Repo, SettingsRepo},
|
||||
store::Store,
|
||||
stream::{IntoStreamer, StreamMap},
|
||||
|
@ -67,21 +68,39 @@ pub(crate) enum ObjectError {
|
|||
Etag,
|
||||
|
||||
#[error("Task cancelled")]
|
||||
Cancelled,
|
||||
Canceled,
|
||||
|
||||
#[error("Invalid status: {0}\n{1}")]
|
||||
Status(StatusCode, String),
|
||||
}
|
||||
|
||||
impl ObjectError {
|
||||
pub(super) const fn error_code(&self) -> ErrorCode {
|
||||
match self {
|
||||
Self::PathGenerator(_) => ErrorCode::PARSE_PATH_ERROR,
|
||||
Self::S3(_)
|
||||
| Self::RequestMiddleware(_)
|
||||
| Self::Request(_)
|
||||
| Self::Xml(_)
|
||||
| Self::Length
|
||||
| Self::Etag
|
||||
| Self::Status(_, _) => ErrorCode::OBJECT_REQUEST_ERROR,
|
||||
Self::IO(_) => ErrorCode::OBJECT_IO_ERROR,
|
||||
Self::Utf8(_) => ErrorCode::PARSE_OBJECT_ID_ERROR,
|
||||
Self::Canceled => ErrorCode::PANIC,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<JoinError> for ObjectError {
|
||||
fn from(_: JoinError) -> Self {
|
||||
Self::Cancelled
|
||||
Self::Canceled
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BlockingError> for ObjectError {
|
||||
fn from(_: BlockingError) -> Self {
|
||||
Self::Cancelled
|
||||
Self::Canceled
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ use crate::{
|
|||
discover::Discovery,
|
||||
either::Either,
|
||||
error::Error,
|
||||
error_code::ErrorCode,
|
||||
formats::{
|
||||
AnimationFormat, AnimationOutput, ImageInput, ImageOutput, InputFile, InputVideoFormat,
|
||||
InternalFormat, Validations,
|
||||
|
@ -38,6 +39,20 @@ pub(crate) enum ValidationError {
|
|||
VideoDisabled,
|
||||
}
|
||||
|
||||
impl ValidationError {
|
||||
pub(crate) const fn error_code(&self) -> ErrorCode {
|
||||
match self {
|
||||
Self::Width => ErrorCode::VALIDATE_WIDTH,
|
||||
Self::Height => ErrorCode::VALIDATE_HEIGHT,
|
||||
Self::Area => ErrorCode::VALIDATE_AREA,
|
||||
Self::Frames => ErrorCode::VALIDATE_FRAMES,
|
||||
Self::Empty => ErrorCode::VALIDATE_FILE_EMPTY,
|
||||
Self::Filesize => ErrorCode::VALIDATE_FILE_SIZE,
|
||||
Self::VideoDisabled => ErrorCode::VIDEO_DISABLED,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const MEGABYTES: usize = 1024 * 1024;
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
|
|
Loading…
Reference in a new issue