onig/
names.rs

1use std::os::raw::{c_int, c_void};
2use std::slice;
3use std::str::from_utf8_unchecked;
4
5use onig_sys::{OnigRegex, OnigUChar};
6
7use super::Regex;
8
9impl Regex {
10    /// Returns the number of named groups into regex.
11    pub fn capture_names_len(&self) -> usize {
12        unsafe { onig_sys::onig_number_of_names(self.raw) as usize }
13    }
14
15    /// Calls `callback` for each named group in the regex. Each callback gets the group name
16    /// and group indices.
17    pub fn foreach_name<F>(&self, mut callback: F) -> i32
18    where
19        F: FnMut(&str, &[u32]) -> bool,
20    {
21        unsafe extern "C" fn foreach_cb<F>(
22            name: *const OnigUChar,
23            name_end: *const OnigUChar,
24            ngroup_num: c_int,
25            group_nums: *mut c_int,
26            _regex: OnigRegex,
27            arg: *mut c_void,
28        ) -> c_int
29        where
30            F: FnMut(&str, &[u32]) -> bool,
31        {
32            let name = from_utf8_unchecked(slice::from_raw_parts(
33                name,
34                name_end as usize - name as usize,
35            ));
36
37            let groups = slice::from_raw_parts(group_nums as *const u32, ngroup_num as usize);
38
39            let callback = &mut *(arg as *mut F);
40
41            if callback(name, groups) {
42                0
43            } else {
44                -1
45            }
46        }
47
48        unsafe {
49            onig_sys::onig_foreach_name(
50                self.raw,
51                Some(foreach_cb::<F>),
52                &mut callback as *mut F as *mut c_void,
53            )
54        }
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use super::super::*;
61
62    #[test]
63    fn test_regex_names_len() {
64        let regex = Regex::new("(he)(l+)(o)").unwrap();
65        assert_eq!(regex.capture_names_len(), 0);
66        let regex = Regex::new("(?<foo>he)(?<bar>l+)(?<bar>o)").unwrap();
67        assert_eq!(regex.capture_names_len(), 2);
68        assert_eq!(regex.capture_histories_len(), 0);
69    }
70
71    #[test]
72    fn test_regex_names() {
73        let regex = Regex::new("(he)(l+)(o)").unwrap();
74        let mut names = Vec::new();
75        regex.foreach_name(|n, i| {
76            names.push((n.to_string(), i.iter().cloned().collect::<Vec<_>>()));
77            true
78        });
79        assert_eq!(names, vec![]);
80        let regex = Regex::new("(?<foo>he)(?<bar>l+)(?<bar>o)").unwrap();
81        let mut names = Vec::new();
82        regex.foreach_name(|n, i| {
83            names.push((n.to_string(), i.iter().cloned().collect::<Vec<_>>()));
84            true
85        });
86        assert_eq!(
87            names,
88            vec![("foo".into(), vec![1u32]), ("bar".into(), vec![2u32, 3])]
89        );
90    }
91}