1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
use utils::parser::{ParseError, ParseResult};
use utils::prelude::*;

/// JSON document numbers.
#[derive(Clone, Debug)]
pub struct Day12 {
    part1: i32,
    part2: i32,
}

impl Day12 {
    pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
        let (part1, part2) = Self::parse.parse_complete(input)?;
        Ok(Self { part1, part2 })
    }

    #[must_use]
    pub fn part1(&self) -> i32 {
        self.part1
    }

    #[must_use]
    pub fn part2(&self) -> i32 {
        self.part2
    }

    fn parse(input: &[u8]) -> ParseResult<(i32, i32)> {
        match input {
            [b'{', ..] => Self::parse_object(&input[1..]),
            [b'[', ..] => Self::parse_array(&input[1..]),
            _ => Err((ParseError::Expected("'{' or '['"), input)),
        }
    }

    fn parse_object(mut input: &[u8]) -> ParseResult<(i32, i32)> {
        let (mut part1, mut part2) = (0, 0);
        let mut red = false;
        loop {
            match input {
                [b'1'..=b'9', ..] | [b'-', b'1'..=b'9', ..] => {
                    let (num, remaining) = parser::i32().parse(input)?;
                    input = remaining;
                    part1 += num;
                    part2 += num;
                }
                [b'"', b'r', b'e', b'd', b'"', ..] => {
                    input = &input[5..];
                    red = true;
                }
                [b'{', ..] => {
                    let (result, remaining) = Self::parse_object(&input[1..])?;
                    input = remaining;
                    part1 += result.0;
                    part2 += result.1;
                }
                [b'[', ..] => {
                    let (result, remaining) = Self::parse_array(&input[1..])?;
                    input = remaining;
                    part1 += result.0;
                    part2 += result.1;
                }
                [b'}', ..] => return Ok(((part1, if red { 0 } else { part2 }), &input[1..])),
                [] => return Err((ParseError::ExpectedByte(b']'), input)),
                _ => input = &input[1..],
            }
        }
    }

    fn parse_array(mut input: &[u8]) -> ParseResult<(i32, i32)> {
        let (mut part1, mut part2) = (0, 0);
        loop {
            match input {
                [b'1'..=b'9', ..] | [b'-', b'1'..=b'9', ..] => {
                    let (num, remaining) = parser::i32().parse(input)?;
                    input = remaining;
                    part1 += num;
                    part2 += num;
                }
                [b'{', ..] => {
                    let (result, remaining) = Self::parse_object(&input[1..])?;
                    input = remaining;
                    part1 += result.0;
                    part2 += result.1;
                }
                [b'[', ..] => {
                    let (result, remaining) = Self::parse_array(&input[1..])?;
                    input = remaining;
                    part1 += result.0;
                    part2 += result.1;
                }
                [b']', ..] => return Ok(((part1, part2), &input[1..])),
                [] => return Err((ParseError::ExpectedByte(b']'), input)),
                _ => input = &input[1..],
            }
        }
    }
}

examples!(Day12 -> (i32, i32) [
    {input: r#"[1,2,3]"#, part1: 6, part2: 6},
    {input: r#"{"a":2,"b":4}"#, part1: 6},
    {input: r#"[[[3]]]"#, part1: 3},
    {input: r#"{"a":{"b":4},"c":-1}"#, part1: 3},
    {input: r#"{"a":[-1,1]}"#, part1: 0},
    {input: r#"[-1,{"a":1}]"#, part1: 0},
    {input: r#"[]"#, part1: 0},
    {input: r#"{}"#, part1: 0},
    {input: r#"[1,{"c":"red","b":2},3]"#, part2: 4},
    {input: r#"{"d":"red","e":[1,2,3,4],"f":5}"#, part2: 0},
    {input: r#"[1,"red",5]"#, part2: 6},
]);