]> git.proxmox.com Git - rustc.git/blob - src/doc/book/tools/src/bin/lfp.rs
32b2ab297590c7e2248a59d017eab897513940aa
[rustc.git] / src / doc / book / tools / src / bin / lfp.rs
1 // We have some long regex literals, so:
2 // ignore-tidy-linelength
3
4 extern crate docopt;
5 extern crate rustc_serialize;
6 extern crate walkdir;
7
8 use docopt::Docopt;
9 use std::{path, fs, io};
10 use std::io::BufRead;
11
12 fn main () {
13 let args: Args = Docopt::new(USAGE)
14 .and_then(|d| d.decode())
15 .unwrap_or_else(|e| e.exit());
16
17 let src_dir = &path::Path::new(&args.arg_src_dir);
18 let found_errs = walkdir::WalkDir::new(src_dir)
19 .min_depth(1)
20 .into_iter()
21 .map(|entry| {
22 match entry {
23 Ok(entry) => entry,
24 Err(err) => {
25 eprintln!("{:?}", err);
26 std::process::exit(911)
27 },
28 }
29 })
30 .map(|entry| {
31 let path = entry.path();
32 if is_file_of_interest(path) {
33 let err_vec = lint_file(path);
34 for err in &err_vec {
35 match *err {
36 LintingError::LineOfInterest(line_num, ref line) =>
37 eprintln!("{}:{}\t{}", path.display(), line_num, line),
38 LintingError::UnableToOpenFile =>
39 eprintln!("Unable to open {}.", path.display()),
40 }
41 }
42 !err_vec.is_empty()
43 } else {
44 false
45 }
46 })
47 .collect::<Vec<_>>()
48 .iter()
49 .any(|result| *result);
50
51 if found_errs {
52 std::process::exit(1)
53 } else {
54 std::process::exit(0)
55 }
56 }
57
58 const USAGE: &'static str = "
59 counter
60 Usage:
61 lfp <src-dir>
62 lfp (-h | --help)
63 Options:
64 -h --help Show this screen.
65 ";
66
67 #[derive(Debug, RustcDecodable)]
68 struct Args {
69 arg_src_dir: String,
70 }
71
72 fn lint_file(path: &path::Path) -> Vec<LintingError> {
73 match fs::File::open(path) {
74 Ok(file) => lint_lines(io::BufReader::new(&file).lines()),
75 Err(_) => vec![LintingError::UnableToOpenFile],
76 }
77 }
78
79 fn lint_lines<I>(lines: I) -> Vec<LintingError>
80 where I: Iterator<Item=io::Result<String>> {
81 lines
82 .enumerate()
83 .map(|(line_num, line)| {
84 let raw_line = line.unwrap();
85 if is_line_of_interest(&raw_line) {
86 Err(LintingError::LineOfInterest(line_num, raw_line))
87 } else {
88 Ok(())
89 }
90 })
91 .filter(|result| result.is_err())
92 .map(|result| result.unwrap_err())
93 .collect()
94 }
95
96 fn is_file_of_interest(path: &path::Path) -> bool {
97 path.extension()
98 .map_or(false, |ext| ext == "md")
99 }
100
101 fn is_line_of_interest(line: &str) -> bool {
102 !line.split_whitespace()
103 .filter(|sub_string|
104 sub_string.contains("file://") &&
105 !sub_string.contains("file:///projects/")
106 )
107 .collect::<Vec<_>>()
108 .is_empty()
109 }
110
111 #[derive(Debug)]
112 enum LintingError {
113 UnableToOpenFile,
114 LineOfInterest(usize, String)
115 }
116
117 #[cfg(test)]
118 mod tests {
119
120 use std::path;
121
122 #[test]
123 fn lint_file_returns_a_vec_with_errs_when_lines_of_interest_are_found() {
124 let string = r#"
125 $ cargo run
126 Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
127 Running `target/guessing_game`
128 Guess the number!
129 The secret number is: 61
130 Please input your guess.
131 10
132 You guessed: 10
133 Too small!
134 Please input your guess.
135 99
136 You guessed: 99
137 Too big!
138 Please input your guess.
139 foo
140 Please input your guess.
141 61
142 You guessed: 61
143 You win!
144 $ cargo run
145 Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
146 Running `target/debug/guessing_game`
147 Guess the number!
148 The secret number is: 7
149 Please input your guess.
150 4
151 You guessed: 4
152 $ cargo run
153 Running `target/debug/guessing_game`
154 Guess the number!
155 The secret number is: 83
156 Please input your guess.
157 5
158 $ cargo run
159 Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
160 Running `target/debug/guessing_game`
161 Hello, world!
162 "#;
163
164 let raw_lines = string.to_string();
165 let lines = raw_lines.lines().map(|line| {
166 Ok(line.to_string())
167 });
168
169 let result_vec = super::lint_lines(lines);
170
171 assert!(!result_vec.is_empty());
172 assert_eq!(3, result_vec.len());
173 }
174
175 #[test]
176 fn lint_file_returns_an_empty_vec_when_no_lines_of_interest_are_found() {
177 let string = r#"
178 $ cargo run
179 Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
180 Running `target/guessing_game`
181 Guess the number!
182 The secret number is: 61
183 Please input your guess.
184 10
185 You guessed: 10
186 Too small!
187 Please input your guess.
188 99
189 You guessed: 99
190 Too big!
191 Please input your guess.
192 foo
193 Please input your guess.
194 61
195 You guessed: 61
196 You win!
197 "#;
198
199 let raw_lines = string.to_string();
200 let lines = raw_lines.lines().map(|line| {
201 Ok(line.to_string())
202 });
203
204 let result_vec = super::lint_lines(lines);
205
206 assert!(result_vec.is_empty());
207 }
208
209 #[test]
210 fn is_file_of_interest_returns_false_when_the_path_is_a_directory() {
211 let uninteresting_fn = "src/img";
212
213 assert!(!super::is_file_of_interest(path::Path::new(uninteresting_fn)));
214 }
215
216 #[test]
217 fn is_file_of_interest_returns_false_when_the_filename_does_not_have_the_md_extension() {
218 let uninteresting_fn = "src/img/foo1.png";
219
220 assert!(!super::is_file_of_interest(path::Path::new(uninteresting_fn)));
221 }
222
223 #[test]
224 fn is_file_of_interest_returns_true_when_the_filename_has_the_md_extension() {
225 let interesting_fn = "src/ch01-00-introduction.md";
226
227 assert!(super::is_file_of_interest(path::Path::new(interesting_fn)));
228 }
229
230 #[test]
231 fn is_line_of_interest_does_not_report_a_line_if_the_line_contains_a_file_url_which_is_directly_followed_by_the_project_path() {
232 let sample_line = "Compiling guessing_game v0.1.0 (file:///projects/guessing_game)";
233
234 assert!(!super::is_line_of_interest(sample_line));
235 }
236
237 #[test]
238 fn is_line_of_interest_reports_a_line_if_the_line_contains_a_file_url_which_is_not_directly_followed_by_the_project_path() {
239 let sample_line = "Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)";
240
241 assert!(super::is_line_of_interest(sample_line));
242 }
243 }