Skip to main content

fraiseql_error/
file.rs

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