Skip to main content

ct_regex_internal/haystack/
item.rs

1use std::fmt::Debug;
2
3use crate::sealed::Sealed;
4
5/// A trait that represents an individual item that can be matched against a
6/// [`Regex`](crate::expr::Regex). The two implementers are [`char`] and [`u8`].
7///
8/// # Sealed
9///
10/// This trait is sealed, preventing implementations because the `regex!` macro can't produce
11/// `Regex` types that match against any `HaystackItem` other than the default. If you need to match
12/// against another item type and want to use this crate, you may as well fork it and update the
13/// proc macro too, so that you don't have to write manual `Matcher` expressions.
14pub trait HaystackItem: Sealed + Debug + Default + Copy + Eq + Ord {
15    /// Creates a `Vec` of this item from the provided `&str`, used to convert string literals from
16    /// parsed regular expressions into individual `HaystackItem`s that can be matched in a
17    /// haystack.
18    fn collect_from_str(value: &str) -> Vec<Self>;
19
20    /// Creates a `Vec` of this item from the provided `&[u8]`, used to convert a series of bytes to
21    /// match from parsed regular expressions into individual `HaystackItem`s.
22    fn collect_from_bytes(value: &[u8]) -> Vec<Self>;
23
24    /// Check is this item is a newline character ('\n' or b'\n'). Used for string and line
25    /// anchoring.
26    fn is_newline(self) -> bool;
27
28    /// Check is this item is a carriage return character ('\r' or b'\r'). Used for string and line
29    /// anchoring.
30    fn is_return(self) -> bool;
31}
32
33/// A helper for getting the first `char` of a provided `&str`. Returns the width of the character
34/// (possibly zero) and the character itself.
35pub(crate) fn first_char_and_width(value: &str) -> (usize, Option<char>) {
36    // Unfortunately, I don't think there is a stable way to get `char`s from a `str` without using
37    // the `chars` or `char_indices` iterators. We can calculate the width easily but may as well
38    // have it done for us.
39    let mut iter = value.char_indices();
40    let first = iter.next();
41    (iter.offset(), first.map(|(_, c)| c))
42}
43
44pub(crate) fn first_char(value: &str) -> Option<char> {
45    value.chars().next()
46}
47
48impl Sealed for char {}
49
50impl HaystackItem for char {
51    fn collect_from_str(value: &str) -> Vec<Self> {
52        value.chars().collect()
53    }
54
55    fn collect_from_bytes(value: &[u8]) -> Vec<Self> {
56        Self::collect_from_str(
57            str::from_utf8(value).expect("failed to convert bytes to valid unicode")
58        )
59
60    }
61
62    fn is_newline(self) -> bool {
63        self == '\n'
64    }
65
66    fn is_return(self) -> bool {
67        self == '\r'
68    }
69}
70
71impl Sealed for u8 {}
72
73impl HaystackItem for u8 {
74    fn collect_from_str(value: &str) -> Vec<Self> {
75        Self::collect_from_bytes(value.as_bytes())
76    }
77
78    fn collect_from_bytes(s: &[u8]) -> Vec<Self> {
79        s.to_vec()
80    }
81
82    fn is_newline(self) -> bool {
83        self == b'\n'
84    }
85
86    fn is_return(self) -> bool {
87        self == b'\r'
88    }
89}