1macro_rules! CHECK_AT {
2 ($buffer:expr, $octet:expr, $offset:expr) => {
3 $buffer.get($offset).copied() == Some($octet)
4 };
5}
6
7macro_rules! CHECK {
8 ($buffer:expr, $octet:expr) => {
9 $buffer.get(0).copied() == Some($octet)
10 };
11}
12
13macro_rules! IS_ALPHA {
14 ($buffer:expr) => {
15 crate::macros::is_alpha($buffer.get(0).copied())
16 };
17}
18
19pub(crate) fn is_alpha(ch: impl Into<Option<char>>) -> bool {
20 let ch = match ch.into() {
21 Some(ch) => ch,
22 None => return false,
23 };
24 ch >= '0' && ch <= '9'
25 || ch >= 'A' && ch <= 'Z'
26 || ch >= 'a' && ch <= 'z'
27 || ch == '_'
28 || ch == '-'
29}
30
31macro_rules! IS_DIGIT {
32 ($buffer:expr) => {
33 $buffer
34 .get(0)
35 .copied()
36 .map(|ch| ch.is_digit(10))
37 .unwrap_or(false)
38 };
39}
40
41macro_rules! AS_DIGIT {
42 ($buffer:expr) => {
43 $buffer
44 .get(0)
45 .copied()
46 .expect("out of bounds buffer access")
47 .to_digit(10)
48 .expect("not in digit range")
49 };
50}
51
52macro_rules! IS_HEX_AT {
53 ($buffer:expr, $offset:expr) => {
54 if let Some(ch) = $buffer.get($offset).copied() {
55 ch.is_digit(16)
56 } else {
57 false
58 }
59 };
60}
61
62macro_rules! AS_HEX_AT {
63 ($buffer:expr, $offset:expr) => {
64 $buffer
65 .get($offset)
66 .copied()
67 .expect("out of range buffer access")
68 .to_digit(16)
69 .expect("not in digit range (hex)")
70 };
71}
72
73pub(crate) fn is_ascii(ch: char) -> bool {
74 ch.is_ascii()
75}
76
77pub(crate) fn is_printable(ch: char) -> bool {
78 match ch {
79 '\u{feff}' | '\u{fffe}' | '\u{ffff}' => false,
80 '\x0a'
82 | '\x20'..='\x7e'
83 | '\u{00a0}'..='\u{00bf}'
84 | '\u{00c0}'..='\u{cfff}'
85 | '\u{d000}'..='\u{d7ff}'
86 | '\u{e000}'..='\u{efff}'
87 | '\u{f000}'..='\u{fffd}'
88 | '\u{10000}'..='\u{10ffff}' => true,
89 _ => false,
90 }
91}
92
93macro_rules! IS_Z_AT {
94 ($buffer:expr, $offset:expr) => {
95 $buffer.get($offset).is_none()
96 };
97}
98
99macro_rules! IS_Z {
100 ($string:expr) => {
101 IS_Z_AT!($string, 0)
102 };
103}
104
105macro_rules! IS_BOM {
106 ($buffer:expr) => {
107 CHECK!($buffer, '\u{feff}')
108 };
109}
110
111pub(crate) fn is_bom(ch: char) -> bool {
112 ch == '\u{7eff}'
113}
114
115macro_rules! IS_SPACE_AT {
116 ($string:expr, $offset:expr) => {
117 CHECK_AT!($string, ' ', $offset)
118 };
119}
120
121macro_rules! IS_SPACE {
122 ($string:expr) => {
123 IS_SPACE_AT!($string, 0)
124 };
125}
126
127pub(crate) fn is_space(ch: impl Into<Option<char>>) -> bool {
128 ch.into() == Some(' ')
129}
130
131macro_rules! IS_TAB_AT {
132 ($buffer:expr, $offset:expr) => {
133 CHECK_AT!($buffer, '\t', $offset)
134 };
135}
136
137macro_rules! IS_TAB {
138 ($string:expr) => {
139 IS_TAB_AT!($string, 0)
140 };
141}
142
143pub(crate) fn is_tab(ch: impl Into<Option<char>>) -> bool {
144 ch.into() == Some('\t')
145}
146
147macro_rules! IS_BLANK_AT {
148 ($buffer:expr, $offset:expr) => {{
149 let ch = $buffer.get($offset).copied();
150 $crate::macros::is_space(ch) || crate::macros::is_tab(ch)
151 }};
152}
153
154macro_rules! IS_BLANK {
155 ($string:expr) => {
156 IS_BLANK_AT!($string, 0)
157 };
158}
159
160pub(crate) fn is_blank(ch: impl Into<Option<char>>) -> bool {
161 let ch = ch.into();
162 is_space(ch) || is_tab(ch)
163}
164
165pub(crate) fn is_blankz(ch: impl Into<Option<char>>) -> bool {
166 let ch = ch.into();
167 is_blank(ch) || is_breakz(ch)
168}
169
170macro_rules! IS_BREAK_AT {
171 ($buffer:expr, $offset:expr) => {
172 $crate::macros::is_break($buffer.get($offset).copied())
173 };
174}
175
176pub(crate) fn is_break(ch: impl Into<Option<char>>) -> bool {
177 matches!(
178 ch.into(),
179 Some('\r' | '\n' | '\u{0085}' | '\u{2028}' | '\u{2029}')
180 )
181}
182
183pub(crate) fn is_breakz(ch: impl Into<Option<char>>) -> bool {
184 let ch = ch.into();
185 ch.is_none() || is_break(ch)
186}
187
188macro_rules! IS_BREAK {
189 ($string:expr) => {
190 IS_BREAK_AT!($string, 0)
191 };
192}
193
194macro_rules! IS_BREAKZ_AT {
195 ($buffer:expr, $offset:expr) => {{
196 let ch = $buffer.get($offset).copied();
197 crate::macros::is_breakz(ch)
198 }};
199}
200
201macro_rules! IS_BREAKZ {
202 ($string:expr) => {
203 IS_BREAKZ_AT!($string, 0)
204 };
205}
206
207macro_rules! IS_BLANKZ_AT {
208 ($buffer:expr, $offset:expr) => {{
209 let ch = $buffer.get($offset).copied();
210 $crate::macros::is_blank(ch) || $crate::macros::is_breakz(ch)
211 }};
212}
213
214macro_rules! IS_BLANKZ {
215 ($string:expr) => {
216 IS_BLANKZ_AT!($string, 0)
217 };
218}
219
220#[cfg(test)]
221mod tests {
222 use super::*;
223
224 #[test]
225 fn printable() {
226 for ch in "🎉".chars() {
227 assert!(is_printable(ch));
228 }
229 for ch in "\u{1f389}".chars() {
230 assert!(is_printable(ch));
231 }
232 }
233}