1
0
Fork 0
aoc2020/src/bin/day4/passport.rs

107 lines
3.0 KiB
Rust

#[derive(Clone, Debug, Default)]
pub struct Passport {
// "byr": birth_year
pub birth_year: Option<String>,
// "iyr": issue_year
pub issue_year: Option<String>,
// "eyr": expiration_year
pub expiration_year: Option<String>,
// "hgt": height
pub height: Option<String>,
// "hcl": hair_color
pub hair_color: Option<String>,
// "ecl": eye_color
pub eye_color: Option<String>,
// "pid": passport_id
pub passport_id: Option<String>,
// "cid": country_id
pub country_id: Option<String>,
}
impl Passport {
pub fn parse(text: &str) -> Self {
let mut result = Self::default();
for token in text.split_whitespace() {
let colon = token.find(':').expect("missing separator");
let key = &token[..colon];
let value = &token[colon+1..];
match key {
"byr" => result.birth_year = Some(value.into()),
"iyr" => result.issue_year = Some(value.into()),
"eyr" => result.expiration_year = Some(value.into()),
"hgt" => result.height = Some(value.into()),
"hcl" => result.hair_color = Some(value.into()),
"ecl" => result.eye_color = Some(value.into()),
"pid" => result.passport_id = Some(value.into()),
"cid" => result.country_id = Some(value.into()),
_ => panic!("unknown field {:?}", key),
}
}
result
}
pub fn parse_list(text: &str) -> Vec<Self> {
text.split("\n\n").map(Self::parse).collect()
}
pub fn require_all_but_country_id(&self) -> bool {
self.birth_year.is_some() &
self.issue_year.is_some() &
self.expiration_year.is_some() &
self.height.is_some() &
self.hair_color.is_some() &
self.eye_color.is_some() &
self.passport_id.is_some()
}
fn _valid(&self) -> Option<()> {
let birth_year = self.birth_year.as_ref()?.parse::<u16>().ok()?;
if birth_year < 1920 || birth_year > 2002 {
return None;
}
let issue_year = self.issue_year.as_ref()?.parse::<u16>().ok()?;
if issue_year < 2010 || issue_year > 2020 {
return None;
}
let expiration_year = self.expiration_year.as_ref()?.parse::<u16>().ok()?;
if expiration_year < 2020 || expiration_year > 2030 {
return None;
}
let height = self.height.as_ref()?;
if height.ends_with("cm") {
let height = height[..height.len() - 2].parse::<u16>().ok()?;
if height < 150 || height > 193 {
return None;
}
} else if height.ends_with("in") {
let height = height[..height.len() - 2].parse::<u16>().ok()?;
if height < 59 || height > 76 {
return None;
}
} else {
return None;
}
let hair_color = self.hair_color.as_ref()?;
if !hair_color.starts_with("#") || hair_color.len() != 7 {
return None;
}
if !hair_color[1..].chars().all(|c| (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')) {
return None;
}
let eye_color = self.eye_color.as_ref()?;
match eye_color.as_str() {
"amb"|"blu"|"brn"|"gry"|"grn"|"hzl"|"oth" => (),
_ => return None,
}
let passport_id = self.passport_id.as_ref()?;
if passport_id.len() != 9 || !passport_id.chars().all(|c| c.is_ascii_digit()) {
return None;
}
Some(())
}
pub fn valid(&self) -> bool {
self._valid().is_some()
}
}