rustrails_storage/
transformations.rs1use std::collections::BTreeMap;
4
5use serde_json::{Value, json};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub struct ResizeTransform {
10 pub width: u32,
12 pub height: u32,
14}
15
16impl ResizeTransform {
17 #[must_use]
19 pub const fn new(width: u32, height: u32) -> Self {
20 Self { width, height }
21 }
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub struct CropTransform {
27 pub x: u32,
29 pub y: u32,
31 pub width: u32,
33 pub height: u32,
35}
36
37impl CropTransform {
38 #[must_use]
40 pub const fn new(x: u32, y: u32, width: u32, height: u32) -> Self {
41 Self {
42 x,
43 y,
44 width,
45 height,
46 }
47 }
48}
49
50#[derive(Debug, Clone, PartialEq, Eq, Default)]
52pub struct ImageTransformations {
53 pub resize: Option<ResizeTransform>,
55 pub crop: Option<CropTransform>,
57 pub format: Option<String>,
59}
60
61impl ImageTransformations {
62 #[must_use]
64 pub fn new() -> Self {
65 Self::default()
66 }
67
68 #[must_use]
70 pub fn resize(mut self, width: u32, height: u32) -> Self {
71 self.resize = Some(ResizeTransform::new(width, height));
72 self
73 }
74
75 #[must_use]
77 pub fn crop(mut self, x: u32, y: u32, width: u32, height: u32) -> Self {
78 self.crop = Some(CropTransform::new(x, y, width, height));
79 self
80 }
81
82 #[must_use]
84 pub fn format(mut self, format: impl Into<String>) -> Self {
85 self.format = Some(format.into());
86 self
87 }
88
89 #[must_use]
91 pub fn to_map(&self) -> BTreeMap<String, Value> {
92 let mut transformations = BTreeMap::new();
93 if let Some(resize) = self.resize {
94 transformations.insert(
95 "resize".to_owned(),
96 json!({"width": resize.width, "height": resize.height}),
97 );
98 }
99 if let Some(crop) = self.crop {
100 transformations.insert(
101 "crop".to_owned(),
102 json!({
103 "x": crop.x,
104 "y": crop.y,
105 "width": crop.width,
106 "height": crop.height,
107 }),
108 );
109 }
110 if let Some(format) = &self.format {
111 transformations.insert("format".to_owned(), Value::String(format.clone()));
112 }
113 transformations
114 }
115}
116
117impl From<ImageTransformations> for BTreeMap<String, Value> {
118 fn from(value: ImageTransformations) -> Self {
119 value.to_map()
120 }
121}
122
123impl From<&ImageTransformations> for BTreeMap<String, Value> {
124 fn from(value: &ImageTransformations) -> Self {
125 value.to_map()
126 }
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132
133 #[test]
134 fn resize_and_crop_transformations_convert_to_variant_map() {
135 let transformations = ImageTransformations::new()
136 .resize(100, 200)
137 .crop(10, 20, 80, 160)
138 .format("png")
139 .to_map();
140 assert_eq!(
141 transformations.get("resize"),
142 Some(&json!({"width": 100, "height": 200})),
143 );
144 assert_eq!(
145 transformations.get("crop"),
146 Some(&json!({"x": 10, "y": 20, "width": 80, "height": 160})),
147 );
148 assert_eq!(
149 transformations.get("format"),
150 Some(&Value::String("png".to_owned()))
151 );
152 }
153
154 #[test]
155 fn empty_transformations_produce_empty_map() {
156 assert!(ImageTransformations::new().to_map().is_empty());
157 }
158}