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 }