use crate::prelude::*; pub fn data(year: u16, day: u8) -> Result { File::open(format!("data/{}/{}.txt", year, day)).map_err(|e| anyhow!(e)) } pub fn raw_lines(fh: R) -> impl Iterator where R: std::io::Read, { let fh = std::io::BufReader::new(fh); fh.lines().map(|res| res.unwrap()) } pub fn lines(fh: R) -> impl Iterator where R: std::io::Read, T: std::str::FromStr, ::Err: std::fmt::Debug, { raw_lines(fh).map(|s| s.trim().parse().unwrap()) } pub struct Chunk<'a, I: Iterator> { it: &'a mut I, } impl<'a, I: Iterator> Iterator for Chunk<'a, I> { type Item = String; fn next(&mut self) -> Option { if let Some(line) = self.it.next() { if line.is_empty() { return None; } else { return Some(line); } } None } } pub fn chunk(it: &mut I) -> Chunk<'_, I> where I: Iterator, { Chunk { it } } pub fn split(fh: R, sep: u8) -> impl Iterator where R: std::io::Read, T: std::str::FromStr, ::Err: std::fmt::Debug, { let fh = std::io::BufReader::new(fh); fh.split(sep) .map(|res| String::from_utf8(res.unwrap()).unwrap()) .map(|s| s.trim().parse().unwrap()) } pub fn bytes(fh: R) -> impl Iterator { fh.bytes().map(|res| res.unwrap()) } pub fn string(fh: R) -> String { let bytes: Vec<_> = bytes(fh).collect(); String::from_utf8(bytes).unwrap() } pub fn bool_grid( lines: impl Iterator, t: u8, f: u8, ) -> Grid { lines .map(|s| { s.as_bytes() .iter() .copied() .map(|b| { if b == f { false } else if b == t { true } else { panic!("unrecognized character {}", char::from(b)) } }) .collect::>() }) .collect() } pub fn digit_grid(lines: impl Iterator) -> Grid { lines .map(|s| { s.as_bytes() .iter() .copied() .map(|b| b - b'0') .collect::>() }) .collect() } // false positive, doing its suggestion gives borrow checker errors #[allow(clippy::redundant_closure)] pub fn grid(lines: impl Iterator, mut f: F) -> Grid where F: FnMut(u8, Row, Col) -> T, T: Default + Clone + Eq + PartialEq + std::hash::Hash, { lines .enumerate() .map(|(row, s)| { s.as_bytes() .iter() .copied() .enumerate() .map(|(col, b)| f(b, Row(row), Col(col))) .collect::>() }) .collect() }