2019-04-05 17:51:32 +02:00
use std ::ffi ::OsString ;
use std ::path ::PathBuf ;
2019-02-11 22:41:08 +01:00
use regex ::Regex ;
2019-04-05 17:51:32 +02:00
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 ,
2019-09-15 19:50:19 +02:00
/// 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 ,
2019-05-23 17:57:04 +02:00
/// 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 < usize > ,
2019-04-05 17:51:32 +02:00
// 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,
2019-05-23 17:57:04 +02:00
2019-09-15 12:47:36 +02:00
// // TODO: implement the recursivity
// /// Recursively treat each child folder as a it's own comic to bind
// #[structopt(short, long)]
// pub recursive: bool,
2019-04-05 17:51:32 +02:00
}
2019-02-11 22:41:08 +01:00
2019-04-05 17:51:32 +02:00
impl Opt {
pub fn new < I , T > ( args : I ) -> Self
where
I : IntoIterator < Item = T > ,
T : Into < OsString > + Clone ,
{
Opt ::from_iter ( args )
}
}
2019-02-11 22:41:08 +01:00
#[ derive(Debug) ]
2019-04-05 17:51:32 +02:00
struct Page {
2019-02-11 22:41:08 +01:00
prefix : String ,
number : String ,
suffix : String ,
}
impl Page {
fn new ( prefix : String , number : String , suffix : String ) -> Page {
Page {
2019-05-23 14:20:58 +02:00
prefix ,
number ,
suffix ,
2019-02-11 22:41:08 +01:00
}
}
fn original_filename ( & self ) -> String {
format! ( " {} {} {} " , self . prefix , self . number , self . suffix )
}
2019-05-23 14:20:58 +02:00
fn new_filename ( & self , pad : usize ) -> String {
2019-04-05 17:51:32 +02:00
// 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 ::< u32 > ( ) . unwrap ( ) ,
self . suffix ,
pad = pad
)
2019-02-11 22:41:08 +01:00
}
}
pub struct ComicBook {
2019-05-23 17:57:04 +02:00
pad : Option < usize > ,
2019-02-11 22:41:08 +01:00
pages : Vec < Page > ,
}
impl ComicBook {
2019-09-19 14:02:30 +02:00
pub fn new ( files : Vec < String > , pad : Option < usize > ) -> ComicBook {
let regex = Regex ::new ( r "(?P<prefix>\D*)(?P<number>\d*)(?P<suffix>.*)" ) . unwrap ( ) ;
2019-02-11 22:41:08 +01:00
ComicBook {
2019-05-23 17:57:04 +02:00
pad ,
2019-02-11 22:41:08 +01:00
pages : files
2019-09-19 14:02:30 +02:00
. iter ( )
2019-02-11 22:41:08 +01:00
. map ( | entry | {
2019-09-19 14:02:30 +02:00
dbg! ( entry . as_str ( ) ) ;
let caps = dbg! ( regex . captures ( entry . as_str ( ) ) ) . unwrap ( ) ;
2019-02-11 22:41:08 +01:00
Page ::new (
String ::from ( caps . name ( " prefix " ) . map_or ( " " , | c | c . as_str ( ) ) ) ,
2019-04-05 17:51:32 +02:00
String ::from ( & caps [ " number " ] ) , // FIXME => ignore the file?
2019-02-11 22:41:08 +01:00
String ::from ( caps . name ( " suffix " ) . map_or ( " " , | c | c . as_str ( ) ) ) ,
)
} )
. collect ( ) ,
//~ book: path.iter().map(|name| Page::new(name)).collect(),
}
}
2019-09-15 19:50:19 +02:00
pub fn bind < T > ( & 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)
2019-02-11 22:41:08 +01:00
for page in self . pages . iter ( ) {
2019-05-23 14:20:58 +02:00
//if let Some(pos) = page.position {
let original_file = page . original_filename ( ) ;
2019-05-23 17:57:04 +02:00
let new_file = page . new_filename ( pad ) ;
2019-02-11 22:41:08 +01:00
2019-09-15 19:50:19 +02:00
transform_page ( original_file , new_file ) ;
2019-05-23 14:20:58 +02:00
//}
2019-02-11 22:41:08 +01:00
}
}
2019-04-05 17:51:32 +02:00
fn get_pad_size ( & self ) -> Option < usize > {
2019-05-23 17:57:04 +02:00
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 )
2019-04-05 17:51:32 +02:00
}
2019-02-11 22:41:08 +01:00
}
#[ 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);
//~ }
}