noa_parser/bytes/
matchers.rs

1//! Byte slice matchers.
2/// Attempt to match a single character against a byte slice.
3///
4/// # Arguments
5///
6/// * `pattern` - The character to match against.
7/// * `data` - The byte slice to match against.
8///
9/// # Returns
10///
11/// A tuple containing a boolean indicating whether the match succeeded and
12/// the number of bytes consumed if the match succeeded.
13pub fn match_char(pattern: char, data: &[u8]) -> (bool, usize) {
14    (pattern as u8 == data[0], 1)
15}
16
17/// Attempt to match a byte slice against a byte slice.
18///
19/// # Arguments
20///
21/// * `pattern` - The byte slice to match against.
22/// * `data` - The byte slice to match against.
23///
24/// # Returns
25///
26/// A tuple containing a boolean indicating whether the match succeeded and
27/// the number of bytes consumed if the match succeeded.
28pub fn match_pattern(pattern: &[u8], data: &[u8]) -> (bool, usize) {
29    if pattern.is_empty() {
30        return (false, 0);
31    }
32
33    if pattern.len() > data.len() {
34        return (false, 0);
35    }
36
37    if pattern.eq_ignore_ascii_case(&data[..pattern.len()]) {
38        return (true, pattern.len());
39    }
40
41    (false, 0)
42}
43
44/// Attempt to match a number against a byte slice.
45///
46/// # Arguments
47///
48/// * `data` - The byte slice to match against.
49///
50/// # Returns
51///
52/// A tuple containing a boolean indicating whether the match succeeded and
53/// the number of bytes consumed if the match succeeded.
54pub fn match_number(data: &[u8]) -> (bool, usize) {
55    if data.is_empty() {
56        return (false, 0);
57    }
58
59    let mut pos = 0;
60    let mut found = false;
61
62    loop {
63        if pos == data.len() {
64            break;
65        }
66        if data[pos].is_ascii_digit() {
67            pos += 1;
68            found = true;
69            continue;
70        }
71        break;
72    }
73
74    (found, pos)
75}
76
77/// Attempt to match a string against a byte slice.
78/// Stop matching when a punctuation character is encountered.
79///  * U+0021 ..= U+002F ! " # $ % & ' ( ) * + , - . /, or
80///  * U+003A ..= U+0040 : ; < = > ? @, or
81///  * U+005B ..= U+0060 [ \ ] ^ _ `, or
82///  * U+007B ..= U+007E { | } ~
83///
84/// # Arguments
85///
86/// * `data` - The byte slice to match against.
87///
88/// # Returns
89///
90/// A tuple containing a boolean indicating whether the match succeeded and
91/// the number of bytes consumed if the match succeeded.
92pub fn match_string(data: &[u8]) -> (bool, usize) {
93    if data.is_empty() {
94        return (false, 0);
95    }
96
97    let mut pos = 0;
98    let mut found = false;
99
100    loop {
101        if pos == data.len() {
102            break;
103        }
104        if !data[pos].is_ascii_punctuation() && !data[pos].is_ascii_whitespace() {
105            pos += 1;
106            found = true;
107            continue;
108        }
109        break;
110    }
111
112    (found, pos)
113}
114
115#[cfg(test)]
116mod tests {
117    use crate::bytes::matchers::{match_char, match_number, match_pattern, match_string};
118
119    #[test]
120    fn test_match_char() {
121        let (result, consumed) = match_char('a', b"abc");
122        assert!(result);
123        assert_eq!(consumed, 1);
124
125        let (result, consumed) = match_char('b', b"abc");
126        assert!(!result);
127        assert_eq!(consumed, 1);
128    }
129
130    #[test]
131    fn test_match_pattern() {
132        let (result, consumed) = match_pattern(b"abc", b"abcdef");
133        assert!(result);
134        assert_eq!(consumed, 3);
135
136        let (result, consumed) = match_pattern(b"abc", b"bbcdefg");
137        assert!(!result);
138        assert_eq!(consumed, 0);
139    }
140
141    #[test]
142    fn test_match_number() {
143        let (result, consumed) = match_number(b"123abc");
144        assert!(result);
145        assert_eq!(consumed, 3);
146
147        let (result, consumed) = match_number(b"abc123");
148        assert!(!result);
149        assert_eq!(consumed, 0);
150    }
151
152    #[test]
153    fn test_match_string() {
154        let (result, consumed) = match_string(b"abc123(");
155        assert!(result);
156        assert_eq!(consumed, 6);
157    }
158}