From cfabd86e212d56069444a3d64371a8fbd21dae2c Mon Sep 17 00:00:00 2001 From: Aleksei Voronov Date: Tue, 16 Aug 2016 00:40:12 +0300 Subject: [PATCH] Add a script that concatenates chapters into one file Closes #183 --- Cargo.lock | 2 + Cargo.toml | 2 + src/bin/concat_chapters.rs | 114 +++++++++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+) create mode 100644 src/bin/concat_chapters.rs diff --git a/Cargo.lock b/Cargo.lock index 4dc998bf6..5cd51aa43 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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)", ] diff --git a/Cargo.toml b/Cargo.toml index b1a45033e..789cd36f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/src/bin/concat_chapters.rs b/src/bin/concat_chapters.rs new file mode 100644 index 000000000..5ed8266b8 --- /dev/null +++ b/src/bin/concat_chapters.rs @@ -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 = env::args().collect(); + + if args.len() < 3 { + println!("Usage: {} ", 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> { + let mut grouped: BTreeMap> = 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, 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 = 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) +}