use std::ffi::OsString; use std::path::PathBuf; use regex::Regex; use structopt::StructOpt; #[derive(Debug, StructOpt)] #[structopt(rename_all = "kebab-case")] pub struct Opt { /// Path of the comic folder to bind /// /// If the recursive option is set, consider the folder given as a library containing multiples /// folders. It will not treat the given folder as a comic in itself. // TODO: check if I really do this or not when implementing the recursive option pub comic_folder: PathBuf, /// Do a dry run of the program printing to stdout the action it would take /// /// Print how each file would be renamed. #[structopt(long)] pub dry_run: bool, /// Set the pad you want to use /// /// If not set or set to a value inferior to the maximum pad of the comic, the value is /// ignored. #[structopt(long)] pub pad: Option, // TODO: implement the prefix // /// Prefix to use for the files // /// // /// This is usefull when the images contains a number before their page number // /// For example: when the title contains the name of the comic with its season: fooS02-42.png // #[structopt(short, long)] // pub prefix: &str, // // TODO: implement the recursivity // /// Recursively treat each child folder as a it's own comic to bind // #[structopt(short, long)] // pub recursive: bool, } impl Opt { pub fn new(args: I) -> Self where I: IntoIterator, T: Into + Clone, { Opt::from_iter(args) } } #[derive(Debug)] struct Page { prefix: String, number: String, suffix: String, } impl Page { fn new(prefix: String, number: String, suffix: String) -> Page { Page { prefix, number, suffix, } } fn original_filename(&self) -> String { format!("{}{}{}", self.prefix, self.number, self.suffix) } fn new_filename(&self, pad: usize) -> String { // format!("{:0pad$}-{}{}{}", number, self.prefix, self.number, self.suffix, pad = pad) // TODO I think I will need this on in the end format!( "{}{:0pad$}{}", self.prefix, self.number.parse::().unwrap(), self.suffix, pad = pad ) } } pub struct ComicBook { pad: Option, pages: Vec, } impl ComicBook { pub fn new(files: Vec, pad: Option) -> ComicBook { let regex = Regex::new(r"(?P\D*)(?P\d*)(?P.*)").unwrap(); ComicBook { pad, pages: files .iter() .map(|entry| { dbg!(entry.as_str()); let caps = dbg!(regex.captures(entry.as_str())).unwrap(); Page::new( String::from(caps.name("prefix").map_or("", |c| c.as_str())), String::from(&caps["number"]), // FIXME => ignore the file? String::from(caps.name("suffix").map_or("", |c| c.as_str())), ) }) .collect(), //~ book: path.iter().map(|name| Page::new(name)).collect(), } } pub fn bind(&mut self, transform_page: T) where T: Fn(String, String), { let pad = self.get_pad_size().expect("get_pad_size"); // TODO have a nice error message (the user may habe specifyed a folder that does not contains cb pages) for page in self.pages.iter() { //if let Some(pos) = page.position { let original_file = page.original_filename(); let new_file = page.new_filename(pad); transform_page(original_file, new_file); //} } } fn get_pad_size(&self) -> Option { let pad_pages = self .pages .iter() .max_by_key(|x| x.number.len())? .number .len(); let pad = if let Some(pad_conf) = self.pad { if pad_conf < pad_pages { pad_pages } else { pad_conf } } else { pad_pages }; Some(pad) } } #[cfg(test)] mod tests { use super::*; use std::path::Path; //~ use std::sys_common::io::test::{TempDir, tmpdir}; #[test] fn comic_book_creation() { //~ let tmpdir = tmpdir(); //~ println!("{}", tmpdir); //~ let comic = ComicBook::new(&[ //~ Path::new("01.png").to_path_buf(), //~ Path::new("02.png").to_path_buf(), //~ ]); //~ println!("{:?}", comic.book); assert!(false); //~ assert_eq!(comic.book, [Page {filename: "01.png", position: None}, Page {filename: "02.png", position: None}]); } //~ #[test] //~ fn detection_alpha() { //~ let l = Unsorted(vec!["1", "2", "10"]); //~ assert_eq!(l.detect_sort_type(), false); //~ } //~ #[test] //~ fn detection_numerical() { //~ let l = Unsorted(vec!["01", "02", "10"]); //~ assert_eq!(l.detect_sort_type(), true); //~ } }