stapix

Yet another static page generator for photo galleries

picture.rs (13875B)

  1 use serde::{
  2     de::{Deserializer, Error, Unexpected},
  3     Deserialize,
  4 };
  5 use std::{fmt::{self, Display}, path::PathBuf};
  6 
  7 const LICENSES: &'static [&'static str] = &[
  8     "PD",
  9     "CC0",
 10     "CC-BY-1",
 11     "CC-BY-2",
 12     "CC-BY-3",
 13     "CC-BY-4",
 14     "CC-BY-SA-1",
 15     "CC-BY-SA-2",
 16     "CC-BY-SA-3",
 17     "CC-BY-SA-4",
 18     "CC-BY-NC-1",
 19     "CC-BY-NC-2",
 20     "CC-BY-NC-3",
 21     "CC-BY-NC-4",
 22     "CC-BY-NC-SA-1",
 23     "CC-BY-NC-SA-2",
 24     "CC-BY-NC-SA-3",
 25     "CC-BY-NC-SA-4",
 26     "CC-BY-ND-1",
 27     "CC-BY-ND-2",
 28     "CC-BY-ND-3",
 29     "CC-BY-ND-4",
 30     "CC-BY-NC-ND-1",
 31     "CC-BY-NC-ND-2",
 32     "CC-BY-NC-ND-3",
 33     "CC-BY-NC-ND-4",
 34 ];
 35 
 36 /// Info on a individual entry on the gallery
 37 #[derive(Debug, Clone, PartialEq, Eq)]
 38 pub struct Picture {
 39     pub path: PathBuf,
 40     pub file_name: String,
 41     pub alt: String,
 42     pub caption: Option<String>,
 43     pub license: Option<LicenseType>,
 44     pub author: String,
 45     pub author_url: Option<String>,
 46 }
 47 
 48 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 49 pub struct LicenseType(CreativeCommons);
 50 
 51 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 52 enum CreativeCommons {
 53     /// Creative Commons (without attribution)
 54     CC0,
 55     /// Creative Commons Attributions (derivatives allowed)
 56     CCBY {
 57         version: CreativeCommonsVersion,
 58         non_commercial: bool,
 59         share_alike: bool,
 60     },
 61     // The ND (non-derivatives) option excludes the SA (share alike) option
 62     /// Creative Commons Attributions Non-Derivatives
 63     CCBYND {
 64         version: CreativeCommonsVersion,
 65         non_commercial: bool,
 66     },
 67 }
 68 
 69 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 70 enum CreativeCommonsVersion {
 71     One,
 72     Two,
 73     Three,
 74     Four,
 75 }
 76 
 77 impl LicenseType {
 78     pub const fn url(&self) -> &'static str {
 79         match self.0 {
 80             // CC0
 81             CreativeCommons::CC0 => {
 82                 "https://creativecommons.org/publicdomain/zero/1.0/"
 83             },
 84             // CC-BY-1
 85             CreativeCommons::CCBY {
 86                 version: CreativeCommonsVersion::One,
 87                 non_commercial: false,
 88                 share_alike: false,
 89             } => "http://creativecommons.org/licenses/by/1.0/",
 90             // CC-BY-2
 91             CreativeCommons::CCBY {
 92                 version: CreativeCommonsVersion::Two,
 93                 non_commercial: false,
 94                 share_alike: false,
 95             } => "http://creativecommons.org/licenses/by/2.0/",
 96             // CC-BY-3
 97             CreativeCommons::CCBY {
 98                 version: CreativeCommonsVersion::Three,
 99                 non_commercial: false,
100                 share_alike: false,
101             } => "http://creativecommons.org/licenses/by/3.0/",
102             // CC-BY-4
103             CreativeCommons::CCBY {
104                 version: CreativeCommonsVersion::Four,
105                 non_commercial: false,
106                 share_alike: false,
107             } => "http://creativecommons.org/licenses/by/4.0/",
108             // CC-BY-SA-1
109             CreativeCommons::CCBY {
110                 version: CreativeCommonsVersion::One,
111                 non_commercial: false,
112                 share_alike: true,
113             } => "http://creativecommons.org/licenses/by-sa/1.0/",
114             // CC-BY-SA-2
115             CreativeCommons::CCBY {
116                 version: CreativeCommonsVersion::Two,
117                 non_commercial: false,
118                 share_alike: true,
119             } => "http://creativecommons.org/licenses/by-sa/2.0/",
120             // CC-BY-SA-3
121             CreativeCommons::CCBY {
122                 version: CreativeCommonsVersion::Three,
123                 non_commercial: false,
124                 share_alike: true,
125             } => "http://creativecommons.org/licenses/by-sa/3.0/",
126             // CC-BY-SA-4
127             CreativeCommons::CCBY {
128                 version: CreativeCommonsVersion::Four,
129                 non_commercial: false,
130                 share_alike: true,
131             } => "http://creativecommons.org/licenses/by-sa/4.0/",
132             // CC-BY-NC-1
133             CreativeCommons::CCBY {
134                 version: CreativeCommonsVersion::One,
135                 non_commercial: true,
136                 share_alike: false,
137             } => "http://creativecommons.org/licenses/by-nc/1.0/",
138             // CC-BY-NC-2
139             CreativeCommons::CCBY {
140                 version: CreativeCommonsVersion::Two,
141                 non_commercial: true,
142                 share_alike: false,
143             } => "http://creativecommons.org/licenses/by-nc/2.0/",
144             // CC-BY-NC-3
145             CreativeCommons::CCBY {
146                 version: CreativeCommonsVersion::Three,
147                 non_commercial: true,
148                 share_alike: false,
149             } => "http://creativecommons.org/licenses/by-nc/3.0/",
150             // CC-BY-NC-4
151             CreativeCommons::CCBY {
152                 version: CreativeCommonsVersion::Four,
153                 non_commercial: true,
154                 share_alike: false,
155             } => "http://creativecommons.org/licenses/by-nc/4.0/",
156             // CC-BY-NC-SA-1
157             CreativeCommons::CCBY {
158                 version: CreativeCommonsVersion::One,
159                 non_commercial: true,
160                 share_alike: true,
161             } => "http://creativecommons.org/licenses/by-nc-sa/1.0/",
162             // CC-BY-NC-SA-2
163             CreativeCommons::CCBY {
164                 version: CreativeCommonsVersion::Two,
165                 non_commercial: true,
166                 share_alike: true,
167             } => "http://creativecommons.org/licenses/by-nc-sa/2.0/",
168             // CC-BY-NC-SA-3
169             CreativeCommons::CCBY {
170                 version: CreativeCommonsVersion::Three,
171                 non_commercial: true,
172                 share_alike: true,
173             } => "http://creativecommons.org/licenses/by-nc-sa/3.0/",
174             // CC-BY-NC-SA-4
175             CreativeCommons::CCBY {
176                 version: CreativeCommonsVersion::Four,
177                 non_commercial: true,
178                 share_alike: true,
179             } => "http://creativecommons.org/licenses/by-nc-sa/4.0/",
180             // CC-BY-ND-1
181             CreativeCommons::CCBYND {
182                 version: CreativeCommonsVersion::One,
183                 non_commercial: false,
184             } => "http://creativecommons.org/licenses/by-nd/1.0/",
185             // CC-BY-ND-2
186             CreativeCommons::CCBYND {
187                 version: CreativeCommonsVersion::Two,
188                 non_commercial: false,
189             } => "http://creativecommons.org/licenses/by-nd/2.0/",
190             // CC-BY-ND-3
191             CreativeCommons::CCBYND {
192                 version: CreativeCommonsVersion::Three,
193                 non_commercial: false,
194             } => "http://creativecommons.org/licenses/by-nd/3.0/",
195             // CC-BY-ND-4
196             CreativeCommons::CCBYND {
197                 version: CreativeCommonsVersion::Four,
198                 non_commercial: false,
199             } => "http://creativecommons.org/licenses/by-nd/4.0/",
200             // CC-BY-NC-ND-1
201             CreativeCommons::CCBYND {
202                 version: CreativeCommonsVersion::One,
203                 non_commercial: true,
204             } => "http://creativecommons.org/licenses/by-nc-nd/1.0/",
205             // CC-BY-NC-ND-2
206             CreativeCommons::CCBYND {
207                 version: CreativeCommonsVersion::Two,
208                 non_commercial: true,
209             } => "http://creativecommons.org/licenses/by-nc-nd/2.0/",
210             // CC-BY-NC-ND-3
211             CreativeCommons::CCBYND {
212                 version: CreativeCommonsVersion::Three,
213                 non_commercial: true,
214             } => "http://creativecommons.org/licenses/by-nc-nd/3.0/",
215             // CC-BY-NC-ND-4
216             CreativeCommons::CCBYND {
217                 version: CreativeCommonsVersion::Four,
218                 non_commercial: true,
219             } => "http://creativecommons.org/licenses/by-nc-nd/4.0/",
220         }
221     }
222 
223     pub fn parse(s: &str) -> Result<Option<Self>, ()> {
224         if !LICENSES.contains(&s) {
225             return Err(());
226         }
227 
228         if s == "PD" {
229             return Ok(None);
230         } else if s == "CC0" {
231             return Ok(Some(Self(CreativeCommons::CC0)));
232         }
233 
234         assert!(s.len() >= 3,
235                 "if s is in LICENSES it should contain at least 3 chars");
236 
237         let version = match &s[s.len() - 1..] {
238             "1" => CreativeCommonsVersion::One,
239             "2" => CreativeCommonsVersion::Two,
240             "3" => CreativeCommonsVersion::Three,
241             "4" => CreativeCommonsVersion::Four,
242             _   => {
243                 unreachable!("if s is in LICENSES we should be able to parse the license version")
244             },
245         };
246 
247         match &s[..s.len() - 1] {
248             "CC-BY-" => {
249                 Ok(
250                     Some(
251                         Self(
252                             CreativeCommons::CCBY {
253                                 version,
254                                 non_commercial: false,
255                                 share_alike: false,
256                             }
257                         )
258                     )
259                 )
260             },
261             "CC-BY-NC-" => {
262                 Ok(
263                     Some(
264                         Self(
265                             CreativeCommons::CCBY {
266                                 version,
267                                 non_commercial: true,
268                                 share_alike: false,
269                             }
270                         )
271                     )
272                 )
273             },
274             "CC-BY-SA-" => {
275                 Ok(
276                     Some(
277                         Self(
278                             CreativeCommons::CCBY {
279                                 version,
280                                 non_commercial: false,
281                                 share_alike: true,
282                             }
283                         )
284                     )
285                 )
286             },
287             "CC-BY-NC-SA-" => {
288                 Ok(
289                     Some(
290                         Self(
291                             CreativeCommons::CCBY {
292                                 version,
293                                 non_commercial: true,
294                                 share_alike: true,
295                             }
296                         )
297                     )
298                 )
299             },
300             "CC-BY-ND-" => {
301                 Ok(
302                     Some(
303                         Self(
304                             CreativeCommons::CCBYND {
305                                 version,
306                                 non_commercial: false,
307                             }
308                         )
309                     )
310                 )
311             },
312             "CC-BY-NC-ND-" => {
313                 Ok(
314                     Some(
315                         Self(
316                             CreativeCommons::CCBYND {
317                                 version,
318                                 non_commercial: true,
319                             }
320                         )
321                     )
322                 )
323             },
324             _ => {
325                 unreachable!("if s is in LICENSES we should be able to parse the license-type")
326             },
327         }
328     }
329 }
330 
331 impl<'de> Deserialize<'de> for Picture {
332     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
333     where
334         D: Deserializer<'de>,
335     {
336         #[derive(Deserialize)]
337         struct Info {
338             path: String,
339             alt: String,
340             caption: Option<String>,
341             license: String,
342             author: String,
343             #[serde(alias = "author-url")]
344             author_url: Option<String>,
345         }
346 
347         let Info {
348             path: path_str,
349             alt,
350             caption,
351             license,
352             author,
353             author_url,
354         } = Info::deserialize(deserializer)?;
355 
356         let license = LicenseType::parse(&license)
357             .map_err(|_| D::Error::unknown_variant(&license, LICENSES))?;
358         let path = PathBuf::from(&path_str);
359 
360         if let Some(file_name) = path.file_name().and_then(|s| s.to_str()) {
361             Ok(Self {
362                 path: path.clone(),
363                 alt: alt.trim().to_string(),
364                 file_name: String::from(file_name),
365                 caption,
366                 author: author.trim().to_string(),
367                 author_url: author_url.map(|s| s.trim().to_string()),
368                 license,
369             })
370         } else {
371             Err(D::Error::invalid_value(
372                 Unexpected::Str(&path_str),
373                 &"valid file path (couldn't extract file name)",
374             ))
375         }
376     }
377 }
378 
379 impl Display for LicenseType {
380     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
381         match self.0 {
382             CreativeCommons::CC0 => write!(f, "CC0"),
383             CreativeCommons::CCBY { version, non_commercial, share_alike } => {
384                 write!(f, "CC-BY-")?;
385                 if non_commercial {
386                     write!(f, "NC-")?;
387                 }
388                 if share_alike {
389                     write!(f, "SA-")?;
390                 }
391                 write!(f, "{}", version)
392             },
393             CreativeCommons::CCBYND { version, non_commercial } => {
394                 write!(f, "CC-BY-")?;
395                 if non_commercial {
396                     write!(f, "NC-")?;
397                 }
398                 write!(f, "ND-{}", version)
399             }
400         }
401     }
402 }
403 
404 impl Display for CreativeCommonsVersion {
405     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
406         match self {
407             CreativeCommonsVersion::One   => write!(f, "1.0"),
408             CreativeCommonsVersion::Two   => write!(f, "2.0"),
409             CreativeCommonsVersion::Three => write!(f, "3.0"),
410             CreativeCommonsVersion::Four  => write!(f, "4.0"),
411         }
412     }
413 }