use std::fs; type PasswordPolicy = (usize, usize, char); fn main() -> Result<(), Box<dyn std::error::Error>> { let input = fs::read_to_string("input")?; println!("{}", count_valid_passwords(&input, &validate_password_new)); Ok(()) } fn parse_password_line(line: &str) -> Result<(PasswordPolicy, &str), Box<dyn std::error::Error>> { let mut sections = line.split(' '); let range = sections .next() .ok_or("Password line too short")? .split('-') .map(|c| c.parse::<usize>()) .collect::<Result<Vec<usize>, _>>()?; let character = sections .next() .ok_or("Password line too short")? .chars() .next() .ok_or("Password line too short")?; let password = sections.next().ok_or("Password line too short")?; Ok(((range[0], range[1], character), password)) } fn validate_password_old(policy: &PasswordPolicy, password: &str) -> bool { let (start, end, character) = policy; let occurrences = password.chars().filter(|c| c == character).count(); *start <= occurrences && occurrences <= *end } fn validate_password_new(policy: &PasswordPolicy, password: &str) -> bool { let (position1, position2, character) = policy; let position1_is_character = password .chars() .nth(position1 - 1) .expect("Password too short") == *character; let position2_is_character = password .chars() .nth(position2 - 1) .expect("Password too short") == *character; (position1_is_character || position2_is_character) && (!position1_is_character || !position2_is_character) } fn count_valid_passwords(input: &str, validate: &dyn Fn(&PasswordPolicy, &str) -> bool) -> usize { input .lines() .filter(|line| { if let Ok((policy, password)) = parse_password_line(line) { validate(&policy, password) } else { false } }) .count() } #[cfg(test)] mod tests { use super::*; use indoc::indoc; #[test] fn test_simple() { let example = indoc!( "1-3 a: abcde 1-3 b: cdefg 2-9 c: ccccccccc" ); assert_eq!(count_valid_passwords(&example, &validate_password_old), 2); } #[test] fn test_simple2() { let example = indoc!( "1-3 a: abcde 1-3 b: cdefg 2-9 c: ccccccccc" ); assert_eq!(count_valid_passwords(&example, &validate_password_new), 1); } }