artifact_api/
image_transform.rs1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
5#[serde(rename_all = "camelCase", deny_unknown_fields)]
6pub struct ImageTransform {
7 pub format: ImageOutputFormat,
8 #[serde(default, skip_serializing_if = "Option::is_none")]
9 pub max_width: Option<u32>,
10 #[serde(default, skip_serializing_if = "Option::is_none")]
11 pub max_height: Option<u32>,
12 #[serde(default, skip_serializing_if = "Option::is_none")]
13 pub quality: Option<u8>,
14 #[serde(default, skip_serializing_if = "Option::is_none")]
16 pub background: Option<String>,
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
20#[serde(rename_all = "UPPERCASE")]
21pub enum ImageOutputFormat {
22 Jpeg,
23 Png,
24}
25
26impl ImageTransform {
27 pub fn validate(&self) -> Result<(), &'static str> {
28 for size in [self.max_width, self.max_height].into_iter().flatten() {
29 if !(1..=16_384).contains(&size) {
30 return Err("image transform dimensions must be between 1 and 16384");
31 }
32 }
33 if let Some(quality) = self.quality {
34 if self.format != ImageOutputFormat::Jpeg || !(1..=100).contains(&quality) {
35 return Err("image transform quality requires JPEG and a value from 1 to 100");
36 }
37 }
38 if let Some(color) = &self.background {
39 if self.format != ImageOutputFormat::Jpeg
40 || color.len() != 7
41 || !color.starts_with('#')
42 || !color.as_bytes()[1..].iter().all(u8::is_ascii_hexdigit)
43 {
44 return Err("image transform background requires JPEG and #RRGGBB");
45 }
46 }
47 Ok(())
48 }
49}