1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
use std::mem::transmute;
use std::ops::Index;
use std::iter::Iterator;
use onig_sys;

#[repr(C)]
#[derive(Debug)]
pub struct CaptureTreeNode {
    raw: onig_sys::OnigCaptureTreeNode,
}

impl CaptureTreeNode {
    pub fn group(&self) -> usize {
        self.raw.group as usize
    }

    pub fn pos(&self) -> (usize, usize) {
        (self.raw.beg as usize, self.raw.end as usize)
    }

    pub fn len(&self) -> usize {
        self.raw.num_childs as usize
    }

    pub fn children<'t>(&'t self) -> CaptureTreeNodeIter<'t> {
        CaptureTreeNodeIter {
            idx: 0,
            node: self,
        }
    }
}

impl Index<usize> for CaptureTreeNode {
    type Output = CaptureTreeNode;

    fn index(&self, index: usize) -> &CaptureTreeNode {
        if index >= self.len() {
            panic!("capture tree node index overflow")
        }
        unsafe { transmute(*self.raw.childs.offset(index as isize)) }
    }
}

#[derive(Debug)]
pub struct CaptureTreeNodeIter<'t> {
    idx: usize,
    node: &'t CaptureTreeNode,
}

impl<'t> Iterator for CaptureTreeNodeIter<'t> {
    type Item = &'t CaptureTreeNode;

    fn next(&mut self) -> Option<&'t CaptureTreeNode> {
        if self.idx < self.node.len() {
            self.idx += 1;
            Some(&self.node[self.idx - 1])
        } else {
            None
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let size = self.node.len();
        (size, Some(size))
    }
}

#[cfg(test)]
mod tests {
    use super::super::*;

    #[test]
    fn test_regex_search_with_region_tree() {
        let mut region = Region::new();
        let mut syntax = Syntax::ruby().clone();
        syntax.enable_operators(SYNTAX_OPERATOR_ATMARK_CAPTURE_HISTORY);

        let regex = Regex::with_options("(?@a+(?@b+))|(?@c+(?@d+))", REGEX_OPTION_NONE, &syntax)
            .unwrap();

        let r = regex.search_with_options("- cd aaabbb -",
                                          0,
                                          13,
                                          SEARCH_OPTION_NONE,
                                          Some(&mut region));

        assert_eq!(r, Some(2));
        assert_eq!(region.len(), 5);

        let tree = region.tree().unwrap();

        assert_eq!(tree.len(), 1);
        assert_eq!(tree.group(), 0);
        assert_eq!(tree.pos(), (2, 4));

        assert_eq!(tree[0].len(), 1);
        assert_eq!(tree[0].group(), 3);
        assert_eq!(tree[0].pos(), (2, 4));

        assert_eq!(tree[0][0].len(), 0);
        assert_eq!(tree[0][0].group(), 4);
        assert_eq!(tree[0][0].pos(), (3, 4));
    }
}