parse_list/
lib.rs

1//! Parse files and lists of stringified things into lists of thingified things.
2//!
3//! That is, if you've got something `Read`-y or `Iterator`-y over `u8`s,
4//! `String`s, or `str`s, and a type that implements `FromStr`, you can also
5//! have an `Iterator` of that type of thing.
6//!
7//! Particularly designed to parse files of newline-separated things,
8//! like these git integers:
9//!
10//! ```no-test
11//! 0
12//! 1
13//! 2
14//! 3
15//! 4
16//! ```
17//!
18//! Load your ints with ease:
19//!
20//! ```rust,ignore
21//! // Create the file of test data
22//! use std::fs;
23//! let tmp_dir = TempDir::new("tmp").unwrap();
24//! let file_path = tmp_dir.path().join("list");
25//! fs::write(&file_path, "0\n1\n2\n3\n4").unwrap();
26//!
27//! // Load from file. Note that each element could result in an individual
28//! // I/O or parse error. Here those are converted into a single `Result<Vec<u32>, _>`.
29//! let v = from_file_lines(&file_path);
30//! let v: Vec<Result<u32, _>> = v.unwrap().collect();
31//! let v: Result<Vec<u32>, _> = v.into_iter().collect();
32//! let v = v.unwrap();
33//! assert!(v == vec![0, 1, 2, 3, 4]);
34//! ```
35//!
36//! Besides parsing from a newline-separated file there are also functions for
37//! parsing from various traits, including iterators.
38//!
39//! ## Tips
40//!
41//! To convert from an iterator of `Result` to a `Result` of `Vec` use `collect`
42//! with a `Result<Vec<_>>` type annotation:
43//!
44//! ```rust
45//! use big_s::S;
46//!
47//! let a = vec![Ok(S("0")), Ok(S("1")), Ok(S("2"))];
48//! let b: Vec<Result<u32, _>> = parse_list::from_iter(a.into_iter()).collect();
49//! let b: Result<Vec<u32>, _> = b.into_iter().collect();
50//! let b = b.unwrap();
51//!
52//! assert!(b == vec![0, 1, 2]);
53//! ```
54//!
55//! To ignore errors parsing any particular list entry, use
56//! `filter_map(Result::ok)`:
57//!
58//! ```rust
59//! use big_s::S;
60//! use std::io;
61//! use std::num::ParseIntError;
62//!
63//! let e: io::Error = io::Error::from(io::ErrorKind::NotFound);
64//! let a: Vec<Result<String, io::Error>> = vec![Ok(S("0")), Err(e), Ok(S("2"))];
65//! let b: Vec<u32> = parse_list::from_iter(a.into_iter())
66//!     .filter_map(Result::ok).collect();
67//!
68//! assert!(b.len() == 2);
69//! assert!(b[0] == 0);
70//! assert!(b[1] == 2);
71//! ```
72
73use std::marker::PhantomData;
74use std::fmt::{self, Display};
75use std::error::Error;
76use std::iter::{Iterator, Filter};
77use std::fs::File;
78use std::io::{self, Read, BufReader, BufRead, Lines};
79use std::path::Path;
80use std::str::FromStr;
81
82pub fn from_file_lines<T>(p: &Path) -> Result<ParseListIterator<T, Filter<Lines<BufReader<File>>, fn(&Result<String, io::Error>) -> bool>>, io::Error>
83where T: FromStr,
84      T::Err: Error + Send + Sync + 'static {
85    let f = File::open(p)?;
86    Ok(from_read_lines(f))
87}
88
89pub fn from_read_lines<T, R>(r: R) -> ParseListIterator<T, Filter<Lines<BufReader<R>>, fn(&Result<String, io::Error>) -> bool>>
90where T: FromStr,
91      T::Err: Error + Send + Sync + 'static,
92      R: Read {
93    let r: BufReader<R> = BufReader::new(r);
94    from_bufread_lines(r)
95}
96
97pub fn from_bufread_lines<T, B>(b: B) -> ParseListIterator<T, Filter<Lines<B>, fn(&Result<String, io::Error>) -> bool>>
98where T: FromStr,
99      T::Err: Error + Send + Sync + 'static,
100      B: BufRead {
101
102    fn nonblank(lr: &Result<String, io::Error>) -> bool {
103        let trimmed = lr.as_ref().map(|l| !l.trim().is_empty());
104        let nonblank = trimmed.unwrap_or(true);
105        nonblank
106    }
107
108    let without_blanks = b.lines().filter(nonblank as fn(&Result<String, io::Error>) -> bool);
109
110    from_iter(without_blanks)
111}
112
113// TODO: abstract io::Error
114
115pub fn from_iter<T, I>(i: I) -> ParseListIterator<T, I>
116where T: FromStr,
117      T::Err: Error + Send + Sync + 'static,
118      I: Iterator<Item = Result<String, io::Error>> {
119    ParseListIterator::<T, I>(i, PhantomData)
120}
121
122pub struct ParseListIterator<T, I> (I, PhantomData<T>)
123where T: FromStr,
124      T::Err: Error + Send + Sync + 'static,
125      I: Iterator<Item = Result<String, io::Error>>;
126
127impl<T, I> Iterator for ParseListIterator<T, I>
128where T: FromStr,
129      T::Err: Error + Send + Sync + 'static,
130      I: Iterator<Item = Result<String, io::Error>>
131{
132
133    type Item = Result<T, ParseListError<T::Err>>;
134
135    fn next(&mut self) -> Option<Self::Item> {
136        self.0.next().map(string_result_to_item_result)
137    }
138}
139
140fn string_result_to_item_result<T>(v: Result<String, io::Error>) -> Result<T, ParseListError<T::Err>>
141where T: FromStr,
142      T::Err: Error + Send + Sync + 'static {
143    match v {
144        Ok(v) => {
145            match str::parse(&v) {
146                Ok(v) => Ok(v),
147                Err(e) => Err(ParseListError::Parse(e))
148            }
149        }
150        Err(e) => Err(ParseListError::Io(e))
151    }
152}
153
154#[derive(Debug)]
155pub enum ParseListError<TE>
156where TE: Error + Send + Sync + 'static {
157    Io(io::Error),
158    Parse(TE),
159}
160
161impl<TE> Error for ParseListError<TE>
162where TE: Error + Send + Sync + 'static { }
163
164impl<TE> Display for ParseListError<TE>
165where TE: Error + Send + Sync + 'static {
166    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167        match self {
168            ParseListError::Io(e) => Display::fmt(e, f),
169            ParseListError::Parse(e) => Display::fmt(e, f),
170        }
171    }
172}
173
174#[cfg(test)]
175mod tests {
176    use tempdir::TempDir;
177    use big_s::S;
178    use super::*;
179
180    #[test]
181    fn from_iter_vec() {
182        let a = vec![Ok(S("0")), Ok(S("1")), Ok(S("2"))];
183        let b: Vec<Result<u32, _>> = from_iter(a.into_iter()).collect();
184        let b: Result<Vec<u32>, _> = b.into_iter().collect();
185        let b = b.unwrap();
186        assert!(b == vec![0, 1, 2]);
187    }
188
189    // What happens with [Ok, Err, Ok]?
190    #[test]
191    fn from_iter_vec_fail_middle() {
192        use std::num::ParseIntError;
193        let e: io::Error = io::Error::from(io::ErrorKind::NotFound);
194        let a: Vec<Result<String, io::Error>> = vec![Ok(S("0")), Err(e), Ok(S("2"))];
195        let b: Vec<Result<u32, ParseListError<ParseIntError>>> = from_iter(a.into_iter()).collect();
196        assert!(b.len() == 3);
197        assert!(b[0].as_ref().unwrap() == &0);
198        assert!(b[1].is_err());
199        assert!(b[2].as_ref().unwrap() == &2);
200    }
201
202
203    #[test]
204    fn from_iter_vec_ignore_errors() {
205        let e: io::Error = io::Error::from(io::ErrorKind::NotFound);
206        let a: Vec<Result<String, io::Error>> = vec![Ok(S("0")), Err(e), Ok(S("2"))];
207        let b: Vec<u32> = from_iter(a.into_iter())
208            .filter_map(Result::ok).collect();
209        assert!(b.len() == 2);
210        assert!(b[0] == 0);
211        assert!(b[1] == 2);
212    }
213
214    #[test]
215    fn from_bufread_lines_slice() {
216        let a = "0\n1\n2".as_bytes();
217        let b: Vec<Result<u32, _>> = from_bufread_lines(a).collect();
218        let b: Result<Vec<u32>, _> = b.into_iter().collect();
219        let b = b.unwrap();
220        assert!(b == vec![0, 1, 2]);
221    }
222
223    #[test]
224    fn from_bufread_lines_slice_fail_middle() {
225        let a = "0\nboop\n2".as_bytes();
226        let b: Vec<Result<u32, _>> = from_bufread_lines(a).collect();
227        assert!(b.len() == 3);
228        assert!(b[0].as_ref().unwrap() == &0);
229        assert!(b[1].is_err());
230        assert!(b[2].as_ref().unwrap() == &2);
231    }
232
233    #[test]
234    fn from_bufread_lines_cursor() {
235        use std::io::Cursor;
236        let a = Cursor::new("0\n1\n2".as_bytes());
237        let b: Vec<Result<u32, _>> = from_bufread_lines(a).collect();
238        let b: Result<Vec<u32>, _> = b.into_iter().collect();
239        let b = b.unwrap();
240        assert!(b == vec![0, 1, 2]);
241    }
242
243    #[test]
244    fn from_read_lines_slice() {
245        let a = "0\n1\n2".as_bytes();
246        let b: Vec<Result<u32, _>> = from_read_lines(a).collect();
247        let b: Result<Vec<u32>, _> = b.into_iter().collect();
248        let b = b.unwrap();
249        assert!(b == vec![0, 1, 2]);
250    }
251
252    #[test]
253    fn from_read_lines_cursor() {
254        use std::io::Cursor;
255        let a = Cursor::new("0\n1\n2".as_bytes());
256        let b: Vec<Result<u32, _>> = from_read_lines(a).collect();
257        let b: Result<Vec<u32>, _> = b.into_iter().collect();
258        let b = b.unwrap();
259        assert!(b == vec![0, 1, 2]);
260    }
261
262    #[test]
263    fn from_read_lines_file() {
264        use std::fs;
265        let tmp_dir = TempDir::new("tmp").unwrap();
266        let file_path = tmp_dir.path().join("list");
267        fs::write(&file_path, "0\n1\n2").unwrap();
268        let f = File::open(file_path).unwrap();
269        let b: Vec<Result<u32, _>> = from_read_lines(f).collect();
270        let b: Result<Vec<u32>, _> = b.into_iter().collect();
271        let b = b.unwrap();
272        assert!(b == vec![0, 1, 2]);
273    }
274
275    #[test]
276    fn from_read_lines_file_slice() {
277        use std::fs;
278        let tmp_dir = TempDir::new("tmp").unwrap();
279        let file_path = tmp_dir.path().join("list");
280        fs::write(&file_path, "0\n1\n2").unwrap();
281        let f = File::open(file_path).unwrap();
282        let b: Vec<Result<u32, _>> = from_read_lines(&f).collect();
283        let b: Result<Vec<u32>, _> = b.into_iter().collect();
284        let b = b.unwrap();
285        assert!(b == vec![0, 1, 2]);
286    }
287
288    #[test]
289    fn from_file_lines_success() {
290        use std::fs;
291        let tmp_dir = TempDir::new("tmp").unwrap();
292        let file_path = tmp_dir.path().join("list");
293        fs::write(&file_path, "0\n1\n2\n3\n4").unwrap();
294
295        let v = from_file_lines(&file_path);
296        let v: Vec<Result<u32, _>> = v.unwrap().collect();
297        let v: Result<Vec<u32>, _> = v.into_iter().collect();
298        let v = v.unwrap();
299        assert!(v == vec![0, 1, 2, 3, 4]);
300    }
301    
302    #[test]
303    fn from_file_lines_success_min_annotations() {
304        use std::fs;
305        let tmp_dir = TempDir::new("tmp").unwrap();
306        let file_path = tmp_dir.path().join("list");
307        fs::write(&file_path, "0\n1\n2\n3\n4").unwrap();
308
309        let v = from_file_lines(&file_path);
310        let v: Vec<_> = v.unwrap().collect();
311        let v: Result<Vec<u32>, _> = v.into_iter().collect();
312        let v = v.unwrap();
313        assert!(v == vec![0, 1, 2, 3, 4]);
314    }
315
316    #[test]
317    fn from_file_lines_ignore_errors() -> Result<(), io::Error> {
318        use std::fs;
319        let tmp_dir = TempDir::new("tmp").unwrap();
320        let file_path = tmp_dir.path().join("list");
321        fs::write(&file_path, "0\n1\n2\n3\n4").unwrap();
322        let b: Vec<u32> = from_file_lines(&file_path)?
323            .filter_map(Result::ok).collect();
324        assert!(b == vec![0, 1, 2, 3, 4]);
325        Ok(())
326    }
327    
328    #[test]
329    fn from_file_lines_not_found() {
330        let tmp_dir = TempDir::new("tmp").unwrap();
331        let file_path = tmp_dir.path().join("list");
332        let b = from_file_lines::<u32>(&file_path);
333        assert!(b.is_err());
334    }
335}
336