Created
December 2, 2020 20:39
-
-
Save ArdaXi/fbcd446fc1d0708a5bcd518b519b75ec to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use anyhow::{anyhow, bail, Context, Error, Result}; | |
use itertools::Itertools; | |
use std::convert::TryFrom; | |
use std::io::{stdin, BufRead}; | |
#[derive(Clone, Debug)] | |
struct Line { | |
min: usize, | |
max: usize, | |
letter: char, | |
pass: String, | |
} | |
impl TryFrom<String> for Line { | |
type Error = anyhow::Error; | |
fn try_from(value: String) -> Result<Self> { | |
let mut iter = value.split(&['-', ' ', ':'][..]).filter(|x| !x.is_empty()); | |
let min = iter | |
.next() | |
.ok_or_else(|| anyhow!("Missing min"))? | |
.parse() | |
.context("Failed to parse min")?; | |
let max = iter | |
.next() | |
.ok_or_else(|| anyhow!("Missing max"))? | |
.parse() | |
.context("Failed to parse max")?; | |
let letter_slice = iter.next().ok_or_else(|| anyhow!("Missing letter"))?; | |
if letter_slice.len() != 1 { | |
bail!("Letter wrong length"); | |
} | |
let letter = letter_slice | |
.chars() | |
.next() | |
.ok_or_else(|| anyhow!("Missing letter somehow"))?; | |
let pass = iter | |
.next() | |
.ok_or_else(|| anyhow!("Missing pass"))? | |
.to_string(); | |
if pass.len() < max { | |
bail!("Password shorter than max value"); | |
} | |
Ok(Line { | |
min, | |
max, | |
letter, | |
pass, | |
}) | |
} | |
} | |
impl Line { | |
fn is_valid(&self) -> bool { | |
let count = self.pass.chars().filter(|c| c == &self.letter).count(); | |
count >= self.min && count <= self.max | |
} | |
fn is_valid2(&self) -> bool { | |
let mut chars = self.pass.chars(); | |
let first = chars.nth(self.min - 1).unwrap(); | |
let second = chars.nth(self.max - self.min - 1).unwrap(); | |
(first == self.letter) ^ (second == self.letter) | |
} | |
} | |
fn until_err<T, E>(err: &mut &mut Result<()>, item: Result<T, E>) -> Option<T> | |
where | |
E: Into<Error>, | |
{ | |
match item { | |
Ok(item) => Some(item), | |
Err(e) => { | |
**err = Err(anyhow!(e)); | |
None | |
} | |
} | |
} | |
fn main() -> Result<()> { | |
let stdin = stdin(); | |
let mut err: Result<()> = Ok(()); | |
let (lines1, lines2) = stdin | |
.lock() | |
.lines() | |
.map(|l| l.map_err(Error::new).and_then(Line::try_from)) | |
.scan(&mut err, until_err) | |
.tee(); | |
let part1 = lines1.filter(Line::is_valid).count(); | |
let part2 = lines2.filter(Line::is_valid2).count(); | |
err.context("Failed to parse all lines")?; | |
println!("part 1: {:?}", part1); | |
println!("part 2: {:?}", part2); | |
Ok(()) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment