summaryrefslogtreecommitdiffstats
path: root/src/parse.rs
blob: 0434ec0a9386c31f9ffde248db5e017c53cfcfd7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use crate::prelude::*;

pub fn data(year: u16, day: u8) -> Result<File> {
    File::open(format!("data/{}/{}.txt", year, day)).map_err(|e| anyhow!(e))
}

pub fn raw_lines<R>(fh: R) -> impl Iterator<Item = String>
where
    R: std::io::Read,
{
    let fh = std::io::BufReader::new(fh);
    fh.lines().map(|res| res.unwrap())
}

pub fn lines<R, T>(fh: R) -> impl Iterator<Item = T>
where
    R: std::io::Read,
    T: std::str::FromStr,
    <T as std::str::FromStr>::Err: std::fmt::Debug,
{
    raw_lines(fh).map(|s| s.trim().parse().unwrap())
}

pub struct Chunk<'a, I: Iterator<Item = String>> {
    it: &'a mut I,
}

impl<'a, I: Iterator<Item = String>> Iterator for Chunk<'a, I> {
    type Item = String;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(line) = self.it.next() {
            if line.is_empty() {
                return None;
            } else {
                return Some(line);
            }
        }
        None
    }
}

pub fn chunk<I>(it: &mut I) -> Chunk<'_, I>
where
    I: Iterator<Item = String>,
{
    Chunk { it }
}

pub fn split<R, T>(fh: R, sep: u8) -> impl Iterator<Item = T>
where
    R: std::io::Read,
    T: std::str::FromStr,
    <T as 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<R: std::io::Read>(fh: R) -> impl Iterator<Item = u8> {
    fh.bytes().map(|res| res.unwrap())
}

pub fn string<R: std::io::Read>(fh: R) -> String {
    let bytes: Vec<_> = bytes(fh).collect();
    String::from_utf8(bytes).unwrap()
}

pub fn bool_grid(
    lines: impl Iterator<Item = String>,
    t: u8,
    f: u8,
) -> Grid<bool> {
    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::<Vec<_>>()
        })
        .collect()
}

pub fn digit_grid(lines: impl Iterator<Item = String>) -> Grid<u8> {
    lines
        .map(|s| {
            s.as_bytes()
                .iter()
                .copied()
                .map(|b| b - b'0')
                .collect::<Vec<_>>()
        })
        .collect()
}

// false positive, doing its suggestion gives borrow checker errors
#[allow(clippy::redundant_closure)]
pub fn grid<F, T>(lines: impl Iterator<Item = String>, mut f: F) -> Grid<T>
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::<Vec<_>>()
        })
        .collect()
}