tikz-gallery-generator

Custum build of stapix for tikz.pablopie.xyz

Commit
b68e180a1f3e8c9ff6e012df1b4498b6b3de24f1
Parent
ccdb0dcabc52c30aeffde118f2a72e90c377e201
Author
Pablo <pablo-escobar@riseup.net>
Date

Renamed the Picture type to GalleryEntry

Also renamed the corresponding module

Diffstat

3 files changed, 421 insertions, 421 deletions

Status File Name N° Changes Insertions Deletions
Added src/gallery_entry.rs 413 413 0
Modified src/main.rs 16 8 8
Deleted src/picture.rs 413 0 413
diff --git a/src/gallery_entry.rs b/src/gallery_entry.rs
@@ -0,0 +1,413 @@
+use serde::{
+    de::{Deserializer, Error, Unexpected},
+    Deserialize,
+};
+use std::{fmt::{self, Display}, path::PathBuf};
+
+const LICENSES: &[&str] = &[
+    "PD",
+    "CC0",
+    "CC-BY-1",
+    "CC-BY-2",
+    "CC-BY-3",
+    "CC-BY-4",
+    "CC-BY-SA-1",
+    "CC-BY-SA-2",
+    "CC-BY-SA-3",
+    "CC-BY-SA-4",
+    "CC-BY-NC-1",
+    "CC-BY-NC-2",
+    "CC-BY-NC-3",
+    "CC-BY-NC-4",
+    "CC-BY-NC-SA-1",
+    "CC-BY-NC-SA-2",
+    "CC-BY-NC-SA-3",
+    "CC-BY-NC-SA-4",
+    "CC-BY-ND-1",
+    "CC-BY-ND-2",
+    "CC-BY-ND-3",
+    "CC-BY-ND-4",
+    "CC-BY-NC-ND-1",
+    "CC-BY-NC-ND-2",
+    "CC-BY-NC-ND-3",
+    "CC-BY-NC-ND-4",
+];
+
+/// Info on a individual entry on the gallery
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct GalleryEntry {
+    pub path: PathBuf,
+    pub file_name: String,
+    pub alt: String,
+    pub caption: Option<String>,
+    pub license: Option<LicenseType>,
+    pub author: String,
+    pub author_url: Option<String>,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct LicenseType(CreativeCommons);
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+enum CreativeCommons {
+    /// Creative Commons (without attribution)
+    Cc0,
+    /// Creative Commons Attributions (derivatives allowed)
+    CcBy {
+        version: CreativeCommonsVersion,
+        non_commercial: bool,
+        share_alike: bool,
+    },
+    // The ND (non-derivatives) option excludes the SA (share alike) option
+    /// Creative Commons Attributions Non-Derivatives
+    CcByNd {
+        version: CreativeCommonsVersion,
+        non_commercial: bool,
+    },
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+enum CreativeCommonsVersion {
+    One,
+    Two,
+    Three,
+    Four,
+}
+
+impl LicenseType {
+    pub const fn url(&self) -> &'static str {
+        match self.0 {
+            // CC0
+            CreativeCommons::Cc0 => {
+                "https://creativecommons.org/publicdomain/zero/1.0/"
+            },
+            // CC-BY-1
+            CreativeCommons::CcBy {
+                version: CreativeCommonsVersion::One,
+                non_commercial: false,
+                share_alike: false,
+            } => "http://creativecommons.org/licenses/by/1.0/",
+            // CC-BY-2
+            CreativeCommons::CcBy {
+                version: CreativeCommonsVersion::Two,
+                non_commercial: false,
+                share_alike: false,
+            } => "http://creativecommons.org/licenses/by/2.0/",
+            // CC-BY-3
+            CreativeCommons::CcBy {
+                version: CreativeCommonsVersion::Three,
+                non_commercial: false,
+                share_alike: false,
+            } => "http://creativecommons.org/licenses/by/3.0/",
+            // CC-BY-4
+            CreativeCommons::CcBy {
+                version: CreativeCommonsVersion::Four,
+                non_commercial: false,
+                share_alike: false,
+            } => "http://creativecommons.org/licenses/by/4.0/",
+            // CC-BY-SA-1
+            CreativeCommons::CcBy {
+                version: CreativeCommonsVersion::One,
+                non_commercial: false,
+                share_alike: true,
+            } => "http://creativecommons.org/licenses/by-sa/1.0/",
+            // CC-BY-SA-2
+            CreativeCommons::CcBy {
+                version: CreativeCommonsVersion::Two,
+                non_commercial: false,
+                share_alike: true,
+            } => "http://creativecommons.org/licenses/by-sa/2.0/",
+            // CC-BY-SA-3
+            CreativeCommons::CcBy {
+                version: CreativeCommonsVersion::Three,
+                non_commercial: false,
+                share_alike: true,
+            } => "http://creativecommons.org/licenses/by-sa/3.0/",
+            // CC-BY-SA-4
+            CreativeCommons::CcBy {
+                version: CreativeCommonsVersion::Four,
+                non_commercial: false,
+                share_alike: true,
+            } => "http://creativecommons.org/licenses/by-sa/4.0/",
+            // CC-BY-NC-1
+            CreativeCommons::CcBy {
+                version: CreativeCommonsVersion::One,
+                non_commercial: true,
+                share_alike: false,
+            } => "http://creativecommons.org/licenses/by-nc/1.0/",
+            // CC-BY-NC-2
+            CreativeCommons::CcBy {
+                version: CreativeCommonsVersion::Two,
+                non_commercial: true,
+                share_alike: false,
+            } => "http://creativecommons.org/licenses/by-nc/2.0/",
+            // CC-BY-NC-3
+            CreativeCommons::CcBy {
+                version: CreativeCommonsVersion::Three,
+                non_commercial: true,
+                share_alike: false,
+            } => "http://creativecommons.org/licenses/by-nc/3.0/",
+            // CC-BY-NC-4
+            CreativeCommons::CcBy {
+                version: CreativeCommonsVersion::Four,
+                non_commercial: true,
+                share_alike: false,
+            } => "http://creativecommons.org/licenses/by-nc/4.0/",
+            // CC-BY-NC-SA-1
+            CreativeCommons::CcBy {
+                version: CreativeCommonsVersion::One,
+                non_commercial: true,
+                share_alike: true,
+            } => "http://creativecommons.org/licenses/by-nc-sa/1.0/",
+            // CC-BY-NC-SA-2
+            CreativeCommons::CcBy {
+                version: CreativeCommonsVersion::Two,
+                non_commercial: true,
+                share_alike: true,
+            } => "http://creativecommons.org/licenses/by-nc-sa/2.0/",
+            // CC-BY-NC-SA-3
+            CreativeCommons::CcBy {
+                version: CreativeCommonsVersion::Three,
+                non_commercial: true,
+                share_alike: true,
+            } => "http://creativecommons.org/licenses/by-nc-sa/3.0/",
+            // CC-BY-NC-SA-4
+            CreativeCommons::CcBy {
+                version: CreativeCommonsVersion::Four,
+                non_commercial: true,
+                share_alike: true,
+            } => "http://creativecommons.org/licenses/by-nc-sa/4.0/",
+            // CC-BY-ND-1
+            CreativeCommons::CcByNd {
+                version: CreativeCommonsVersion::One,
+                non_commercial: false,
+            } => "http://creativecommons.org/licenses/by-nd/1.0/",
+            // CC-BY-ND-2
+            CreativeCommons::CcByNd {
+                version: CreativeCommonsVersion::Two,
+                non_commercial: false,
+            } => "http://creativecommons.org/licenses/by-nd/2.0/",
+            // CC-BY-ND-3
+            CreativeCommons::CcByNd {
+                version: CreativeCommonsVersion::Three,
+                non_commercial: false,
+            } => "http://creativecommons.org/licenses/by-nd/3.0/",
+            // CC-BY-ND-4
+            CreativeCommons::CcByNd {
+                version: CreativeCommonsVersion::Four,
+                non_commercial: false,
+            } => "http://creativecommons.org/licenses/by-nd/4.0/",
+            // CC-BY-NC-ND-1
+            CreativeCommons::CcByNd {
+                version: CreativeCommonsVersion::One,
+                non_commercial: true,
+            } => "http://creativecommons.org/licenses/by-nc-nd/1.0/",
+            // CC-BY-NC-ND-2
+            CreativeCommons::CcByNd {
+                version: CreativeCommonsVersion::Two,
+                non_commercial: true,
+            } => "http://creativecommons.org/licenses/by-nc-nd/2.0/",
+            // CC-BY-NC-ND-3
+            CreativeCommons::CcByNd {
+                version: CreativeCommonsVersion::Three,
+                non_commercial: true,
+            } => "http://creativecommons.org/licenses/by-nc-nd/3.0/",
+            // CC-BY-NC-ND-4
+            CreativeCommons::CcByNd {
+                version: CreativeCommonsVersion::Four,
+                non_commercial: true,
+            } => "http://creativecommons.org/licenses/by-nc-nd/4.0/",
+        }
+    }
+
+    pub fn parse(s: &str) -> Result<Option<Self>, ()> {
+        if !LICENSES.contains(&s) {
+            return Err(());
+        }
+
+        if s == "PD" {
+            return Ok(None);
+        } else if s == "CC0" {
+            return Ok(Some(Self(CreativeCommons::Cc0)));
+        }
+
+        assert!(s.len() >= 3,
+                "if s is in LICENSES it should contain at least 3 chars");
+
+        let version = match &s[s.len() - 1..] {
+            "1" => CreativeCommonsVersion::One,
+            "2" => CreativeCommonsVersion::Two,
+            "3" => CreativeCommonsVersion::Three,
+            "4" => CreativeCommonsVersion::Four,
+            _   => {
+                unreachable!("if s is in LICENSES we should be able to parse the license version")
+            },
+        };
+
+        match &s[..s.len() - 1] {
+            "CC-BY-" => {
+                Ok(
+                    Some(
+                        Self(
+                            CreativeCommons::CcBy {
+                                version,
+                                non_commercial: false,
+                                share_alike: false,
+                            }
+                        )
+                    )
+                )
+            },
+            "CC-BY-NC-" => {
+                Ok(
+                    Some(
+                        Self(
+                            CreativeCommons::CcBy {
+                                version,
+                                non_commercial: true,
+                                share_alike: false,
+                            }
+                        )
+                    )
+                )
+            },
+            "CC-BY-SA-" => {
+                Ok(
+                    Some(
+                        Self(
+                            CreativeCommons::CcBy {
+                                version,
+                                non_commercial: false,
+                                share_alike: true,
+                            }
+                        )
+                    )
+                )
+            },
+            "CC-BY-NC-SA-" => {
+                Ok(
+                    Some(
+                        Self(
+                            CreativeCommons::CcBy {
+                                version,
+                                non_commercial: true,
+                                share_alike: true,
+                            }
+                        )
+                    )
+                )
+            },
+            "CC-BY-ND-" => {
+                Ok(
+                    Some(
+                        Self(
+                            CreativeCommons::CcByNd {
+                                version,
+                                non_commercial: false,
+                            }
+                        )
+                    )
+                )
+            },
+            "CC-BY-NC-ND-" => {
+                Ok(
+                    Some(
+                        Self(
+                            CreativeCommons::CcByNd {
+                                version,
+                                non_commercial: true,
+                            }
+                        )
+                    )
+                )
+            },
+            _ => {
+                unreachable!("if s is in LICENSES we should be able to parse the license-type")
+            },
+        }
+    }
+}
+
+impl<'de> Deserialize<'de> for GalleryEntry {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        #[derive(Deserialize)]
+        struct Info {
+            path: String,
+            alt: String,
+            caption: Option<String>,
+            license: String,
+            author: String,
+            #[serde(alias = "author-url")]
+            author_url: Option<String>,
+        }
+
+        let Info {
+            path: path_str,
+            alt,
+            caption,
+            license,
+            author,
+            author_url,
+        } = Info::deserialize(deserializer)?;
+
+        let license = LicenseType::parse(&license)
+            .map_err(|_| D::Error::unknown_variant(&license, LICENSES))?;
+        let path = PathBuf::from(&path_str);
+
+        if let Some(file_name) = path.file_name().and_then(|s| s.to_str()) {
+            Ok(Self {
+                path: path.clone(),
+                alt: alt.trim().to_string(),
+                file_name: String::from(file_name),
+                caption,
+                author: author.trim().to_string(),
+                author_url: author_url.map(|s| s.trim().to_string()),
+                license,
+            })
+        } else {
+            Err(D::Error::invalid_value(
+                Unexpected::Str(&path_str),
+                &"valid file path (couldn't extract file name)",
+            ))
+        }
+    }
+}
+
+impl Display for LicenseType {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+        match self.0 {
+            CreativeCommons::Cc0 => write!(f, "CC0"),
+            CreativeCommons::CcBy { version, non_commercial, share_alike } => {
+                write!(f, "CC-BY")?;
+                if non_commercial {
+                    write!(f, "-NC")?;
+                }
+                if share_alike {
+                    write!(f, "-SA")?;
+                }
+                write!(f, " {version}")
+            },
+            CreativeCommons::CcByNd { version, non_commercial } => {
+                write!(f, "CC-BY")?;
+                if non_commercial {
+                    write!(f, "-NC")?;
+                }
+                write!(f, "-ND {version}")
+            }
+        }
+    }
+}
+
+impl Display for CreativeCommonsVersion {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+        match self {
+            CreativeCommonsVersion::One   => write!(f, "1.0"),
+            CreativeCommonsVersion::Two   => write!(f, "2.0"),
+            CreativeCommonsVersion::Three => write!(f, "3.0"),
+            CreativeCommonsVersion::Four  => write!(f, "4.0"),
+        }
+    }
+}
diff --git a/src/main.rs b/src/main.rs
@@ -10,10 +10,10 @@ use std::{
     process::ExitCode,
     sync::mpsc,
 };
-use picture::Picture;
+use gallery_entry::GalleryEntry;
 use threadpool::ThreadPool;
 
-mod picture;
+mod gallery_entry;
 
 /// A wrapper for HTML-escaped strings
 pub struct Escaped<'a>(pub &'a str);
@@ -151,7 +151,7 @@ fn main() -> ExitCode {
     };
 
     let f = File::open(config);
-    match f.map(serde_yaml::from_reader::<_, Vec<Picture>>) {
+    match f.map(serde_yaml::from_reader::<_, Vec<GalleryEntry>>) {
         // Error opening the config file
         Err(err) => {
             errorln!("Couldn't open {config:?}: {err}",
@@ -171,7 +171,7 @@ fn main() -> ExitCode {
 }
 
 /// Coordinates the rendering of all the pages and file conversions
-fn render_gallery(pics: Vec<Picture>) -> ExitCode {
+fn render_gallery(pics: Vec<GalleryEntry>) -> ExitCode {
     log!("Copying image files to the target directory...");
 
     for pic in &pics {
@@ -244,7 +244,7 @@ fn render_gallery(pics: Vec<Picture>) -> ExitCode {
     ExitCode::SUCCESS
 }
 
-fn render_index(pics: &Vec<Picture>) -> io::Result<()> {
+fn render_index(pics: &Vec<GalleryEntry>) -> io::Result<()> {
     let mut path = PathBuf::from(TARGET_PATH);
     path.push("index.html");
 
@@ -257,7 +257,7 @@ fn render_index(pics: &Vec<Picture>) -> io::Result<()> {
     writeln!(f, "<title>{PAGE_TITLE}</title>")?;
     write_head(&mut f)?;
 
-    for pic in pics.into_iter().take(10) {
+    for pic in pics.iter().take(10) {
         // TODO: Preload mp4 thumbnails for GIF files
         writeln!(
             f,
@@ -304,7 +304,7 @@ fn render_index(pics: &Vec<Picture>) -> io::Result<()> {
     writeln!(f, "</html>")
 }
 
-fn render_pic_page(pic: &Picture) -> io::Result<()> {
+fn render_pic_page(pic: &GalleryEntry) -> io::Result<()> {
     let mut path = PathBuf::from(TARGET_PATH);
     path.push(PAGES_PATH);
     path.push(pic.file_name.clone() + ".html");
@@ -448,7 +448,7 @@ fn write_license(f: &mut File) -> io::Result<()> {
 /// Returns `true` if rendering succeded (or was skipped) and `false`
 /// otherwise. All warinings and error messages should be logged in here.
 // TODO: Render GIF files as mp4 instead
-fn render_thumbnail(pic: Picture) -> bool {
+fn render_thumbnail(pic: GalleryEntry) -> bool {
     let mut thumb_path = PathBuf::from(TARGET_PATH);
     thumb_path.push(THUMBS_PATH);
     thumb_path.push(pic.file_name.clone() + ".webp");
diff --git a/src/picture.rs b/src/picture.rs
@@ -1,413 +0,0 @@
-use serde::{
-    de::{Deserializer, Error, Unexpected},
-    Deserialize,
-};
-use std::{fmt::{self, Display}, path::PathBuf};
-
-const LICENSES: &'static [&'static str] = &[
-    "PD",
-    "CC0",
-    "CC-BY-1",
-    "CC-BY-2",
-    "CC-BY-3",
-    "CC-BY-4",
-    "CC-BY-SA-1",
-    "CC-BY-SA-2",
-    "CC-BY-SA-3",
-    "CC-BY-SA-4",
-    "CC-BY-NC-1",
-    "CC-BY-NC-2",
-    "CC-BY-NC-3",
-    "CC-BY-NC-4",
-    "CC-BY-NC-SA-1",
-    "CC-BY-NC-SA-2",
-    "CC-BY-NC-SA-3",
-    "CC-BY-NC-SA-4",
-    "CC-BY-ND-1",
-    "CC-BY-ND-2",
-    "CC-BY-ND-3",
-    "CC-BY-ND-4",
-    "CC-BY-NC-ND-1",
-    "CC-BY-NC-ND-2",
-    "CC-BY-NC-ND-3",
-    "CC-BY-NC-ND-4",
-];
-
-/// Info on a individual entry on the gallery
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct Picture {
-    pub path: PathBuf,
-    pub file_name: String,
-    pub alt: String,
-    pub caption: Option<String>,
-    pub license: Option<LicenseType>,
-    pub author: String,
-    pub author_url: Option<String>,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub struct LicenseType(CreativeCommons);
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-enum CreativeCommons {
-    /// Creative Commons (without attribution)
-    Cc0,
-    /// Creative Commons Attributions (derivatives allowed)
-    CcBy {
-        version: CreativeCommonsVersion,
-        non_commercial: bool,
-        share_alike: bool,
-    },
-    // The ND (non-derivatives) option excludes the SA (share alike) option
-    /// Creative Commons Attributions Non-Derivatives
-    CcByNd {
-        version: CreativeCommonsVersion,
-        non_commercial: bool,
-    },
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-enum CreativeCommonsVersion {
-    One,
-    Two,
-    Three,
-    Four,
-}
-
-impl LicenseType {
-    pub const fn url(&self) -> &'static str {
-        match self.0 {
-            // CC0
-            CreativeCommons::Cc0 => {
-                "https://creativecommons.org/publicdomain/zero/1.0/"
-            },
-            // CC-BY-1
-            CreativeCommons::CcBy {
-                version: CreativeCommonsVersion::One,
-                non_commercial: false,
-                share_alike: false,
-            } => "http://creativecommons.org/licenses/by/1.0/",
-            // CC-BY-2
-            CreativeCommons::CcBy {
-                version: CreativeCommonsVersion::Two,
-                non_commercial: false,
-                share_alike: false,
-            } => "http://creativecommons.org/licenses/by/2.0/",
-            // CC-BY-3
-            CreativeCommons::CcBy {
-                version: CreativeCommonsVersion::Three,
-                non_commercial: false,
-                share_alike: false,
-            } => "http://creativecommons.org/licenses/by/3.0/",
-            // CC-BY-4
-            CreativeCommons::CcBy {
-                version: CreativeCommonsVersion::Four,
-                non_commercial: false,
-                share_alike: false,
-            } => "http://creativecommons.org/licenses/by/4.0/",
-            // CC-BY-SA-1
-            CreativeCommons::CcBy {
-                version: CreativeCommonsVersion::One,
-                non_commercial: false,
-                share_alike: true,
-            } => "http://creativecommons.org/licenses/by-sa/1.0/",
-            // CC-BY-SA-2
-            CreativeCommons::CcBy {
-                version: CreativeCommonsVersion::Two,
-                non_commercial: false,
-                share_alike: true,
-            } => "http://creativecommons.org/licenses/by-sa/2.0/",
-            // CC-BY-SA-3
-            CreativeCommons::CcBy {
-                version: CreativeCommonsVersion::Three,
-                non_commercial: false,
-                share_alike: true,
-            } => "http://creativecommons.org/licenses/by-sa/3.0/",
-            // CC-BY-SA-4
-            CreativeCommons::CcBy {
-                version: CreativeCommonsVersion::Four,
-                non_commercial: false,
-                share_alike: true,
-            } => "http://creativecommons.org/licenses/by-sa/4.0/",
-            // CC-BY-NC-1
-            CreativeCommons::CcBy {
-                version: CreativeCommonsVersion::One,
-                non_commercial: true,
-                share_alike: false,
-            } => "http://creativecommons.org/licenses/by-nc/1.0/",
-            // CC-BY-NC-2
-            CreativeCommons::CcBy {
-                version: CreativeCommonsVersion::Two,
-                non_commercial: true,
-                share_alike: false,
-            } => "http://creativecommons.org/licenses/by-nc/2.0/",
-            // CC-BY-NC-3
-            CreativeCommons::CcBy {
-                version: CreativeCommonsVersion::Three,
-                non_commercial: true,
-                share_alike: false,
-            } => "http://creativecommons.org/licenses/by-nc/3.0/",
-            // CC-BY-NC-4
-            CreativeCommons::CcBy {
-                version: CreativeCommonsVersion::Four,
-                non_commercial: true,
-                share_alike: false,
-            } => "http://creativecommons.org/licenses/by-nc/4.0/",
-            // CC-BY-NC-SA-1
-            CreativeCommons::CcBy {
-                version: CreativeCommonsVersion::One,
-                non_commercial: true,
-                share_alike: true,
-            } => "http://creativecommons.org/licenses/by-nc-sa/1.0/",
-            // CC-BY-NC-SA-2
-            CreativeCommons::CcBy {
-                version: CreativeCommonsVersion::Two,
-                non_commercial: true,
-                share_alike: true,
-            } => "http://creativecommons.org/licenses/by-nc-sa/2.0/",
-            // CC-BY-NC-SA-3
-            CreativeCommons::CcBy {
-                version: CreativeCommonsVersion::Three,
-                non_commercial: true,
-                share_alike: true,
-            } => "http://creativecommons.org/licenses/by-nc-sa/3.0/",
-            // CC-BY-NC-SA-4
-            CreativeCommons::CcBy {
-                version: CreativeCommonsVersion::Four,
-                non_commercial: true,
-                share_alike: true,
-            } => "http://creativecommons.org/licenses/by-nc-sa/4.0/",
-            // CC-BY-ND-1
-            CreativeCommons::CcByNd {
-                version: CreativeCommonsVersion::One,
-                non_commercial: false,
-            } => "http://creativecommons.org/licenses/by-nd/1.0/",
-            // CC-BY-ND-2
-            CreativeCommons::CcByNd {
-                version: CreativeCommonsVersion::Two,
-                non_commercial: false,
-            } => "http://creativecommons.org/licenses/by-nd/2.0/",
-            // CC-BY-ND-3
-            CreativeCommons::CcByNd {
-                version: CreativeCommonsVersion::Three,
-                non_commercial: false,
-            } => "http://creativecommons.org/licenses/by-nd/3.0/",
-            // CC-BY-ND-4
-            CreativeCommons::CcByNd {
-                version: CreativeCommonsVersion::Four,
-                non_commercial: false,
-            } => "http://creativecommons.org/licenses/by-nd/4.0/",
-            // CC-BY-NC-ND-1
-            CreativeCommons::CcByNd {
-                version: CreativeCommonsVersion::One,
-                non_commercial: true,
-            } => "http://creativecommons.org/licenses/by-nc-nd/1.0/",
-            // CC-BY-NC-ND-2
-            CreativeCommons::CcByNd {
-                version: CreativeCommonsVersion::Two,
-                non_commercial: true,
-            } => "http://creativecommons.org/licenses/by-nc-nd/2.0/",
-            // CC-BY-NC-ND-3
-            CreativeCommons::CcByNd {
-                version: CreativeCommonsVersion::Three,
-                non_commercial: true,
-            } => "http://creativecommons.org/licenses/by-nc-nd/3.0/",
-            // CC-BY-NC-ND-4
-            CreativeCommons::CcByNd {
-                version: CreativeCommonsVersion::Four,
-                non_commercial: true,
-            } => "http://creativecommons.org/licenses/by-nc-nd/4.0/",
-        }
-    }
-
-    pub fn parse(s: &str) -> Result<Option<Self>, ()> {
-        if !LICENSES.contains(&s) {
-            return Err(());
-        }
-
-        if s == "PD" {
-            return Ok(None);
-        } else if s == "CC0" {
-            return Ok(Some(Self(CreativeCommons::Cc0)));
-        }
-
-        assert!(s.len() >= 3,
-                "if s is in LICENSES it should contain at least 3 chars");
-
-        let version = match &s[s.len() - 1..] {
-            "1" => CreativeCommonsVersion::One,
-            "2" => CreativeCommonsVersion::Two,
-            "3" => CreativeCommonsVersion::Three,
-            "4" => CreativeCommonsVersion::Four,
-            _   => {
-                unreachable!("if s is in LICENSES we should be able to parse the license version")
-            },
-        };
-
-        match &s[..s.len() - 1] {
-            "CC-BY-" => {
-                Ok(
-                    Some(
-                        Self(
-                            CreativeCommons::CcBy {
-                                version,
-                                non_commercial: false,
-                                share_alike: false,
-                            }
-                        )
-                    )
-                )
-            },
-            "CC-BY-NC-" => {
-                Ok(
-                    Some(
-                        Self(
-                            CreativeCommons::CcBy {
-                                version,
-                                non_commercial: true,
-                                share_alike: false,
-                            }
-                        )
-                    )
-                )
-            },
-            "CC-BY-SA-" => {
-                Ok(
-                    Some(
-                        Self(
-                            CreativeCommons::CcBy {
-                                version,
-                                non_commercial: false,
-                                share_alike: true,
-                            }
-                        )
-                    )
-                )
-            },
-            "CC-BY-NC-SA-" => {
-                Ok(
-                    Some(
-                        Self(
-                            CreativeCommons::CcBy {
-                                version,
-                                non_commercial: true,
-                                share_alike: true,
-                            }
-                        )
-                    )
-                )
-            },
-            "CC-BY-ND-" => {
-                Ok(
-                    Some(
-                        Self(
-                            CreativeCommons::CcByNd {
-                                version,
-                                non_commercial: false,
-                            }
-                        )
-                    )
-                )
-            },
-            "CC-BY-NC-ND-" => {
-                Ok(
-                    Some(
-                        Self(
-                            CreativeCommons::CcByNd {
-                                version,
-                                non_commercial: true,
-                            }
-                        )
-                    )
-                )
-            },
-            _ => {
-                unreachable!("if s is in LICENSES we should be able to parse the license-type")
-            },
-        }
-    }
-}
-
-impl<'de> Deserialize<'de> for Picture {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-    where
-        D: Deserializer<'de>,
-    {
-        #[derive(Deserialize)]
-        struct Info {
-            path: String,
-            alt: String,
-            caption: Option<String>,
-            license: String,
-            author: String,
-            #[serde(alias = "author-url")]
-            author_url: Option<String>,
-        }
-
-        let Info {
-            path: path_str,
-            alt,
-            caption,
-            license,
-            author,
-            author_url,
-        } = Info::deserialize(deserializer)?;
-
-        let license = LicenseType::parse(&license)
-            .map_err(|_| D::Error::unknown_variant(&license, LICENSES))?;
-        let path = PathBuf::from(&path_str);
-
-        if let Some(file_name) = path.file_name().and_then(|s| s.to_str()) {
-            Ok(Self {
-                path: path.clone(),
-                alt: alt.trim().to_string(),
-                file_name: String::from(file_name),
-                caption,
-                author: author.trim().to_string(),
-                author_url: author_url.map(|s| s.trim().to_string()),
-                license,
-            })
-        } else {
-            Err(D::Error::invalid_value(
-                Unexpected::Str(&path_str),
-                &"valid file path (couldn't extract file name)",
-            ))
-        }
-    }
-}
-
-impl Display for LicenseType {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
-        match self.0 {
-            CreativeCommons::Cc0 => write!(f, "CC0"),
-            CreativeCommons::CcBy { version, non_commercial, share_alike } => {
-                write!(f, "CC-BY")?;
-                if non_commercial {
-                    write!(f, "-NC")?;
-                }
-                if share_alike {
-                    write!(f, "-SA")?;
-                }
-                write!(f, " {version}")
-            },
-            CreativeCommons::CcByNd { version, non_commercial } => {
-                write!(f, "CC-BY")?;
-                if non_commercial {
-                    write!(f, "-NC")?;
-                }
-                write!(f, "-ND {version}")
-            }
-        }
-    }
-}
-
-impl Display for CreativeCommonsVersion {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
-        match self {
-            CreativeCommonsVersion::One   => write!(f, "1.0"),
-            CreativeCommonsVersion::Two   => write!(f, "2.0"),
-            CreativeCommonsVersion::Three => write!(f, "3.0"),
-            CreativeCommonsVersion::Four  => write!(f, "4.0"),
-        }
-    }
-}