use std::collections::HashMap; use std::fs; fn main() -> Result<(), Box<dyn std::error::Error>> { let input = fs::read_to_string("input")?; let passports = parse_passports(&input)?; println!("{}", validate_passports(&passports)); Ok(()) } fn parse_passports(input: &str) -> Result<Vec<HashMap<&str, &str>>, &str> { input .split("\n\n") .map(|passport| { passport .trim() .split(|c| c == ' ' || c == '\n') .map(|field| { let split = field .find(':') .ok_or("Invalid passport entry; no separator")?; let item = field.split_at(split); let key = item.0; let value = item .1 .strip_prefix(":") .expect("We've already checked the separator exists"); Ok((key, value)) }) .collect() }) .collect() } fn validate_passports(passports: &Vec<HashMap<&str, &str>>) -> usize { let required_fields = ["byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid"]; let rules = [ |year: &str| match year.parse() { Ok(1920..=2002) => true, _ => false, }, |year: &str| match year.parse() { Ok(2010..=2020) => true, _ => false, }, |year: &str| match year.parse() { Ok(2020..=2030) => true, _ => false, }, |height: &str| match height.split_at(height.chars().count() - 2) { (height, "cm") => match height.parse() { Ok(150..=193) => true, _ => false, }, (height, "in") => match height.parse() { Ok(59..=76) => true, _ => false, }, _ => false, }, |color: &str| match color.split_at(1) { ("#", code) => code.chars().all(|a| char::is_ascii_hexdigit(&a)), _ => false, }, |color: &str| { ["amb", "blu", "brn", "gry", "grn", "hzl", "oth"] .iter() .any(|c| c == &color) }, |pid: &str| pid.chars().count() == 9 && pid.chars().all(char::is_numeric), ]; passports .iter() .filter(|passport| { required_fields .iter() .zip(rules.iter()) .all(|(field, rule)| { if let Some(value) = passport.get(field) { rule(value) } else { false } }) }) .count() } #[cfg(test)] mod tests { use super::*; use indoc::indoc; #[test] fn test_simple() -> Result<(), String> { let example = indoc!( "ecl:gry pid:860033327 eyr:2020 hcl:#fffffd byr:1937 iyr:2017 cid:147 hgt:183cm iyr:2013 ecl:amb cid:350 eyr:2023 pid:028048884 hcl:#cfa07d byr:1929 hcl:#ae17e1 iyr:2013 eyr:2024 ecl:brn pid:760753108 byr:1931 hgt:179cm hcl:#cfa07d eyr:2025 pid:166559648 iyr:2011 ecl:brn hgt:59in " ); let passports = parse_passports(example)?; assert_eq!(validate_passports(&passports), 2); Ok(()) } }