]>
Commit | Line | Data |
---|---|---|
13cf67c4 XL |
1 | // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
11 | #[macro_use] extern crate lazy_static; | |
12 | extern crate regex; | |
13 | ||
14 | use std::env; | |
15 | use std::io; | |
16 | use std::io::{Read, Write}; | |
17 | use std::process::exit; | |
18 | use std::fs::{create_dir, read_dir, File}; | |
19 | use std::path::{Path, PathBuf}; | |
20 | use std::collections::BTreeMap; | |
21 | ||
22 | use regex::Regex; | |
23 | ||
24 | static PATTERNS: &'static [(&'static str, &'static str)] = &[ | |
25 | (r"ch(\d\d)-\d\d-.*\.md", "chapter$1.md"), | |
26 | (r"appendix-(\d\d).*\.md", "appendix.md"), | |
27 | ]; | |
28 | ||
29 | lazy_static! { | |
30 | static ref MATCHERS: Vec<(Regex, &'static str)> = { | |
31 | PATTERNS.iter() | |
32 | .map(|&(expr, repl)| (Regex::new(expr).unwrap(), repl)) | |
33 | .collect() | |
34 | }; | |
35 | } | |
36 | ||
37 | fn main() { | |
38 | let args: Vec<String> = env::args().collect(); | |
39 | ||
40 | if args.len() < 3 { | |
41 | println!("Usage: {} <src-dir> <target-dir>", args[0]); | |
42 | exit(1); | |
43 | } | |
44 | ||
45 | let source_dir = ensure_dir_exists(&args[1]).unwrap(); | |
46 | let target_dir = ensure_dir_exists(&args[2]).unwrap(); | |
47 | ||
48 | let mut matched_files = match_files(source_dir, target_dir); | |
49 | matched_files.sort(); | |
50 | ||
51 | for (target_path, source_paths) in group_by_target(matched_files) { | |
52 | concat_files(source_paths, target_path).unwrap(); | |
53 | } | |
54 | } | |
55 | ||
56 | fn match_files(source_dir: &Path, target_dir: &Path) -> Vec<(PathBuf, PathBuf)> { | |
57 | read_dir(source_dir) | |
58 | .expect("Unable to read source directory") | |
59 | .filter_map(|maybe_entry| maybe_entry.ok()) | |
60 | .filter_map(|entry| { | |
61 | let source_filename = entry.file_name(); | |
62 | let source_filename = &source_filename.to_string_lossy().into_owned(); | |
63 | for &(ref regex, replacement) in MATCHERS.iter() { | |
64 | if regex.is_match(source_filename) { | |
65 | let target_filename = regex.replace_all(source_filename, replacement); | |
66 | let source_path = entry.path(); | |
67 | let mut target_path = PathBuf::from(&target_dir); | |
68 | target_path.push(target_filename); | |
69 | return Some((source_path, target_path)); | |
70 | } | |
71 | } | |
72 | None | |
73 | }) | |
74 | .collect() | |
75 | } | |
76 | ||
77 | fn group_by_target(matched_files: Vec<(PathBuf, PathBuf)>) -> BTreeMap<PathBuf, Vec<PathBuf>> { | |
78 | let mut grouped: BTreeMap<PathBuf, Vec<PathBuf>> = BTreeMap::new(); | |
79 | for (source, target) in matched_files { | |
80 | if let Some(source_paths) = grouped.get_mut(&target) { | |
81 | source_paths.push(source); | |
82 | continue; | |
83 | } | |
84 | let source_paths = vec![source]; | |
85 | grouped.insert(target.clone(), source_paths); | |
86 | } | |
87 | grouped | |
88 | } | |
89 | ||
90 | fn concat_files(source_paths: Vec<PathBuf>, target_path: PathBuf) -> io::Result<()> { | |
91 | println!("Concatenating into {}:", target_path.to_string_lossy()); | |
92 | let mut target = try!(File::create(target_path)); | |
93 | try!(target.write_all(b"\n[TOC]\n")); | |
94 | ||
95 | for path in source_paths { | |
96 | println!(" {}", path.to_string_lossy()); | |
97 | let mut source = try!(File::open(path)); | |
98 | let mut contents: Vec<u8> = Vec::new(); | |
99 | try!(source.read_to_end(&mut contents)); | |
100 | ||
101 | try!(target.write_all(b"\n")); | |
102 | try!(target.write_all(&contents)); | |
103 | try!(target.write_all(b"\n")); | |
104 | } | |
105 | Ok(()) | |
106 | } | |
107 | ||
108 | fn ensure_dir_exists(dir_string: &str) -> io::Result<&Path> { | |
109 | let path = Path::new(dir_string); | |
110 | if !path.exists() { | |
111 | try!(create_dir(path)); | |
112 | } | |
113 | Ok(&path) | |
114 | } |