use std::fmt;
#[derive(Debug)]
pub enum StorageConfigError {
EmptyRoot,
EmptyBucket,
}
impl fmt::Display for StorageConfigError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::EmptyRoot => write!(formatter, "filesystem root must not be empty"),
Self::EmptyBucket => write!(formatter, "s3 bucket must not be empty"),
}
}
}
impl std::error::Error for StorageConfigError {}
#[derive(Debug)]
pub enum StorageError {
Backend {
source: opendal::Error,
},
Path {
source: StoragePathError,
},
}
impl fmt::Display for StorageError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Backend { source } => write!(formatter, "storage operation failed: {source}"),
Self::Path { source } => write!(formatter, "invalid storage path: {source}"),
}
}
}
impl std::error::Error for StorageError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Backend { source } => Some(source),
Self::Path { source } => Some(source),
}
}
}
impl From<opendal::Error> for StorageError {
fn from(source: opendal::Error) -> Self {
Self::Backend { source }
}
}
impl From<StoragePathError> for StorageError {
fn from(source: StoragePathError) -> Self {
Self::Path { source }
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StoragePathError {
Empty,
Absolute,
Traversal,
Backslash,
ControlChar,
EmptySegment,
}
impl fmt::Display for StoragePathError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Empty => write!(formatter, "object key must not be empty"),
Self::Absolute => write!(formatter, "object key must not start with '/'"),
Self::Traversal => {
write!(formatter, "object key must not contain a '..' path segment")
}
Self::Backslash => write!(formatter, "object key must not contain a backslash"),
Self::ControlChar => {
write!(formatter, "object key must not contain control characters")
}
Self::EmptySegment => {
write!(formatter, "object key must not contain empty path segments")
}
}
}
}
impl std::error::Error for StoragePathError {}
#[cfg(feature = "uploads")]
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FilenameError {
Empty,
TooLong,
ControlChar,
Traversal,
MissingExtension,
InvalidExtension,
ExtensionNotAllowed,
EmptyStem,
ReservedName,
}
#[cfg(feature = "uploads")]
impl fmt::Display for FilenameError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Empty => write!(formatter, "filename must not be empty"),
Self::TooLong => write!(formatter, "filename is too long"),
Self::ControlChar => write!(
formatter,
"filename must not contain control or invisible formatting characters"
),
Self::Traversal => {
write!(formatter, "filename must not be a path traversal component")
}
Self::MissingExtension => write!(formatter, "filename must have a file extension"),
Self::InvalidExtension => write!(
formatter,
"file extension must be 1 to 16 ASCII alphanumeric characters"
),
Self::ExtensionNotAllowed => write!(formatter, "file extension is not allowed"),
Self::EmptyStem => write!(formatter, "filename must have a name before its extension"),
Self::ReservedName => write!(formatter, "filename is a reserved device name"),
}
}
}
#[cfg(feature = "uploads")]
impl std::error::Error for FilenameError {}
#[derive(Debug)]
pub enum StorageConnectError {
Config {
source: StorageConfigError,
},
Backend {
source: opendal::Error,
},
}
impl fmt::Display for StorageConnectError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Config { source } => write!(formatter, "storage configuration invalid: {source}"),
Self::Backend { source } => {
write!(formatter, "storage backend construction failed: {source}")
}
}
}
}
impl std::error::Error for StorageConnectError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Config { source } => Some(source),
Self::Backend { source } => Some(source),
}
}
}
impl From<StorageConfigError> for StorageConnectError {
fn from(source: StorageConfigError) -> Self {
Self::Config { source }
}
}
impl From<opendal::Error> for StorageConnectError {
fn from(source: opendal::Error) -> Self {
Self::Backend { source }
}
}
#[cfg(feature = "uploads")]
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SniffError {
Unrecognized {
declared: crate::storage::filename::Extension,
},
Mismatch {
declared: crate::storage::filename::Extension,
sniffed: &'static str,
},
}
#[cfg(feature = "uploads")]
impl fmt::Display for SniffError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Unrecognized { declared } => write!(
formatter,
"file contents are not a recognized {declared} file"
),
Self::Mismatch { declared, sniffed } => write!(
formatter,
"file contents are {sniffed}, which does not match the .{declared} extension"
),
}
}
}
#[cfg(feature = "uploads")]
impl std::error::Error for SniffError {}
#[cfg(feature = "uploads")]
#[non_exhaustive]
#[derive(Debug)]
pub enum UploadError {
Storage {
source: StorageError,
},
Content {
source: SniffError,
},
}
#[cfg(feature = "uploads")]
impl From<StorageError> for UploadError {
fn from(source: StorageError) -> Self {
Self::Storage { source }
}
}
#[cfg(feature = "uploads")]
impl From<SniffError> for UploadError {
fn from(source: SniffError) -> Self {
Self::Content { source }
}
}
#[cfg(feature = "uploads")]
impl fmt::Display for UploadError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Storage { source } => write!(formatter, "upload failed: {source}"),
Self::Content { source } => write!(formatter, "upload rejected: {source}"),
}
}
}
#[cfg(feature = "uploads")]
impl std::error::Error for UploadError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Storage { source } => Some(source),
Self::Content { source } => Some(source),
}
}
}