stapix

Yet another static page generator for photo galleries

Commit
9c6d0f2fa8c4865a9dcab107f7a2dbf05313d838
Parent
85d61356a8e725f2aab0de5f157dc88cf60b0d66
Author
Pablo <pablo-escobar@riseup.net>
Date

Added support for CC-2.1 and CC-2.5 licenses

Also had to implement a wrapper for printing the URL of licenses to avoid the combinatorial explosion of case analysis

Diffstat

5 files changed, 172 insertions, 249 deletions

Status File Name N° Changes Insertions Deletions
Modified Cargo.lock 2 1 1
Modified Cargo.toml 2 1 1
Modified README.md 14 8 6
Modified src/gallery_entry.rs 399 160 239
Modified src/main.rs 4 2 2
diff --git a/Cargo.lock b/Cargo.lock
@@ -584,7 +584,7 @@ dependencies = [
 
 [[package]]
 name = "stapix"
-version = "0.2.2"
+version = "0.2.3"
 dependencies = [
  "crossterm",
  "image",
diff --git a/Cargo.toml b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "stapix"
-version = "0.2.2"
+version = "0.2.3"
 edition = "2021"
 license = "GPLv3"
 
diff --git a/README.md b/README.md
@@ -59,12 +59,14 @@ Each entry in the list should contain the following fields:
 * **`author`:** The name of the author of the picture
 * **`author-url` (optional):** A URL to a webpage by/on the picture's author
 * **`license`:** The license type of the picture. Should be one of `PD` (public
-  domain), `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` or
-  `CC-BY-NC-ND-4`
+  domain), `CC0`, `CC-BY-1`, `CC-BY-2`, `CC-BY-2.1`, `CC-BY-2.5`, `CC-BY-3`,
+  `CC-BY-4`, `CC-BY-SA-1`, `CC-BY-SA-2`, `CC-BY-SA-2.1`, `CC-BY-SA-2.5`,
+  `CC-BY-SA-3`, `CC-BY-SA-4`, `CC-BY-NC-1`, `CC-BY-NC-2`, `CC-BY-NC-2.1`,
+  `CC-BY-NC-2.5`, `CC-BY-NC-3`, `CC-BY-NC-4`, `CC-BY-NC-SA-1`, `CC-BY-NC-SA-2`,
+  `CC-BY-NC-SA-2.1`, `CC-BY-NC-SA-2.5`, `CC-BY-NC-SA-3`, `CC-BY-NC-SA-4`,
+  `CC-BY-ND-1`, `CC-BY-ND-2`, `CC-BY-ND-2.1`, `CC-BY-ND-2.5`, `CC-BY-ND-3`,
+  `CC-BY-ND-4`, `CC-BY-NC-ND-1`, `CC-BY-NC-ND-2`, `CC-BY-NC-ND-2.1`,
+  `CC-BY-NC-ND-2.5`, `CC-BY-NC-ND-3` or `CC-BY-NC-ND-4`
 
 For best accessibility, the `alt` field should contain a concise visual
 description of the picture in question (including subjects, colors and scenery)
diff --git a/src/gallery_entry.rs b/src/gallery_entry.rs
@@ -9,26 +9,38 @@ const LICENSES: &[&str] = &[
     "CC0",
     "CC-BY-1",
     "CC-BY-2",
+    "CC-BY-2.1",
+    "CC-BY-2.5",
     "CC-BY-3",
     "CC-BY-4",
     "CC-BY-SA-1",
     "CC-BY-SA-2",
+    "CC-BY-SA-2.1",
+    "CC-BY-SA-2.5",
     "CC-BY-SA-3",
     "CC-BY-SA-4",
     "CC-BY-NC-1",
     "CC-BY-NC-2",
+    "CC-BY-NC-2.1",
+    "CC-BY-NC-2.5",
     "CC-BY-NC-3",
     "CC-BY-NC-4",
     "CC-BY-NC-SA-1",
     "CC-BY-NC-SA-2",
+    "CC-BY-NC-SA-2.1",
+    "CC-BY-NC-SA-2.5",
     "CC-BY-NC-SA-3",
     "CC-BY-NC-SA-4",
     "CC-BY-ND-1",
     "CC-BY-ND-2",
+    "CC-BY-ND-2.1",
+    "CC-BY-ND-2.5",
     "CC-BY-ND-3",
     "CC-BY-ND-4",
     "CC-BY-NC-ND-1",
     "CC-BY-NC-ND-2",
+    "CC-BY-NC-ND-2.1",
+    "CC-BY-NC-ND-2.5",
     "CC-BY-NC-ND-3",
     "CC-BY-NC-ND-4",
 ];
@@ -40,286 +52,127 @@ pub struct GalleryEntry {
     pub file_name: String,
     pub alt: String,
     pub caption: Option<String>,
-    pub license: Option<LicenseType>,
+    pub license: LicenseType,
     pub author: String,
     pub author_url: Option<String>,
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub struct LicenseType(CreativeCommons);
+pub enum LicenseType {
+    PublicDomain,
+    Cc(CreativeCommons),
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct CreativeCommons(CcInternal);
+
+#[derive(Debug, Clone, Copy)]
+/// Wrapper for printing the url of a Creative Commons license
+pub struct LicenseUrl(CcInternal);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
-enum CreativeCommons {
+enum CcInternal {
     /// Creative Commons (without attribution)
     Cc0,
     /// Creative Commons Attributions (derivatives allowed)
     CcBy {
-        version: CreativeCommonsVersion,
+        version: CcVersion,
         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,
+        version: CcVersion,
         non_commercial: bool,
     },
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
-enum CreativeCommonsVersion {
+enum CcVersion {
     One,
     Two,
+    TwoOne,
+    TwoFive,
     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/",
-        }
+impl CreativeCommons {
+    pub const fn url(&self) -> LicenseUrl {
+        LicenseUrl(self.0)
     }
+}
 
-    pub fn parse(s: &str) -> Result<Option<Self>, ()> {
+impl LicenseType {
+    pub fn parse(s: &str) -> Result<Self, ()> {
         if !LICENSES.contains(&s) {
             return Err(());
         }
 
         if s == "PD" {
-            return Ok(None);
+            return Ok(Self::PublicDomain);
         } else if s == "CC0" {
-            return Ok(Some(Self(CreativeCommons::Cc0)));
+            return Ok(Self::Cc(CreativeCommons(CcInternal::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,
-            _   => {
+        let (license, version) = s.rsplit_once('-').ok_or(())?;
+
+        let version = match version {
+            "1"   => CcVersion::One,
+            "2"   => CcVersion::Two,
+            "2.1" => CcVersion::TwoOne,
+            "2.5" => CcVersion::TwoFive,
+            "3"   => CcVersion::Three,
+            "4"   => CcVersion::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,
-                            }
-                        )
-                    )
-                )
+        match license {
+            "CC-BY" => {
+                Ok(Self::Cc(CreativeCommons(CcInternal::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-NC" => {
+                Ok(Self::Cc(CreativeCommons(CcInternal::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-SA" => {
+                Ok(Self::Cc(CreativeCommons(CcInternal::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-NC-SA" => {
+                Ok(Self::Cc(CreativeCommons(CcInternal::CcBy {
+                    version,
+                    non_commercial: true,
+                    share_alike: true,
+                })))
             },
-            "CC-BY-ND-" => {
-                Ok(
-                    Some(
-                        Self(
-                            CreativeCommons::CcByNd {
-                                version,
-                                non_commercial: false,
-                            }
-                        )
-                    )
-                )
+            "CC-BY-ND" => {
+                Ok(Self::Cc(CreativeCommons(CcInternal::CcByNd {
+                    version,
+                    non_commercial: false,
+                })))
             },
-            "CC-BY-NC-ND-" => {
-                Ok(
-                    Some(
-                        Self(
-                            CreativeCommons::CcByNd {
-                                version,
-                                non_commercial: true,
-                            }
-                        )
-                    )
-                )
+            "CC-BY-NC-ND" => {
+                Ok(Self::Cc(CreativeCommons(CcInternal::CcByNd {
+                    version,
+                    non_commercial: true,
+                })))
             },
             _ => {
                 unreachable!("if s is in LICENSES we should be able to parse the license-type")
@@ -376,11 +229,11 @@ impl<'de> Deserialize<'de> for GalleryEntry {
     }
 }
 
-impl Display for LicenseType {
+impl Display for CreativeCommons {
     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 } => {
+            CcInternal::Cc0 => write!(f, "CC0"),
+            CcInternal::CcBy { version, non_commercial, share_alike } => {
                 write!(f, "CC-BY")?;
                 if non_commercial {
                     write!(f, "-NC")?;
@@ -390,7 +243,7 @@ impl Display for LicenseType {
                 }
                 write!(f, " {version}")
             },
-            CreativeCommons::CcByNd { version, non_commercial } => {
+            CcInternal::CcByNd { version, non_commercial } => {
                 write!(f, "CC-BY")?;
                 if non_commercial {
                     write!(f, "-NC")?;
@@ -401,13 +254,81 @@ impl Display for LicenseType {
     }
 }
 
-impl Display for CreativeCommonsVersion {
+impl Display for CcVersion {
     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"),
+            CcVersion::One     => write!(f, "1.0"),
+            CcVersion::Two     => write!(f, "2.0"),
+            CcVersion::TwoOne  => write!(f, "2.1"),
+            CcVersion::TwoFive => write!(f, "2.5"),
+            CcVersion::Three   => write!(f, "3.0"),
+            CcVersion::Four    => write!(f, "4.0"),
+        }
+    }
+}
+
+impl Display for LicenseUrl {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+        match self.0 {
+            // CC0
+            CcInternal::Cc0 => {
+                write!(f, "https://creativecommons.org/publicdomain/zero/1.0/")
+            },
+            // CC-BY-x
+            CcInternal::CcBy {
+                version,
+                non_commercial: false,
+                share_alike: false,
+            } => {
+                write!(f, "http://creativecommons.org/licenses/by/{version}/")
+            }
+            // CC-BY-SA-x
+            CcInternal::CcBy {
+                version,
+                non_commercial: false,
+                share_alike: true,
+            } => {
+                write!(
+                    f,
+                    "http://creativecommons.org/licenses/by-sa/{version}/"
+                )
+            }
+            // CC-BY-NC-x
+            CcInternal::CcBy {
+                version,
+                non_commercial: true,
+                share_alike: false,
+            } => {
+                write!(
+                    f,
+                    "http://creativecommons.org/licenses/by-nc/{version}/"
+                )
+            }
+            // CC-BY-NC-SA-x
+            CcInternal::CcBy {
+                version,
+                non_commercial: true,
+                share_alike: true,
+            } => {
+                write!(
+                    f,
+                    "http://creativecommons.org/licenses/by-nc-sa/{version}/"
+                )
+            }
+            // CC-BY-ND-x
+            CcInternal::CcByNd { version, non_commercial: false, } => {
+                write!(
+                    f,
+                    "http://creativecommons.org/licenses/by-nd/{version}/"
+                )
+            }
+            // CC-BY-NC-ND-x
+            CcInternal::CcByNd { version, non_commercial: true, } => {
+                write!(
+                    f,
+                    "http://creativecommons.org/licenses/by-nc-nd/{version}/"
+                )
+            }
         }
     }
 }
diff --git a/src/main.rs b/src/main.rs
@@ -10,7 +10,7 @@ use std::{
     process::ExitCode,
     sync::mpsc,
 };
-use gallery_entry::GalleryEntry;
+use gallery_entry::{GalleryEntry, LicenseType};
 use threadpool::ThreadPool;
 
 mod gallery_entry;
@@ -370,7 +370,7 @@ fn render_pic_page(pic: &GalleryEntry) -> io::Result<()> {
         writeln!(f, "{}", Escaped(&pic.author))?;
     }
     writeln!(f, "<br>")?;
-    if let Some(license) = &pic.license {
+    if let LicenseType::Cc(license) = &pic.license {
         writeln!(f, "licensed under <a role=\"license\" href=\"{url}\">{license}</a>",
                  url = license.url())?;
     } else {