Skip to main content

glib/
regex.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3// rustdoc-stripper-ignore-next
4//! This module is inefficient and should not be used by Rust programs except for
5//! compatibility with GLib.Regex based APIs.
6
7use crate::{
8    GStr, GStringPtr, MatchInfo, PtrSlice, Regex, RegexCompileFlags, RegexMatchFlags, ffi,
9    translate::*,
10};
11use std::{mem, ptr};
12
13impl Regex {
14    /// Retrieves the number of the subexpression named @name.
15    /// ## `name`
16    /// name of the subexpression
17    ///
18    /// # Returns
19    ///
20    /// The number of the subexpression or -1 if @name
21    ///   does not exists
22    #[doc(alias = "g_regex_get_string_number")]
23    #[doc(alias = "get_string_number")]
24    pub fn string_number(&self, name: impl IntoGStr) -> i32 {
25        name.run_with_gstr(|name| unsafe {
26            ffi::g_regex_get_string_number(self.to_glib_none().0, name.to_glib_none().0)
27        })
28    }
29
30    /// Escapes the nul characters in @string to "\x00".  It can be used
31    /// to compile a regex with embedded nul characters.
32    ///
33    /// For completeness, @length can be -1 for a nul-terminated string.
34    /// In this case the output string will be of course equal to @string.
35    /// ## `string`
36    /// the string to escape
37    /// ## `length`
38    /// the length of @string
39    ///
40    /// # Returns
41    ///
42    /// a newly-allocated escaped string
43    #[doc(alias = "g_regex_escape_nul")]
44    pub fn escape_nul(string: impl IntoGStr) -> crate::GString {
45        unsafe {
46            string.run_with_gstr(|string| {
47                from_glib_full(ffi::g_regex_escape_nul(
48                    string.to_glib_none().0,
49                    string.len() as _,
50                ))
51            })
52        }
53    }
54
55    /// Escapes the special characters used for regular expressions
56    /// in @string, for instance "a.b*c" becomes "a\.b\*c". This
57    /// function is useful to dynamically generate regular expressions.
58    ///
59    /// @string can contain nul characters that are replaced with "\0",
60    /// in this case remember to specify the correct length of @string
61    /// in @length.
62    /// ## `string`
63    /// the string to escape
64    /// ## `length`
65    /// the length of @string, in bytes, or -1 if @string is nul-terminated
66    ///
67    /// # Returns
68    ///
69    /// a newly-allocated escaped string
70    #[doc(alias = "g_regex_escape_string")]
71    pub fn escape_string(string: impl IntoGStr) -> crate::GString {
72        unsafe {
73            string.run_with_gstr(|string| {
74                from_glib_full(ffi::g_regex_escape_string(
75                    string.to_glib_none().0,
76                    string.len() as _,
77                ))
78            })
79        }
80    }
81
82    /// Checks whether @replacement is a valid replacement string
83    /// (see g_regex_replace()), i.e. that all escape sequences in
84    /// it are valid.
85    ///
86    /// If @has_references is not [`None`] then @replacement is checked
87    /// for pattern references. For instance, replacement text 'foo\n'
88    /// does not contain references and may be evaluated without information
89    /// about actual match, but '\0\1' (whole match followed by first
90    /// subpattern) requires valid #GMatchInfo object.
91    /// ## `replacement`
92    /// the replacement string
93    ///
94    /// # Returns
95    ///
96    /// whether @replacement is a valid replacement string
97    ///
98    /// ## `has_references`
99    /// location to store information about
100    ///   references in @replacement or [`None`]
101    #[doc(alias = "g_regex_check_replacement")]
102    pub fn check_replacement(replacement: impl IntoGStr) -> Result<bool, crate::Error> {
103        replacement.run_with_gstr(|replacement| unsafe {
104            let mut has_references = mem::MaybeUninit::uninit();
105            let mut error = ptr::null_mut();
106            let is_ok = ffi::g_regex_check_replacement(
107                replacement.to_glib_none().0,
108                has_references.as_mut_ptr(),
109                &mut error,
110            );
111            debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
112            if error.is_null() {
113                Ok(from_glib(has_references.assume_init()))
114            } else {
115                Err(from_glib_full(error))
116            }
117        })
118    }
119
120    /// Scans for a match in @string for @pattern.
121    ///
122    /// This function is equivalent to g_regex_match() but it does not
123    /// require to compile the pattern with g_regex_new(), avoiding some
124    /// lines of code when you need just to do a match without extracting
125    /// substrings, capture counts, and so on.
126    ///
127    /// If this function is to be called on the same @pattern more than
128    /// once, it's more efficient to compile the pattern once with
129    /// g_regex_new() and then use g_regex_match().
130    /// ## `pattern`
131    /// the regular expression
132    /// ## `string`
133    /// the string to scan for matches
134    /// ## `compile_options`
135    /// compile options for the regular expression, or 0
136    /// ## `match_options`
137    /// match options, or 0
138    ///
139    /// # Returns
140    ///
141    /// [`true`] if the string matched, [`false`] otherwise
142    #[doc(alias = "g_regex_match_simple")]
143    pub fn match_simple(
144        pattern: impl IntoGStr,
145        string: impl IntoGStr,
146        compile_options: RegexCompileFlags,
147        match_options: RegexMatchFlags,
148    ) -> bool {
149        pattern.run_with_gstr(|pattern| {
150            string.run_with_gstr(|string| unsafe {
151                from_glib(ffi::g_regex_match_simple(
152                    pattern.to_glib_none().0,
153                    string.to_glib_none().0,
154                    compile_options.into_glib(),
155                    match_options.into_glib(),
156                ))
157            })
158        })
159    }
160
161    /// Replaces all occurrences of the pattern in @self with the
162    /// replacement text. Backreferences of the form `\number` or
163    /// `\g<number>` in the replacement text are interpolated by the
164    /// number-th captured subexpression of the match, `\g<name>` refers
165    /// to the captured subexpression with the given name. `\0` refers
166    /// to the complete match, but `\0` followed by a number is the octal
167    /// representation of a character. To include a literal `\` in the
168    /// replacement, write `\\\\`.
169    ///
170    /// There are also escapes that changes the case of the following text:
171    ///
172    /// - `\l`: Convert to lower case the next character
173    /// - `\u`: Convert to upper case the next character
174    /// - `\L`: Convert to lower case until the next `\E`
175    /// - `\U`: Convert to upper case until the next `\E`
176    /// - `\E`: End case modification
177    ///
178    /// If you do not need to use backreferences use g_regex_replace_literal().
179    ///
180    /// The @replacement string must be UTF-8 encoded even if [`RegexCompileFlags::RAW`][crate::RegexCompileFlags::RAW] was
181    /// passed to g_regex_new(). If you want to use not UTF-8 encoded strings
182    /// you can use g_regex_replace_literal().
183    ///
184    /// Setting @start_position differs from just passing over a shortened
185    /// string and setting [`RegexMatchFlags::NOTBOL`][crate::RegexMatchFlags::NOTBOL] in the case of a pattern that
186    /// begins with any kind of lookbehind assertion, such as `"\b"`.
187    /// ## `string`
188    /// the string to perform matches against
189    /// ## `string_len`
190    /// the length of @string, in bytes, or -1 if @string is nul-terminated
191    /// ## `start_position`
192    /// starting index of the string to match, in bytes
193    /// ## `replacement`
194    /// text to replace each match with
195    /// ## `match_options`
196    /// options for the match
197    ///
198    /// # Returns
199    ///
200    /// a newly allocated string containing the replacements
201    #[doc(alias = "g_regex_replace")]
202    pub fn replace(
203        &self,
204        string: impl IntoGStr,
205        start_position: i32,
206        replacement: impl IntoGStr,
207        match_options: RegexMatchFlags,
208    ) -> Result<crate::GString, crate::Error> {
209        unsafe {
210            string.run_with_gstr(|string| {
211                replacement.run_with_gstr(|replacement| {
212                    let mut error = ptr::null_mut();
213                    let ret = ffi::g_regex_replace(
214                        self.to_glib_none().0,
215                        string.as_ptr() as *const _,
216                        string.len() as _,
217                        start_position,
218                        replacement.to_glib_none().0,
219                        match_options.into_glib(),
220                        &mut error,
221                    );
222                    debug_assert_eq!(ret.is_null(), !error.is_null());
223                    if error.is_null() {
224                        Ok(from_glib_full(ret))
225                    } else {
226                        Err(from_glib_full(error))
227                    }
228                })
229            })
230        }
231    }
232
233    /// Using the standard algorithm for regular expression matching only
234    /// the longest match in the string is retrieved. This function uses
235    /// a different algorithm so it can retrieve all the possible matches.
236    /// For more documentation see g_regex_match_all_full().
237    ///
238    /// A #GMatchInfo structure, used to get information on the match, is
239    /// stored in @match_info if not [`None`]. Note that if @match_info is
240    /// not [`None`] then it is created even if the function returns [`false`],
241    /// i.e. you must free it regardless if regular expression actually
242    /// matched.
243    ///
244    /// @string is not copied and is used in #GMatchInfo internally. If
245    /// you use any #GMatchInfo method (except g_match_info_free()) after
246    /// freeing or modifying @string then the behaviour is undefined.
247    /// ## `string`
248    /// the string to scan for matches
249    /// ## `match_options`
250    /// match options
251    ///
252    /// # Returns
253    ///
254    /// [`true`] is the string matched, [`false`] otherwise
255    ///
256    /// ## `match_info`
257    /// pointer to location where to store
258    ///     the #GMatchInfo, or [`None`] if you do not need it
259    #[doc(alias = "g_regex_match_all")]
260    pub fn match_all<'input>(
261        &self,
262        string: &'input GStr,
263        match_options: RegexMatchFlags,
264    ) -> Result<MatchInfo<'input>, crate::Error> {
265        self.match_all_full(string, 0, match_options)
266    }
267
268    /// Using the standard algorithm for regular expression matching only
269    /// the longest match in the @string is retrieved, it is not possible
270    /// to obtain all the available matches. For instance matching
271    /// `"<a> <b> <c>"` against the pattern `"<.*>"`
272    /// you get `"<a> <b> <c>"`.
273    ///
274    /// This function uses a different algorithm (called DFA, i.e. deterministic
275    /// finite automaton), so it can retrieve all the possible matches, all
276    /// starting at the same point in the string. For instance matching
277    /// `"<a> <b> <c>"` against the pattern `"<.*>"`
278    /// you would obtain three matches: `"<a> <b> <c>"`,
279    /// `"<a> <b>"` and `"<a>"`.
280    ///
281    /// The number of matched strings is retrieved using
282    /// g_match_info_get_match_count(). To obtain the matched strings and
283    /// their position you can use, respectively, g_match_info_fetch() and
284    /// g_match_info_fetch_pos(). Note that the strings are returned in
285    /// reverse order of length; that is, the longest matching string is
286    /// given first.
287    ///
288    /// Note that the DFA algorithm is slower than the standard one and it
289    /// is not able to capture substrings, so backreferences do not work.
290    ///
291    /// Setting @start_position differs from just passing over a shortened
292    /// string and setting [`RegexMatchFlags::NOTBOL`][crate::RegexMatchFlags::NOTBOL] in the case of a pattern
293    /// that begins with any kind of lookbehind assertion, such as "\b".
294    ///
295    /// Unless [`RegexCompileFlags::RAW`][crate::RegexCompileFlags::RAW] is specified in the options, @string must be valid UTF-8.
296    ///
297    /// A #GMatchInfo structure, used to get information on the match, is
298    /// stored in @match_info if not [`None`]. Note that if @match_info is
299    /// not [`None`] then it is created even if the function returns [`false`],
300    /// i.e. you must free it regardless if regular expression actually
301    /// matched.
302    ///
303    /// @string is not copied and is used in #GMatchInfo internally. If
304    /// you use any #GMatchInfo method (except g_match_info_free()) after
305    /// freeing or modifying @string then the behaviour is undefined.
306    /// ## `string`
307    /// the string to scan for matches
308    /// ## `string_len`
309    /// the length of @string, in bytes, or -1 if @string is nul-terminated
310    /// ## `start_position`
311    /// starting index of the string to match, in bytes
312    /// ## `match_options`
313    /// match options
314    ///
315    /// # Returns
316    ///
317    /// [`true`] is the string matched, [`false`] otherwise
318    ///
319    /// ## `match_info`
320    /// pointer to location where to store
321    ///     the #GMatchInfo, or [`None`] if you do not need it
322    #[doc(alias = "g_regex_match_all_full")]
323    pub fn match_all_full<'input>(
324        &self,
325        string: &'input GStr,
326        start_position: i32,
327        match_options: RegexMatchFlags,
328    ) -> Result<MatchInfo<'input>, crate::Error> {
329        unsafe {
330            let mut match_info = ptr::null_mut();
331            let mut error = ptr::null_mut();
332            let res = ffi::g_regex_match_all_full(
333                self.to_glib_none().0,
334                string.to_glib_none().0,
335                string.len() as _,
336                start_position,
337                match_options.into_glib(),
338                &mut match_info,
339                &mut error,
340            );
341            if error.is_null() {
342                let match_info = MatchInfo::from_glib_full(match_info);
343                debug_assert_eq!(match_info.matches(), from_glib(res));
344                Ok(match_info)
345            } else {
346                debug_assert!(match_info.is_null());
347                Err(from_glib_full(error))
348            }
349        }
350    }
351
352    /// Scans for a match in @string for the pattern in @self.
353    /// The @match_options are combined with the match options specified
354    /// when the @self structure was created, letting you have more
355    /// flexibility in reusing #GRegex structures.
356    ///
357    /// Unless [`RegexCompileFlags::RAW`][crate::RegexCompileFlags::RAW] is specified in the options, @string must be valid UTF-8.
358    ///
359    /// A #GMatchInfo structure, used to get information on the match,
360    /// is stored in @match_info if not [`None`]. Note that if @match_info
361    /// is not [`None`] then it is created even if the function returns [`false`],
362    /// i.e. you must free it regardless if regular expression actually matched.
363    ///
364    /// To retrieve all the non-overlapping matches of the pattern in
365    /// string you can use g_match_info_next().
366    ///
367    ///
368    ///
369    /// **⚠️ The following code is in C ⚠️**
370    ///
371    /// ```C
372    /// static void
373    /// print_uppercase_words (const gchar *string)
374    /// {
375    ///   // Print all uppercase-only words.
376    ///   GRegex *regex;
377    ///   GMatchInfo *match_info;
378    ///
379    ///   regex = g_regex_new ("[A-Z]+", G_REGEX_DEFAULT, G_REGEX_MATCH_DEFAULT, NULL);
380    ///   g_regex_match (regex, string, 0, &match_info);
381    ///   while (g_match_info_matches (match_info))
382    ///     {
383    ///       gchar *word = g_match_info_fetch (match_info, 0);
384    ///       g_print ("Found: %s\n", word);
385    ///       g_free (word);
386    ///       g_match_info_next (match_info, NULL);
387    ///     }
388    ///   g_match_info_free (match_info);
389    ///   g_regex_unref (regex);
390    /// }
391    /// ```
392    ///
393    /// @string is not copied and is used in #GMatchInfo internally. If
394    /// you use any #GMatchInfo method (except g_match_info_free()) after
395    /// freeing or modifying @string then the behaviour is undefined.
396    /// ## `string`
397    /// the string to scan for matches
398    /// ## `match_options`
399    /// match options
400    ///
401    /// # Returns
402    ///
403    /// [`true`] is the string matched, [`false`] otherwise
404    ///
405    /// ## `match_info`
406    /// pointer to location where to store
407    ///     the #GMatchInfo, or [`None`] if you do not need it
408    #[doc(alias = "g_regex_match")]
409    pub fn match_<'input>(
410        &self,
411        string: &'input GStr,
412        match_options: RegexMatchFlags,
413    ) -> Result<MatchInfo<'input>, crate::Error> {
414        self.match_full(string, 0, match_options)
415    }
416
417    /// Scans for a match in @string for the pattern in @self.
418    /// The @match_options are combined with the match options specified
419    /// when the @self structure was created, letting you have more
420    /// flexibility in reusing #GRegex structures.
421    ///
422    /// Setting @start_position differs from just passing over a shortened
423    /// string and setting [`RegexMatchFlags::NOTBOL`][crate::RegexMatchFlags::NOTBOL] in the case of a pattern
424    /// that begins with any kind of lookbehind assertion, such as "\b".
425    ///
426    /// Unless [`RegexCompileFlags::RAW`][crate::RegexCompileFlags::RAW] is specified in the options, @string must be valid UTF-8.
427    ///
428    /// A #GMatchInfo structure, used to get information on the match, is
429    /// stored in @match_info if not [`None`]. Note that if @match_info is
430    /// not [`None`] then it is created even if the function returns [`false`],
431    /// i.e. you must free it regardless if regular expression actually
432    /// matched.
433    ///
434    /// @string is not copied and is used in #GMatchInfo internally. If
435    /// you use any #GMatchInfo method (except g_match_info_free()) after
436    /// freeing or modifying @string then the behaviour is undefined.
437    ///
438    /// To retrieve all the non-overlapping matches of the pattern in
439    /// string you can use g_match_info_next().
440    ///
441    ///
442    ///
443    /// **⚠️ The following code is in C ⚠️**
444    ///
445    /// ```C
446    /// static void
447    /// print_uppercase_words (const gchar *string)
448    /// {
449    ///   // Print all uppercase-only words.
450    ///   GRegex *regex;
451    ///   GMatchInfo *match_info;
452    ///   GError *error = NULL;
453    ///
454    ///   regex = g_regex_new ("[A-Z]+", G_REGEX_DEFAULT, G_REGEX_MATCH_DEFAULT, NULL);
455    ///   g_regex_match_full (regex, string, -1, 0, 0, &match_info, &error);
456    ///   while (g_match_info_matches (match_info))
457    ///     {
458    ///       gchar *word = g_match_info_fetch (match_info, 0);
459    ///       g_print ("Found: %s\n", word);
460    ///       g_free (word);
461    ///       g_match_info_next (match_info, &error);
462    ///     }
463    ///   g_match_info_free (match_info);
464    ///   g_regex_unref (regex);
465    ///   if (error != NULL)
466    ///     {
467    ///       g_printerr ("Error while matching: %s\n", error->message);
468    ///       g_error_free (error);
469    ///     }
470    /// }
471    /// ```
472    /// ## `string`
473    /// the string to scan for matches
474    /// ## `string_len`
475    /// the length of @string, in bytes, or -1 if @string is nul-terminated
476    /// ## `start_position`
477    /// starting index of the string to match, in bytes
478    /// ## `match_options`
479    /// match options
480    ///
481    /// # Returns
482    ///
483    /// [`true`] is the string matched, [`false`] otherwise
484    ///
485    /// ## `match_info`
486    /// pointer to location where to store
487    ///     the #GMatchInfo, or [`None`] if you do not need it
488    #[doc(alias = "g_regex_match_full")]
489    pub fn match_full<'input>(
490        &self,
491        string: &'input GStr,
492        start_position: i32,
493        match_options: RegexMatchFlags,
494    ) -> Result<MatchInfo<'input>, crate::Error> {
495        unsafe {
496            let mut match_info = ptr::null_mut();
497            let mut error = ptr::null_mut();
498            let res = ffi::g_regex_match_full(
499                self.to_glib_none().0,
500                string.to_glib_none().0,
501                string.len() as _,
502                start_position,
503                match_options.into_glib(),
504                &mut match_info,
505                &mut error,
506            );
507            if error.is_null() {
508                let match_info = MatchInfo::from_glib_full(match_info);
509                debug_assert_eq!(match_info.matches(), from_glib(res));
510                Ok(match_info)
511            } else {
512                debug_assert!(match_info.is_null());
513                Err(from_glib_full(error))
514            }
515        }
516    }
517
518    /// Replaces all occurrences of the pattern in @self with the
519    /// replacement text. @replacement is replaced literally, to
520    /// include backreferences use g_regex_replace().
521    ///
522    /// Setting @start_position differs from just passing over a
523    /// shortened string and setting [`RegexMatchFlags::NOTBOL`][crate::RegexMatchFlags::NOTBOL] in the
524    /// case of a pattern that begins with any kind of lookbehind
525    /// assertion, such as "\b".
526    /// ## `string`
527    /// the string to perform matches against
528    /// ## `string_len`
529    /// the length of @string, in bytes, or -1 if @string is nul-terminated
530    /// ## `start_position`
531    /// starting index of the string to match, in bytes
532    /// ## `replacement`
533    /// text to replace each match with
534    /// ## `match_options`
535    /// options for the match
536    ///
537    /// # Returns
538    ///
539    /// a newly allocated string containing the replacements
540    #[doc(alias = "g_regex_replace_literal")]
541    pub fn replace_literal(
542        &self,
543        string: impl IntoGStr,
544        start_position: i32,
545        replacement: impl IntoGStr,
546        match_options: RegexMatchFlags,
547    ) -> Result<crate::GString, crate::Error> {
548        unsafe {
549            string.run_with_gstr(|string| {
550                replacement.run_with_gstr(|replacement| {
551                    let mut error = ptr::null_mut();
552                    let ret = ffi::g_regex_replace_literal(
553                        self.to_glib_none().0,
554                        string.to_glib_none().0,
555                        string.len() as _,
556                        start_position,
557                        replacement.to_glib_none().0,
558                        match_options.into_glib(),
559                        &mut error,
560                    );
561                    debug_assert_eq!(ret.is_null(), !error.is_null());
562                    if error.is_null() {
563                        Ok(from_glib_full(ret))
564                    } else {
565                        Err(from_glib_full(error))
566                    }
567                })
568            })
569        }
570    }
571
572    /// Breaks the string on the pattern, and returns an array of the tokens.
573    /// If the pattern contains capturing parentheses, then the text for each
574    /// of the substrings will also be returned. If the pattern does not match
575    /// anywhere in the string, then the whole string is returned as the first
576    /// token.
577    ///
578    /// As a special case, the result of splitting the empty string "" is an
579    /// empty vector, not a vector containing a single string. The reason for
580    /// this special case is that being able to represent an empty vector is
581    /// typically more useful than consistent handling of empty elements. If
582    /// you do need to represent empty elements, you'll need to check for the
583    /// empty string before calling this function.
584    ///
585    /// A pattern that can match empty strings splits @string into separate
586    /// characters wherever it matches the empty string between characters.
587    /// For example splitting "ab c" using as a separator "\s*", you will get
588    /// "a", "b" and "c".
589    /// ## `string`
590    /// the string to split with the pattern
591    /// ## `match_options`
592    /// match time option flags
593    ///
594    /// # Returns
595    ///
596    /// a [`None`]-terminated gchar ** array. Free
597    /// it using g_strfreev()
598    #[doc(alias = "g_regex_split")]
599    pub fn split(
600        &self,
601        string: impl IntoGStr,
602        match_options: RegexMatchFlags,
603    ) -> PtrSlice<GStringPtr> {
604        self.split_full(string, 0, match_options, 0)
605            .unwrap_or_default()
606    }
607
608    /// Breaks the string on the pattern, and returns an array of the tokens.
609    /// If the pattern contains capturing parentheses, then the text for each
610    /// of the substrings will also be returned. If the pattern does not match
611    /// anywhere in the string, then the whole string is returned as the first
612    /// token.
613    ///
614    /// As a special case, the result of splitting the empty string "" is an
615    /// empty vector, not a vector containing a single string. The reason for
616    /// this special case is that being able to represent an empty vector is
617    /// typically more useful than consistent handling of empty elements. If
618    /// you do need to represent empty elements, you'll need to check for the
619    /// empty string before calling this function.
620    ///
621    /// A pattern that can match empty strings splits @string into separate
622    /// characters wherever it matches the empty string between characters.
623    /// For example splitting "ab c" using as a separator "\s*", you will get
624    /// "a", "b" and "c".
625    ///
626    /// Setting @start_position differs from just passing over a shortened
627    /// string and setting [`RegexMatchFlags::NOTBOL`][crate::RegexMatchFlags::NOTBOL] in the case of a pattern
628    /// that begins with any kind of lookbehind assertion, such as "\b".
629    /// ## `string`
630    /// the string to split with the pattern
631    /// ## `string_len`
632    /// the length of @string, in bytes, or -1 if @string is nul-terminated
633    /// ## `start_position`
634    /// starting index of the string to match, in bytes
635    /// ## `match_options`
636    /// match time option flags
637    /// ## `max_tokens`
638    /// the maximum number of tokens to split @string into.
639    ///   If this is less than 1, the string is split completely
640    ///
641    /// # Returns
642    ///
643    /// a [`None`]-terminated gchar ** array. Free
644    /// it using g_strfreev()
645    #[doc(alias = "g_regex_split_full")]
646    pub fn split_full(
647        &self,
648        string: impl IntoGStr,
649        start_position: i32,
650        match_options: RegexMatchFlags,
651        max_tokens: i32,
652    ) -> Result<PtrSlice<GStringPtr>, crate::Error> {
653        unsafe {
654            let mut error = ptr::null_mut();
655            string.run_with_gstr(|string| {
656                let ret = ffi::g_regex_split_full(
657                    self.to_glib_none().0,
658                    string.to_glib_none().0,
659                    string.len() as _,
660                    start_position,
661                    match_options.into_glib(),
662                    max_tokens,
663                    &mut error,
664                );
665                debug_assert_eq!(ret.is_null(), !error.is_null());
666                if error.is_null() {
667                    Ok(FromGlibPtrContainer::from_glib_full(ret))
668                } else {
669                    Err(from_glib_full(error))
670                }
671            })
672        }
673    }
674
675    /// Breaks the string on the pattern, and returns an array of
676    /// the tokens. If the pattern contains capturing parentheses,
677    /// then the text for each of the substrings will also be returned.
678    /// If the pattern does not match anywhere in the string, then the
679    /// whole string is returned as the first token.
680    ///
681    /// This function is equivalent to g_regex_split() but it does
682    /// not require to compile the pattern with g_regex_new(), avoiding
683    /// some lines of code when you need just to do a split without
684    /// extracting substrings, capture counts, and so on.
685    ///
686    /// If this function is to be called on the same @pattern more than
687    /// once, it's more efficient to compile the pattern once with
688    /// g_regex_new() and then use g_regex_split().
689    ///
690    /// As a special case, the result of splitting the empty string ""
691    /// is an empty vector, not a vector containing a single string.
692    /// The reason for this special case is that being able to represent
693    /// an empty vector is typically more useful than consistent handling
694    /// of empty elements. If you do need to represent empty elements,
695    /// you'll need to check for the empty string before calling this
696    /// function.
697    ///
698    /// A pattern that can match empty strings splits @string into
699    /// separate characters wherever it matches the empty string between
700    /// characters. For example splitting "ab c" using as a separator
701    /// "\s*", you will get "a", "b" and "c".
702    /// ## `pattern`
703    /// the regular expression
704    /// ## `string`
705    /// the string to scan for matches
706    /// ## `compile_options`
707    /// compile options for the regular expression, or 0
708    /// ## `match_options`
709    /// match options, or 0
710    ///
711    /// # Returns
712    ///
713    /// a [`None`]-terminated array of strings. Free
714    /// it using g_strfreev()
715    #[doc(alias = "g_regex_split_simple")]
716    pub fn split_simple(
717        pattern: impl IntoGStr,
718        string: impl IntoGStr,
719        compile_options: RegexCompileFlags,
720        match_options: RegexMatchFlags,
721    ) -> PtrSlice<GStringPtr> {
722        pattern.run_with_gstr(|pattern| {
723            string.run_with_gstr(|string| unsafe {
724                FromGlibPtrContainer::from_glib_full(ffi::g_regex_split_simple(
725                    pattern.to_glib_none().0,
726                    string.to_glib_none().0,
727                    compile_options.into_glib(),
728                    match_options.into_glib(),
729                ))
730            })
731        })
732    }
733}
734
735#[cfg(test)]
736mod tests {
737    use super::*;
738
739    #[test]
740    fn test_replace_literal() {
741        let regex = Regex::new(
742            "s[ai]mple",
743            RegexCompileFlags::OPTIMIZE,
744            RegexMatchFlags::DEFAULT,
745        )
746        .expect("Regex new")
747        .expect("Null regex");
748
749        let quote = "This is a simple sample.";
750        let result = regex
751            .replace_literal(quote, 0, "XXX", RegexMatchFlags::DEFAULT)
752            .expect("regex replace");
753
754        assert_eq!(result, "This is a XXX XXX.");
755    }
756
757    #[test]
758    fn test_split() {
759        let regex = Regex::new(
760            "s[ai]mple",
761            RegexCompileFlags::OPTIMIZE,
762            RegexMatchFlags::DEFAULT,
763        )
764        .expect("Regex new")
765        .expect("Null regex");
766
767        let quote = "This is a simple sample.";
768        let result = regex.split(quote, RegexMatchFlags::DEFAULT);
769
770        assert_eq!(result.len(), 3);
771        assert_eq!(result[0], "This is a ");
772        assert_eq!(result[1], " ");
773        assert_eq!(result[2], ".");
774    }
775
776    #[test]
777    fn test_match() {
778        let regex = Regex::new(r"\d", RegexCompileFlags::DEFAULT, RegexMatchFlags::DEFAULT)
779            .expect("Regex new")
780            .expect("Null regex");
781
782        let input = crate::GString::from("87");
783        let m = regex.match_(input.as_gstr(), RegexMatchFlags::DEFAULT);
784        let m = m.unwrap();
785        assert!(m.matches());
786        assert_eq!(m.match_count(), 1);
787        assert_eq!(m.fetch(0).as_deref(), Some("8"));
788        assert!(m.next().unwrap());
789        assert_eq!(m.fetch(0).as_deref(), Some("7"));
790        assert!(!m.next().unwrap());
791        assert!(m.fetch(0).is_none());
792
793        let input = crate::GString::from("a");
794        let m = regex.match_(input.as_gstr(), RegexMatchFlags::DEFAULT);
795        let m = m.unwrap();
796        assert!(!m.matches());
797        assert_eq!(m.match_count(), 0);
798        assert!(m.fetch(0).is_none());
799    }
800}