1use std::fs::File;
21use std::io;
22use std::io::{Read,stdin};
23use std::borrow::Borrow;
24
25
26#[derive(Debug, Eq, PartialEq, Clone)]
28pub enum Source {
29 Stdin,
31 File(String),
33}
34
35fn make_source_vec<T>(filenames: &[T]) -> Vec<Source> where T: Borrow<str> {
36 if filenames.is_empty() {
37 return vec![Source::Stdin];
38 }
39
40 let mut sources = Vec::with_capacity(filenames.len());
41 for filename in filenames {
42 sources.push(match filename.borrow() {
43 "-" => Source::Stdin,
44 filename => Source::File(filename.to_string()),
45 });
46 }
47 sources
48}
49
50struct State {
51 source: Source,
52 reader: Box<Read>,
53}
54
55pub struct FileInput {
57 sources: Vec<Source>,
58 state: Option<State>,
59}
60
61impl FileInput {
62 pub fn new<T>(paths: &[T]) -> Self where T: Borrow<str> {
64 FileInput {
65 sources: make_source_vec(paths),
66 state: None,
67 }
68 }
69
70 pub fn source(&self) -> Option<Source> {
75 self.state.as_ref().map(|s| s.source.clone())
76 }
77
78 fn open_next_file(&mut self) -> io::Result<()> {
79 let next_source = self.sources.remove(0);
80 let reader: Box<Read> = match &next_source {
81 &Source::Stdin => Box::new(stdin()),
82 &Source::File(ref path) => Box::new(try!(File::open(path))),
83 };
84
85 self.state = Some(State {
86 source: next_source,
87 reader: reader,
88 });
89
90 Ok(())
91 }
92}
93
94impl Read for FileInput {
95 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
96 loop {
97 if self.state.is_none() {
98 if self.sources.is_empty() {
99 return Ok(0);
100 }
101
102 try!(self.open_next_file());
103 }
104
105 let bytes_read = try!(self.state.as_mut().unwrap().reader.read(buf));
106
107 if bytes_read == 0 {
108 self.state = None;
109 continue;
110 }
111
112 return Ok(bytes_read);
113 }
114 }
115}
116
117#[cfg(test)]
118mod test {
119 mod source_vec {
120 use super::super::{make_source_vec,Source};
121
122 #[test]
123 fn empty_list_makes_stdin() {
124 let names: Vec<String> = vec![];
125 let paths = make_source_vec(&names);
126 assert_eq!(paths, [Source::Stdin]);
127 }
128
129 #[test]
130 fn dash_makes_stdin() {
131 let names = vec!["-"];
132 let paths = make_source_vec(&names);
133 assert_eq!(paths, [Source::Stdin]);
134 }
135
136 #[test]
137 fn filename_makes_path() {
138 let names = vec!["example-file"];
139 let paths = make_source_vec(&names);
140 assert_eq!(paths, [Source::File("example-file".to_string())]);
141 }
142
143 #[test]
144 fn mixed() {
145 let names = vec!["one", "two", "-", "three"];
146 let paths = make_source_vec(&names);
147 assert_eq!(paths, [Source::File("one".to_string()), Source::File("two".to_string()), Source::Stdin, Source::File("three".to_string())]);
148 }
149 }
150
151 mod fileinput {
152 use super::super::*;
153 use std::io::{Read,ErrorKind,BufRead,BufReader};
154
155 #[test]
156 fn read_files() {
157 let paths = vec!["testdata/1", "testdata/2"];
158 let mut fileinput = FileInput::new(&paths);
159 let mut buffer = String::new();
160
161 fileinput.read_to_string(&mut buffer).unwrap();
162
163 assert_eq!(buffer, "One.\nTwo.\nTwo.\n");
164 }
165
166 #[test]
167 fn skip_empty_file() {
168 let paths = vec!["testdata/1", "testdata/empty", "testdata/2"];
169 let mut fileinput = FileInput::new(&paths);
170 let mut buffer = String::new();
171
172 fileinput.read_to_string(&mut buffer).unwrap();
173
174 assert_eq!(buffer, "One.\nTwo.\nTwo.\n");
175 }
176
177 #[test]
178 fn get_source() {
179 let paths = vec!["testdata/1", "testdata/2"];
180 let fileinput = FileInput::new(&paths);
181 let mut reader = BufReader::new(fileinput);
182 let mut buffer = String::new();
183
184 assert_eq!(reader.get_ref().source(), None);
185 reader.read_line(&mut buffer).unwrap();
186 assert_eq!(reader.get_ref().source(), Some(Source::File("testdata/1".to_string())));
187 reader.read_line(&mut buffer).unwrap();
188 assert_eq!(reader.get_ref().source(), Some(Source::File("testdata/2".to_string())));
189 reader.read_line(&mut buffer).unwrap();
190 reader.read_line(&mut buffer).unwrap();
191 assert_eq!(reader.get_ref().source(), None);
192 }
193
194 #[test]
195 fn error_on_nonexistent_file() {
196 let paths = vec!["testdata/NOPE"];
197 let mut fileinput = FileInput::new(&paths);
198 let mut buffer = String::new();
199 let result = fileinput.read_to_string(&mut buffer);
200
201 assert_eq!(result.unwrap_err().kind(), ErrorKind::NotFound);
202 }
203 }
204}