95 lines
2.5 KiB
Rust
95 lines
2.5 KiB
Rust
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);
|
|
}
|
|
}
|