Skip to main content

aoc/year2020/
day04.rs

1//! # Passport Processing
2//!
3//! [Regular expressions](https://en.wikipedia.org/wiki/Regular_expression) are a good fit for this
4//! problem. However, as the principles of this crate are to avoid external dependencies and
5//! maximize speed we'll instead hand code validation functions for each of the
6//! passport field criteria.
7use std::ops::RangeInclusive;
8
9use crate::util::iter::*;
10use crate::util::parse::*;
11
12type Input = (u32, u32);
13
14pub fn parse(input: &str) -> Input {
15    let mut passport = Vec::new();
16
17    input.split("\n\n").fold((0, 0), |(part_one, part_two), block| {
18        passport.clear();
19        passport
20            .extend(block.split([':', ' ', '\n']).chunk::<2>().filter(|&[key, _]| key != "cid"));
21
22        if passport.len() == 7 {
23            (part_one + 1, part_two + passport.iter().all(validate_field) as u32)
24        } else {
25            (part_one, part_two)
26        }
27    })
28}
29
30pub fn part1(input: &Input) -> u32 {
31    input.0
32}
33
34pub fn part2(input: &Input) -> u32 {
35    input.1
36}
37
38fn validate_field(&[key, value]: &[&str; 2]) -> bool {
39    match key {
40        "byr" => validate_range(value, 1920..=2002),
41        "iyr" => validate_range(value, 2010..=2020),
42        "eyr" => validate_range(value, 2020..=2030),
43        "hgt" => validate_height(value),
44        "hcl" => validate_hair_color(value),
45        "ecl" => validate_eye_color(value),
46        "pid" => validate_passport_id(value),
47        _ => unreachable!(),
48    }
49}
50
51fn validate_range(s: &str, range: RangeInclusive<u32>) -> bool {
52    range.contains(&s.unsigned())
53}
54
55fn validate_height(hgt: &str) -> bool {
56    if let Some(n) = hgt.strip_suffix("in") {
57        validate_range(n, 59..=76)
58    } else if let Some(n) = hgt.strip_suffix("cm") {
59        validate_range(n, 150..=193)
60    } else {
61        false
62    }
63}
64
65fn validate_hair_color(hcl: &str) -> bool {
66    let hcl = hcl.as_bytes();
67    hcl.len() == 7 && hcl[0] == b'#' && hcl[1..].iter().all(u8::is_ascii_hexdigit)
68}
69
70fn validate_eye_color(ecl: &str) -> bool {
71    matches!(ecl, "amb" | "blu" | "brn" | "gry" | "grn" | "hzl" | "oth")
72}
73
74fn validate_passport_id(pid: &str) -> bool {
75    pid.len() == 9 && pid.bytes().all(|b| b.is_ascii_digit())
76}