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