master
Avril 3 years ago
parent b107f62c09
commit bc38a6ff1b
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -0,0 +1,15 @@
[package]
name = "day4"
version = "0.1.0"
authors = ["Avril <flanchan@cumallover.me>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
test= []
part2 = []
[dependencies]
lazy_static = "1.4.0"
smallmap = "1.2.0"

@ -0,0 +1,21 @@
CARGO_FEATURE_FLAGS?=
.PHONY: all
.NOTPARALLEL: all
all:
$(MAKE) part1
$(MAKE) part2
.NOTPARALLEL: part1
part1: $(wildcard src/*.rs)
cargo build --release $(addprefix --features ,$(CARGO_FEATURE_FLAGS))
mv -f target/release/day4 $@
.NOTPARALLEL: part2
part2: $(wildcard src/*.rs)
cargo build --release --features $@ $(addprefix --features ,$(CARGO_FEATURE_FLAGS))
mv -f target/release/day4 $@
clean:
rm -f part{1,2}

@ -0,0 +1,156 @@
use std::{
fmt, error,
};
#[derive(Debug)]
pub struct InfoParseError;
impl error::Error for InfoParseError{}
impl fmt::Display for InfoParseError
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "failed to parse passport info structure")
}
}
// ---
#[derive(Debug)]
pub enum BoundedU16FromStrError
{
Parse,
Bound(std::ops::RangeInclusive<usize>),
}
impl error::Error for BoundedU16FromStrError{}
impl fmt::Display for BoundedU16FromStrError
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
match self {
Self::Parse => write!(f, "failed to parse u8"),
Self::Bound(b) => write!(f, "value was not in bounds {:?}", b),
}
}
}
#[derive(Debug)]
pub enum HeightFromStrError
{
Parse(BoundedU16FromStrError),
Unit,
}
impl error::Error for HeightFromStrError
{
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match &self
{
Self::Parse(s) => Some(s),
_ => None,
}
}
}
impl fmt::Display for HeightFromStrError
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
match self {
Self::Unit => write!(f, "invalid/unknown height unit"),
Self::Parse(_) => write!(f, "number was not in bounds for unit"),
}
}
}
#[derive(Debug)]
pub struct HairColourFromStrError;
impl error::Error for HairColourFromStrError{}
impl fmt::Display for HairColourFromStrError
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "invalid hair colour")
}
}
#[derive(Debug)]
pub struct EyeColourFromStrError;
impl error::Error for EyeColourFromStrError{}
impl fmt::Display for EyeColourFromStrError
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "invalid eye colour")
}
}
#[derive(Debug)]
pub struct PassportIdFromStrError;
impl error::Error for PassportIdFromStrError{}
impl fmt::Display for PassportIdFromStrError
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "invalid passport ID")
}
}
// --
#[derive(Debug)]
pub enum PassportParseError
{
PassportID(PassportIdFromStrError),
EyeColour(EyeColourFromStrError),
HairColour(HairColourFromStrError),
Height(HeightFromStrError),
ExprYear(BoundedU16FromStrError),
IssueYear(BoundedU16FromStrError),
BirthYear(BoundedU16FromStrError),
}
impl error::Error for PassportParseError
{
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
use PassportParseError::*;
#[allow(non_snake_case)]
Some(match &self {
PassportID(passportIdFromStrError) => passportIdFromStrError,
EyeColour(eyeColourFromStrError) => eyeColourFromStrError,
HairColour(hairColourFromStrError) => hairColourFromStrError,
Height(heightFromStrError) => heightFromStrError,
ExprYear(boundedU8FromStrError) => boundedU8FromStrError,
IssueYear(boundedU8FromStrError) => boundedU8FromStrError,
BirthYear(boundedU8FromStrError) => boundedU8FromStrError,
})
}
}
impl fmt::Display for PassportParseError
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
use PassportParseError::*;
write!(f, "invalid ")?;
match self {
PassportID(_) => write!(f, "passport id"),
EyeColour(_) => write!(f, "eye colour"),
HairColour(_) =>write!(f, "hair colour"),
Height(_) => write!(f, "height"),
ExprYear(_) => write!(f, "expire year"),
IssueYear(_) => write!(f, "issue year"),
BirthYear(_) => write!(f, "birth year"),
}?;
write!(f, ": ")?;
use error::Error;
write!(f, "{}", self.source().unwrap())
}
}

@ -0,0 +1,63 @@
#![feature(str_split_once)]
#![feature(min_const_generics)]
use std::{
fs,
io::{
self,
BufReader,
BufRead,
},
convert::TryFrom,
};
#[cfg(not(feature="test"))] const INPUT: &str = "input";
#[cfg(feature="test")] const INPUT: &str = "input-test";
mod error;
mod passport;
fn parse_single(from: &mut (impl BufRead + ?Sized)) -> Result<String, io::Error>
{
let mut string = String::new();
loop {
let n = string.len();
let r = from.read_line(&mut string)?;
let nw = &string[n..(n+r)];
if nw.trim().len()==0 {
break;
}
}
Ok(string)
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut input = BufReader::new(fs::OpenOptions::new()
.read(true)
.open(INPUT)?);
let mut valid = 0;
loop {
let line = parse_single(&mut input)?;
if line.trim().len() > 0 {
#[allow(unused_variables)]
if let Ok(ppinf) = passport::PassportInfo::try_from(&line[..]) {
#[cfg(not(feature="part2"))] { valid+=1; }
#[cfg(feature="part2")] {
match passport::Passport::try_from(ppinf) {
Err(err) => {
eprintln!("Error: {}", err);
},
Ok(_) => valid+=1,
}
}
}
} else {
break;
}
}
println!("{}", valid);
Ok(())
}

@ -0,0 +1,226 @@
use std::{
convert::TryFrom,
collections::HashMap,
str,
};
use super::error::*;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PassportInfo<'a>
{
birth_year: &'a str,
issue_year: &'a str,
expr_year: &'a str,
height: &'a str,
hair_colour: &'a str,
eye_colour: &'a str,
pp_id: &'a str,
cn_id: Option<&'a str>,
}
impl<'a> TryFrom<&'a str> for PassportInfo<'a>
{
type Error = InfoParseError;
fn try_from(from: &'a str) -> Result<Self, Self::Error>
{
let mut items = HashMap::new();
for item in from.split_whitespace() {
if let Some((k, v)) = item.split_once(':') {
if items.insert(k, v).is_some() {
return Err(InfoParseError);
}
}
}
macro_rules! parse {
(? $l:literal) => {
items.get($l).map(|&x| x)
};
($l:literal) => {
parse!(? $l).ok_or(InfoParseError)?;
};
}
Ok(Self{
birth_year: parse!("byr"),
issue_year: parse!("iyr"),
expr_year: parse!("eyr"),
height: parse!("hgt"),
hair_colour: parse!("hcl"),
eye_colour: parse!("ecl"),
pp_id: parse!("pid"),
cn_id: parse!(? "cid"),
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct BoundedU16<const LOW: usize, const HIGH: usize>
{
value:u16
}
impl<const LOW: usize, const HIGH:usize> BoundedU16<LOW, HIGH>
{
pub const fn bound() -> std::ops::RangeInclusive<usize>
{
LOW..=HIGH
}
pub const fn contains(value: u16) -> bool
{
let value = value as usize;
value >= LOW && value <= HIGH
}
}
impl<const LOW: usize, const HIGH:usize> str::FromStr for BoundedU16<LOW, HIGH>
{
type Err = BoundedU16FromStrError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let v: u16 = s.parse().map_err(|_| BoundedU16FromStrError::Parse)?;
//println!("{:?}: {}", Self::bound(), v);
if Self::contains(v) {
Ok(Self{value: v})
} else {
Err(BoundedU16FromStrError::Bound(Self::bound()))
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Height
{
Cm(BoundedU16<150, 193>),
In(BoundedU16<56, 76>),
}
impl str::FromStr for Height
{
type Err = HeightFromStrError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.ends_with("cm")
{
let num = &s[..(s.len()-2)];
Ok(Self::Cm(num.parse().map_err(HeightFromStrError::Parse)?))
} else if s.ends_with("in")
{
let num = &s[..(s.len()-2)];
Ok(Self::In(num.parse().map_err(HeightFromStrError::Parse)?))
} else {
Err(HeightFromStrError::Unit)
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct HairColour(String);
impl str::FromStr for HairColour
{
type Err = HairColourFromStrError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
const HEX: &[u8] = b"0123456789abcdef";
lazy_static::lazy_static! {
static ref VALID_CHARS: smallmap::Map<u8, ()> = {
let mut map = smallmap::Map::new();
for &b in HEX.iter()
{
map.insert(b, ());
}
map
};
}
if s.starts_with("#") && s.len() == 7 {
let s = &s[1..];
let bytes = s.as_bytes();
for b in bytes.iter()
{
if !VALID_CHARS.contains_key(b) {
return Err(HairColourFromStrError);
}
}
Ok(Self(s.to_owned()))
} else {
Err(HairColourFromStrError)
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
#[repr(u8)]
pub enum EyeColour
{
Amb, Blu, Brn, Gry, Grn, Hzl, Oth,
}
impl str::FromStr for EyeColour
{
type Err = EyeColourFromStrError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s.trim() {
"amb" => Self::Amb,
"blu" => Self::Blu,
"brn" => Self::Brn,
"gry" => Self::Gry,
"grn" => Self::Grn,
"hzl" => Self::Hzl,
"oth" => Self::Oth,
_ => return Err(EyeColourFromStrError),
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PassportId
{
id: u64,
}
impl str::FromStr for PassportId
{
type Err = PassportIdFromStrError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() == 9 {
Ok(Self{
id: s.parse().map_err(|_| PassportIdFromStrError)?,
})
} else {
Err(PassportIdFromStrError)
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Passport
{
birth_year: BoundedU16<1920, 2002>,
issue_year: BoundedU16<2010, 2020>,
expr_year: BoundedU16<2020, 2030>,
height: Height,
hair_colour: HairColour,
eye_colour: EyeColour,
pp_id: PassportId,
cn_id: Option<()>,
}
impl<'a> TryFrom<PassportInfo<'a>> for Passport
{
type Error = PassportParseError;
fn try_from(from: PassportInfo<'a>) -> Result<Self, Self::Error>
{
Ok(Self{
birth_year: from.birth_year.parse().map_err(PassportParseError::BirthYear)?,
issue_year: from.issue_year.parse().map_err(PassportParseError::IssueYear)?,
expr_year: from.expr_year.parse().map_err(PassportParseError::ExprYear)?,
height: from.height.parse().map_err(PassportParseError::Height)?,
hair_colour: from.hair_colour.parse().map_err(PassportParseError::HairColour)?,
eye_colour: from.eye_colour.parse().map_err(PassportParseError::EyeColour)?,
pp_id: from.pp_id.parse().map_err(PassportParseError::PassportID)?,
cn_id: from.cn_id.map(|_| ()),
})
}
}
Loading…
Cancel
Save