Skip to main content

g_string/
query.rs

1use core::str::{Bytes, CharIndices, Chars};
2
3use crate::{GString, GStringError, Validator};
4
5// basic
6impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool>
7    GString<V, MIN, MAX, ASCII_ONLY>
8{
9    /// Get length of GString in bytes.
10    #[inline]
11    pub const fn len(&self) -> usize {
12        self.len
13    }
14
15    /// Tells maximum capacity returning MAX.
16    #[inline]
17    pub const fn capacity(&self) -> usize {
18        MAX
19    }
20
21    /// Counts Unicode scalar values (`char`).
22    #[inline]
23    pub fn count(&self) -> usize {
24        self.chars().count()
25    }
26
27    /// Check if empty.
28    #[inline]
29    pub const fn is_empty(&self) -> bool {
30        self.len == 0
31    }
32
33    /// Tells if maximum capacity met.
34    #[inline]
35    pub const fn is_full(&self) -> bool {
36        self.len == MAX
37    }
38
39    #[inline]
40    pub const fn is_char_boundary(&self, index: usize) -> bool {
41        self.as_str().is_char_boundary(index)
42    }
43
44    /// Show `GString` as `&str`.
45    #[inline]
46    pub const fn as_str(&self) -> &str {
47        // SAFETY:
48        // GString is always built from valid UTF-8 encoded string.
49        unsafe {
50            let slice = core::slice::from_raw_parts(self.buf.as_ptr(), self.len);
51            core::str::from_utf8_unchecked(slice)
52        }
53    }
54
55    /// Show `GString` as bytes.
56    #[inline(always)]
57    pub const fn as_bytes(&self) -> &[u8] {
58        unsafe { core::slice::from_raw_parts(self.buf.as_ptr(), self.len) }
59    }
60}
61
62// graphemes
63#[cfg(feature = "grapheme")]
64impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool>
65    GString<V, MIN, MAX, ASCII_ONLY>
66{
67    /// Counts user-perceived characters (grapheme clusters).
68    #[inline]
69    pub fn grapheme_count(&self) -> usize {
70        use crate::UnicodeSegmentation;
71        self.as_str().graphemes(true).count()
72    }
73
74    /// Iterate over graphemes.
75    #[inline]
76    pub fn graphemes(&self) -> crate::Graphemes<'_> {
77        use crate::UnicodeSegmentation;
78        self.as_str().graphemes(true)
79    }
80}
81
82// iterator
83impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool>
84    GString<V, MIN, MAX, ASCII_ONLY>
85{
86    /// Returns an iterator over the chars of a string slice.
87    #[inline]
88    pub fn chars(&self) -> Chars<'_> {
89        self.as_str().chars()
90    }
91
92    /// Returns an iterator over the chars of a string slice, and their positions.
93    #[inline]
94    pub fn char_indices(&self) -> CharIndices<'_> {
95        self.as_str().char_indices()
96    }
97
98    /// Returns an iterator over the bytes of a string slice.
99    #[inline]
100    pub fn bytes(&self) -> Bytes<'_> {
101        self.as_str().bytes()
102    }
103}
104
105use core::str::FromStr;
106use core::str::{EncodeUtf16, EscapeDebug, EscapeDefault, EscapeUnicode, Lines, SplitWhitespace};
107
108pub trait Pattern {
109    type SplitIter<'a>: Iterator<Item = &'a str>
110    where
111        Self: 'a;
112
113    type MatchesIter<'a>: Iterator<Item = &'a str>
114    where
115        Self: 'a;
116
117    type RMatchesIter<'a>: Iterator<Item = &'a str>
118    where
119        Self: 'a;
120
121    type MatchIndicesIter<'a>: Iterator<Item = (usize, &'a str)>
122    where
123        Self: 'a;
124
125    type RMatchIndicesIter<'a>: Iterator<Item = (usize, &'a str)>
126    where
127        Self: 'a;
128
129    fn split<'a>(self, s: &'a str) -> Self::SplitIter<'a>
130    where
131        Self: 'a;
132
133    fn split_once<'a>(&self, s: &'a str) -> Option<(&'a str, &'a str)>;
134
135    fn rsplit_once<'a>(&self, s: &'a str) -> Option<(&'a str, &'a str)>;
136
137    fn strip_prefix<'a>(&self, s: &'a str) -> Option<&'a str>;
138
139    fn strip_suffix<'a>(&self, s: &'a str) -> Option<&'a str>;
140
141    fn matches<'a>(self, s: &'a str) -> Self::MatchesIter<'a>
142    where
143        Self: 'a;
144
145    fn rmatches<'a>(self, s: &'a str) -> Self::RMatchesIter<'a>
146    where
147        Self: 'a;
148
149    fn match_indices<'a>(self, s: &'a str) -> Self::MatchIndicesIter<'a>
150    where
151        Self: 'a;
152
153    fn rmatch_indices<'a>(self, s: &'a str) -> Self::RMatchIndicesIter<'a>
154    where
155        Self: 'a;
156
157    // searching
158    //
159
160    fn contains(&self, s: &str) -> bool;
161
162    fn starts_with(&self, s: &str) -> bool;
163
164    fn ends_with(&self, s: &str) -> bool;
165
166    fn find(&self, s: &str) -> Option<usize>;
167
168    fn rfind(&self, s: &str) -> Option<usize>;
169}
170
171// search
172impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool>
173    GString<V, MIN, MAX, ASCII_ONLY>
174{
175    #[inline]
176    pub fn contains<P>(&self, pat: P) -> bool
177    where
178        P: Pattern,
179    {
180        pat.contains(self.as_str())
181    }
182
183    #[inline]
184    pub fn starts_with<P>(&self, pat: P) -> bool
185    where
186        P: Pattern,
187    {
188        pat.starts_with(self.as_str())
189    }
190
191    #[inline]
192    pub fn ends_with<P>(&self, pat: P) -> bool
193    where
194        P: Pattern,
195    {
196        pat.ends_with(self.as_str())
197    }
198
199    #[inline]
200    pub fn find<P>(&self, pat: P) -> Option<usize>
201    where
202        P: Pattern,
203    {
204        pat.find(self.as_str())
205    }
206
207    #[inline]
208    pub fn rfind<P>(&self, pat: P) -> Option<usize>
209    where
210        P: Pattern,
211    {
212        pat.rfind(self.as_str())
213    }
214}
215
216// slicing
217impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool>
218    GString<V, MIN, MAX, ASCII_ONLY>
219{
220    #[inline]
221    pub fn get<I>(&self, i: I) -> Option<&<I as core::slice::SliceIndex<str>>::Output>
222    where
223        I: core::slice::SliceIndex<str>,
224    {
225        self.as_str().get(i)
226    }
227}
228
229// splitting
230
231impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool>
232    GString<V, MIN, MAX, ASCII_ONLY>
233{
234    /// Returns an iterator over substrings of this string slice, separated by
235    /// characters matched by a pattern.
236    #[inline]
237    pub fn split<P: Pattern>(&self, pat: P) -> P::SplitIter<'_> {
238        pat.split(self.as_str())
239    }
240
241    /// Returns an iterator over the non-whitespace substrings of this string
242    /// slice, separated by any amount of whitespace.
243    #[inline]
244    pub fn split_whitespace(&self) -> SplitWhitespace<'_> {
245        self.as_str().split_whitespace()
246    }
247
248    /// Returns an iterator over the lines of this string slice.
249    #[inline]
250    pub fn lines(&self) -> Lines<'_> {
251        self.as_str().lines()
252    }
253
254    /// Divides this string slice into two at the first occurrence of a pattern.
255    #[inline]
256    pub fn split_once<P: Pattern>(&self, delimiter: P) -> Option<(&str, &str)> {
257        delimiter.split_once(self.as_str())
258    }
259
260    /// Divides this string slice into two at the last occurrence of a pattern.
261    #[inline]
262    pub fn rsplit_once<P: Pattern>(&self, delimiter: P) -> Option<(&str, &str)> {
263        delimiter.rsplit_once(self.as_str())
264    }
265}
266
267// trimming
268
269impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool>
270    GString<V, MIN, MAX, ASCII_ONLY>
271{
272    /// Returns a trimmed copy of this string.
273    #[inline]
274    pub fn try_trim(&self) -> Result<Self, GStringError<V::Err>> {
275        Self::try_new(self.as_str().trim())
276    }
277
278    /// Returns a copy of this string with leading whitespace removed.
279    #[inline]
280    pub fn try_trim_start(&self) -> Result<Self, GStringError<V::Err>> {
281        Self::try_new(self.as_str().trim_start())
282    }
283
284    /// Returns a copy of this string with trailing whitespace removed.
285    #[inline]
286    pub fn try_trim_end(&self) -> Result<Self, GStringError<V::Err>> {
287        Self::try_new(self.as_str().trim_end())
288    }
289
290    /// Returns a string slice with the given prefix removed.
291    #[inline]
292    pub fn strip_prefix<P: Pattern>(&self, prefix: P) -> Option<&str> {
293        prefix.strip_prefix(self.as_str())
294    }
295
296    /// Returns a string slice with the given suffix removed.
297    #[inline]
298    pub fn strip_suffix<P: Pattern>(&self, suffix: P) -> Option<&str> {
299        suffix.strip_suffix(self.as_str())
300    }
301}
302
303// matching
304
305impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool>
306    GString<V, MIN, MAX, ASCII_ONLY>
307{
308    /// Returns an iterator over matches of a pattern within this string slice.
309    #[inline]
310    pub fn matches<P: Pattern>(&self, pat: P) -> P::MatchesIter<'_> {
311        pat.matches(self.as_str())
312    }
313
314    /// Returns an iterator over matches of a pattern within this string slice,
315    /// yielded in reverse order.
316    #[inline]
317    pub fn rmatches<P: Pattern>(&self, pat: P) -> P::RMatchesIter<'_> {
318        pat.rmatches(self.as_str())
319    }
320
321    /// Returns an iterator over the disjoint matches of a pattern and their
322    /// positions within this string slice.
323    #[inline]
324    pub fn match_indices<P: Pattern>(&self, pat: P) -> P::MatchIndicesIter<'_> {
325        pat.match_indices(self.as_str())
326    }
327
328    /// Returns an iterator over the disjoint matches of a pattern and their
329    /// positions within this string slice, yielded in reverse order.
330    #[inline]
331    pub fn rmatch_indices<P: Pattern>(&self, pat: P) -> P::RMatchIndicesIter<'_> {
332        pat.rmatch_indices(self.as_str())
333    }
334}
335
336// misc
337
338impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool>
339    GString<V, MIN, MAX, ASCII_ONLY>
340{
341    /// Parses this string slice into another type.
342    #[inline]
343    pub fn parse<F>(&self) -> Result<F, F::Err>
344    where
345        F: FromStr,
346    {
347        self.as_str().parse()
348    }
349
350    /// Checks if all characters in this string are within the ASCII range.
351    #[inline]
352    pub fn is_ascii(&self) -> bool {
353        self.as_str().is_ascii()
354    }
355
356    /// Compares two strings in a case-insensitive ASCII manner.
357    #[inline]
358    pub fn eq_ignore_ascii_case(&self, other: &str) -> bool {
359        self.as_str().eq_ignore_ascii_case(other)
360    }
361}
362
363// escaping
364
365impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool>
366    GString<V, MIN, MAX, ASCII_ONLY>
367{
368    /// Returns an iterator that escapes this string using `char::escape_debug`.
369    #[inline]
370    pub fn escape_debug(&self) -> EscapeDebug<'_> {
371        self.as_str().escape_debug()
372    }
373
374    /// Returns an iterator that escapes this string using
375    /// `char::escape_default`.
376    #[inline]
377    pub fn escape_default(&self) -> EscapeDefault<'_> {
378        self.as_str().escape_default()
379    }
380
381    /// Returns an iterator that escapes this string using
382    /// `char::escape_unicode`.
383    #[inline]
384    pub fn escape_unicode(&self) -> EscapeUnicode<'_> {
385        self.as_str().escape_unicode()
386    }
387}
388
389// encoding
390
391impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool>
392    GString<V, MIN, MAX, ASCII_ONLY>
393{
394    /// Returns an iterator of UTF-16 code units for this string slice.
395    #[inline]
396    pub fn encode_utf16(&self) -> EncodeUtf16<'_> {
397        self.as_str().encode_utf16()
398    }
399}