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