aocoracle 0.1.2

Universal solver for advent of code problems
Documentation
use std::collections::HashMap;
use std::fs;
type AnyError = Box<dyn std::error::Error>;
type Census = HashMap<u32, u64>;
fn _census(line: &str) -> Result<Census, AnyError> {
    let mut result = HashMap::new();
    for countdown in line.trim_end().split(',') {
        let countdown = countdown.parse::<u32>()?;
        *(result.entry(countdown).or_insert(0)) += 1;
    }
    match *result
        .keys()
        .max()
        .ok_or("Expected at least 1 fish but got 0")?
    {
        v if v > 8 => {
            Err(format!("Highest cooldown is {}, this is probably day 7 input", v).into())
        }
        _ => Ok(result),
    }
}

fn _next_census(prev: &Census) -> Census {
    let mut result = HashMap::with_capacity(prev.len());
    for (countdown, count) in prev {
        match countdown {
            0 => {
                *(result.entry(6).or_insert(0)) += count;
                *(result.entry(8).or_insert(0)) += count;
            }
            _ => {
                *(result.entry(countdown - 1).or_insert(0)) += count;
            }
        }
    }
    result
}

fn _nth_census(initial: Census, n: u32) -> Census {
    let mut census = initial;
    for _ in 0..n {
        census = _next_census(&census);
    }
    census
}

pub fn part_1(input: &str) -> Result<String, AnyError> {
    let initial = _census(input)?;
    let num_fish = _nth_census(initial, 80).values().sum::<u64>();
    Ok(format!("{}", num_fish))
}

pub fn part_2(input: &str) -> Result<String, AnyError> {
    let initial = _census(input)?;
    let num_fish = _nth_census(initial, 256).values().sum::<u64>();
    Ok(format!("{}", num_fish))
}

fn _from_file<F, T>(func: F, stem: &str) -> T
where
    F: Fn(&str) -> Result<T, AnyError>,
{
    func(&fs::read_to_string(format!("inputs/06/{}.txt", stem)).unwrap()).unwrap()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn part_1_works_on_example() {
        assert_eq!(_from_file(part_1, "example"), "5934");
    }

    #[test]
    fn part_1_works_on_input() {
        assert_eq!(_from_file(part_1, "input"), "372300");
    }

    #[test]
    fn part_2_works_on_example() {
        assert_eq!(_from_file(part_2, "example"), "26984457539");
    }

    #[test]
    fn part_2_works_on_input() {
        assert_eq!(_from_file(part_2, "input"), "1675781200288");
    }
}