bicycle_book_wordcount_r1k/
lib.rs

1use regex::Regex;
2use std::collections::HashMap;
3use std::io::BufRead;
4
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
7pub enum CountOption {
8    Char,
9    Word,
10    Line,
11}
12
13
14impl Default for CountOption {
15    fn default() -> Self {
16        CountOption::Word
17    }
18}
19
20
21pub fn count(input: impl BufRead, option: CountOption) -> HashMap<String, usize> {
22    let re = Regex::new(r"\w+").unwrap();
23    let mut freqs = HashMap::new();
24
25    for line in input.lines () {
26        let line = line.unwrap();
27        use crate::CountOption::*;
28        match option {
29            Char => {
30                for c in line.chars() {
31                    *freqs.entry(c.to_string()).or_insert(0) += 1;
32                }
33            }
34            Word => {
35                for m in re.find_iter(&line) {
36                    let word = m.as_str().to_string();
37                    *freqs.entry(word).or_insert(0) += 1;
38                }
39            }
40            Line => *freqs.entry(line.to_string()).or_insert(0) += 1,
41        }
42    }
43    freqs
44}
45
46
47
48
49#[cfg(test)]
50mod test {
51    use super::*;
52    use std::io::Cursor;
53
54    #[test]
55    fn word_count_works() {
56        let mut exp = HashMap::new();
57        exp.insert("aa".to_string(), 1);
58        exp.insert("bb".to_string(), 2);
59        exp.insert("cc".to_string(), 1);
60
61        assert_eq!(count(Cursor::new("aa bb cc bb"), CountOption::Word), exp);
62    }
63
64    #[test]
65    fn word_count_fails() {
66        use std::io::Cursor;
67        let mut exp = HashMap::new();
68        exp.insert("aa".to_string(), 1);
69        exp.insert("cc".to_string(), 1);
70        exp.insert("dd".to_string(), 1);
71
72        assert_eq!(count(Cursor::new("aa  cc dd"), CountOption::Word), exp);
73    }
74
75    #[test]
76    #[should_panic]
77    fn word_count_invalid_charcode() {
78        use std::io::Cursor;
79
80        count(
81            Cursor::new([
82                b'a',
83                0xf0, 0x90, 0x80, // random
84                0xe3, 0x81, 0x82, // 「あ」
85            ]),
86            CountOption::Word,
87        );
88
89    }
90
91    macro_rules! assert_map {
92        ($expr: expr, {$($key: expr => $value:expr),*}) => {
93            $(assert_eq!($expr[$key], $value));*
94        };
95    }
96
97    #[test]
98    fn word_count_works3() {
99        use std::io::Cursor;
100        let freqs = count(Cursor::new("aa cc dd"), CountOption::Word);
101        assert_eq!(freqs.len(), 3);
102        assert_map!(freqs, {"aa" => 1, "cc" => 1, "dd" =>1});
103    }
104}