Feels like it could have been cleaner, my first round was worse though! I refactored to use Result and the ? error propagation operator, which was fun.
useregex::Regex;useanyhow::{anyhow,Result};#[derive(Clone)]structPassport{byr:Option<String>,iyr:Option<String>,eyr:Option<String>,hgt:Option<String>,hcl:Option<String>,ecl:Option<String>,pid:Option<String>,cid:Option<String>,}implDefaultforPassport{fndefault()->Self{Passport{byr:None,iyr:None,eyr:None,hgt:None,hcl:None,ecl:None,pid:None,cid:None,}}}fnmain(){letinput=std::fs::read_to_string("input.txt").unwrap();letpassports:Vec<Passport>=input.trim().split("\n\n").map(parse_passport).collect();letnum_valid=passports.into_iter().filter(|p|{// validate_passport(p).is_ok()matchvalidate_passport(p){Ok(_)=>true,Err(e)=>{println!("{}",e);false},}}).count();println!("Num Valid: {}",num_valid);}fnparse_passport(input:&str)->Passport{letmutp:Passport=Passport::default();forentryininput.replace("\n"," ").split(" "){letparts:Vec<&str>=entry.split(":").collect();let(label,value)=(parts[0],parts[1]);matchlabel{"byr"=>{p.byr=Some(value.to_owned())},"iyr"=>{p.iyr=Some(value.to_owned())},"eyr"=>{p.eyr=Some(value.to_owned())},"hgt"=>{p.hgt=Some(value.to_owned())},"hcl"=>{p.hcl=Some(value.to_owned())},"ecl"=>{p.ecl=Some(value.to_owned())},"pid"=>{p.pid=Some(value.to_owned())},"cid"=>{p.cid=Some(value.to_owned())},_=>{},}}p}fnvalidate_passport(p:&Passport)->Result<()>{check_year_range(p.clone().byr,1920,2002)?;check_year_range(p.clone().iyr,2010,2020)?;check_year_range(p.clone().eyr,2020,2030)?;check_height(p.clone().hgt)?;check_regex(p.clone().hcl,"#[0-9A-Fa-f]{6}")?;check_regex(p.clone().ecl,"^(amb|blu|brn|gry|grn|hzl|oth)$")?;check_regex(p.clone().pid,"^[0-9]{9}$")?;Ok(())}fncheck_year_range(year_str:Option<String>,min:usize,max:usize)->Result<()>{letyear=year_str.ok_or(anyhow!("Missing value"))?.parse::<usize>().map_err(|_e|anyhow!("Failed to parse year_str"))?;ifyear<min||year>max{returnErr(anyhow!("Year out of range"));}Ok(())}fncheck_height(height:Option<String>)->Result<()>{lethgt=height.ok_or(anyhow!("Missing hgt"))?;letre=Regex::new(r"^([0-9]+)(cm|in)$").unwrap();letcaps=re.captures(hgt.as_str()).ok_or(anyhow!("Invalid height format"))?;letnum_str=caps.get(1).unwrap().as_str();letunit=caps.get(2).unwrap();letnum=num_str.parse::<usize>().map_err(|_e|anyhow!("Failed to parse height value"))?;matchunit.as_str(){"in"ifnum<59||num>76=>{returnErr(anyhow!("Height out of range"));},"cm"ifnum<150||num>193=>{returnErr(anyhow!("Height out of range"));},_=>{},}Ok(())}fncheck_regex(field:Option<String>,re_str:&str)->Result<()>{letre=Regex::new(re_str).unwrap();letval=field.ok_or(anyhow!("Field missing"))?;matchre.is_match(val.as_str()){true=>Ok(()),false=>Err(anyhow!(format!("Regex mismatch: {}, {}",re_str,val))),}}
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
Feels like it could have been cleaner, my first round was worse though! I refactored to use
Result
and the?
error propagation operator, which was fun.