1 use std::io::prelude::*;
2 use std::{io, fs, thread, process};
3 use std::sync::mpsc::{sync_channel, SyncSender, Receiver};
12 use self::OutputMode::*;
17 output_mode: OutputMode,
26 fn read_files(options: Arc<Options>, out_channel: SyncSender<Line>) {
27 for (fileidx, file) in options.files.iter().enumerate() {
28 let file = fs::File::open(file).unwrap();
29 let file = io::BufReader::new(file);
30 for (lineidx, line) in file.lines().enumerate() {
31 let line = Line { data: line.unwrap(), file: fileidx, line: lineidx };
32 out_channel.send(line).unwrap();
37 fn filter_lines(options: Arc<Options>, in_channel: Receiver<Line>, out_channel: SyncSender<Line>) {
38 for line in in_channel.iter() {
39 if line.data.contains(&options.pattern) {
40 out_channel.send(line).unwrap();
45 fn output_lines(options: Arc<Options>, in_channel: Receiver<Line>) {
46 match options.output_mode {
48 for line in in_channel.iter() {
49 println!("{}:{}: {}", options.files[line.file], line.line, line.data);
53 let count = in_channel.iter().count();
54 println!("{} hits for {}.", count, options.pattern);
57 let _data: Vec<Line> = in_channel.iter().collect();
63 static USAGE: &'static str = "
64 Usage: rgrep [-c] [-s] <pattern> <file>...
67 -c, --count Count number of matching lines (rather than printing them).
68 -s, --sort Sort the lines before printing.
71 fn get_options() -> Options {
74 // Parse argv and exit the program with an error message if it fails.
75 let args = Docopt::new(USAGE).and_then(|d| d.parse()).unwrap_or_else(|e| e.exit());
76 let count = args.get_bool("-c");
77 let sort = args.get_bool("-s");
78 let pattern = args.get_str("<pattern>");
79 let files = args.get_vec("<file>");
81 println!("Setting both '-c' and '-s' at the same time does not make any sense.");
85 // We need to make the strings owned to construct the `Options` instance.
87 files: files.iter().map(|file| file.to_string()).collect(),
88 pattern: pattern.to_string(),
89 output_mode: if count { Count } else if sort { SortAndPrint } else { Print },
93 fn run(options: Options) {
94 let options = Arc::new(options);
96 // Set up the chain of threads. Use `sync_channel` with buffer-size of 16 to avoid needlessly filling RAM.
97 let (line_sender, line_receiver) = sync_channel(16);
98 let (filtered_sender, filtered_receiver) = sync_channel(16);
100 let options1 = options.clone();
101 let handle1 = thread::spawn(move || read_files(options1, line_sender));
102 let options2 = options.clone();
103 let handle2 = thread::spawn(move || filter_lines(options2, line_receiver, filtered_sender));
104 let options3 = options.clone();
105 let handle3 = thread::spawn(move || output_lines(options3, filtered_receiver));
106 handle1.join().unwrap();
107 handle2.join().unwrap();
108 handle3.join().unwrap();