yozora_tokenizer_html_block/util/
eat_html_attribute.rs1use 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}