use structopt::StructOpt; use serde::Deserialize; use std::collections::HashMap; use std::fs; use std::io; use std::path::PathBuf; #[derive(Debug, StructOpt)] #[structopt(name = "gallery")] pub struct Options { #[structopt(long, help = "Just a thing")] pub check: bool, #[structopt(short, long, help = "Config file location")] pub config: Option, #[structopt(help = "Picture location")] pub load: PathBuf, #[structopt(short, long, default_value = "png", help = "Image extension to use for converted files")] pub ext: String, #[structopt(long, short, default_value = "build", help = "Where to build site")] pub builddir: PathBuf, #[structopt(long, default_value = "1080", help = "Scaled size for image")] pub size_scaled: u32, #[structopt(long, default_value = "720", help = "Thumbnail size for image")] pub size_thumb: u32, } #[derive(Debug)] pub enum ConfigError { Reading(io::Error), Parsing(serde_yaml::Error), CompilePattern(glob::PatternError), } #[derive(Deserialize, Debug)] pub struct Config { pub imageglob: String, pub info: HashMap, #[serde(skip)] pub imageglob_compiled: glob::Pattern, } #[derive(Debug)] pub struct Context { pub options: Options, pub config: Config, pub imgdir: PathBuf, } impl Context { pub fn new_with_args() -> Result { let opts = Options::from_args(); let config = Config::load_with_options(&opts)?; let imgdir = opts.builddir.join("imgs"); // Create img dir if let Err(err) = fs::create_dir(&imgdir) { if err.kind() != io::ErrorKind::AlreadyExists { return Err(ConfigError::from(err)); } } Ok(Context { options: opts, config: config, imgdir: imgdir, }) } pub fn get_image_files(&self) -> Result, io::Error> { let files = fs::read_dir(&self.options.load)? // Remove errored entries (SILENTLY) .filter(|entry| entry.is_ok()) .map(|entry| entry.unwrap().path()) // Filter out directories and files that do not match pattern .filter(|path| path.file_name() .map_or(false, |file| self.config.imageglob_compiled.matches(file.to_str().unwrap())) ) .collect(); Ok(files) } } impl Config { fn load_from_file(path: &PathBuf) -> Result { let content = fs::read_to_string(path)?; let mut config: Config = serde_yaml::from_str(&content)?; config.imageglob_compiled = glob::Pattern::new(&config.imageglob)?; Ok(config) } pub fn load_with_options(opts: &Options) -> Result { let config_path = match &opts.config { Some(path) => path.to_path_buf(), None => opts.load.join("imginfo.yml"), }; Config::load_from_file(&config_path) } } impl From for ConfigError { fn from(error: io::Error) -> Self { ConfigError::Reading(error) } } impl From for ConfigError { fn from(error: serde_yaml::Error) -> Self { ConfigError::Parsing(error) } } impl From for ConfigError { fn from(error: glob::PatternError) -> Self { ConfigError::CompilePattern(error) } }