Skip to main content

fraiseql_error/
file.rs

1/// Errors that occur during file upload, validation, storage, or retrieval.
2#[derive(Debug, thiserror::Error)]
3#[non_exhaustive]
4pub enum FileError {
5    /// The uploaded file exceeds the configured maximum size.
6    #[error("File too large: {size} bytes (max: {max} bytes)")]
7    TooLarge {
8        /// Actual size of the uploaded file in bytes.
9        size: usize,
10        /// Maximum allowed file size in bytes.
11        max:  usize,
12    },
13
14    /// The file's extension or declared MIME type is not on the allow-list.
15    #[error("Invalid file type: {got} (allowed: {allowed:?})")]
16    InvalidType {
17        /// The MIME type or extension that was supplied.
18        got:     String,
19        /// The set of allowed MIME types or extensions.
20        allowed: Vec<String>,
21    },
22
23    /// The file's declared MIME type does not match its detected MIME type.
24    ///
25    /// This can indicate a spoofed `Content-Type` header.
26    #[error("MIME type mismatch: declared {declared}, detected {detected}")]
27    MimeMismatch {
28        /// The MIME type stated by the client.
29        declared: String,
30        /// The MIME type detected by content inspection.
31        detected: String,
32    },
33
34    /// An error occurred while writing to or reading from the backing storage
35    /// system (e.g. local disk, object store).
36    #[error("Storage error: {message}")]
37    Storage {
38        /// Description of the storage failure.
39        message: String,
40        /// Optional chained error from the storage backend.
41        #[source]
42        source:  Option<Box<dyn std::error::Error + Send + Sync>>,
43    },
44
45    /// An error occurred while processing the file contents (e.g. image
46    /// resizing, format conversion).
47    #[error("Processing error: {message}")]
48    Processing {
49        /// Description of the processing failure.
50        message: String,
51    },
52
53    /// The requested file does not exist in the storage backend.
54    #[error("File not found: {id}")]
55    NotFound {
56        /// Identifier of the file that was not found.
57        id: String,
58    },
59
60    /// A virus or malware scanner flagged the uploaded file.
61    #[error("Virus detected: {details}")]
62    VirusDetected {
63        /// Scanner-provided details about the detected threat (server-side only).
64        details: String,
65    },
66
67    /// The user or tenant has exhausted their file storage quota.
68    #[error("Upload quota exceeded")]
69    QuotaExceeded,
70}
71
72impl FileError {
73    /// Returns a short, stable error code string suitable for API responses and
74    /// structured logging.
75    pub const fn error_code(&self) -> &'static str {
76        match self {
77            Self::TooLarge { .. } => "file_too_large",
78            Self::InvalidType { .. } => "file_invalid_type",
79            Self::MimeMismatch { .. } => "file_mime_mismatch",
80            Self::Storage { .. } => "file_storage_error",
81            Self::Processing { .. } => "file_processing_error",
82            Self::NotFound { .. } => "file_not_found",
83            Self::VirusDetected { .. } => "file_virus_detected",
84            Self::QuotaExceeded => "file_quota_exceeded",
85        }
86    }
87}