Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: Ida98135cf868db0f5a46a64b8ac562366a6a6964
145 lines
3.3 KiB
Rust
145 lines
3.3 KiB
Rust
use std::path::PathBuf;
|
|
|
|
use thiserror::Error;
|
|
|
|
#[derive(Debug, Error)]
|
|
pub enum PinakesError {
|
|
#[error("IO error: {0}")]
|
|
Io(#[from] std::io::Error),
|
|
|
|
#[error("database error: {0}")]
|
|
Database(String),
|
|
|
|
#[error("migration error: {0}")]
|
|
Migration(String),
|
|
|
|
#[error("configuration error: {0}")]
|
|
Config(String),
|
|
|
|
#[error("media item not found: {0}")]
|
|
NotFound(String),
|
|
|
|
#[error("duplicate content hash: {0}")]
|
|
DuplicateHash(String),
|
|
|
|
#[error("unsupported media type for path: {0}")]
|
|
UnsupportedMediaType(PathBuf),
|
|
|
|
#[error("metadata extraction failed: {0}")]
|
|
MetadataExtraction(String),
|
|
|
|
#[error("thumbnail generation failed: {0}")]
|
|
ThumbnailGeneration(String),
|
|
|
|
#[error("search query parse error: {0}")]
|
|
SearchParse(String),
|
|
|
|
#[error("file not found at path: {0}")]
|
|
FileNotFound(PathBuf),
|
|
|
|
#[error("tag not found: {0}")]
|
|
TagNotFound(String),
|
|
|
|
#[error("collection not found: {0}")]
|
|
CollectionNotFound(String),
|
|
|
|
#[error("invalid operation: {0}")]
|
|
InvalidOperation(String),
|
|
|
|
#[error("invalid data: {0}")]
|
|
InvalidData(String),
|
|
|
|
#[error("authentication error: {0}")]
|
|
Authentication(String),
|
|
|
|
#[error("authorization error: {0}")]
|
|
Authorization(String),
|
|
|
|
#[error("path not allowed: {0}")]
|
|
PathNotAllowed(String),
|
|
|
|
#[error("external API error: {0}")]
|
|
External(String),
|
|
|
|
// Managed Storage errors
|
|
#[error("managed storage not enabled")]
|
|
ManagedStorageDisabled,
|
|
|
|
#[error("upload too large: {0} bytes exceeds limit")]
|
|
UploadTooLarge(u64),
|
|
|
|
#[error("blob not found: {0}")]
|
|
BlobNotFound(String),
|
|
|
|
#[error("storage integrity error: {0}")]
|
|
StorageIntegrity(String),
|
|
|
|
// Sync errors
|
|
#[error("sync not enabled")]
|
|
SyncDisabled,
|
|
|
|
#[error("device not found: {0}")]
|
|
DeviceNotFound(String),
|
|
|
|
#[error("sync conflict: {0}")]
|
|
SyncConflict(String),
|
|
|
|
#[error("upload session expired: {0}")]
|
|
UploadSessionExpired(String),
|
|
|
|
#[error("upload session not found: {0}")]
|
|
UploadSessionNotFound(String),
|
|
|
|
#[error("chunk out of order: expected {expected}, got {actual}")]
|
|
ChunkOutOfOrder { expected: u64, actual: u64 },
|
|
|
|
// Sharing errors
|
|
#[error("share not found: {0}")]
|
|
ShareNotFound(String),
|
|
|
|
#[error("share expired: {0}")]
|
|
ShareExpired(String),
|
|
|
|
#[error("share password required")]
|
|
SharePasswordRequired,
|
|
|
|
#[error("share password invalid")]
|
|
SharePasswordInvalid,
|
|
|
|
#[error("insufficient share permissions")]
|
|
InsufficientSharePermissions,
|
|
|
|
#[error("serialization error: {0}")]
|
|
Serialization(String),
|
|
}
|
|
|
|
impl From<rusqlite::Error> for PinakesError {
|
|
fn from(e: rusqlite::Error) -> Self {
|
|
Self::Database(e.to_string())
|
|
}
|
|
}
|
|
|
|
impl From<tokio_postgres::Error> for PinakesError {
|
|
fn from(e: tokio_postgres::Error) -> Self {
|
|
Self::Database(e.to_string())
|
|
}
|
|
}
|
|
|
|
impl From<serde_json::Error> for PinakesError {
|
|
fn from(e: serde_json::Error) -> Self {
|
|
Self::Serialization(e.to_string())
|
|
}
|
|
}
|
|
|
|
/// Build a closure that wraps a database error with operation context.
|
|
///
|
|
/// Usage: `stmt.execute(params).map_err(db_ctx("insert_media", media_id))?;`
|
|
pub fn db_ctx<E: std::fmt::Display>(
|
|
operation: &str,
|
|
entity: impl std::fmt::Display,
|
|
) -> impl FnOnce(E) -> PinakesError {
|
|
let context = format!("{operation} [{entity}]");
|
|
move |e| PinakesError::Database(format!("{context}: {e}"))
|
|
}
|
|
|
|
pub type Result<T> = std::result::Result<T, PinakesError>;
|