1use std::env::args_os;
14use std::iter::ExactSizeIterator;
15use std::io::{self, Read};
16use std::io::{BufReader, BufRead};
17use std::fs::File;
18use std::path::Path;
19use std::error::Error;
20use std::fmt::{self, Display, Formatter};
21use std::convert::From;
22
23#[derive(Debug)]
24pub struct FailReadFileError {
25 pub inner: io::Error,
26 pub filename: String
27}
28
29impl Display for FailReadFileError {
30 fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
31 writeln!(f, "could not read file {}", self.filename)?;
32 write!(f, "caused by: {}", self.inner)?;
33 Ok(())
34 }
35}
36
37impl Error for FailReadFileError {
38 fn description(&self) -> &str {
39 "failed to read file"
40 }
41
42 fn cause(&self) -> Option<&Error> {
43 Some(&self.inner)
44 }
45}
46
47#[derive(Debug)]
48pub struct InputError {
49 pub badfiles: Vec<FailReadFileError>
50}
51
52impl Display for InputError {
53 fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
54 for e in &self.badfiles {
55 writeln!(f, "{}", e)?;
56 }
57 Ok(())
58 }
59}
60
61impl Error for InputError {
62 fn description(&self) -> &str {
63 "failed to read one or more files"
64 }
65
66 fn cause(&self) -> Option<&Error> {
67 let first = self.badfiles.first();
68
69 match first {
72 Some(err) => Some(err),
73 None => None
74 }
75 }
76}
77
78impl From<Vec<FailReadFileError>> for InputError {
79 fn from(err: Vec<FailReadFileError>) -> Self {
80 InputError { badfiles: err }
81 }
82}
83
84trait TryIterator {
86 type Item;
87 type JIter: ExactSizeIterator<Item=Self::Item>;
88
89 fn attempt_map<F, T, E>(self, mapper: F) -> Result<Vec<T>, Vec<E>> where
93 F: Fn(Self::Item) -> Result<T, E>;
94}
95
96impl<I> TryIterator for I where
97 I: ExactSizeIterator
98{
99 type Item = I::Item;
100 type JIter = I;
101
102 fn attempt_map<F, T, E>(self, mapper: F) -> Result<Vec<T>, Vec<E>> where
103 F: Fn(Self::Item) -> Result<T, E>
104 {
105 let mut any_failure = false;
106 let mut successes = Vec::new();
107 let mut failures = Vec::new();
108
109 for obj in self {
110 match mapper(obj) {
111 Ok(output) => {
112 if !any_failure {
113 successes.push(output);
114 }
115 },
116 Err(err) => {
117 any_failure = true;
118 failures.push(err);
119 }
120 };
121 }
122
123 if any_failure { Err(failures) } else { Ok(successes) }
124 }
125}
126
127pub type Lines = io::Lines<BufReader<Box<Read>>>;
128
129pub fn argf_lines() -> Result<Lines, InputError> {
134 let chained = argf()?;
135 let buffered = BufReader::new(chained);
136
137 Ok(buffered.lines())
138}
139
140pub fn argf() -> Result<Box<Read>, InputError> {
149 let args = args_os().skip(1);
150 input(args)
151}
152
153pub fn input_lines<I, J, S>(inputs: I) -> Result<Lines, InputError> where
157 I: IntoIterator<Item=S, IntoIter=J>,
158 J: ExactSizeIterator<Item=S>,
159 S: AsRef<Path>
160{
161 let chained = input(inputs)?;
162 let buffered = BufReader::new(chained);
163
164 Ok(buffered.lines())
165}
166
167pub fn input<I, J, S>(inputs: I) -> Result<Box<Read>, InputError> where
178 I: IntoIterator<Item=S, IntoIter=J>,
179 J: ExactSizeIterator<Item=S>,
180 S: AsRef<Path>
181{
182 let iter = inputs.into_iter();
183
184 if iter.len() == 0 {
185 Ok(Box::new(io::stdin()))
186 } else {
187 let reads = iter.attempt_map(|path| from_arg(path.as_ref()))?;
188
189 Ok(chain_all_reads(reads))
190 }
191}
192
193fn chain_all_reads<I>(reads: I) -> Box<Read> where
194 I: IntoIterator<Item=Box<Read>>
195{
196 reads.into_iter().fold(Box::new(io::empty()), |read, next| {
197 Box::new(read.chain(next))
198 })
199}
200
201fn from_arg<'a>(arg: &'a Path) -> Result<Box<Read>, FailReadFileError> {
202 let str_repr = arg.to_string_lossy();
203 if str_repr == "-" {
204 Ok(Box::new(io::stdin()))
205 } else {
206 let file = File::open(arg).map_err(|err| {
207 FailReadFileError {
208 inner: err,
209 filename: arg.to_string_lossy().to_string()
210 }
211 })?;
212 Ok(Box::new(file))
213 }
214}