use anyhow::{Result, anyhow}; use itertools::Itertools; use std::env::args; use std::fs::File; use std::io::{BufRead, BufReader}; use std::path::Path; use std::str::FromStr; #[derive(Debug)] struct PasswordPolicy { character: char, min: usize, max: usize, } impl FromStr for PasswordPolicy { type Err = anyhow::Error; fn from_str(s: &str) -> Result { let mut parts = s.split(' '); let range = parts.next().ok_or_else(|| anyhow!("Could not find range: {}", s))?; let character = parts.next().ok_or_else(|| anyhow!("Could not find char: {}", s))? .chars().next().ok_or_else(|| anyhow!("Coult not parse char: {}", s))?; let ( min, max ) = match range.split('-') .map(|v| v.parse::()).collect::, _>>() { Ok(vals) => { vals.into_iter().collect_tuple().ok_or_else(|| anyhow!("Invalid range"))? }, _ => return Err(anyhow!("Couldn't parse {}", s)), }; Ok(PasswordPolicy { min, max, character }) } } impl From<&str> for PasswordPolicy { fn from(s: &str) -> Self { PasswordPolicy::from_str(s).expect(s) } } fn read_input>(filename: T) -> Result> { let f = File::open(filename)?; let reader = BufReader::new(f); reader.lines().map(|l| { let line = l?; let mut s = line.split(':'); let policy = From::from(s.next().ok_or_else(|| anyhow!("Could not parse policy"))?.trim()); let password = From::from(s.next() .ok_or_else(|| anyhow!("Could not parse password"))?.trim()); Ok(( policy, password )) }).collect() } fn part1(input: &[(PasswordPolicy, String)]) -> usize { input.iter().filter(|row| { let ( policy, password ) = row; let occurances = password.chars().filter(|c| *c == policy.character).count(); occurances >= policy.min && occurances <= policy.max }).count() } fn part2(input: &[(PasswordPolicy, String)]) -> usize { input.iter().filter(|row| { let ( policy, password ) = row; let first = password.chars().nth(policy.min - 1); let second = password.chars().nth(policy.max - 1); first != second && (first == Some(policy.character) || second == Some(policy.character)) }).count() } fn main() { let ( do_part_1, do_part_2 ) = aoc::do_parts(); let filename = match args().nth(1) { Some(f) => f, None => { eprintln!("Missing input filename"); std::process::exit(1); }, }; match read_input(filename) { Ok(input) => { if do_part_1 { let solution = part1(&input); println!("Part1, product found to be: {}", solution); } if do_part_2 { let solution = part2(&input); println!("Part2, product found to be: {}", solution); } }, Err(err) => eprintln!("Could not read input: {}", err), } }