1use {
2 crate::{core::Config, utils::ResizedImageDetails},
3 serde::{Deserialize, Serialize},
4 std::{collections::HashMap, path::PathBuf},
5};
6
7type PathBufPictureRegister = HashMap<PathBuf, Picture>;
8
9#[derive(Serialize, Deserialize, Debug)]
12pub enum MediaWidth {
13 Max(String),
14 Min(String),
15}
16
17#[derive(Serialize, Deserialize, Debug)]
19pub struct SourceAttributes {
20 pub media_width: MediaWidth,
21 pub srcset: String,
22}
23
24#[derive(Serialize, Deserialize, Debug)]
26pub struct Picture {
27 pub sources: Vec<SourceAttributes>,
29 pub fallback_uri: String,
31}
32
33impl Picture {
34 pub fn from(
37 image_file_name: &PathBuf,
38 scaled_images_count: u8,
39 ) -> Result<Self, String> {
40 if scaled_images_count == 0 {
41 return Err("scaled_images_count must be > 0".to_string());
42 }
43 let resized_image_details =
44 ResizedImageDetails::from(&image_file_name, scaled_images_count)?;
45 let mut sources = vec![];
46 let mut input_dir = image_file_name.clone();
47 input_dir.pop();
48 for details in &resized_image_details {
49 let out_file_name =
50 match input_dir.join(&details.output_file_name).to_str() {
51 Some(v) => String::from(v),
52 None => {
53 return Err(String::from(
54 "Could not convert output_file_name!",
55 ));
56 }
57 };
58 sources.push(SourceAttributes {
59 media_width: MediaWidth::Max(details.width.to_string()),
60 srcset: out_file_name,
61 });
62 }
63 let mut full_scale_image = image_file_name.clone();
64 full_scale_image.set_extension("webp");
65 let full_scale_image = match full_scale_image.to_str() {
66 Some(v) => String::from(v),
67 None => {
68 return Err(String::from(
69 "Could not convert full_scale_image file name!",
70 ));
71 }
72 };
73 sources.push(SourceAttributes {
74 media_width: MediaWidth::Min(
75 (resized_image_details.last().unwrap().width + 1).to_string(),
77 ),
78 srcset: full_scale_image,
79 });
80
81 Ok(Self {
82 sources,
83 fallback_uri: image_file_name.to_str().unwrap().to_string(),
84 })
85 }
86
87 pub fn to_html_string(
90 &self,
91 srcset_prefix: Option<String>,
92 alt_text: &str,
93 ) -> String {
94 let mut html = String::from("<picture>");
95 let uri_prefix = match &srcset_prefix {
96 Some(v) => format!("{}/", v),
97 None => String::new(),
98 };
99 for src_attrs in &self.sources {
100 let (min_max, value) = match &src_attrs.media_width {
101 MediaWidth::Max(v) => ("max", v),
102 MediaWidth::Min(v) => ("min", v),
103 };
104 html.push_str(&format!(
105 "<source media=\"({}-width: {}px)\" srcset=\"{}{}\">",
106 min_max, value, &uri_prefix, src_attrs.srcset
107 ));
108 }
109 html.push_str(&format!(
111 "<img src=\"{}{}\" alt=\"{}\" />",
112 uri_prefix, self.fallback_uri, alt_text
113 ));
114 html.push_str("</picture>");
115 html
116 }
117}
118
119#[derive(Debug)]
126pub struct PictureRegister {
127 register: PathBufPictureRegister,
128}
129
130impl PictureRegister {
131 pub fn from(config: &Config) -> Result<Self, String> {
133 match &config.install_images_into {
134 None => {
135 return Err(
136 "The install_images_into parameter needs to be set!"
137 .to_string(),
138 );
139 }
140 Some(v) => {
141 if !v.is_dir() {
142 return Err("The install_images_into parameter is not a valid directory".to_string());
143 }
144 }
145 }
146
147 Ok(Self {
148 register: Self::create_register(&config)?,
149 })
150 }
151
152 fn create_register(
154 config: &Config,
155 ) -> Result<PathBufPictureRegister, String> {
156 let images_path = match &config.install_images_into {
157 None => {
158 return Err(
159 "The install_images_into parameter needs to be set!"
160 .to_string(),
161 );
162 }
163 Some(v) => {
164 if !v.is_dir() {
165 return Err("The install_images_into parameter is not a valid directory".to_string());
166 }
167 v
168 }
169 };
170
171 let mut register = PathBufPictureRegister::new();
172 let png_file_names = crate::collect_png_file_names(&images_path, None);
173 for png in png_file_names {
174 let pic = Picture::from(&png, config.scaled_images_count)?;
175 register.insert(png, pic);
176 }
177 Ok(register)
178 }
179
180 pub fn get(&self, image: &PathBuf) -> Result<&Picture, String> {
187 match self.register.get(image) {
188 None => Err("Image not found!".to_string()),
189 Some(v) => Ok(v),
190 }
191 }
192}