cbb/lib.rs
1use std::ffi::OsString;
2use std::path::PathBuf;
3
4use regex::Regex;
5use structopt::StructOpt;
6
7#[derive(Debug, StructOpt)]
8#[structopt(rename_all = "kebab-case")]
9pub struct Opt {
10 /// Path of the comic folder to bind
11 ///
12 /// If the recursive option is set, consider the folder given as a library containing multiples
13 /// folders. It will not treat the given folder as a comic in itself.
14 // TODO: check if I really do this or not when implementing the recursive option
15 pub comic_folder: PathBuf,
16
17 /// Do a dry run of the program printing to stdout the action it would take
18 ///
19 /// Print how each file would be renamed.
20 #[structopt(long)]
21 pub dry_run: bool,
22
23 /// Set the pad you want to use
24 ///
25 /// If not set or set to a value inferior to the maximum pad of the comic, the value is
26 /// ignored.
27 #[structopt(long)]
28 pub pad: Option<usize>,
29
30 // TODO: implement the prefix
31 // /// Prefix to use for the files
32 // ///
33 // /// This is usefull when the images contains a number before their page number
34 // /// For example: when the title contains the name of the comic with its season: fooS02-42.png
35 // #[structopt(short, long)]
36 // pub prefix: &str,
37
38 // // TODO: implement the recursivity
39 // /// Recursively treat each child folder as a it's own comic to bind
40 // #[structopt(short, long)]
41 // pub recursive: bool,
42}
43
44impl Opt {
45 pub fn new<I, T>(args: I) -> Self
46 where
47 I: IntoIterator<Item = T>,
48 T: Into<OsString> + Clone,
49 {
50 Opt::from_iter(args)
51 }
52}
53
54#[derive(Debug)]
55struct Page {
56 prefix: String,
57 number: String,
58 suffix: String,
59}
60
61impl Page {
62 fn new(prefix: String, number: String, suffix: String) -> Page {
63 Page {
64 prefix,
65 number,
66 suffix,
67 }
68 }
69
70 fn original_filename(&self) -> String {
71 format!("{}{}{}", self.prefix, self.number, self.suffix)
72 }
73
74 fn new_filename(&self, pad: usize) -> String {
75 // format!("{:0pad$}-{}{}{}", number, self.prefix, self.number, self.suffix, pad = pad)
76 // TODO I think I will need this on in the end
77 format!(
78 "{}{:0pad$}{}",
79 self.prefix,
80 self.number.parse::<u32>().unwrap(),
81 self.suffix,
82 pad = pad
83 )
84 }
85}
86
87pub struct ComicBook {
88 pad: Option<usize>,
89 pages: Vec<Page>,
90}
91
92impl ComicBook {
93 pub fn new(files: Vec<String>, pad: Option<usize>) -> ComicBook {
94 let regex = Regex::new(r"(?P<prefix>\D*)(?P<number>\d*)(?P<suffix>.*)").unwrap();
95 ComicBook {
96 pad,
97 pages: files
98 .iter()
99 .map(|entry| {
100 dbg!(entry.as_str());
101 let caps = dbg!(regex.captures(entry.as_str())).unwrap();
102
103 Page::new(
104 String::from(caps.name("prefix").map_or("", |c| c.as_str())),
105 String::from(&caps["number"]), // FIXME => ignore the file?
106 String::from(caps.name("suffix").map_or("", |c| c.as_str())),
107 )
108 })
109 .collect(),
110 //~ book: path.iter().map(|name| Page::new(name)).collect(),
111 }
112 }
113
114 pub fn bind<T>(&mut self, transform_page: T)
115 where
116 T: Fn(String, String),
117 {
118 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)
119
120 for page in self.pages.iter() {
121 //if let Some(pos) = page.position {
122 let original_file = page.original_filename();
123 let new_file = page.new_filename(pad);
124
125 transform_page(original_file, new_file);
126 //}
127 }
128 }
129
130 fn get_pad_size(&self) -> Option<usize> {
131 let pad_pages = self
132 .pages
133 .iter()
134 .max_by_key(|x| x.number.len())?
135 .number
136 .len();
137
138 let pad = if let Some(pad_conf) = self.pad {
139 if pad_conf < pad_pages {
140 pad_pages
141 } else {
142 pad_conf
143 }
144 } else {
145 pad_pages
146 };
147 Some(pad)
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154 use std::path::Path;
155 //~ use std::sys_common::io::test::{TempDir, tmpdir};
156
157 #[test]
158 fn comic_book_creation() {
159 //~ let tmpdir = tmpdir();
160 //~ println!("{}", tmpdir);
161 //~ let comic = ComicBook::new(&[
162 //~ Path::new("01.png").to_path_buf(),
163 //~ Path::new("02.png").to_path_buf(),
164 //~ ]);
165
166 //~ println!("{:?}", comic.book);
167
168 assert!(false);
169 //~ assert_eq!(comic.book, [Page {filename: "01.png", position: None}, Page {filename: "02.png", position: None}]);
170 }
171
172 //~ #[test]
173 //~ fn detection_alpha() {
174 //~ let l = Unsorted(vec!["1", "2", "10"]);
175 //~ assert_eq!(l.detect_sort_type(), false);
176 //~ }
177
178 //~ #[test]
179 //~ fn detection_numerical() {
180 //~ let l = Unsorted(vec!["01", "02", "10"]);
181 //~ assert_eq!(l.detect_sort_type(), true);
182 //~ }
183}