Add a script that concatenates chapters into one file

Closes #183
This commit is contained in:
Aleksei Voronov 2016-08-16 00:40:12 +03:00
parent 85fd039271
commit cfabd86e21
3 changed files with 118 additions and 0 deletions

2
Cargo.lock generated
View File

@ -3,6 +3,8 @@ name = "rust-book"
version = "0.0.1"
dependencies = [
"docopt 0.6.82 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.1.73 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"walkdir 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]

View File

@ -8,3 +8,5 @@ description = "The Rust Book"
walkdir = "0.1.5"
docopt = "0.6.82"
rustc-serialize = "0.3.19"
regex = "0.1.73"
lazy_static = "0.2.1"

114
src/bin/concat_chapters.rs Normal file
View File

@ -0,0 +1,114 @@
#[macro_use] extern crate lazy_static;
extern crate regex;
use std::env;
use std::io;
use std::io::{Read, Write};
use std::process::exit;
use std::fs::{create_dir, read_dir, File};
use std::path::{Path, PathBuf};
use std::collections::BTreeMap;
use regex::Regex;
static PATTERNS: &'static [(&'static str, &'static str)] = &[
(r"ch(\d\d)-\d\d-.*\.md", "chapter$1.md"),
(r"appendix-(\d\d).*\.md", "appendix.md"),
];
lazy_static! {
static ref MATCHERS: Vec<(Regex, &'static str)> = {
PATTERNS.iter()
.map(|&(expr, repl)| (Regex::new(expr).unwrap(), repl))
.collect()
};
}
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 3 {
println!("Usage: {} <src-dir> <target-dir>", args[0]);
exit(1);
}
let source_dir = ensure_dir_exists(&args[1]).unwrap();
let target_dir = ensure_dir_exists(&args[2]).unwrap();
let mut matched_files = match_files(source_dir, target_dir);
matched_files.sort();
for (target_path, source_paths) in group_by_target(matched_files) {
concat_files(source_paths, target_path).unwrap();
}
}
fn match_files(source_dir: &Path, target_dir: &Path) -> Vec<(PathBuf, PathBuf)> {
read_dir(source_dir)
.expect("Unable to read source directory")
.filter_map(|maybe_entry| maybe_entry.ok())
.filter_map(|entry| {
let source_filename = entry.file_name();
let source_filename = &source_filename.to_string_lossy().into_owned();
for &(ref regex, replacement) in MATCHERS.iter() {
if regex.is_match(source_filename) {
let target_filename = regex.replace_all(source_filename, replacement);
let source_path = entry.path();
let mut target_path = PathBuf::from(&target_dir);
target_path.push(target_filename);
return Some((source_path, target_path));
}
}
None
})
.collect()
}
fn group_by_target(matched_files: Vec<(PathBuf, PathBuf)>) -> BTreeMap<PathBuf, Vec<PathBuf>> {
let mut grouped: BTreeMap<PathBuf, Vec<PathBuf>> = BTreeMap::new();
for (source, target) in matched_files {
if let Some(source_paths) = grouped.get_mut(&target) {
source_paths.push(source);
continue;
}
let source_paths = vec![source];
grouped.insert(target.clone(), source_paths);
}
grouped
}
fn concat_files(source_paths: Vec<PathBuf>, target_path: PathBuf) -> io::Result<()> {
println!("Concatenating into {}:", target_path.to_string_lossy());
let mut target = try!(File::create(target_path));
let mut is_first = true;
for path in source_paths {
println!(" {}", path.to_string_lossy());
let mut source = try!(File::open(path));
let mut contents: Vec<u8> = Vec::new();
try!(source.read_to_end(&mut contents));
if !is_first {
try!(target.write_all(b"\n"));
}
try!(target.write_all(&contents));
is_first = false;
}
Ok(())
}
fn ensure_dir_exists(dir_string: &str) -> io::Result<&Path> {
let path = Path::new(dir_string);
if !path.exists() {
try!(create_dir(path));
}
Ok(&path)
}