Skip to main content

yozora_tokenizer_html_block/util/
eat_html_attribute.rs

1use yozora_character::{
2    is_ascii_digit_character, is_ascii_letter, is_whitespace_character, AsciiCodePoint, NodePoint,
3};
4use yozora_core_tokenizer::{eat_optional_whitespaces, NodeInterval};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub struct RawHtmlAttribute {
8    pub name: NodeInterval,
9    pub value: Option<NodeInterval>,
10}
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub struct EatHtmlAttributeResult {
14    pub attribute: RawHtmlAttribute,
15    pub next_index: usize,
16}
17
18pub fn eat_html_attribute(
19    node_points: &[NodePoint],
20    start_index: usize,
21    end_index: usize,
22) -> Option<EatHtmlAttributeResult> {
23    let mut i = eat_optional_whitespaces(node_points, start_index, end_index);
24    if i <= start_index || i >= end_index {
25        return None;
26    }
27
28    let attr_name_start_index = i;
29    let mut code_point = node_points[i].code_point;
30    if !is_ascii_letter(code_point)
31        && code_point != AsciiCodePoint::UNDERSCORE as i32
32        && code_point != AsciiCodePoint::COLON as i32
33    {
34        return None;
35    }
36
37    i = attr_name_start_index + 1;
38    while i < end_index {
39        code_point = node_points[i].code_point;
40        if is_ascii_letter(code_point)
41            || is_ascii_digit_character(code_point)
42            || code_point == AsciiCodePoint::UNDERSCORE as i32
43            || code_point == AsciiCodePoint::DOT as i32
44            || code_point == AsciiCodePoint::COLON as i32
45            || code_point == AsciiCodePoint::MINUS_SIGN as i32
46        {
47            i += 1;
48            continue;
49        }
50        break;
51    }
52    let attr_name_end_index = i;
53    let mut attribute = RawHtmlAttribute {
54        name: NodeInterval {
55            start_index: attr_name_start_index,
56            end_index: attr_name_end_index,
57        },
58        value: None,
59    };
60
61    i = eat_optional_whitespaces(node_points, attr_name_end_index, end_index);
62    if i < end_index && node_points[i].code_point == AsciiCodePoint::EQUALS_SIGN as i32 {
63        i = eat_optional_whitespaces(node_points, i + 1, end_index);
64        if i < end_index {
65            match node_points[i].code_point {
66                code_point if code_point == AsciiCodePoint::DOUBLE_QUOTE as i32 => {
67                    let attr_value_start_index = i + 1;
68                    i = attr_value_start_index;
69                    while i < end_index
70                        && node_points[i].code_point != AsciiCodePoint::DOUBLE_QUOTE as i32
71                    {
72                        i += 1;
73                    }
74                    if i < end_index {
75                        attribute.value = Some(NodeInterval {
76                            start_index: attr_value_start_index,
77                            end_index: i,
78                        });
79                        i += 1;
80                    }
81                }
82                code_point if code_point == AsciiCodePoint::SINGLE_QUOTE as i32 => {
83                    let attr_value_start_index = i + 1;
84                    i = attr_value_start_index;
85                    while i < end_index
86                        && node_points[i].code_point != AsciiCodePoint::SINGLE_QUOTE as i32
87                    {
88                        i += 1;
89                    }
90                    if i < end_index {
91                        attribute.value = Some(NodeInterval {
92                            start_index: attr_value_start_index,
93                            end_index: i,
94                        });
95                        i += 1;
96                    }
97                }
98                _ => {
99                    let attr_value_start_index = i;
100                    while i < end_index {
101                        code_point = node_points[i].code_point;
102                        if is_whitespace_character(code_point)
103                            || code_point == AsciiCodePoint::DOUBLE_QUOTE as i32
104                            || code_point == AsciiCodePoint::SINGLE_QUOTE as i32
105                            || code_point == AsciiCodePoint::EQUALS_SIGN as i32
106                            || code_point == AsciiCodePoint::OPEN_ANGLE as i32
107                            || code_point == AsciiCodePoint::CLOSE_ANGLE as i32
108                            || code_point == AsciiCodePoint::BACKTICK as i32
109                        {
110                            break;
111                        }
112                        i += 1;
113                    }
114                    if i > attr_value_start_index {
115                        attribute.value = Some(NodeInterval {
116                            start_index: attr_value_start_index,
117                            end_index: i,
118                        });
119                    }
120                }
121            }
122
123            if attribute.value.is_some() {
124                return Some(EatHtmlAttributeResult {
125                    attribute,
126                    next_index: i,
127                });
128            }
129        }
130    }
131
132    Some(EatHtmlAttributeResult {
133        attribute,
134        next_index: attr_name_end_index,
135    })
136}