comic-book-binder/src/lib.rs

187 lines
5.4 KiB
Rust
Raw Normal View History

2019-04-05 17:51:32 +02:00
use std::ffi::OsString;
use std::fs::ReadDir;
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,
// TODO: implement the recursivity
/// Recursively treat each child folder as a it's own comic to bind
#[structopt(short, long)]
pub recursive: bool,
// 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-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 {
position: Option<u32>, // FIXME: is it usefull or not ?
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 {
println!("N p:{} n: {} s: {}", prefix, number, suffix);
Page {
position: None,
prefix: prefix,
number: number,
suffix: suffix,
}
}
2019-04-05 17:51:32 +02:00
fn set_position(&mut self, pos: u32) {
2019-02-11 22:41:08 +01:00
self.position = Some(pos);
}
fn original_filename(&self) -> String {
println!("OF p:{} n: {} s: {}", self.prefix, self.number, self.suffix);
format!("{}{}{}", self.prefix, self.number, self.suffix)
}
2019-04-05 17:51:32 +02:00
fn new_filename(&self, number: &str, pad: usize) -> String {
println!(
"NF n: '{}' p: '{}' n: '{}' s: '{}'",
number, self.prefix, self.number, self.suffix
);
// format!("{:0pad$}-{}{}{}", number, self.prefix, self.number, self.suffix, pad = pad)
// TODO I think I will need this on in the end
// FIXME position is bad
format!(
"{}{:0pad$}{}",
self.prefix,
self.number.parse::<u32>().unwrap(),
self.suffix,
pad = pad
)
2019-02-11 22:41:08 +01:00
}
}
pub struct ComicBook {
pages: Vec<Page>,
is_binded: bool,
}
impl ComicBook {
pub fn new(files: ReadDir) -> ComicBook {
let regex = Regex::new(r"\./(?P<prefix>\D*)(?P<number>\d*)(?P<suffix>.*)").unwrap();
ComicBook {
pages: files
.map(|entry| entry.unwrap().path())
.filter(|entry| entry.is_file())
.map(|entry| {
let caps = regex.captures(entry.to_str().unwrap()).unwrap();
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(),
is_binded: false,
}
}
pub fn bind(&mut self) {
self.sort();
2019-04-05 17:51:32 +02:00
let pad = self.get_pad_size().unwrap();
2019-02-11 22:41:08 +01:00
for page in self.pages.iter() {
if let Some(pos) = page.position {
let original_file = page.original_filename();
2019-04-05 17:51:32 +02:00
let new_file = page.new_filename(&pos.to_string(), dbg!(pad));
2019-02-11 22:41:08 +01:00
2019-04-05 17:51:32 +02:00
println!("{} -> {}", original_file, dbg!(new_file));
2019-02-11 22:41:08 +01:00
2019-04-05 17:51:32 +02:00
// fs::rename(original_file, new_file).expect("RENAME FAILED");
2019-02-11 22:41:08 +01:00
}
}
}
fn sort(&mut self) {
//~ self.book.sort();
self.pages.sort_by(|a, b| {
a.number
2019-04-05 17:51:32 +02:00
.parse::<u32>()
2019-02-11 22:41:08 +01:00
.unwrap()
2019-04-05 17:51:32 +02:00
.cmp(&b.number.parse::<u32>().unwrap())
2019-02-11 22:41:08 +01:00
});
self.is_binded = true;
// FIXME: Do not use enumerate but the parsed value: there may be gaps in the collection
for (i, page) in self.pages.iter_mut().enumerate() {
2019-04-05 17:51:32 +02:00
page.set_position(i as u32);
2019-02-11 22:41:08 +01:00
}
}
2019-04-05 17:51:32 +02:00
fn get_pad_size(&self) -> Option<usize> {
if !self.is_binded {
return None;
}
Some(self.pages.last().unwrap().number.len())
}
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);
//~ }
}