stapix

Yet another static page generator for photo galleries

gallery_entry.rs (9418B)

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