libyaml_safer/
macros.rs

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        // ASCII
81        '\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}