···97 #[error("error-smokesignal-blob-37 No header file provided")]
98 NoHeaderFile,
990000000100 /// Error from image processing operations.
101 ///
102 /// This error wraps `ImageError` and is automatically converted from
···97 #[error("error-smokesignal-blob-37 No header file provided")]
98 NoHeaderFile,
99100+ /// Error when no thumbnail file is provided in the upload.
101+ ///
102+ /// This error occurs when the expected thumbnail file field is missing
103+ /// from the multipart form data.
104+ #[error("error-smokesignal-blob-47 No thumbnail file provided")]
105+ NoThumbnailFile,
106+107 /// Error from image processing operations.
108 ///
109 /// This error wraps `ImageError` and is automatically converted from
···80 Ok(png_buffer.into_inner())
81}
8283-/// Process event header image: validate 16:9 aspect ratio, resize to 1600x900, convert to PNG
84pub(crate) fn process_event_header(data: &[u8]) -> Result<Vec<u8>, ImageError> {
85 // Load the image
86 let img = image::load_from_memory(data)
···8889 let (width, height) = img.dimensions();
9091- // Validate 16:9 aspect ratio (allow 10% deviation)
92 let aspect_ratio = width as f32 / height as f32;
93- let expected_ratio = 16.0 / 9.0;
94 if (aspect_ratio - expected_ratio).abs() / expected_ratio > 0.10 {
95 return Err(ImageError::InvalidEventHeaderAspectRatio { width, height });
96 }
9798- // Resize to 1600x900 if needed
99- let resized = if width != 1600 || height != 900 {
100- img.resize_exact(1600, 900, FilterType::Lanczos3)
101 } else {
102 img
103 };
···110111 Ok(png_buffer.into_inner())
112}
000000000000000000000000000000000000
···80 Ok(png_buffer.into_inner())
81}
8283+/// Process event header image: validate 3:1 aspect ratio, resize to 1500x500, convert to PNG
84pub(crate) fn process_event_header(data: &[u8]) -> Result<Vec<u8>, ImageError> {
85 // Load the image
86 let img = image::load_from_memory(data)
···8889 let (width, height) = img.dimensions();
9091+ // Validate 3:1 aspect ratio (allow 10% deviation)
92 let aspect_ratio = width as f32 / height as f32;
93+ let expected_ratio = 3.0 / 1.0;
94 if (aspect_ratio - expected_ratio).abs() / expected_ratio > 0.10 {
95 return Err(ImageError::InvalidEventHeaderAspectRatio { width, height });
96 }
9798+ // Resize to 1500x500 if needed
99+ let resized = if width != 1500 || height != 500 {
100+ img.resize_exact(1500, 500, FilterType::Lanczos3)
101 } else {
102 img
103 };
···110111 Ok(png_buffer.into_inner())
112}
113+114+/// Process event thumbnail image: validate 1:1 aspect ratio, resize within 512x512 to 1024x1024, convert to PNG
115+pub(crate) fn process_event_thumbnail(data: &[u8]) -> Result<Vec<u8>, ImageError> {
116+ // Load the image
117+ let img = image::load_from_memory(data)
118+ .map_err(|e| ImageError::ThumbnailLoadFailed(e.to_string()))?;
119+120+ let (width, height) = img.dimensions();
121+122+ // Validate 1:1 aspect ratio (allow 5% deviation)
123+ let aspect_ratio = width as f32 / height as f32;
124+ if (aspect_ratio - 1.0).abs() > 0.05 {
125+ return Err(ImageError::InvalidThumbnailAspectRatio { width, height });
126+ }
127+128+ // Validate minimum size of 512x512
129+ if width < 512 || height < 512 {
130+ return Err(ImageError::ThumbnailTooSmall { width, height });
131+ }
132+133+ // Resize to maximum 1024x1024 if larger, otherwise keep original size
134+ let resized = if width > 1024 || height > 1024 {
135+ img.resize_exact(1024, 1024, FilterType::Lanczos3)
136+ } else {
137+ // Keep original size if within bounds (512-1024)
138+ img
139+ };
140+141+ // Convert to PNG
142+ let mut png_buffer = std::io::Cursor::new(Vec::new());
143+ resized
144+ .write_to(&mut png_buffer, ImageFormat::Png)
145+ .map_err(|e| ImageError::ThumbnailEncodeFailed(e.to_string()))?;
146+147+ Ok(png_buffer.into_inner())
148+}
+41-3
src/image_errors.rs
···84 #[error("error-smokesignal-image-9 Failed to load event header image: {0}")]
85 EventHeaderLoadFailed(String),
8687- /// Error when event header aspect ratio is not 16:9.
88 ///
89 /// This error occurs when an event header image doesn't have the required
90- /// 16:9 aspect ratio (allowing 10% deviation).
91- #[error("error-smokesignal-image-10 Event header must have 16:9 aspect ratio: got {width}:{height}")]
92 InvalidEventHeaderAspectRatio {
93 /// The width of the image in pixels.
94 width: u32,
···102 /// and processing the event header image.
103 #[error("error-smokesignal-image-11 Failed to encode event header as PNG: {0}")]
104 EventHeaderEncodeFailed(String),
00000000000000000000000000000000000000105}
···84 #[error("error-smokesignal-image-9 Failed to load event header image: {0}")]
85 EventHeaderLoadFailed(String),
8687+ /// Error when event header aspect ratio is not 3:1.
88 ///
89 /// This error occurs when an event header image doesn't have the required
90+ /// 3:1 aspect ratio (allowing 10% deviation).
91+ #[error("error-smokesignal-image-10 Event header must have 3:1 aspect ratio: got {width}:{height}")]
92 InvalidEventHeaderAspectRatio {
93 /// The width of the image in pixels.
94 width: u32,
···102 /// and processing the event header image.
103 #[error("error-smokesignal-image-11 Failed to encode event header as PNG: {0}")]
104 EventHeaderEncodeFailed(String),
105+106+ /// Error when loading a thumbnail image fails.
107+ ///
108+ /// This error occurs when attempting to load image data for thumbnail
109+ /// processing, typically due to invalid format or corrupted data.
110+ #[error("error-smokesignal-image-12 Failed to load thumbnail image: {0}")]
111+ ThumbnailLoadFailed(String),
112+113+ /// Error when thumbnail aspect ratio is not 1:1.
114+ ///
115+ /// This error occurs when a thumbnail image doesn't have the required
116+ /// square aspect ratio (allowing 5% deviation).
117+ #[error("error-smokesignal-image-13 Thumbnail must have 1:1 aspect ratio: got {width}:{height}")]
118+ InvalidThumbnailAspectRatio {
119+ /// The width of the image in pixels.
120+ width: u32,
121+ /// The height of the image in pixels.
122+ height: u32,
123+ },
124+125+ /// Error when thumbnail image is too small.
126+ ///
127+ /// This error occurs when a thumbnail image is smaller than the minimum
128+ /// required size of 512x512 pixels.
129+ #[error("error-smokesignal-image-14 Thumbnail must be at least 512x512: got {width}x{height}")]
130+ ThumbnailTooSmall {
131+ /// The width of the image in pixels.
132+ width: u32,
133+ /// The height of the image in pixels.
134+ height: u32,
135+ },
136+137+ /// Error when encoding a thumbnail as PNG fails.
138+ ///
139+ /// This error occurs when the PNG encoding process fails after resizing
140+ /// and processing the thumbnail image.
141+ #[error("error-smokesignal-image-15 Failed to encode thumbnail as PNG: {0}")]
142+ ThumbnailEncodeFailed(String),
143}