tikz-gallery-generator

Custum build of stapix for tikz.pablopie.xyz

gallery_entry.rs (10631B)

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