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 }