Thanks to visit codestin.com
Credit goes to docs.rs

magnus/
r_match.rs

1use std::{fmt, os::raw::c_int};
2
3use rb_sys::{
4    rb_reg_backref_number, rb_reg_last_match, rb_reg_match_last, rb_reg_match_post,
5    rb_reg_match_pre, rb_reg_nth_defined, rb_reg_nth_match, ruby_value_type, VALUE,
6};
7
8use crate::{
9    error::{protect, Error},
10    into_value::IntoValue,
11    object::Object,
12    r_string::{IntoRString, RString},
13    try_convert::TryConvert,
14    value::{
15        private::{self, ReprValue as _},
16        NonZeroValue, ReprValue, Value,
17    },
18    Ruby,
19};
20
21/// A Value pointer to a RMatch struct, Ruby's internal representation of the
22/// MatchData returned from a regex match.
23///
24/// See the [`ReprValue`] and [`Object`] traits for additional methods
25/// available on this type.
26#[derive(Clone, Copy)]
27#[repr(transparent)]
28pub struct RMatch(NonZeroValue);
29
30impl RMatch {
31    /// Return `Some(RMatch)` if `val` is a `RMatch`, `None` otherwise.
32    ///
33    /// # Examples
34    ///
35    /// ```
36    /// use magnus::{eval, RMatch};
37    /// # let _cleanup = unsafe { magnus::embed::init() };
38    ///
39    /// assert!(RMatch::from_value(eval(r#""foo".match(/o/)"#).unwrap()).is_some());
40    /// assert!(RMatch::from_value(eval(r#""o""#).unwrap()).is_none());
41    /// ```
42    #[inline]
43    pub fn from_value(val: Value) -> Option<Self> {
44        unsafe {
45            (val.rb_type() == ruby_value_type::RUBY_T_MATCH)
46                .then(|| Self(NonZeroValue::new_unchecked(val)))
47        }
48    }
49
50    #[inline]
51    pub(crate) unsafe fn from_rb_value_unchecked(val: VALUE) -> Self {
52        Self(NonZeroValue::new_unchecked(Value::new(val)))
53    }
54
55    /// Returns whether the `n`th capture group is set.
56    ///
57    /// Returns `Some(true)` when there is an `n`th capture and it has a value.
58    /// Returns `Some(false)` when there is an `n`th capture but it is empty.
59    /// Returns `None` when there is no `n`th capture.
60    ///
61    /// This function is similar to [`nth_match`](Self::nth_match), but can be
62    /// used to avoid allocating a Ruby string if the value of the capture is
63    /// not needed.
64    ///
65    /// # Examples
66    ///
67    /// ```
68    /// use magnus::{Error, Ruby};
69    ///
70    /// fn example(ruby: &Ruby) -> Result<(), Error> {
71    ///     let regexp = ruby.reg_new(".([a-z])([a-z]*)([0-9])?", Default::default())?;
72    ///     regexp.reg_match("ex")?;
73    ///     let match_data = ruby.backref_get().unwrap();
74    ///     // 0th group is the whole match
75    ///     assert_eq!(match_data.nth_defined(0), Some(true));
76    ///     // the `([a-z])` group
77    ///     assert_eq!(match_data.nth_defined(1), Some(true));
78    ///     // the `([a-z]*)` group
79    ///     assert_eq!(match_data.nth_defined(2), Some(true));
80    ///     // the `([0-9])?` group
81    ///     assert_eq!(match_data.nth_defined(3), Some(false));
82    ///     // no 4th group
83    ///     assert_eq!(match_data.nth_defined(4), None);
84    ///
85    ///     Ok(())
86    /// }
87    /// # Ruby::init(example).unwrap()
88    /// ```
89    pub fn nth_defined(self, n: isize) -> Option<bool> {
90        let value = unsafe { Value::new(rb_reg_nth_defined(n as c_int, self.as_rb_value())) };
91        Option::<bool>::try_convert(value).unwrap() // infallible
92    }
93
94    /// Returns the string captured by the `n`th capture group.
95    ///
96    /// Returns `None` when there is no `n`th capture.
97    ///
98    /// # Examples
99    ///
100    /// ```
101    /// use magnus::{Error, Ruby};
102    ///
103    /// fn example(ruby: &Ruby) -> Result<(), Error> {
104    ///     let regexp = ruby.reg_new(".([a-z])([a-z]*)([0-9])?", Default::default())?;
105    ///     regexp.reg_match("ex")?;
106    ///     let match_data = ruby.backref_get().unwrap();
107    ///     // 0th group is the whole match
108    ///     assert_eq!(
109    ///         match_data.nth_match(0).map(|s| s.to_string().unwrap()),
110    ///         Some(String::from("ex"))
111    ///     );
112    ///     // the `([a-z])` group
113    ///     assert_eq!(
114    ///         match_data.nth_match(1).map(|s| s.to_string().unwrap()),
115    ///         Some(String::from("x"))
116    ///     );
117    ///     // the `([a-z]*)` group
118    ///     assert_eq!(
119    ///         match_data.nth_match(2).map(|s| s.to_string().unwrap()),
120    ///         Some(String::from(""))
121    ///     );
122    ///     // the `([0-9])?` group
123    ///     assert_eq!(
124    ///         match_data.nth_match(3).map(|s| s.to_string().unwrap()),
125    ///         None
126    ///     );
127    ///     // no 4th group
128    ///     assert_eq!(
129    ///         match_data.nth_match(4).map(|s| s.to_string().unwrap()),
130    ///         None
131    ///     );
132    ///
133    ///     Ok(())
134    /// }
135    /// # Ruby::init(example).unwrap()
136    /// ```
137    pub fn nth_match(self, n: isize) -> Option<RString> {
138        let value = unsafe { Value::new(rb_reg_nth_match(n as c_int, self.as_rb_value())) };
139        (!value.is_nil()).then(|| unsafe { RString::from_rb_value_unchecked(value.as_rb_value()) })
140    }
141
142    /// Returns the index for the named capture group.
143    ///
144    /// Returns `Err` if there's is no named capture group with the given name.
145    ///
146    /// # Examples
147    ///
148    /// ```
149    /// use magnus::{Error, Ruby};
150    ///
151    /// fn example(ruby: &Ruby) -> Result<(), Error> {
152    ///     let regexp = ruby.reg_new("Hello, (?<subject>.*)!", Default::default())?;
153    ///     regexp.reg_match("Hello, World!")?;
154    ///     let match_data = ruby.backref_get().unwrap();
155    ///     assert_eq!(match_data.backref_number("subject")?, 1);
156    ///     assert!(match_data.backref_number("foo").is_err());
157    ///
158    ///     Ok(())
159    /// }
160    /// # Ruby::init(example).unwrap()
161    /// ```
162    pub fn backref_number<T>(self, name: T) -> Result<usize, Error>
163    where
164        T: IntoRString,
165    {
166        let handle = Ruby::get_with(self);
167        let name = name.into_r_string_with(&handle);
168        let mut n = 0;
169        protect(|| unsafe {
170            n = rb_reg_backref_number(self.as_rb_value(), name.as_rb_value()) as usize;
171            handle.qnil()
172        })?;
173        Ok(n)
174    }
175
176    /// Returns the string matched.
177    ///
178    /// # Examples
179    ///
180    /// ```
181    /// use magnus::{Error, Ruby};
182    ///
183    /// fn example(ruby: &Ruby) -> Result<(), Error> {
184    ///     let regexp = ruby.reg_new("b(.)r", Default::default())?;
185    ///     regexp.reg_match("foo bar baz")?;
186    ///
187    ///     let match_data = ruby.backref_get().unwrap();
188    ///     assert_eq!(match_data.matched().to_string()?, "bar");
189    ///
190    ///     Ok(())
191    /// }
192    /// # Ruby::init(example).unwrap()
193    /// ```
194    pub fn matched(self) -> RString {
195        unsafe { RString::from_rb_value_unchecked(rb_reg_last_match(self.as_rb_value())) }
196    }
197
198    /// Returns the string before the segment matched.
199    ///
200    /// # Examples
201    ///
202    /// ```
203    /// use magnus::{Error, Ruby};
204    ///
205    /// fn example(ruby: &Ruby) -> Result<(), Error> {
206    ///     let regexp = ruby.reg_new("b(.)r", Default::default())?;
207    ///     regexp.reg_match("foo bar baz")?;
208    ///
209    ///     let match_data = ruby.backref_get().unwrap();
210    ///     assert_eq!(match_data.pre().to_string()?, "foo ");
211    ///
212    ///     Ok(())
213    /// }
214    /// # Ruby::init(example).unwrap()
215    /// ```
216    pub fn pre(self) -> RString {
217        unsafe { RString::from_rb_value_unchecked(rb_reg_match_pre(self.as_rb_value())) }
218    }
219
220    /// Returns the string after the segment matched.
221    ///
222    /// # Examples
223    ///
224    /// ```
225    /// use magnus::{Error, Ruby};
226    ///
227    /// fn example(ruby: &Ruby) -> Result<(), Error> {
228    ///     let regexp = ruby.reg_new("b(.)r", Default::default())?;
229    ///     regexp.reg_match("foo bar baz")?;
230    ///
231    ///     let match_data = ruby.backref_get().unwrap();
232    ///     assert_eq!(match_data.post().to_string()?, " baz");
233    ///
234    ///     Ok(())
235    /// }
236    /// # Ruby::init(example).unwrap()
237    /// ```
238    pub fn post(self) -> RString {
239        unsafe { RString::from_rb_value_unchecked(rb_reg_match_post(self.as_rb_value())) }
240    }
241
242    /// Returns the last capture.
243    ///
244    /// Returns `None` if there are no capture groups.
245    ///
246    /// # Examples
247    ///
248    /// ```
249    /// use magnus::{Error, Ruby};
250    ///
251    /// fn example(ruby: &Ruby) -> Result<(), Error> {
252    ///     let regexp = ruby.reg_new("(.)oo b(.)r ba(.)", Default::default())?;
253    ///     regexp.reg_match("foo bar baz")?;
254    ///
255    ///     let match_data = ruby.backref_get().unwrap();
256    ///     assert_eq!(match_data.last().unwrap().to_string()?, "z");
257    ///
258    ///     Ok(())
259    /// }
260    /// # Ruby::init(example).unwrap()
261    /// ```
262    pub fn last(self) -> Option<RString> {
263        let value = unsafe { Value::new(rb_reg_match_last(self.as_rb_value())) };
264        (!value.is_nil()).then(|| unsafe { RString::from_rb_value_unchecked(value.as_rb_value()) })
265    }
266}
267
268impl fmt::Display for RMatch {
269    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
270        write!(f, "{}", unsafe { self.to_s_infallible() })
271    }
272}
273
274impl fmt::Debug for RMatch {
275    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
276        write!(f, "{}", self.inspect())
277    }
278}
279
280impl IntoValue for RMatch {
281    #[inline]
282    fn into_value_with(self, _: &Ruby) -> Value {
283        self.0.get()
284    }
285}
286
287impl Object for RMatch {}
288
289unsafe impl private::ReprValue for RMatch {}
290
291impl ReprValue for RMatch {}
292
293impl TryConvert for RMatch {
294    fn try_convert(val: Value) -> Result<Self, Error> {
295        Self::from_value(val).ok_or_else(|| {
296            Error::new(
297                Ruby::get_with(val).exception_type_error(),
298                format!("no implicit conversion of {} into MatchData", unsafe {
299                    val.classname()
300                },),
301            )
302        })
303    }
304}