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 for PinakesError { fn from(e: rusqlite::Error) -> Self { Self::Database(e.to_string()) } } impl From for PinakesError { fn from(e: tokio_postgres::Error) -> Self { Self::Database(e.to_string()) } } impl From 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( 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 = std::result::Result;