From 03bce03c20587f598dbdcb40cde7541000a41a56 Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Fri, 3 Feb 2023 11:27:53 +0000 Subject: [PATCH] glib: Implement Regex These modules are inefficient and should not be used by Rust programs except for compatibility with GLib.Regex based APIs. All methods are implemented except for g_regex_replace_eval --- glib/Gir.toml | 80 +++++++++ glib/src/auto/flags.rs | 120 +++++++++++++ glib/src/auto/match_info.rs | 91 ++++++++++ glib/src/auto/mod.rs | 8 + glib/src/auto/regex.rs | 87 ++++++++++ glib/src/lib.rs | 2 + glib/src/match_info.rs | 55 ++++++ glib/src/regex.rs | 324 ++++++++++++++++++++++++++++++++++++ 8 files changed, 767 insertions(+) create mode 100644 glib/src/auto/match_info.rs create mode 100644 glib/src/auto/regex.rs create mode 100644 glib/src/match_info.rs create mode 100644 glib/src/regex.rs diff --git a/glib/Gir.toml b/glib/Gir.toml index 674d744e360b..3e842d0eb138 100644 --- a/glib/Gir.toml +++ b/glib/Gir.toml @@ -27,6 +27,8 @@ generate = [ "GLib.NormalizeMode", "GLib.OptionArg", "GLib.OptionFlags", + "GLib.RegexCompileFlags", + "GLib.RegexMatchFlags", "GLib.SeekType", "GLib.SpawnFlags", "GLib.Time", @@ -714,6 +716,84 @@ status = "generate" name = "get_user_data" ignore = true # unsafe pointer +[[object]] +name = "GLib.MatchInfo" +status = "generate" + [[object.function]] + name = "expand_references" + # impl IntoGStr for parameters instead of &str + manual = true + [[object.function]] + name = "fetch_named" + # impl IntoGStr for parameters instead of &str + manual = true + [[object.function]] + name = "fetch_named_pos" + # impl IntoGStr for parameters instead of &str + manual = true + +[[object]] +name = "GLib.Regex" +status = "generate" + [[object.function]] + name = "check_replacement" + # impl IntoGStr for parameters instead of &str + manual = true + [[object.function]] + name = "escape_nul" + # impl IntoGStr for parameters instead of &str + manual = true + [[object.function]] + name = "escape_string" + # impl IntoGStr for parameters instead of &str + manual = true + [[object.function]] + name = "match" + # implement in terms of match_full + manual = true + [[object.function]] + name = "match_all" + # implement in terms of match_all_full + manual = true + [[object.function]] + name = "match_all_full" + # impl IntoGStr for parameters instead of &str + manual = true + [[object.function]] + name = "match_simple" + # impl IntoGStr for parameters instead of &str + manual = true + [[object.function]] + name = "match_full" + # impl IntoGStr for parameters instead of &str + manual = true + [[object.function]] + name = "replace" + # impl IntoGStr for parameters instead of &str + manual = true + [[object.function]] + name = "replace_literal" + # impl IntoGStr for parameters instead of &str + manual = true + [[object.function]] + name = "split" + # implement in terms of split_full + manual = true + [[object.function]] + name = "split_full" + # impl IntoGStr for parameters instead of &str + # return slice of str pointers + manual = true + [[object.function]] + name = "split_simple" + # impl IntoGStr for parameters instead of &str + # return slice of str pointers + manual = true + [[object.function]] + name = "get_string_number" + # impl IntoGStr for parameters instead of &str + manual = true + [[object]] name = "GLib.Source" status = "generate" diff --git a/glib/src/auto/flags.rs b/glib/src/auto/flags.rs index 28fcb8142031..16696e75dc48 100644 --- a/glib/src/auto/flags.rs +++ b/glib/src/auto/flags.rs @@ -362,6 +362,126 @@ impl FromGlib for OptionFlags { } } +bitflags! { + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] + #[doc(alias = "GRegexCompileFlags")] + pub struct RegexCompileFlags: u32 { + #[doc(alias = "G_REGEX_DEFAULT")] + const DEFAULT = ffi::G_REGEX_DEFAULT as _; + #[doc(alias = "G_REGEX_CASELESS")] + const CASELESS = ffi::G_REGEX_CASELESS as _; + #[doc(alias = "G_REGEX_MULTILINE")] + const MULTILINE = ffi::G_REGEX_MULTILINE as _; + #[doc(alias = "G_REGEX_DOTALL")] + const DOTALL = ffi::G_REGEX_DOTALL as _; + #[doc(alias = "G_REGEX_EXTENDED")] + const EXTENDED = ffi::G_REGEX_EXTENDED as _; + #[doc(alias = "G_REGEX_ANCHORED")] + const ANCHORED = ffi::G_REGEX_ANCHORED as _; + #[doc(alias = "G_REGEX_DOLLAR_ENDONLY")] + const DOLLAR_ENDONLY = ffi::G_REGEX_DOLLAR_ENDONLY as _; + #[doc(alias = "G_REGEX_UNGREEDY")] + const UNGREEDY = ffi::G_REGEX_UNGREEDY as _; + #[doc(alias = "G_REGEX_RAW")] + const RAW = ffi::G_REGEX_RAW as _; + #[doc(alias = "G_REGEX_NO_AUTO_CAPTURE")] + const NO_AUTO_CAPTURE = ffi::G_REGEX_NO_AUTO_CAPTURE as _; + #[doc(alias = "G_REGEX_OPTIMIZE")] + const OPTIMIZE = ffi::G_REGEX_OPTIMIZE as _; + #[doc(alias = "G_REGEX_FIRSTLINE")] + const FIRSTLINE = ffi::G_REGEX_FIRSTLINE as _; + #[doc(alias = "G_REGEX_DUPNAMES")] + const DUPNAMES = ffi::G_REGEX_DUPNAMES as _; + #[doc(alias = "G_REGEX_NEWLINE_CR")] + const NEWLINE_CR = ffi::G_REGEX_NEWLINE_CR as _; + #[doc(alias = "G_REGEX_NEWLINE_LF")] + const NEWLINE_LF = ffi::G_REGEX_NEWLINE_LF as _; + #[doc(alias = "G_REGEX_NEWLINE_CRLF")] + const NEWLINE_CRLF = ffi::G_REGEX_NEWLINE_CRLF as _; + #[doc(alias = "G_REGEX_NEWLINE_ANYCRLF")] + const NEWLINE_ANYCRLF = ffi::G_REGEX_NEWLINE_ANYCRLF as _; + #[doc(alias = "G_REGEX_BSR_ANYCRLF")] + const BSR_ANYCRLF = ffi::G_REGEX_BSR_ANYCRLF as _; + #[doc(alias = "G_REGEX_JAVASCRIPT_COMPAT")] + const JAVASCRIPT_COMPAT = ffi::G_REGEX_JAVASCRIPT_COMPAT as _; + } +} + +#[doc(hidden)] +impl IntoGlib for RegexCompileFlags { + type GlibType = ffi::GRegexCompileFlags; + + #[inline] + fn into_glib(self) -> ffi::GRegexCompileFlags { + self.bits() + } +} + +#[doc(hidden)] +impl FromGlib for RegexCompileFlags { + #[inline] + unsafe fn from_glib(value: ffi::GRegexCompileFlags) -> Self { + Self::from_bits_truncate(value) + } +} + +bitflags! { + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] + #[doc(alias = "GRegexMatchFlags")] + pub struct RegexMatchFlags: u32 { + #[doc(alias = "G_REGEX_MATCH_DEFAULT")] + const DEFAULT = ffi::G_REGEX_MATCH_DEFAULT as _; + #[doc(alias = "G_REGEX_MATCH_ANCHORED")] + const ANCHORED = ffi::G_REGEX_MATCH_ANCHORED as _; + #[doc(alias = "G_REGEX_MATCH_NOTBOL")] + const NOTBOL = ffi::G_REGEX_MATCH_NOTBOL as _; + #[doc(alias = "G_REGEX_MATCH_NOTEOL")] + const NOTEOL = ffi::G_REGEX_MATCH_NOTEOL as _; + #[doc(alias = "G_REGEX_MATCH_NOTEMPTY")] + const NOTEMPTY = ffi::G_REGEX_MATCH_NOTEMPTY as _; + #[doc(alias = "G_REGEX_MATCH_PARTIAL")] + const PARTIAL = ffi::G_REGEX_MATCH_PARTIAL as _; + #[doc(alias = "G_REGEX_MATCH_NEWLINE_CR")] + const NEWLINE_CR = ffi::G_REGEX_MATCH_NEWLINE_CR as _; + #[doc(alias = "G_REGEX_MATCH_NEWLINE_LF")] + const NEWLINE_LF = ffi::G_REGEX_MATCH_NEWLINE_LF as _; + #[doc(alias = "G_REGEX_MATCH_NEWLINE_CRLF")] + const NEWLINE_CRLF = ffi::G_REGEX_MATCH_NEWLINE_CRLF as _; + #[doc(alias = "G_REGEX_MATCH_NEWLINE_ANY")] + const NEWLINE_ANY = ffi::G_REGEX_MATCH_NEWLINE_ANY as _; + #[doc(alias = "G_REGEX_MATCH_NEWLINE_ANYCRLF")] + const NEWLINE_ANYCRLF = ffi::G_REGEX_MATCH_NEWLINE_ANYCRLF as _; + #[doc(alias = "G_REGEX_MATCH_BSR_ANYCRLF")] + const BSR_ANYCRLF = ffi::G_REGEX_MATCH_BSR_ANYCRLF as _; + #[doc(alias = "G_REGEX_MATCH_BSR_ANY")] + const BSR_ANY = ffi::G_REGEX_MATCH_BSR_ANY as _; + #[doc(alias = "G_REGEX_MATCH_PARTIAL_SOFT")] + const PARTIAL_SOFT = ffi::G_REGEX_MATCH_PARTIAL_SOFT as _; + #[doc(alias = "G_REGEX_MATCH_PARTIAL_HARD")] + const PARTIAL_HARD = ffi::G_REGEX_MATCH_PARTIAL_HARD as _; + #[doc(alias = "G_REGEX_MATCH_NOTEMPTY_ATSTART")] + const NOTEMPTY_ATSTART = ffi::G_REGEX_MATCH_NOTEMPTY_ATSTART as _; + } +} + +#[doc(hidden)] +impl IntoGlib for RegexMatchFlags { + type GlibType = ffi::GRegexMatchFlags; + + #[inline] + fn into_glib(self) -> ffi::GRegexMatchFlags { + self.bits() + } +} + +#[doc(hidden)] +impl FromGlib for RegexMatchFlags { + #[inline] + unsafe fn from_glib(value: ffi::GRegexMatchFlags) -> Self { + Self::from_bits_truncate(value) + } +} + bitflags! { #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[doc(alias = "GSpawnFlags")] diff --git a/glib/src/auto/match_info.rs b/glib/src/auto/match_info.rs new file mode 100644 index 000000000000..0862e063dc2a --- /dev/null +++ b/glib/src/auto/match_info.rs @@ -0,0 +1,91 @@ +// This file was generated by gir (https://github.com/gtk-rs/gir) +// from gir-files (https://github.com/gtk-rs/gir-files) +// DO NOT EDIT + +use crate::{translate::*, Error, Regex}; + +crate::wrapper! { + #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct MatchInfo(Shared); + + match fn { + ref => |ptr| ffi::g_match_info_ref(ptr), + unref => |ptr| ffi::g_match_info_unref(ptr), + type_ => || ffi::g_match_info_get_type(), + } +} + +impl MatchInfo { + #[doc(alias = "g_match_info_fetch")] + pub fn fetch(&self, match_num: i32) -> Option { + unsafe { from_glib_full(ffi::g_match_info_fetch(self.to_glib_none().0, match_num)) } + } + + #[doc(alias = "g_match_info_fetch_all")] + pub fn fetch_all(&self) -> Vec { + unsafe { + FromGlibPtrContainer::from_glib_full(ffi::g_match_info_fetch_all(self.to_glib_none().0)) + } + } + + #[doc(alias = "g_match_info_fetch_pos")] + pub fn fetch_pos(&self, match_num: i32) -> Option<(i32, i32)> { + unsafe { + let mut start_pos = std::mem::MaybeUninit::uninit(); + let mut end_pos = std::mem::MaybeUninit::uninit(); + let ret = from_glib(ffi::g_match_info_fetch_pos( + self.to_glib_none().0, + match_num, + start_pos.as_mut_ptr(), + end_pos.as_mut_ptr(), + )); + if ret { + Some((start_pos.assume_init(), end_pos.assume_init())) + } else { + None + } + } + } + + #[doc(alias = "g_match_info_get_match_count")] + #[doc(alias = "get_match_count")] + pub fn match_count(&self) -> i32 { + unsafe { ffi::g_match_info_get_match_count(self.to_glib_none().0) } + } + + #[doc(alias = "g_match_info_get_regex")] + #[doc(alias = "get_regex")] + pub fn regex(&self) -> Regex { + unsafe { from_glib_none(ffi::g_match_info_get_regex(self.to_glib_none().0)) } + } + + #[doc(alias = "g_match_info_get_string")] + #[doc(alias = "get_string")] + pub fn string(&self) -> crate::GString { + unsafe { from_glib_none(ffi::g_match_info_get_string(self.to_glib_none().0)) } + } + + #[doc(alias = "g_match_info_is_partial_match")] + pub fn is_partial_match(&self) -> bool { + unsafe { from_glib(ffi::g_match_info_is_partial_match(self.to_glib_none().0)) } + } + + #[doc(alias = "g_match_info_matches")] + pub fn matches(&self) -> bool { + unsafe { from_glib(ffi::g_match_info_matches(self.to_glib_none().0)) } + } + + #[doc(alias = "g_match_info_next")] + pub fn next(&self) -> Result<(), crate::Error> { + unsafe { + let mut error = std::ptr::null_mut(); + let is_ok = ffi::g_match_info_next(self.to_glib_none().0, &mut error); + debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + } + } + } +} diff --git a/glib/src/auto/mod.rs b/glib/src/auto/mod.rs index 2d4e5fd2678f..c845d6ec1961 100644 --- a/glib/src/auto/mod.rs +++ b/glib/src/auto/mod.rs @@ -20,6 +20,12 @@ pub use self::main_loop::MainLoop; mod markup_parse_context; pub use self::markup_parse_context::MarkupParseContext; +mod match_info; +pub use self::match_info::MatchInfo; + +mod regex; +pub use self::regex::Regex; + mod source; pub use self::source::Source; @@ -67,6 +73,8 @@ pub use self::flags::LogLevelFlags; #[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))] pub use self::flags::MainContextFlags; pub use self::flags::OptionFlags; +pub use self::flags::RegexCompileFlags; +pub use self::flags::RegexMatchFlags; pub use self::flags::SpawnFlags; #[cfg(feature = "v2_66")] #[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))] diff --git a/glib/src/auto/regex.rs b/glib/src/auto/regex.rs new file mode 100644 index 000000000000..d41ce1f7354b --- /dev/null +++ b/glib/src/auto/regex.rs @@ -0,0 +1,87 @@ +// This file was generated by gir (https://github.com/gtk-rs/gir) +// from gir-files (https://github.com/gtk-rs/gir-files) +// DO NOT EDIT + +use crate::{translate::*, Error, RegexCompileFlags, RegexMatchFlags}; + +crate::wrapper! { + #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct Regex(Shared); + + match fn { + ref => |ptr| ffi::g_regex_ref(ptr), + unref => |ptr| ffi::g_regex_unref(ptr), + type_ => || ffi::g_regex_get_type(), + } +} + +impl Regex { + #[doc(alias = "g_regex_new")] + pub fn new( + pattern: &str, + compile_options: RegexCompileFlags, + match_options: RegexMatchFlags, + ) -> Result, crate::Error> { + unsafe { + let mut error = std::ptr::null_mut(); + let ret = ffi::g_regex_new( + pattern.to_glib_none().0, + compile_options.into_glib(), + match_options.into_glib(), + &mut error, + ); + if error.is_null() { + Ok(from_glib_full(ret)) + } else { + Err(from_glib_full(error)) + } + } + } + + #[doc(alias = "g_regex_get_capture_count")] + #[doc(alias = "get_capture_count")] + pub fn capture_count(&self) -> i32 { + unsafe { ffi::g_regex_get_capture_count(self.to_glib_none().0) } + } + + #[doc(alias = "g_regex_get_compile_flags")] + #[doc(alias = "get_compile_flags")] + pub fn compile_flags(&self) -> RegexCompileFlags { + unsafe { from_glib(ffi::g_regex_get_compile_flags(self.to_glib_none().0)) } + } + + #[doc(alias = "g_regex_get_has_cr_or_lf")] + #[doc(alias = "get_has_cr_or_lf")] + pub fn has_cr_or_lf(&self) -> bool { + unsafe { from_glib(ffi::g_regex_get_has_cr_or_lf(self.to_glib_none().0)) } + } + + #[doc(alias = "g_regex_get_match_flags")] + #[doc(alias = "get_match_flags")] + pub fn match_flags(&self) -> RegexMatchFlags { + unsafe { from_glib(ffi::g_regex_get_match_flags(self.to_glib_none().0)) } + } + + #[doc(alias = "g_regex_get_max_backref")] + #[doc(alias = "get_max_backref")] + pub fn max_backref(&self) -> i32 { + unsafe { ffi::g_regex_get_max_backref(self.to_glib_none().0) } + } + + #[doc(alias = "g_regex_get_max_lookbehind")] + #[doc(alias = "get_max_lookbehind")] + pub fn max_lookbehind(&self) -> i32 { + unsafe { ffi::g_regex_get_max_lookbehind(self.to_glib_none().0) } + } + + #[doc(alias = "g_regex_get_pattern")] + #[doc(alias = "get_pattern")] + pub fn pattern(&self) -> crate::GString { + unsafe { from_glib_none(ffi::g_regex_get_pattern(self.to_glib_none().0)) } + } + + //#[doc(alias = "g_regex_replace_eval")] + //pub fn replace_eval(&self, string: &[&str], start_position: i32, match_options: RegexMatchFlags, eval: /*Unimplemented*/Fn(&MatchInfo, /*Ignored*/String) -> bool, user_data: /*Unimplemented*/Option) -> Result { + // unsafe { TODO: call ffi:g_regex_replace_eval() } + //} +} diff --git a/glib/src/lib.rs b/glib/src/lib.rs index 0bc9e35b8e81..4d5dbc017e1e 100644 --- a/glib/src/lib.rs +++ b/glib/src/lib.rs @@ -174,6 +174,8 @@ mod property; pub use self::property::*; mod quark; pub use self::quark::Quark; +pub mod match_info; +pub mod regex; #[macro_use] mod log; #[doc(hidden)] diff --git a/glib/src/match_info.rs b/glib/src/match_info.rs new file mode 100644 index 000000000000..29fa565650f3 --- /dev/null +++ b/glib/src/match_info.rs @@ -0,0 +1,55 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use crate::{translate::*, IntoGStr, MatchInfo}; +use std::{mem, ptr}; + +impl MatchInfo { + #[doc(alias = "g_match_info_expand_references")] + pub fn expand_references( + &self, + string_to_expand: impl IntoGStr, + ) -> Result, crate::Error> { + string_to_expand.run_with_gstr(|string_to_expand| unsafe { + let mut error = ptr::null_mut(); + let ret = ffi::g_match_info_expand_references( + self.to_glib_none().0, + string_to_expand.to_glib_none().0, + &mut error, + ); + if error.is_null() { + Ok(from_glib_full(ret)) + } else { + Err(from_glib_full(error)) + } + }) + } + + #[doc(alias = "g_match_info_fetch_named")] + pub fn fetch_named(&self, name: impl IntoGStr) -> Option { + name.run_with_gstr(|name| unsafe { + from_glib_full(ffi::g_match_info_fetch_named( + self.to_glib_none().0, + name.to_glib_none().0, + )) + }) + } + + #[doc(alias = "g_match_info_fetch_named_pos")] + pub fn fetch_named_pos(&self, name: impl IntoGStr) -> Option<(i32, i32)> { + name.run_with_gstr(|name| unsafe { + let mut start_pos = mem::MaybeUninit::uninit(); + let mut end_pos = mem::MaybeUninit::uninit(); + let ret = from_glib(ffi::g_match_info_fetch_named_pos( + self.to_glib_none().0, + name.to_glib_none().0, + start_pos.as_mut_ptr(), + end_pos.as_mut_ptr(), + )); + if ret { + Some((start_pos.assume_init(), end_pos.assume_init())) + } else { + None + } + }) + } +} diff --git a/glib/src/regex.rs b/glib/src/regex.rs new file mode 100644 index 000000000000..10d15f066978 --- /dev/null +++ b/glib/src/regex.rs @@ -0,0 +1,324 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +// rustdoc-stripper-ignore-next +//! This module is inefficient and should not be used by Rust programs except for +//! compatibility with GLib.Regex based APIs. + +use crate::{ + translate::*, GStringPtr, IntoGStr, MatchInfo, PtrSlice, Regex, RegexCompileFlags, + RegexMatchFlags, +}; +use std::{mem, ptr}; + +impl Regex { + #[doc(alias = "g_regex_get_string_number")] + #[doc(alias = "get_string_number")] + pub fn string_number(&self, name: impl IntoGStr) -> i32 { + name.run_with_gstr(|name| unsafe { + ffi::g_regex_get_string_number(self.to_glib_none().0, name.to_glib_none().0) + }) + } + + #[doc(alias = "g_regex_escape_nul")] + pub fn escape_nul(string: impl IntoGStr) -> crate::GString { + unsafe { + string.run_with_gstr(|string| { + from_glib_full(ffi::g_regex_escape_nul( + string.to_glib_none().0, + string.len() as _, + )) + }) + } + } + + #[doc(alias = "g_regex_escape_string")] + pub fn escape_string(string: impl IntoGStr) -> crate::GString { + unsafe { + string.run_with_gstr(|string| { + from_glib_full(ffi::g_regex_escape_string( + string.to_glib_none().0, + string.len() as _, + )) + }) + } + } + + #[doc(alias = "g_regex_check_replacement")] + pub fn check_replacement(replacement: impl IntoGStr) -> Result { + replacement.run_with_gstr(|replacement| unsafe { + let mut has_references = mem::MaybeUninit::uninit(); + let mut error = ptr::null_mut(); + let is_ok = ffi::g_regex_check_replacement( + replacement.to_glib_none().0, + has_references.as_mut_ptr(), + &mut error, + ); + debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(from_glib(has_references.assume_init())) + } else { + Err(from_glib_full(error)) + } + }) + } + + #[doc(alias = "g_regex_match_simple")] + pub fn match_simple( + pattern: impl IntoGStr, + string: impl IntoGStr, + compile_options: RegexCompileFlags, + match_options: RegexMatchFlags, + ) -> bool { + pattern.run_with_gstr(|pattern| { + string.run_with_gstr(|string| unsafe { + from_glib(ffi::g_regex_match_simple( + pattern.to_glib_none().0, + string.to_glib_none().0, + compile_options.into_glib(), + match_options.into_glib(), + )) + }) + }) + } + + #[doc(alias = "g_regex_replace")] + pub fn replace( + &self, + string: impl IntoGStr, + start_position: i32, + replacement: impl IntoGStr, + match_options: RegexMatchFlags, + ) -> Result { + unsafe { + string.run_with_gstr(|string| { + replacement.run_with_gstr(|replacement| { + let mut error = ptr::null_mut(); + let ret = ffi::g_regex_replace( + self.to_glib_none().0, + string.as_ptr() as *const _, + string.len() as _, + start_position, + replacement.to_glib_none().0, + match_options.into_glib(), + &mut error, + ); + if error.is_null() { + Ok(from_glib_full(ret)) + } else { + Err(from_glib_full(error)) + } + }) + }) + } + } + + #[doc(alias = "g_regex_match_all")] + pub fn match_all( + &self, + string: impl IntoGStr, + match_options: RegexMatchFlags, + ) -> Option { + self.match_all_full(string, 0, match_options).ok() + } + + #[doc(alias = "g_regex_match_all_full")] + pub fn match_all_full( + &self, + string: impl IntoGStr, + start_position: i32, + match_options: RegexMatchFlags, + ) -> Result { + unsafe { + string.run_with_gstr(|string| { + let mut match_info = ptr::null_mut(); + let mut error = ptr::null_mut(); + let is_ok = ffi::g_regex_match_all_full( + self.to_glib_none().0, + string.to_glib_none().0, + string.len() as _, + start_position, + match_options.into_glib(), + &mut match_info, + &mut error, + ); + debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(from_glib_full(match_info)) + } else { + Err(from_glib_full(error)) + } + }) + } + } + + #[doc(alias = "g_regex_match")] + pub fn match_( + &self, + string: impl IntoGStr, + match_options: RegexMatchFlags, + ) -> Option { + self.match_full(string, 0, match_options).ok() + } + + #[doc(alias = "g_regex_match_full")] + pub fn match_full( + &self, + string: impl IntoGStr, + start_position: i32, + match_options: RegexMatchFlags, + ) -> Result { + unsafe { + string.run_with_gstr(|string| { + let mut match_info = ptr::null_mut(); + let mut error = ptr::null_mut(); + let is_ok = ffi::g_regex_match_full( + self.to_glib_none().0, + string.to_glib_none().0, + string.len() as _, + start_position, + match_options.into_glib(), + &mut match_info, + &mut error, + ); + debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(from_glib_full(match_info)) + } else { + Err(from_glib_full(error)) + } + }) + } + } + + #[doc(alias = "g_regex_replace_literal")] + pub fn replace_literal( + &self, + string: impl IntoGStr, + start_position: i32, + replacement: impl IntoGStr, + match_options: RegexMatchFlags, + ) -> Result { + unsafe { + string.run_with_gstr(|string| { + replacement.run_with_gstr(|replacement| { + let mut error = ptr::null_mut(); + let ret = ffi::g_regex_replace_literal( + self.to_glib_none().0, + string.to_glib_none().0, + string.len() as _, + start_position, + replacement.to_glib_none().0, + match_options.into_glib(), + &mut error, + ); + if error.is_null() { + Ok(from_glib_full(ret)) + } else { + Err(from_glib_full(error)) + } + }) + }) + } + } + + #[doc(alias = "g_regex_split")] + pub fn split( + &self, + string: impl IntoGStr, + match_options: RegexMatchFlags, + ) -> PtrSlice { + self.split_full(string, 0, match_options, 0) + .unwrap_or_default() + } + + #[doc(alias = "g_regex_split_full")] + pub fn split_full( + &self, + string: impl IntoGStr, + start_position: i32, + match_options: RegexMatchFlags, + max_tokens: i32, + ) -> Result, crate::Error> { + unsafe { + let mut error = ptr::null_mut(); + string.run_with_gstr(|string| { + let ret = ffi::g_regex_split_full( + self.to_glib_none().0, + string.to_glib_none().0, + string.len() as _, + start_position, + match_options.into_glib(), + max_tokens, + &mut error, + ); + if error.is_null() { + Ok(FromGlibPtrContainer::from_glib_full(ret)) + } else { + Err(from_glib_full(error)) + } + }) + } + } + + #[doc(alias = "g_regex_split_simple")] + pub fn split_simple( + pattern: impl IntoGStr, + string: impl IntoGStr, + compile_options: RegexCompileFlags, + match_options: RegexMatchFlags, + ) -> PtrSlice { + pattern.run_with_gstr(|pattern| { + string.run_with_gstr(|string| unsafe { + FromGlibPtrContainer::from_glib_full(ffi::g_regex_split_simple( + pattern.to_glib_none().0, + string.to_glib_none().0, + compile_options.into_glib(), + match_options.into_glib(), + )) + }) + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::RegexCompileFlags; + + #[test] + fn test_replace_literal() { + let regex = Regex::new( + "s[ai]mple", + RegexCompileFlags::OPTIMIZE, + RegexMatchFlags::DEFAULT, + ) + .expect("Regex new") + .expect("Null regex"); + + let quote = "This is a simple sample."; + let result = regex + .replace_literal(quote, 0, "XXX", RegexMatchFlags::DEFAULT) + .expect("regex replace"); + + assert_eq!(result, "This is a XXX XXX."); + } + + #[test] + fn test_split() { + let regex = Regex::new( + "s[ai]mple", + RegexCompileFlags::OPTIMIZE, + RegexMatchFlags::DEFAULT, + ) + .expect("Regex new") + .expect("Null regex"); + + let quote = "This is a simple sample."; + let result = regex.split(quote, RegexMatchFlags::DEFAULT); + + assert_eq!(result.len(), 3); + assert_eq!(result[0], "This is a "); + assert_eq!(result[1], " "); + assert_eq!(result[2], "."); + } +}