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
// reference.rs

use std::borrow::Cow;

use nom::{
    branch::alt,
    bytes::complete::tag,
    character::complete::{digit1, hex_digit1},
    combinator::map,
    multi::many1,
    sequence::delimited,
    IResult,
};

use crate::decode::{decode_digit, decode_hex};
use crate::parse::Parse;

#[derive(Clone, PartialEq)]
pub enum Reference<'a> {
    EntityRef(Cow<'a, str>),
    CharRef {
        value: Cow<'a, str>,
        state: CharRefState,
    },
}

impl<'a> Parse<'a> for Reference<'a> {
    fn parse(input: &'a str) -> IResult<&'a str, Reference<'a>> {
        alt((Self::parse_entity_ref, Self::parse_char_reference))(input)
    }
}

impl<'a> ParseReference<'a> for Reference<'a> {}

#[derive(Clone, PartialEq)]
pub enum CharRefState {
    Decimal,
    Hexadecimal,
}

pub trait ParseReference<'a>: Parse<'a> {
    fn parse_entity_ref(input: &'a str) -> IResult<&'a str, Reference<'a>> {
        let (input, _) = tag("&")(input)?;
        let (input, name) = Self::parse_name(input)?;
        let (input, _) = tag(";")(input)?;
        Ok((input, Reference::EntityRef(name)))
    }

    fn parse_parameter_reference(input: &'a str) -> IResult<&'a str, Reference<'a>> {
        let (input, _) = tag("%")(input)?;
        let (input, name) = Self::parse_name(input)?;
        let (input, _) = tag(";")(input)?;
        Ok((input, Reference::EntityRef(name)))
    }

    fn parse_char_reference(input: &'a str) -> IResult<&'a str, Reference<'a>> {
        alt((
            map(
                delimited(tag("&#"), many1(digit1), tag(";")),
                |digits_vec: Vec<&str>| {
                    let digits_str = digits_vec.concat();
                    let (_, decoded) = decode_digit("", &digits_str).unwrap();
                    Reference::CharRef {
                        value: Cow::Owned(decoded.into_owned()),
                        state: CharRefState::Decimal,
                    }
                },
            ),
            map(
                delimited(tag("&#x"), many1(hex_digit1), tag(";")),
                |hex_vec: Vec<&str>| {
                    let hex_str = hex_vec.concat();
                    let (_, decoded) = decode_hex("", &hex_str).unwrap();
                    Reference::CharRef {
                        value: Cow::Owned(decoded.into_owned()),
                        state: CharRefState::Hexadecimal,
                    }
                },
            ),
        ))(input)
    }
}