<commit_before>use super::super::{Coord, Grid, X, Y};
use super::*;
use grid_loop::{Edge, GridLoop, GridLoopField};
use std::cmp;

#[derive(Clone)]
pub struct Field {
    grid_loop: GridLoop,
    clue: Grid<Clue>,
    cell: Grid<Cell>,
}

const FOUR_NEIGHBORS: [(i32, i32); 4] = [(-1, 0), (0, -1), (1, 0), (0, 1)];

impl Field {
    pub fn new(clue: &Grid<Clue>) -> Field {
        let height = clue.height();
        let width = clue.width();

        let mut cell = Grid::new(height, width, Cell::Undecided);
        let mut grid_loop = GridLoop::new(height - 1, width - 1);

        {
            let mut handle = GridLoop::get_handle(&mut grid_loop);
            for y in 0..height {
                for x in 0..width {
                    let c = clue[(Y(y), X(x))];
                    if c != Clue::NoClue {
                        cell[(Y(y), X(x))] = Cell::Clue;
                        GridLoop::decide_edge(&mut *handle, (Y(y * 2 - 1), X(x * 2)), Edge::Blank);
                        GridLoop::decide_edge(&mut *handle, (Y(y * 2 + 1), X(x * 2)), Edge::Blank);
                        GridLoop::decide_edge(&mut *handle, (Y(y * 2), X(x * 2 - 1)), Edge::Blank);
                        GridLoop::decide_edge(&mut *handle, (Y(y * 2), X(x * 2 + 1)), Edge::Blank);
                    }
                }
            }
        }
        Field {
            grid_loop,
            clue: clue.clone(),
            cell,
        }
    }
    pub fn height(&self) -> i32 {
        self.clue.height()
    }
    pub fn width(&self) -> i32 {
        self.clue.width()
    }
    pub fn inconsistent(&self) -> bool {
        self.grid_loop.inconsistent()
    }
    pub fn set_inconsistent(&mut self) {
        self.grid_loop.set_inconsistent()
    }
    pub fn fully_solved(&self) -> bool {
        self.grid_loop.fully_solved()
    }
    pub fn get_cell(&self, cd: Coord) -> Cell {
        self.cell[cd]
    }
    pub fn get_cell_safe(&self, cd: Coord) -> Cell {
        if self.cell.is_valid_coord(cd) {
            self.cell[cd]
        } else {
            // The outside of the field can be identified with a (meaningless) clue
            Cell::Clue
        }
    }
    pub fn get_edge(&self, cd: Coord) -> Edge {
        self.grid_loop.get_edge(cd)
    }
    pub fn get_edge_safe(&self, cd: Coord) -> Edge {
        self.grid_loop.get_edge_safe(cd)
    }

    pub fn set_cell(&mut self, cd: Coord, v: Cell) {
        let mut handle = GridLoop::get_handle(self);
        handle.set_cell_internal(cd, v);
    }
    pub fn check_all_cell(&mut self) {
        let height = self.height();
        let width = self.width();
        let mut handle = GridLoop::get_handle(self);
        for y in 0..height {
            for x in 0..width {
                GridLoop::check(&mut *handle, (Y(y * 2), X(x * 2)));
            }
        }
    }

    fn set_cell_internal(&mut self, cd: Coord, v: Cell) {
        let current = self.cell[cd];
        if current != Cell::Undecided {
            if current != v {
                self.set_inconsistent();
            }
            return;
        }

        self.cell[cd] = v;
        let (Y(y), X(x)) = cd;
        match v {
            Cell::Undecided => (),
            Cell::Clue => (), // don't do this!
            Cell::Line => GridLoop::check(self, (Y(y * 2), X(x * 2))),
            Cell::Blocked => for &(dy, dx) in &FOUR_NEIGHBORS {
                if self.get_cell_safe((Y(y + dy), X(x + dx))) != Cell::Clue {
                    self.set_cell_internal((Y(y + dy), X(x + dx)), Cell::Line);
                }
                GridLoop::decide_edge(self, (Y(y * 2 + dy), X(x * 2 + dx)), Edge::Blank);
            },
        }
    }
    fn inspect_clue(&mut self, cell_cd: Coord) {
        let (Y(y), X(x)) = cell_cd;
        let clue = self.clue[cell_cd];
        let (dy, dx, mut n) = match clue {
            Clue::NoClue | Clue::Empty => return,
            Clue::Up(n) => (-1, 0, n),
            Clue::Left(n) => (0, -1, n),
            Clue::Right(n) => (0, 1, n),
            Clue::Down(n) => (1, 0, n),
        };
        let mut involving_cells = 0;
        {
            let mut y = y + dy;
            let mut x = x + dx;
            while self.clue.is_valid_coord((Y(y), X(x))) {
                let c = self.clue[(Y(y), X(x))];
                if c.same_shape(clue) {
                    n -= c.clue_number();
                    break;
                }
                y += dy;
                x += dx;
                involving_cells += 1;
            }
        }
        let mut dp_left = vec![(0, 0); (involving_cells + 1) as usize];
        let mut dp_right = vec![(0, 0); (involving_cells + 1) as usize];

        for i in 0..involving_cells {
            let c = self.get_cell((Y(y + dy * (i + 1)), X(x + dx * (i + 1))));
            dp_left[(i + 1) as usize] = match c {
                Cell::Undecided => {
                    let (lo, hi) = dp_left[cmp::max(0, i - 1) as usize];
                    (lo, hi + 1)
                }
                Cell::Clue | Cell::Line => dp_left[i as usize],
                Cell::Blocked => {
                    let (lo, hi) = dp_left[cmp::max(0, i - 1) as usize];
                    (lo + 1, hi + 1)
                }
            };
        }
        for i in 0..involving_cells {
            let i = involving_cells - 1 - i;
            let c = self.get_cell((Y(y + dy * (i + 1)), X(x + dx * (i + 1))));
            dp_right[i as usize] = match c {
                Cell::Undecided => {
                    let (lo, hi) = dp_right[cmp::min(involving_cells, i + 2) as usize];
                    (lo, hi + 1)
                }
                Cell::Clue | Cell::Line => dp_right[(i + 1) as usize],
                Cell::Blocked => {
                    let (lo, hi) = dp_right[cmp::min(involving_cells, i + 2) as usize];
                    (lo + 1, hi + 1)
                }
            };
        }
        for i in 0..involving_cells {
            let (left_lo, left_hi) = dp_left[i as usize];
            let (right_lo, right_hi) = dp_right[(i + 1) as usize];

            if left_hi + right_hi < n - 1 {
                self.set_inconsistent();
                return;
            } else if left_hi + right_hi == n - 1 {
                self.set_cell_internal((Y(y + dy * (i + 1)), X(x + dx * (i + 1))), Cell::Blocked);
            }

            if left_lo + right_lo > n {
                self.set_inconsistent();
                return;
            } else if left_lo + right_lo == n {
                self.set_cell_internal((Y(y + dy * (i + 1)), X(x + dx * (i + 1))), Cell::Line);
            }
        }
    }
}

impl GridLoopField for Field {
    fn grid_loop(&mut self) -> &mut GridLoop {
        &mut self.grid_loop
    }
    fn check_neighborhood(&mut self, (Y(y), X(x)): Coord) {
        if y % 2 == 0 {
            GridLoop::check(self, (Y(y), X(x - 1)));
            GridLoop::check(self, (Y(y), X(x + 1)));
        } else {
            GridLoop::check(self, (Y(y - 1), X(x)));
            GridLoop::check(self, (Y(y + 1), X(x)));
        }
    }
    fn inspect(&mut self, cd: Coord) {
        let (Y(y), X(x)) = cd;
        if !(y % 2 == 0 && x % 2 == 0) {
            return;
        }
        let cell = self.get_cell((Y(y / 2), X(x / 2)));
        if cell == Cell::Line || cell == Cell::Undecided {
            let (n_line, n_undecided) = self.grid_loop.neighbor_summary(cd);

            if cell == Cell::Line {
                if n_line + n_undecided <= 1 {
                    self.set_inconsistent();
                    return;
                } else if n_line + n_undecided <= 2 {
                    for &(dy, dx) in &FOUR_NEIGHBORS {
                        if self.get_edge_safe((Y(y + dy), X(x + dx))) != Edge::Blank {
                            GridLoop::decide_edge(self, (Y(y + dy), X(x + dx)), Edge::Line);
                        }
                    }
                }
            } else {
                if n_line == 0 && n_undecided == 2 {
                    for &(dy, dx) in &FOUR_NEIGHBORS {
                        if self.get_edge_safe((Y(y + dy), X(x + dx))) == Edge::Undecided {
                            self.set_cell_internal((Y(y / 2 + dy), X(x / 2 + dx)), Cell::Line);
                        }
                    }
                }
            }

            if cell == Cell::Undecided {
                if n_line >= 1 {
                    self.set_cell_internal((Y(y / 2), X(x / 2)), Cell::Line);
                }
                if n_line == 0 && n_undecided <= 1 {
                    self.set_cell_internal((Y(y / 2), X(x / 2)), Cell::Blocked);
                }
            }
        } else if cell == Cell::Clue {
            self.inspect_clue((Y(y / 2), X(x / 2)));
        }
    }
}

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

    #[test]
    fn test_yajilin_clue() {
        {
            // Loop must not pass through clue cells
            let mut problem = Grid::new(5, 5, Clue::NoClue);
            problem[(Y(0), X(0))] = Clue::Right(1);

            let mut field = Field::new(&problem);
            field.check_all_cell();

            assert_eq!(field.inconsistent(), false);
            assert_eq!(field.get_edge((Y(0), X(1))), Edge::Blank);
            assert_eq!(field.get_edge((Y(1), X(0))), Edge::Blank);
        }
        {
            // 0 block cells
            let mut problem = Grid::new(5, 7, Clue::NoClue);
            problem[(Y(0), X(3))] = Clue::Right(0);

            let mut field = Field::new(&problem);
            field.check_all_cell();

            assert_eq!(field.inconsistent(), false);
            assert_eq!(field.get_cell((Y(0), X(4))), Cell::Line);
            assert_eq!(field.get_cell((Y(0), X(5))), Cell::Line);
            assert_eq!(field.get_cell((Y(0), X(6))), Cell::Line);
            assert_eq!(field.get_edge((Y(1), X(8))), Edge::Line);
        }
        {
            // 2 block cells in 3 consecutive cells
            let mut problem = Grid::new(5, 5, Clue::NoClue);
            problem[(Y(2), X(1))] = Clue::Right(2);

            let mut field = Field::new(&problem);
            field.check_all_cell();

            assert_eq!(field.inconsistent(), false);
            assert_eq!(field.fully_solved(), true);
            assert_eq!(field.get_cell((Y(2), X(2))), Cell::Blocked);
            assert_eq!(field.get_cell((Y(2), X(4))), Cell::Blocked);
            assert_eq!(field.get_cell((Y(2), X(0))), Cell::Line);
            assert_eq!(field.get_edge((Y(3), X(6))), Edge::Line);
            assert_eq!(field.get_edge((Y(5), X(6))), Edge::Line);
            assert_eq!(field.get_edge((Y(2), X(3))), Edge::Line);
            assert_eq!(field.get_edge((Y(3), X(0))), Edge::Line);
        }
        {
            // Cells which the loop does not pass through must be blocked (or have a clue)
            let mut problem = Grid::new(6, 6, Clue::NoClue);
            problem[(Y(2), X(4))] = Clue::Up(1);
            problem[(Y(4), X(4))] = Clue::Left(1);

            let mut field = Field::new(&problem);
            field.check_all_cell();

            assert_eq!(field.inconsistent(), false);
            assert_eq!(field.get_cell((Y(1), X(4))), Cell::Blocked);
            assert_eq!(field.get_cell((Y(3), X(4))), Cell::Blocked);
        }
        {
            // Clues to the same direction
            let mut problem = Grid::new(6, 6, Clue::NoClue);
            problem[(Y(2), X(0))] = Clue::Right(2);
            problem[(Y(2), X(2))] = Clue::Right(1);

            let mut field = Field::new(&problem);
            field.check_all_cell();

            assert_eq!(field.inconsistent(), false);
            assert_eq!(field.get_cell((Y(2), X(1))), Cell::Blocked);
        }
    }
}
<commit_msg>Yajilin: bug fix, support Display<commit_after>use super::super::{Coord, Grid, X, Y};
use super::*;
use grid_loop::{Edge, GridLoop, GridLoopField};
use std::cmp;
use std::fmt;

#[derive(Clone)]
pub struct Field {
    grid_loop: GridLoop,
    clue: Grid<Clue>,
    cell: Grid<Cell>,
}

const FOUR_NEIGHBORS: [(i32, i32); 4] = [(-1, 0), (0, -1), (1, 0), (0, 1)];

impl Field {
    pub fn new(clue: &Grid<Clue>) -> Field {
        let height = clue.height();
        let width = clue.width();

        let mut cell = Grid::new(height, width, Cell::Undecided);
        let mut grid_loop = GridLoop::new(height - 1, width - 1);

        {
            let mut handle = GridLoop::get_handle(&mut grid_loop);
            for y in 0..height {
                for x in 0..width {
                    let c = clue[(Y(y), X(x))];
                    if c != Clue::NoClue {
                        cell[(Y(y), X(x))] = Cell::Clue;
                        GridLoop::decide_edge(&mut *handle, (Y(y * 2 - 1), X(x * 2)), Edge::Blank);
                        GridLoop::decide_edge(&mut *handle, (Y(y * 2 + 1), X(x * 2)), Edge::Blank);
                        GridLoop::decide_edge(&mut *handle, (Y(y * 2), X(x * 2 - 1)), Edge::Blank);
                        GridLoop::decide_edge(&mut *handle, (Y(y * 2), X(x * 2 + 1)), Edge::Blank);
                    }
                }
            }
        }
        Field {
            grid_loop,
            clue: clue.clone(),
            cell,
        }
    }
    pub fn height(&self) -> i32 {
        self.clue.height()
    }
    pub fn width(&self) -> i32 {
        self.clue.width()
    }
    pub fn inconsistent(&self) -> bool {
        self.grid_loop.inconsistent()
    }
    pub fn set_inconsistent(&mut self) {
        self.grid_loop.set_inconsistent()
    }
    pub fn fully_solved(&self) -> bool {
        self.grid_loop.fully_solved()
    }
    pub fn get_cell(&self, cd: Coord) -> Cell {
        self.cell[cd]
    }
    pub fn get_cell_safe(&self, cd: Coord) -> Cell {
        if self.cell.is_valid_coord(cd) {
            self.cell[cd]
        } else {
            // The outside of the field can be identified with a (meaningless) clue
            Cell::Clue
        }
    }
    pub fn get_edge(&self, cd: Coord) -> Edge {
        self.grid_loop.get_edge(cd)
    }
    pub fn get_edge_safe(&self, cd: Coord) -> Edge {
        self.grid_loop.get_edge_safe(cd)
    }

    pub fn set_cell(&mut self, cd: Coord, v: Cell) {
        let mut handle = GridLoop::get_handle(self);
        handle.set_cell_internal(cd, v);
    }
    pub fn check_all_cell(&mut self) {
        let height = self.height();
        let width = self.width();
        let mut handle = GridLoop::get_handle(self);
        for y in 0..height {
            for x in 0..width {
                GridLoop::check(&mut *handle, (Y(y * 2), X(x * 2)));
            }
        }
    }

    fn set_cell_internal(&mut self, cd: Coord, v: Cell) {
        let current = self.cell[cd];
        if current != Cell::Undecided {
            if current != v {
                self.set_inconsistent();
            }
            return;
        }

        self.cell[cd] = v;
        let (Y(y), X(x)) = cd;
        match v {
            Cell::Undecided => (),
            Cell::Clue => (), // don't do this!
            Cell::Line => GridLoop::check(self, (Y(y * 2), X(x * 2))),
            Cell::Blocked => for &(dy, dx) in &FOUR_NEIGHBORS {
                if self.get_cell_safe((Y(y + dy), X(x + dx))) != Cell::Clue {
                    self.set_cell_internal((Y(y + dy), X(x + dx)), Cell::Line);
                }
                GridLoop::decide_edge(self, (Y(y * 2 + dy), X(x * 2 + dx)), Edge::Blank);
            },
        }
    }
    fn inspect_clue(&mut self, cell_cd: Coord) {
        let (Y(y), X(x)) = cell_cd;
        let clue = self.clue[cell_cd];
        let (dy, dx, mut n) = match clue {
            Clue::NoClue | Clue::Empty => return,
            Clue::Up(n) => (-1, 0, n),
            Clue::Left(n) => (0, -1, n),
            Clue::Right(n) => (0, 1, n),
            Clue::Down(n) => (1, 0, n),
        };
        let mut involving_cells = 0;
        {
            let mut y = y + dy;
            let mut x = x + dx;
            while self.clue.is_valid_coord((Y(y), X(x))) {
                let c = self.clue[(Y(y), X(x))];
                if c.same_shape(clue) {
                    n -= c.clue_number();
                    break;
                }
                y += dy;
                x += dx;
                involving_cells += 1;
            }
        }
        let mut dp_left = vec![(0, 0); (involving_cells + 1) as usize];
        let mut dp_right = vec![(0, 0); (involving_cells + 1) as usize];

        for i in 0..involving_cells {
            let c = self.get_cell((Y(y + dy * (i + 1)), X(x + dx * (i + 1))));
            dp_left[(i + 1) as usize] = match c {
                Cell::Undecided => {
                    let (lo, hi) = dp_left[cmp::max(0, i - 1) as usize];
                    (lo, hi + 1)
                }
                Cell::Clue | Cell::Line => dp_left[i as usize],
                Cell::Blocked => {
                    let (lo, hi) = dp_left[cmp::max(0, i - 1) as usize];
                    (lo + 1, hi + 1)
                }
            };
        }
        for i in 0..involving_cells {
            let i = involving_cells - 1 - i;
            let c = self.get_cell((Y(y + dy * (i + 1)), X(x + dx * (i + 1))));
            dp_right[i as usize] = match c {
                Cell::Undecided => {
                    let (lo, hi) = dp_right[cmp::min(involving_cells, i + 2) as usize];
                    (lo, hi + 1)
                }
                Cell::Clue | Cell::Line => dp_right[(i + 1) as usize],
                Cell::Blocked => {
                    let (lo, hi) = dp_right[cmp::min(involving_cells, i + 2) as usize];
                    (lo + 1, hi + 1)
                }
            };
        }
        for i in 0..involving_cells {
            let (left_lo, left_hi) = dp_left[i as usize];
            let (right_lo, right_hi) = dp_right[(i + 1) as usize];

            if left_hi + right_hi < n - 1 {
                self.set_inconsistent();
                return;
            } else if left_hi + right_hi == n - 1 {
                self.set_cell_internal((Y(y + dy * (i + 1)), X(x + dx * (i + 1))), Cell::Blocked);
            }

            if left_lo + right_lo > n {
                self.set_inconsistent();
                return;
            } else if left_lo + right_lo == n {
                if self.get_cell((Y(y + dy * (i + 1)), X(x + dx * (i + 1)))) != Cell::Clue {
                    self.set_cell_internal((Y(y + dy * (i + 1)), X(x + dx * (i + 1))), Cell::Line);
                }
            }
        }
    }
}

impl GridLoopField for Field {
    fn grid_loop(&mut self) -> &mut GridLoop {
        &mut self.grid_loop
    }
    fn check_neighborhood(&mut self, (Y(y), X(x)): Coord) {
        if y % 2 == 0 {
            GridLoop::check(self, (Y(y), X(x - 1)));
            GridLoop::check(self, (Y(y), X(x + 1)));
        } else {
            GridLoop::check(self, (Y(y - 1), X(x)));
            GridLoop::check(self, (Y(y + 1), X(x)));
        }
    }
    fn inspect(&mut self, cd: Coord) {
        let (Y(y), X(x)) = cd;
        if !(y % 2 == 0 && x % 2 == 0) {
            return;
        }
        let cell = self.get_cell((Y(y / 2), X(x / 2)));
        if cell == Cell::Line || cell == Cell::Undecided {
            let (n_line, n_undecided) = self.grid_loop.neighbor_summary(cd);

            if cell == Cell::Line {
                if n_line + n_undecided <= 1 {
                    self.set_inconsistent();
                    return;
                } else if n_line + n_undecided <= 2 {
                    for &(dy, dx) in &FOUR_NEIGHBORS {
                        if self.get_edge_safe((Y(y + dy), X(x + dx))) != Edge::Blank {
                            GridLoop::decide_edge(self, (Y(y + dy), X(x + dx)), Edge::Line);
                        }
                    }
                }
            } else {
                if n_line == 0 && n_undecided == 2 {
                    for &(dy, dx) in &FOUR_NEIGHBORS {
                        if self.get_edge_safe((Y(y + dy), X(x + dx))) == Edge::Undecided {
                            self.set_cell_internal((Y(y / 2 + dy), X(x / 2 + dx)), Cell::Line);
                        }
                    }
                }
            }

            if cell == Cell::Undecided {
                if n_line >= 1 {
                    self.set_cell_internal((Y(y / 2), X(x / 2)), Cell::Line);
                }
                if n_line == 0 && n_undecided <= 1 {
                    self.set_cell_internal((Y(y / 2), X(x / 2)), Cell::Blocked);
                }
            }
        } else if cell == Cell::Clue {
            self.inspect_clue((Y(y / 2), X(x / 2)));
        }
    }
}

impl fmt::Display for Field {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let height = self.height();
        let width = self.width();
        for y in 0..(2 * height - 1) {
            for x in 0..(2 * width - 1) {
                match (y % 2, x % 2) {
                    (0, 0) => match self.get_cell((Y(y / 2), X(x / 2))) {
                        Cell::Undecided => write!(f, "+")?,
                        Cell::Line => write!(f, "*")?,
                        Cell::Blocked => write!(f, "#")?,
                        Cell::Clue => match self.clue[(Y(y / 2), X(x / 2))] {
                            Clue::NoClue => panic!(),
                            Clue::Empty => write!(f, "? ")?,
                            Clue::Up(n) => write!(f, "^{}", n)?,
                            Clue::Left(n) => write!(f, "<{}", n)?,
                            Clue::Right(n) => write!(f, ">{}", n)?,
                            Clue::Down(n) => write!(f, "v{}", n)?,
                        },
                    },
                    (0, 1) => if self.get_cell((Y(y / 2), X(x / 2))) == Cell::Clue {
                        write!(f, "  ")?;
                    } else if self.get_cell((Y(y / 2), X(x / 2 + 1))) == Cell::Clue {
                        write!(f, "   ")?;
                    } else {
                        match self.get_edge((Y(y), X(x))) {
                            Edge::Line => write!(f, "---")?,
                            Edge::Blank => write!(f, " x ")?,
                            Edge::Undecided => write!(f, "   ")?,
                        }
                    },
                    (1, 0) => if self.get_cell((Y(y / 2), X(x / 2))) == Cell::Clue
                        || self.get_cell((Y(y / 2 + 1), X(x / 2))) == Cell::Clue
                    {
                        write!(f, " ")?;
                    } else {
                        match self.get_edge((Y(y), X(x))) {
                            Edge::Line => write!(f, "|")?,
                            Edge::Blank => write!(f, "x")?,
                            Edge::Undecided => write!(f, " ")?,
                        }
                    },
                    (1, 1) => write!(f, "   ")?,
                    _ => unreachable!(),
                }
            }
            writeln!(f)?;
        }
        Ok(())
    }
}

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

    #[test]
    fn test_yajilin_clue() {
        {
            // Loop must not pass through clue cells
            let mut problem = Grid::new(5, 5, Clue::NoClue);
            problem[(Y(0), X(0))] = Clue::Right(1);

            let mut field = Field::new(&problem);
            field.check_all_cell();

            assert_eq!(field.inconsistent(), false);
            assert_eq!(field.get_edge((Y(0), X(1))), Edge::Blank);
            assert_eq!(field.get_edge((Y(1), X(0))), Edge::Blank);
        }
        {
            // 0 block cells
            let mut problem = Grid::new(5, 7, Clue::NoClue);
            problem[(Y(0), X(3))] = Clue::Right(0);

            let mut field = Field::new(&problem);
            field.check_all_cell();

            assert_eq!(field.inconsistent(), false);
            assert_eq!(field.get_cell((Y(0), X(4))), Cell::Line);
            assert_eq!(field.get_cell((Y(0), X(5))), Cell::Line);
            assert_eq!(field.get_cell((Y(0), X(6))), Cell::Line);
            assert_eq!(field.get_edge((Y(1), X(8))), Edge::Line);
        }
        {
            // 2 block cells in 3 consecutive cells
            let mut problem = Grid::new(5, 5, Clue::NoClue);
            problem[(Y(2), X(1))] = Clue::Right(2);

            let mut field = Field::new(&problem);
            field.check_all_cell();

            assert_eq!(field.inconsistent(), false);
            assert_eq!(field.fully_solved(), true);
            assert_eq!(field.get_cell((Y(2), X(2))), Cell::Blocked);
            assert_eq!(field.get_cell((Y(2), X(4))), Cell::Blocked);
            assert_eq!(field.get_cell((Y(2), X(0))), Cell::Line);
            assert_eq!(field.get_edge((Y(3), X(6))), Edge::Line);
            assert_eq!(field.get_edge((Y(5), X(6))), Edge::Line);
            assert_eq!(field.get_edge((Y(2), X(3))), Edge::Line);
            assert_eq!(field.get_edge((Y(3), X(0))), Edge::Line);
        }
        {
            // Cells which the loop does not pass through must be blocked (or have a clue)
            let mut problem = Grid::new(6, 6, Clue::NoClue);
            problem[(Y(2), X(4))] = Clue::Up(1);
            problem[(Y(4), X(4))] = Clue::Left(1);

            let mut field = Field::new(&problem);
            field.check_all_cell();

            assert_eq!(field.inconsistent(), false);
            assert_eq!(field.get_cell((Y(1), X(4))), Cell::Blocked);
            assert_eq!(field.get_cell((Y(3), X(4))), Cell::Blocked);
        }
        {
            // Clues to the same direction
            let mut problem = Grid::new(6, 6, Clue::NoClue);
            problem[(Y(2), X(0))] = Clue::Right(2);
            problem[(Y(2), X(2))] = Clue::Right(1);

            let mut field = Field::new(&problem);
            field.check_all_cell();

            assert_eq!(field.inconsistent(), false);
            assert_eq!(field.get_cell((Y(2), X(1))), Cell::Blocked);
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 Vincent Vigneron. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at.your option.
// This file may not be copied, modified, or distributed
// except according to those terms.

extern crate itertools;

use std::collections::{HashSet,HashMap};
use std::fmt;                          // Formatter, format!, Display, Debug, write!
use std::error;
use std::result;

/// The `DFAError` type.
#[derive(Debug)]
pub enum DFAError {
    /// The transition from state `usize` with symbol `char` is defined twice.
    DuplicatedTransition(char,usize),
    /// No final state is specified.
    MissingFinalStates,
    /// No starting state is specified.
    MissingStartingState,
}

impl fmt::Display for DFAError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            DFAError::DuplicatedTransition(symb,state) => write!(f, "Duplicated transition ('{}',{}).", symb, state),
            DFAError::MissingFinalStates => write!(f, "Missing final states."),
            DFAError::MissingStartingState => write!(f, "Missing starting state."),
        }
    }
}

impl error::Error for DFAError {
    fn description(&self) -> &str {
        match *self {
            DFAError::DuplicatedTransition(_,_) => "Duplicated transition.",
            DFAError::MissingFinalStates => "Missing final states.",
            DFAError::MissingStartingState => "Missing starting state.",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        None
    }
}

/// The type `DFA` represents a Deterministic Finite Automaton. The transitions
/// of the automatonn are stored in a hashtable.
#[derive(Debug)]
pub struct DFA {
    transitions : HashMap<(char,usize),usize>,
    start       : usize,
    finals      : HashSet<usize>,
}
/*
/// a
struct SrcState( u64 );
/// a
struct DstState( u64 );
/// a
struct Symbol( char );
*/

/// The `DFABuilder` follows the builder pattern and allows to create a Deterministic
/// Finite Automaton. The builder is moved at each call so it is necessary to bind
/// to a new variable the return value for each function of the builder.
///
/// # Errors
///
/// Return an error if the starting state is not specified.
///
/// Return an error if the final states are not specified.
///
/// # Examples
///
/// ```
/// extern crate automaton;
///
/// use automaton::dfa::core::*;
/// use std::error::Error;
///
/// fn main() {
///     // (abc)*
///     let dfa = DFABuilder::new()
///         .add_start(0)
///         .add_final(0)
///         .add_transition('a', 0, 1)
///         .add_transition('b', 1, 2)
///         .add_transition('c', 2, 0)
///         .finalize();
/// }
/// ```
///
/// ```
/// extern crate automaton;
///
/// use automaton::dfa::core::*;
/// use std::error::Error;
///
/// fn main() {
///     let dfa = DFABuilder::new()
///         .add_start(4)
///         .add_transition('t', 0, 1)
///         .finalize();
///     match dfa {
///         Err(DFAError::MissingFinalStates) => assert!(true),
///         _ => assert!(false),
///     }
/// }
/// ```
///
/// ```
/// extern crate automaton;
///
/// use automaton::dfa::core::*;
/// use std::error::Error;
///
/// fn main() {
///     let dfa = DFABuilder::new()
///         .add_start(4)
///         .add_transition('t', 0, 1)
///         .add_transition('t', 0, 2)
///         .finalize();
///     match dfa {
///         Err(DFAError::DuplicatedTransition(symb,src)) => assert!((symb,src)==('t',0)),
///         _ => assert!(false),
///     }
/// }
/// ```
///
/// ```
/// extern crate automaton;
///
/// use automaton::dfa::core::*;
/// use std::error::Error;
///
/// fn main() {
///     let dfa = DFABuilder::new()
///         .add_final(4)
///         .add_transition('t', 0, 1)
///         .finalize();
///     match dfa {
///         Err(DFAError::MissingStartingState) => assert!(true),
///         _ => assert!(false),
///     }
/// }
/// ```
///
#[derive(Debug)]
pub struct DFABuilder {
    transitions : HashMap<(char,usize),usize>,
    start       : Option<usize>,
    finals      : HashSet<usize>,
}

/// Alias for result::Result<T,DFAError>.
pub type Result<T> = result::Result<T,DFAError>;

/// DFABuilding is the trait assiociated to the DFABuilder type. Each DFABuilder
/// should implement DFABuilding trait.
///
/// DFABuilder can generate some errors during the building stage. For instance,
/// one could try to insert a transition with two different destination states.
///
/// #Errors
///
/// If self contains a DFAerror then each function should transfer this error.
pub trait DFABuilding {
    /// Add a starting state to the DFA.
    ///
    /// # Errors
    ///
    /// In the futur will return a DFAError::DuplicatedStartingState if
    /// two starting states are added.
    fn add_start(self, state: usize) -> Result<DFABuilder>;

    /// Add a final state to the DFA.
    fn add_final(self, state: usize) -> Result<DFABuilder>;

    /// Add a transition to the DFA.
    ///
    /// # Errors
    ///
    /// Return a DFAError::DuplicatedTransition(symb,src) if a transtion
    /// with the same symb and src has already been inserted, even if
    /// the destination state is the same.
    fn add_transition(self, symb: char, src: usize, dest: usize) -> Result<DFABuilder>;

    /// Finalize the building of the DFA.
    ///
    /// # Errors
    ///
    /// Return a DFAError::MissingStartingState if no starting state is specified.
    ///
    /// Return a DFAError::MissingFinalStates if no final state is specified.
    fn finalize(self) -> Result<DFA>;
}

impl DFABuilder {
    /// Creates a new DFABuilder.
    pub fn new() -> Result<DFABuilder> {
        Ok(DFABuilder{transitions: HashMap::new(), start: None, finals: HashSet::new()})
    }
}

impl DFABuilding for DFABuilder {
    fn add_start(self, state: usize) -> Result<DFABuilder> {
        Ok(self).add_start(state)
    }

    fn add_final(self, state: usize) -> Result<DFABuilder> {
        Ok(self).add_final(state)
    }

    fn add_transition(self, symb: char, src: usize, dest: usize) -> Result<DFABuilder> {
        Ok(self).add_transition(symb,src,dest)
    }

    fn finalize(self) -> Result<DFA> {
        Ok(self).finalize()
    }
}

/// Implementing DFABuilding trait for Result<DFABuilder> allows
/// to chain the return value of the DFABuilder instead of unwrapping them
/// at each stage of the building process.
impl DFABuilding for Result<DFABuilder> {
    fn add_start(self, state: usize) -> Result<DFABuilder> {
        self.map(|mut dfa| {
            dfa.start = Some(state);
            dfa
        })
    }

    fn add_final(self, state: usize) -> Result<DFABuilder> {
        self.map(|mut dfa| {
            dfa.finals.insert(state);
            dfa
        })
    }

    fn add_transition(self, symb: char, src: usize, dest: usize) -> Result<DFABuilder> {
        self.and_then(|mut dfa| {
            if dfa.transitions.insert((symb,src), dest).is_some() {
                return Err(DFAError::DuplicatedTransition(symb,src));
            }
            Ok(dfa)
        })
    }

    fn finalize(self) -> Result<DFA> {
        self.and_then(|dfa| {
            if dfa.start.is_none() {
                Err(DFAError::MissingStartingState)
            } else if dfa.finals.is_empty() {
                Err(DFAError::MissingFinalStates)
            } else {
                Ok(DFA{transitions: dfa.transitions, start: dfa.start.unwrap(), finals: dfa.finals})
            }
        })
    }
}

impl DFA {
    /// Test if an input string is a word of the language defined by the DFA.
    ///
    /// # Examples
    ///
    /// ```
    /// extern crate automaton;
    ///
    /// use automaton::dfa::core::*;
    /// use std::error::Error;
    ///
    /// fn main() {
    ///     // (abc)*
    ///     let dfa = DFABuilder::new()
    ///         .add_start(0)
    ///         .add_final(0)
    ///         .add_transition('a', 0, 1)
    ///         .add_transition('b', 1, 2)
    ///         .add_transition('c', 2, 0)
    ///         .finalize();
    ///     match dfa {
    ///         Ok(dfa) => {
    ///            assert!(dfa.test("abc"));
    ///            assert!(dfa.test(""));
    ///            assert!(!dfa.test("a"));
    ///            assert!(!dfa.test("ab"));
    ///            assert!(!dfa.test("abca"));
    ///            assert!(!dfa.test("abcab"));
    ///            assert!(dfa.test("abcabcabc"));
    ///         },
    ///         Err(e) => println!("{}", e),
    ///     }
    /// }
    /// ```
    pub fn test(&self, input: &str) -> bool {
        let f = input
            .chars()
            .fold(Some(self.start), |state,c| {
                match state {
                    Some(n) => self.transitions.get(&(c,n)).map(|v| *v),
                    None => None,
                }
            });
        match f {
            Some(n) => self.finals.contains(&n),
            None => false
        }
    }
}

impl fmt::Display for DFA {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(writeln!(f, "START: {}", self.start));
        try!(writeln!(f, "FINALS:"));
        for fi in self.finals.iter() {
            try!(writeln!(f,"  {}", fi));
        }
        try!(writeln!(f, "TRANSITIONS:"));
        for (tr,d) in self.transitions.iter() {
            let (c,s) = *tr;
            try!(writeln!(f, "  ({},{}) => {}", c, s, d));
        }
        write!(f, "")
    }
}

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

    #[test]
    fn test_dfa() {
        let dfa = DFABuilder::new()
            .add_start(0)
            .add_final(3)
            .add_transition('a', 0, 1)
            .add_transition('c', 0, 3)
            .add_transition('b', 1, 2)
            .add_transition('a', 2, 1)
            .add_transition('c', 2, 3)
            .finalize()
            .unwrap();
        let samples =
            vec![("ababac", false),
                 ("ababc", true),
                 ("", false),
                 ("abc", true),
                 ("c", true),
                 ("ac", false),
                 ("ababababababababababababababababababababc", true),];

        for (input,expected_result) in samples {
            assert!(dfa.test(input) == expected_result, "input false for: \"{}\"", input);
        }
    }

    #[test]
    fn test_dfa_builder() {
        let _dfa = DFABuilder::new()
            .add_start(0)
            .add_final(3)
            .add_transition('a', 0, 1)
            .add_transition('c', 0, 3)
            .add_transition('b', 1, 2)
            .add_transition('a', 2, 1)
            .add_transition('c', 2, 3)
            .finalize()
            .unwrap();
    }

    #[test]
    fn test_dfa_builder_duplicated_transition() {
        let dfa = DFABuilder::new()
            .add_start(0)
            .add_final(3)
            .add_transition('a', 0, 1)
            .add_transition('c', 0, 3)
            .add_transition('b', 1, 2)
            .add_transition('a', 2, 1)
            .add_transition('c', 2, 3)
            .add_transition('a', 0, 2)
            .finalize();
        match dfa {
            Err(DFAError::DuplicatedTransition(sy,sr)) => assert!((sy,sr) == ('a',0)),
            _ => assert!(false, "DuplicatedTransition expected."),
        }
    }

    #[test]
    fn test_dfa_builder_missing_start() {
        let dfa = DFABuilder::new()
            .add_final(3)
            .add_transition('a', 0, 1)
            .finalize();
        match dfa {
            Err(DFAError::MissingStartingState) => assert!(true),
            _ => assert!(false, "MissingStartingState expected."),
        }
    }

    #[test]
    fn test_dfa_builder_missing_finals() {
        let dfa = DFABuilder::new()
            .add_start(0)
            .add_transition('a', 0, 1)
            .finalize();
        match dfa {
            Err(DFAError::MissingFinalStates) => assert!(true),
            _ => assert!(false, "MissingFinalStates expected."),
        }
    }
}
<commit_msg>More flexible trait<commit_after>// Copyright 2016 Vincent Vigneron. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at.your option.
// This file may not be copied, modified, or distributed
// except according to those terms.

extern crate itertools;

use std::collections::{HashSet,HashMap};
use std::fmt;                          // Formatter, format!, Display, Debug, write!
use std::error;
use std::result;

/// The `DFAError` type.
#[derive(Debug)]
pub enum DFAError {
    /// The transition from state `usize` with symbol `char` is defined twice.
    DuplicatedTransition(char,usize),
    /// No final state is specified.
    MissingFinalStates,
    /// No starting state is specified.
    MissingStartingState,
}

impl fmt::Display for DFAError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            DFAError::DuplicatedTransition(symb,state) => write!(f, "Duplicated transition ('{}',{}).", symb, state),
            DFAError::MissingFinalStates => write!(f, "Missing final states."),
            DFAError::MissingStartingState => write!(f, "Missing starting state."),
        }
    }
}

impl error::Error for DFAError {
    fn description(&self) -> &str {
        match *self {
            DFAError::DuplicatedTransition(_,_) => "Duplicated transition.",
            DFAError::MissingFinalStates => "Missing final states.",
            DFAError::MissingStartingState => "Missing starting state.",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        None
    }
}

/// The type `DFA` represents a Deterministic Finite Automaton. The transitions
/// of the automatonn are stored in a hashtable.
#[derive(Debug)]
pub struct DFA {
    transitions : HashMap<(char,usize),usize>,
    start       : usize,
    finals      : HashSet<usize>,
}
/*
/// a
struct SrcState( u64 );
/// a
struct DstState( u64 );
/// a
struct Symbol( char );
*/

/// The `DFABuilder` follows the builder pattern and allows to create a Deterministic
/// Finite Automaton. The builder is moved at each call so it is necessary to bind
/// to a new variable the return value for each function of the builder.
///
/// # Errors
///
/// Return an error if the starting state is not specified.
///
/// Return an error if the final states are not specified.
///
/// # Examples
///
/// ```
/// extern crate automaton;
///
/// use automaton::dfa::core::*;
/// use std::error::Error;
///
/// fn main() {
///     // (abc)*
///     let dfa = DFABuilder::new()
///         .add_start(0)
///         .add_final(0)
///         .add_transition('a', 0, 1)
///         .add_transition('b', 1, 2)
///         .add_transition('c', 2, 0)
///         .finalize();
/// }
/// ```
///
/// ```
/// extern crate automaton;
///
/// use automaton::dfa::core::*;
/// use std::error::Error;
///
/// fn main() {
///     let dfa = DFABuilder::new()
///         .add_start(4)
///         .add_transition('t', 0, 1)
///         .finalize();
///     match dfa {
///         Err(DFAError::MissingFinalStates) => assert!(true),
///         _ => assert!(false),
///     }
/// }
/// ```
///
/// ```
/// extern crate automaton;
///
/// use automaton::dfa::core::*;
/// use std::error::Error;
///
/// fn main() {
///     let dfa = DFABuilder::new()
///         .add_start(4)
///         .add_transition('t', 0, 1)
///         .add_transition('t', 0, 2)
///         .finalize();
///     match dfa {
///         Err(DFAError::DuplicatedTransition(symb,src)) => assert!((symb,src)==('t',0)),
///         _ => assert!(false),
///     }
/// }
/// ```
///
/// ```
/// extern crate automaton;
///
/// use automaton::dfa::core::*;
/// use std::error::Error;
///
/// fn main() {
///     let dfa = DFABuilder::new()
///         .add_final(4)
///         .add_transition('t', 0, 1)
///         .finalize();
///     match dfa {
///         Err(DFAError::MissingStartingState) => assert!(true),
///         _ => assert!(false),
///     }
/// }
/// ```
///
#[derive(Debug)]
pub struct DFABuilder {
    transitions : HashMap<(char,usize),usize>,
    start       : Option<usize>,
    finals      : HashSet<usize>,
}

/// Alias for result::Result<T,DFAError>.
pub type Result<T> = result::Result<T,DFAError>;

/// DFABuilding is the trait assiociated to the DFABuilder type. Each DFABuilder
/// should implement DFABuilding trait.
///
/// DFABuilder can generate some errors during the building stage. For instance,
/// one could try to insert a transition with two different destination states.
///
/// #Errors
///
/// If self contains a DFAerror then each function should transfer this error.
pub trait DFABuilding {
    /// Type of DFA returned
    type Type: Sized;
    /// Type of the builder used
    type Builder: Sized;
    /// Add a starting state to the DFA.
    ///
    /// # Errors
    ///
    /// In the futur will return a DFAError::DuplicatedStartingState if
    /// two starting states are added.
    fn add_start(self, state: usize) -> Result<Self::Builder>;

    /// Add a final state to the DFA.
    fn add_final(self, state: usize) -> Result<Self::Builder>;

    /// Add a transition to the DFA.
    ///
    /// # Errors
    ///
    /// Return a DFAError::DuplicatedTransition(symb,src) if a transtion
    /// with the same symb and src has already been inserted, even if
    /// the destination state is the same.
    fn add_transition(self, symb: char, src: usize, dest: usize) -> Result<Self::Builder>;

    /// Finalize the building of the DFA.
    ///
    /// # Errors
    ///
    /// Return a DFAError::MissingStartingState if no starting state is specified.
    ///
    /// Return a DFAError::MissingFinalStates if no final state is specified.
    fn finalize(self) -> Result<Self::Type>;
}

impl DFABuilder {
    /// Creates a new DFABuilder.
    pub fn new() -> Result<DFABuilder> {
        Ok(DFABuilder{transitions: HashMap::new(), start: None, finals: HashSet::new()})
    }
}

impl DFABuilding for DFABuilder {
    type Type = DFA;
    type Builder = DFABuilder;

    fn add_start(self, state: usize) -> Result<Self::Builder> {
        Ok(self).add_start(state)
    }

    fn add_final(self, state: usize) -> Result<Self::Builder> {
        Ok(self).add_final(state)
    }

    fn add_transition(self, symb: char, src: usize, dest: usize) -> Result<Self::Builder> {
        Ok(self).add_transition(symb,src,dest)
    }

    fn finalize(self) -> Result<Self::Type> {
        Ok(self).finalize()
    }
}

/// Implementing DFABuilding trait for Result<DFABuilder> allows
/// to chain the return value of the DFABuilder instead of unwrapping them
/// at each stage of the building process.
impl DFABuilding for Result<DFABuilder> {
    type Type = DFA;
    type Builder = DFABuilder;

    fn add_start(self, state: usize) -> Result<Self::Builder> {
        self.map(|mut dfa| {
            dfa.start = Some(state);
            dfa
        })
    }

    fn add_final(self, state: usize) -> Result<Self::Builder> {
        self.map(|mut dfa| {
            dfa.finals.insert(state);
            dfa
        })
    }

    fn add_transition(self, symb: char, src: usize, dest: usize) -> Result<Self::Builder> {
        self.and_then(|mut dfa| {
            if dfa.transitions.insert((symb,src), dest).is_some() {
                return Err(DFAError::DuplicatedTransition(symb,src));
            }
            Ok(dfa)
        })
    }

    fn finalize(self) -> Result<Self::Type> {
        self.and_then(|dfa| {
            if dfa.start.is_none() {
                Err(DFAError::MissingStartingState)
            } else if dfa.finals.is_empty() {
                Err(DFAError::MissingFinalStates)
            } else {
                Ok(DFA{transitions: dfa.transitions, start: dfa.start.unwrap(), finals: dfa.finals})
            }
        })
    }
}

impl DFA {
    /// Test if an input string is a word of the language defined by the DFA.
    ///
    /// # Examples
    ///
    /// ```
    /// extern crate automaton;
    ///
    /// use automaton::dfa::core::*;
    /// use std::error::Error;
    ///
    /// fn main() {
    ///     // (abc)*
    ///     let dfa = DFABuilder::new()
    ///         .add_start(0)
    ///         .add_final(0)
    ///         .add_transition('a', 0, 1)
    ///         .add_transition('b', 1, 2)
    ///         .add_transition('c', 2, 0)
    ///         .finalize();
    ///     match dfa {
    ///         Ok(dfa) => {
    ///            assert!(dfa.test("abc"));
    ///            assert!(dfa.test(""));
    ///            assert!(!dfa.test("a"));
    ///            assert!(!dfa.test("ab"));
    ///            assert!(!dfa.test("abca"));
    ///            assert!(!dfa.test("abcab"));
    ///            assert!(dfa.test("abcabcabc"));
    ///         },
    ///         Err(e) => println!("{}", e),
    ///     }
    /// }
    /// ```
    pub fn test(&self, input: &str) -> bool {
        let f = input
            .chars()
            .fold(Some(self.start), |state,c| {
                match state {
                    Some(n) => self.transitions.get(&(c,n)).map(|v| *v),
                    None => None,
                }
            });
        match f {
            Some(n) => self.finals.contains(&n),
            None => false
        }
    }
}

impl fmt::Display for DFA {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(writeln!(f, "START: {}", self.start));
        try!(writeln!(f, "FINALS:"));
        for fi in self.finals.iter() {
            try!(writeln!(f,"  {}", fi));
        }
        try!(writeln!(f, "TRANSITIONS:"));
        for (tr,d) in self.transitions.iter() {
            let (c,s) = *tr;
            try!(writeln!(f, "  ({},{}) => {}", c, s, d));
        }
        write!(f, "")
    }
}

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

    #[test]
    fn test_dfa() {
        let dfa = DFABuilder::new()
            .add_start(0)
            .add_final(3)
            .add_transition('a', 0, 1)
            .add_transition('c', 0, 3)
            .add_transition('b', 1, 2)
            .add_transition('a', 2, 1)
            .add_transition('c', 2, 3)
            .finalize()
            .unwrap();
        let samples =
            vec![("ababac", false),
                 ("ababc", true),
                 ("", false),
                 ("abc", true),
                 ("c", true),
                 ("ac", false),
                 ("ababababababababababababababababababababc", true),];

        for (input,expected_result) in samples {
            assert!(dfa.test(input) == expected_result, "input false for: \"{}\"", input);
        }
    }

    #[test]
    fn test_dfa_builder() {
        let _dfa = DFABuilder::new()
            .add_start(0)
            .add_final(3)
            .add_transition('a', 0, 1)
            .add_transition('c', 0, 3)
            .add_transition('b', 1, 2)
            .add_transition('a', 2, 1)
            .add_transition('c', 2, 3)
            .finalize()
            .unwrap();
    }

    #[test]
    fn test_dfa_builder_duplicated_transition() {
        let dfa = DFABuilder::new()
            .add_start(0)
            .add_final(3)
            .add_transition('a', 0, 1)
            .add_transition('c', 0, 3)
            .add_transition('b', 1, 2)
            .add_transition('a', 2, 1)
            .add_transition('c', 2, 3)
            .add_transition('a', 0, 2)
            .finalize();
        match dfa {
            Err(DFAError::DuplicatedTransition(sy,sr)) => assert!((sy,sr) == ('a',0)),
            _ => assert!(false, "DuplicatedTransition expected."),
        }
    }

    #[test]
    fn test_dfa_builder_missing_start() {
        let dfa = DFABuilder::new()
            .add_final(3)
            .add_transition('a', 0, 1)
            .finalize();
        match dfa {
            Err(DFAError::MissingStartingState) => assert!(true),
            _ => assert!(false, "MissingStartingState expected."),
        }
    }

    #[test]
    fn test_dfa_builder_missing_finals() {
        let dfa = DFABuilder::new()
            .add_start(0)
            .add_transition('a', 0, 1)
            .finalize();
        match dfa {
            Err(DFAError::MissingFinalStates) => assert!(true),
            _ => assert!(false, "MissingFinalStates expected."),
        }
    }
}
<|endoftext|>


<commit_before>extern crate clap;

use clap::{App, Arg, SubCommand};

fn main() {
    let args = "-f --flag... 'tests flags'
                -o --option=[opt]... 'tests options'
                [positional] 'tests positionals'";
    // Test version from Cargo.toml
    let version = format!("{}.{}.{}{}",
                          env!("CARGO_PKG_VERSION_MAJOR"),
                          env!("CARGO_PKG_VERSION_MINOR"),
                          env!("CARGO_PKG_VERSION_PATCH"),
                          option_env!("CARGO_PKG_VERSION_PRE").unwrap_or(""));
    let matches = App::new("claptests")
                        .version(&version[..])
                        .about("tests clap library")
                        .author("Kevin K. <kbknapp@gmail.com>")
                        .args_from_usage(args)
                        .args(vec![
                            Arg::from_usage("[flag2] -F 'tests flags with exclusions'").mutually_excludes("flag").requires("option2"),
                            Arg::from_usage("--long-option-2 [option2] 'tests long options with exclusions'").mutually_excludes("option").requires("positional2"),
                            Arg::from_usage("[positional2] 'tests positionals with exclusions'"),
                            Arg::from_usage("-O [option3] 'tests options with specific value sets'").possible_values(vec!["fast", "slow"]),
                            Arg::from_usage("[positional3]... 'tests positionals with specific values'").possible_values(vec!["vi", "emacs"])
                        ])
                        .subcommand(SubCommand::new("subcmd")
                                                .about("tests subcommands")
                                                .version("0.1")
                                                .author("Kevin K. <kbknapp@gmail.com>")
                                                .arg_from_usage("[scflag] -f --flag... 'tests flags'")
                                                .arg_from_usage("-o --option [scoption]... 'tests options'")
                                                .arg_from_usage("[scpositional] 'tests positionals'"))
                        .get_matches();

    if matches.is_present("flag") {
        println!("flag present {} times", matches.occurrences_of("flag"));
    } else {
        println!("flag NOT present");
    }

    if matches.is_present("opt") {
        if let Some(v) = matches.value_of("opt") {
            println!("option present {} times with value: {}",matches.occurrences_of("opt"), v);
        }
        if let Some(ref ov) = matches.values_of("opt") {
            for o in ov {
                println!("An option: {}", o);
            }
        }
    } else {
        println!("option NOT present");
    }

    if let Some(p) = matches.value_of("positional") {
        println!("positional present with value: {}", p);
    } else {
        println!("positional NOT present");
    }

    if matches.is_present("flag2") {
        println!("flag2 present");
        println!("option2 present with value of: {}", matches.value_of("option2").unwrap());
        println!("positional2 present with value of: {}", matches.value_of("positional2").unwrap());
    } else {
        println!("flag2 NOT present");
        println!("option2 maybe present with value of: {}", matches.value_of("option2").unwrap_or("Nothing"));
        println!("positional2 maybe present with value of: {}", matches.value_of("positional2").unwrap_or("Nothing"));
    }

    match matches.value_of("option3").unwrap_or("") {
        "fast" => println!("option3 present quickly"),
        "slow" => println!("option3 present slowly"),
        _      => println!("option3 NOT present")
    }

    match matches.value_of("positional3").unwrap_or("") {
        "vi" => println!("positional3 present in vi mode"),
        "emacs" => println!("positional3 present in emacs mode"),
        _      => println!("positional3 NOT present")
    }

    if matches.is_present("opt") {
        if let Some(v) = matches.value_of("opt") {
            println!("option present {} times with value: {}",matches.occurrences_of("opt"), v);
        }
        if let Some(ref ov) = matches.values_of("opt") {
            for o in ov {
                println!("An option: {}", o);
            }
        }
    } else {
        println!("option NOT present");
    }

    if let Some(p) = matches.value_of("positional") {
        println!("positional present with value: {}", p);
    } else {
        println!("positional NOT present");
    }
    if matches.is_present("subcmd") {
        println!("subcmd present");
        if let Some(matches) = matches.subcommand_matches("subcmd") {
            if matches.is_present("scflag") {
                println!("scflag present {} times", matches.occurrences_of("scflag"));
            } else {
                println!("scflag NOT present");
            }

            if matches.is_present("scoption") {
                if let Some(v) = matches.value_of("scoption") {
                    println!("scoption present with value: {}", v);
                }
                if let Some(ref ov) = matches.values_of("scoption") {
                    for o in ov {
                        println!("An scoption: {}", o);
                    }
                }
            } else {
                println!("scoption NOT present");
            }

            if let Some(p) = matches.value_of("scpositional") {
                println!("scpositional present with value: {}", p);
            }
        }
    } else {
        println!("subcmd NOT present");
    }
}
<commit_msg>tests(main.rs): change version to new macros<commit_after>#[macro_use]
extern crate clap;

use clap::{App, Arg, SubCommand};

fn main() {
    let args = "-f --flag... 'tests flags'
                -o --option=[opt]... 'tests options'
                [positional] 'tests positionals'";
    let matches = App::new("claptests")
                        // Test version from Cargo.toml
                        .version(&crate_version!()[..])
                        .about("tests clap library")
                        .author("Kevin K. <kbknapp@gmail.com>")
                        .args_from_usage(args)
                        .args(vec![
                            Arg::from_usage("[flag2] -F 'tests flags with exclusions'").mutually_excludes("flag").requires("option2"),
                            Arg::from_usage("--long-option-2 [option2] 'tests long options with exclusions'").mutually_excludes("option").requires("positional2"),
                            Arg::from_usage("[positional2] 'tests positionals with exclusions'"),
                            Arg::from_usage("-O [option3] 'tests options with specific value sets'").possible_values(vec!["fast", "slow"]),
                            Arg::from_usage("[positional3]... 'tests positionals with specific values'").possible_values(vec!["vi", "emacs"])
                        ])
                        .subcommand(SubCommand::new("subcmd")
                                                .about("tests subcommands")
                                                .version("0.1")
                                                .author("Kevin K. <kbknapp@gmail.com>")
                                                .arg_from_usage("[scflag] -f --flag... 'tests flags'")
                                                .arg_from_usage("-o --option [scoption]... 'tests options'")
                                                .arg_from_usage("[scpositional] 'tests positionals'"))
                        .get_matches();

    if matches.is_present("flag") {
        println!("flag present {} times", matches.occurrences_of("flag"));
    } else {
        println!("flag NOT present");
    }

    if matches.is_present("opt") {
        if let Some(v) = matches.value_of("opt") {
            println!("option present {} times with value: {}",matches.occurrences_of("opt"), v);
        }
        if let Some(ref ov) = matches.values_of("opt") {
            for o in ov {
                println!("An option: {}", o);
            }
        }
    } else {
        println!("option NOT present");
    }

    if let Some(p) = matches.value_of("positional") {
        println!("positional present with value: {}", p);
    } else {
        println!("positional NOT present");
    }

    if matches.is_present("flag2") {
        println!("flag2 present");
        println!("option2 present with value of: {}", matches.value_of("option2").unwrap());
        println!("positional2 present with value of: {}", matches.value_of("positional2").unwrap());
    } else {
        println!("flag2 NOT present");
        println!("option2 maybe present with value of: {}", matches.value_of("option2").unwrap_or("Nothing"));
        println!("positional2 maybe present with value of: {}", matches.value_of("positional2").unwrap_or("Nothing"));
    }

    match matches.value_of("option3").unwrap_or("") {
        "fast" => println!("option3 present quickly"),
        "slow" => println!("option3 present slowly"),
        _      => println!("option3 NOT present")
    }

    match matches.value_of("positional3").unwrap_or("") {
        "vi" => println!("positional3 present in vi mode"),
        "emacs" => println!("positional3 present in emacs mode"),
        _      => println!("positional3 NOT present")
    }

    if matches.is_present("opt") {
        if let Some(v) = matches.value_of("opt") {
            println!("option present {} times with value: {}",matches.occurrences_of("opt"), v);
        }
        if let Some(ref ov) = matches.values_of("opt") {
            for o in ov {
                println!("An option: {}", o);
            }
        }
    } else {
        println!("option NOT present");
    }

    if let Some(p) = matches.value_of("positional") {
        println!("positional present with value: {}", p);
    } else {
        println!("positional NOT present");
    }
    if matches.is_present("subcmd") {
        println!("subcmd present");
        if let Some(matches) = matches.subcommand_matches("subcmd") {
            if matches.is_present("scflag") {
                println!("scflag present {} times", matches.occurrences_of("scflag"));
            } else {
                println!("scflag NOT present");
            }

            if matches.is_present("scoption") {
                if let Some(v) = matches.value_of("scoption") {
                    println!("scoption present with value: {}", v);
                }
                if let Some(ref ov) = matches.values_of("scoption") {
                    for o in ov {
                        println!("An scoption: {}", o);
                    }
                }
            } else {
                println!("scoption NOT present");
            }

            if let Some(p) = matches.value_of("scpositional") {
                println!("scpositional present with value: {}", p);
            }
        }
    } else {
        println!("subcmd NOT present");
    }
}
<|endoftext|>


<commit_before>/// Relocation computations
/// R_X86_64_NONE 0 none none
/// R_X86_64_64 1 word64 S + A
/// R_X86_64_PC32 2 word32 S + A - P
/// R_X86_64_GOT32 3 word32 G + A
/// R_X86_64_PLT32 4 word32 L + A - P
/// R_X86_64_COPY 5 none none
/// R_X86_64_GLOB_DAT 6 word64 S
/// R_X86_64_JUMP_SLOT 7 word64 S
/// R_X86_64_RELATIVE 8 word64 B + A
/// R_X86_64_GOTPCREL 9 word32 G + GOT + A - P
/// R_X86_64_32 10 word32 S + A
/// R_X86_64_32S 11 word32 S + A
/// R_X86_64_16 12 word16 S + A
/// R_X86_64_PC16 13 word16 S + A - P
/// R_X86_64_8 14 word8 S + A
/// R_X86_64_PC8 15 word8 S + A - P
/// R_X86_64_DTPMOD64 16 word64
/// R_X86_64_DTPOFF64 17 word64
/// R_X86_64_TPOFF64 18 word64
/// R_X86_64_TLSGD 19 word32
/// R_X86_64_TLSLD 20 word32
/// R_X86_64_DTPOFF32 21 word32
/// R_X86_64_GOTTPOFF 22 word32
/// R_X86_64_TPOFF32 23 word32
/// R_X86_64_PC64 24 word64 S + A - P
/// R_X86_64_GOTOFF64 25 word64 S + A - GOT
/// R_X86_64_GOTPC32 26 word32 GOT + A - P
/// R_X86_64_SIZE32 32 word32 Z + A
/// R_X86_64_SIZE64 33 word64 Z + A
/// R_X86_64_GOTPC32_TLSDESC 34 word32
/// R_X86_64_TLSDESC_CALL 35 none
/// R_X86_64_TLSDESC 36 word64×2
/// R_X86_64_IRELATIVE 37 word64 indirect (B + A)
///
/// TLS information is at http://people.redhat.com/aoliva/writeups/TLS/RFC-TLSDESC-x86.txt
/// `R_X86_64_IRELATIVE` is similar to `R_X86_64_RELATIVE` except that
/// the value used in this relocation is the program address returned by the function,
/// which takes no arguments, at the address of the result of the corresponding
/// `R_X86_64_RELATIVE` relocation.

macro_rules! elf_rela {
    ($size:ident) => {
    #[repr(C)]
    #[derive(Clone, Copy, PartialEq, Default)]
    pub struct Rela {
      /// Address
      pub r_offset: $size,
      /// Relocation type and symbol index
      pub r_info: $size,
      /// Addend
      pub r_addend: signed_from_unsigned!($size),
    }
    };
}

macro_rules! signed_from_unsigned {
  (u32) => {i32};
  (u64) => {i64}
}

/// No reloc.
pub const R_X86_64_NONE: u64 = 0;
/// Direct 64 bit.
pub const R_X86_64_64: u64 = 1;
/// PC relative 32 bit signed.
pub const R_X86_64_PC32: u64 = 2;
/// 32 bit GOT entry.
pub const R_X86_64_GOT32: u64 = 3;
/// 32 bit PLT address.
pub const R_X86_64_PLT32: u64 = 4;
/// Copy symbol at runtime.
pub const R_X86_64_COPY: u64 = 5;
/// Create GOT entry.
pub const R_X86_64_GLOB_DAT: u64 = 6;
/// Create PLT entry.
pub const R_X86_64_JUMP_SLOT: u64 = 7;
/// Adjust by program base.
pub const R_X86_64_RELATIVE: u64 = 8;
/// 32 bit signed PC relative offset to GOT.
pub const R_X86_64_GOTPCREL: u64 = 9;
/// Direct 32 bit zero extended.
pub const R_X86_64_32: u64 = 10;
/// Direct 32 bit sign extended.
pub const R_X86_64_32S: u64 = 11;
/// Direct 16 bit zero extended.
pub const R_X86_64_16: u64 = 12;
/// 16 bit sign extended pc relative.
pub const R_X86_64_PC16: u64 = 13;
/// Direct 8 bit sign extended.
pub const R_X86_64_8: u64 = 14;
/// 8 bit sign extended pc relative.
pub const R_X86_64_PC8: u64 = 15;
/// ID of module containing symbol.
pub const R_X86_64_DTPMOD64: u64 = 16;
/// Offset in module's TLS block.
pub const R_X86_64_DTPOFF64: u64 = 17;
/// Offset in initial TLS block.
pub const R_X86_64_TPOFF64: u64 = 18;
/// 32 bit signed PC relative offset to two GOT entries for GD symbol.
pub const R_X86_64_TLSGD: u64 = 19;
/// 32 bit signed PC relative offset to two GOT entries for LD symbol.
pub const R_X86_64_TLSLD: u64 = 20;
/// Offset in TLS block.
pub const R_X86_64_DTPOFF32: u64 = 21;
/// 32 bit signed PC relative offset to GOT entry for IE symbol.
pub const R_X86_64_GOTTPOFF: u64 = 22;
/// Offset in initial TLS block.
pub const R_X86_64_TPOFF32: u64 = 23;
/// PC relative 64 bit.
pub const R_X86_64_PC64: u64 = 24;
/// 64 bit offset to GOT.
pub const R_X86_64_GOTOFF64: u64 = 25;
/// 32 bit signed pc relative offset to GOT.
pub const R_X86_64_GOTPC32: u64 = 26;
/// 64-bit GOT entry offset.
pub const R_X86_64_GOT64: u64 = 27;
/// 64-bit PC relative offset to GOT entry.
pub const R_X86_64_GOTPCREL64: u64 = 28;
/// 64-bit PC relative offset to GOT.
pub const R_X86_64_GOTPC64: u64 = 29;
/// like GOT64, says PLT entry needed.
pub const R_X86_64_GOTPLT64: u64 = 30;
/// 64-bit GOT relative offset to PLT entry.
pub const R_X86_64_PLTOFF64: u64 = 31;
/// Size of symbol plus 32-bit addend.
pub const R_X86_64_SIZE32: u64 = 32;
/// Size of symbol plus 64-bit addend.
pub const R_X86_64_SIZE64: u64 = 33;
/// GOT offset for TLS descriptor..
pub const R_X86_64_GOTPC32_TLSDESC: u64 = 34;
/// Marker for call through TLS descriptor..
pub const R_X86_64_TLSDESC_CALL: u64 = 35;
/// TLS descriptor..
pub const R_X86_64_TLSDESC: u64 = 36;
/// Adjust indirectly by program base.
pub const R_X86_64_IRELATIVE: u64 = 37;
/// 64-bit adjust by program base.
pub const R_X86_64_RELATIVE64: u64 = 38;
pub const R_X86_64_NUM: u64 = 39;

#[inline]
pub fn type_to_str(typ: u64) -> &'static str {
    match typ {
        R_X86_64_NONE => "NONE",
        R_X86_64_64 => "64",
        R_X86_64_PC32 => "PC32",
        R_X86_64_GOT32 => "GOT32",
        R_X86_64_PLT32 => "PLT32",
        R_X86_64_COPY => "COPY",
        R_X86_64_GLOB_DAT => "GLOB_DAT",
        R_X86_64_JUMP_SLOT => "JUMP_SLOT",
        R_X86_64_RELATIVE => "RELATIVE",
        R_X86_64_GOTPCREL => "GOTPCREL",
        R_X86_64_32 => "32",
        R_X86_64_32S => "32S",
        R_X86_64_16 => "16",
        R_X86_64_PC16 => "PC16",
        R_X86_64_8 => "8",
        R_X86_64_PC8 => "PC8",
        R_X86_64_DTPMOD64 => "DTPMOD64",
        R_X86_64_DTPOFF64 => "DTPOFF64",
        R_X86_64_TPOFF64 => "TPOFF64",
        R_X86_64_TLSGD => "TLSGD",
        R_X86_64_TLSLD => "TLSLD",
        R_X86_64_DTPOFF32 => "DTPOFF32",
        R_X86_64_GOTTPOFF => "GOTTPOFF",
        R_X86_64_TPOFF32 => "TPOFF32",
        R_X86_64_PC64 => "PC64",
        R_X86_64_GOTOFF64 => "GOTOFF64",
        R_X86_64_GOTPC32 => "GOTPC32",
        R_X86_64_GOT64 => "GOT64",
        R_X86_64_GOTPCREL64 => "GOTPCREL64",
        R_X86_64_GOTPC64 => "GOTPC64",
        R_X86_64_GOTPLT64 => "GOTPLT64",
        R_X86_64_PLTOFF64 => "PLTOFF64",
        R_X86_64_SIZE32 => "SIZE32",
        R_X86_64_SIZE64 => "SIZE64",
        R_X86_64_GOTPC32_TLSDESC => "GOTPC32_TLSDESC",
        R_X86_64_TLSDESC_CALL => "TLSDESC_CALL",
        R_X86_64_TLSDESC => "TLSDESC",
        R_X86_64_IRELATIVE => "IRELATIVE",
        R_X86_64_RELATIVE64 => "RELATIVE64",
        _ => "UNKNOWN_RELA_TYPE",
    }
}

macro_rules! elf_rela_impure_impl { ($from_fd_endian:item) => {

        #[cfg(not(feature = "pure"))]
        pub use self::impure::*;

        #[cfg(not(feature = "pure"))]
        mod impure {

            use super::*;

            use std::fs::File;
            use std::io::Seek;
            use std::io::SeekFrom::Start;
            use std::io;
            use std::fmt;
            use std::slice;

            impl fmt::Debug for Rela {
                fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                    let sym = r_sym(self.r_info);
                    let typ = r_type(self.r_info);
                    write!(f,
                           "r_offset: {:x} {} @ {} r_addend: {:x}",
                           self.r_offset,
                           type_to_str(typ as u64),
                           sym,
                           self.r_addend)
                }
            }

    /// Gets the rela entries given a rela u64 and the _size_ of the rela section in the binary, in bytes.  Works for regular rela and the pltrela table.
    /// Assumes the pointer is valid and can safely return a slice of memory pointing to the relas because:
    /// 1. `rela` points to memory received from the kernel (i.e., it loaded the executable), _or_
    /// 2. The binary has already been mmapped (i.e., it's a `SharedObject`), and hence it's safe to return a slice of that memory.
    /// 3. Or if you obtained the pointer in some other lawful manner
            pub unsafe fn from_raw<'a>(ptr: *const Rela, size: usize) -> &'a [Rela] {
                slice::from_raw_parts(ptr, size / SIZEOF_RELA)
            }

            #[cfg(feature = "no_endian_fd")]
            pub fn from_fd(fd: &mut File, offset: usize, size: usize, _: bool) -> io::Result<Vec<Rela>> {
                use std::io::Read;
                let count = size / SIZEOF_RELA;
                let mut bytes = vec![0u8; size];
                try!(fd.seek(Start(offset as u64)));
                try!(fd.read(&mut bytes));
                let bytes = unsafe { slice::from_raw_parts(bytes.as_ptr() as *mut Rela, count) };
                let mut res = Vec::with_capacity(count);
                res.extend_from_slice(bytes);
                Ok(res)
            }

            #[cfg(not(feature = "no_endian_fd"))]
            $from_fd_endian
        }
    };}
<commit_msg>elf.rela: doc up some common relocations for x86-64<commit_after>//! # Relocation computations
//! Below are some common x86_64 relocation computations you might find useful:
//!
//! | Relocation | Value | Size | Formula |
//! |:-----------|:------|:-----|:-------|
//! | R_X86_64_NONE | 0 | none | none |
//! | R_X86_64_64 | 1 | word64 | S + A |
//! | R_X86_64_PC32 | 2 | word32 | S + A - P |
//! | R_X86_64_GOT32 | 3 | word32 | G + A |
//! | R_X86_64_PLT32 | 4 | word32 | L + A - P |
//! | R_X86_64_COPY | 5 | none | none |
//! | R_X86_64_GLOB_DAT | 6 | word64 | S |
//! | R_X86_64_JUMP_SLOT | 7 | word64 | S |
//! | R_X86_64_RELATIVE | 8 | word64 | B + A |
//! | R_X86_64_GOTPCREL | 9 | word32 | G + GOT + A - P |
//! | R_X86_64_32 | 10 | word32 | S + A |
//! | R_X86_64_32S | 11 | word32 | S + A |
//! | R_X86_64_16 | 12 | word16 | S + A |
//! | R_X86_64_PC16 | 13 | word16 | S + A - P |
//! | R_X86_64_8 | 14 | word8 | S + A |
//! | R_X86_64_PC8 | 15 | word8 | S + A - P |
//! | R_X86_64_DTPMOD64 | 16 | word64 | |
//! | R_X86_64_DTPOFF64 | 17 | word64 | |
//! | R_X86_64_TPOFF64 | 18 | word64 | |
//! | R_X86_64_TLSGD | 19 | word32 | |
//! | R_X86_64_TLSLD | 20 | word32 | |
//! | R_X86_64_DTPOFF32 | 21 | word32 | |
//! | R_X86_64_GOTTPOFF | 22 | word32 | |
//! | R_X86_64_TPOFF32 | 23 | word32 | |
//! | R_X86_64_PC64 | 24 | word64 | S + A - P |
//! | R_X86_64_GOTOFF64 | 25 | word64 | S + A - GOT |
//! | R_X86_64_GOTPC32 | 26 | word32 | GOT + A - P |
//! | R_X86_64_SIZE32 | 32 | word32 | Z + A |
//! | R_X86_64_SIZE64 | 33 | word64 | Z + A |
//! | R_X86_64_GOTPC32_TLSDESC | 34 | word32 | |
//! | R_X86_64_TLSDESC_CALL | 35 | none| |
//! | R_X86_64_TLSDESC | 36 | word64×2 | |
//! | R_X86_64_IRELATIVE | 37 | word64 | indirect (B + A) |
//!
//! TLS information is at http://people.redhat.com/aoliva/writeups/TLS/RFC-TLSDESC-x86.txt
//!
//! `R_X86_64_IRELATIVE` is similar to `R_X86_64_RELATIVE` except that
//! the value used in this relocation is the program address returned by the function,
//! which takes no arguments, at the address of the result of the corresponding
//! `R_X86_64_RELATIVE` relocation.

macro_rules! elf_rela {
    ($size:ident) => {
    #[repr(C)]
    #[derive(Clone, Copy, PartialEq, Default)]
    pub struct Rela {
      /// Address
      pub r_offset: $size,
      /// Relocation type and symbol index
      pub r_info: $size,
      /// Addend
      pub r_addend: signed_from_unsigned!($size),
    }
    };
}

macro_rules! signed_from_unsigned {
  (u32) => {i32};
  (u64) => {i64}
}

/// No reloc.
pub const R_X86_64_NONE: u64 = 0;
/// Direct 64 bit.
pub const R_X86_64_64: u64 = 1;
/// PC relative 32 bit signed.
pub const R_X86_64_PC32: u64 = 2;
/// 32 bit GOT entry.
pub const R_X86_64_GOT32: u64 = 3;
/// 32 bit PLT address.
pub const R_X86_64_PLT32: u64 = 4;
/// Copy symbol at runtime.
pub const R_X86_64_COPY: u64 = 5;
/// Create GOT entry.
pub const R_X86_64_GLOB_DAT: u64 = 6;
/// Create PLT entry.
pub const R_X86_64_JUMP_SLOT: u64 = 7;
/// Adjust by program base.
pub const R_X86_64_RELATIVE: u64 = 8;
/// 32 bit signed PC relative offset to GOT.
pub const R_X86_64_GOTPCREL: u64 = 9;
/// Direct 32 bit zero extended.
pub const R_X86_64_32: u64 = 10;
/// Direct 32 bit sign extended.
pub const R_X86_64_32S: u64 = 11;
/// Direct 16 bit zero extended.
pub const R_X86_64_16: u64 = 12;
/// 16 bit sign extended pc relative.
pub const R_X86_64_PC16: u64 = 13;
/// Direct 8 bit sign extended.
pub const R_X86_64_8: u64 = 14;
/// 8 bit sign extended pc relative.
pub const R_X86_64_PC8: u64 = 15;
/// ID of module containing symbol.
pub const R_X86_64_DTPMOD64: u64 = 16;
/// Offset in module's TLS block.
pub const R_X86_64_DTPOFF64: u64 = 17;
/// Offset in initial TLS block.
pub const R_X86_64_TPOFF64: u64 = 18;
/// 32 bit signed PC relative offset to two GOT entries for GD symbol.
pub const R_X86_64_TLSGD: u64 = 19;
/// 32 bit signed PC relative offset to two GOT entries for LD symbol.
pub const R_X86_64_TLSLD: u64 = 20;
/// Offset in TLS block.
pub const R_X86_64_DTPOFF32: u64 = 21;
/// 32 bit signed PC relative offset to GOT entry for IE symbol.
pub const R_X86_64_GOTTPOFF: u64 = 22;
/// Offset in initial TLS block.
pub const R_X86_64_TPOFF32: u64 = 23;
/// PC relative 64 bit.
pub const R_X86_64_PC64: u64 = 24;
/// 64 bit offset to GOT.
pub const R_X86_64_GOTOFF64: u64 = 25;
/// 32 bit signed pc relative offset to GOT.
pub const R_X86_64_GOTPC32: u64 = 26;
/// 64-bit GOT entry offset.
pub const R_X86_64_GOT64: u64 = 27;
/// 64-bit PC relative offset to GOT entry.
pub const R_X86_64_GOTPCREL64: u64 = 28;
/// 64-bit PC relative offset to GOT.
pub const R_X86_64_GOTPC64: u64 = 29;
/// like GOT64, says PLT entry needed.
pub const R_X86_64_GOTPLT64: u64 = 30;
/// 64-bit GOT relative offset to PLT entry.
pub const R_X86_64_PLTOFF64: u64 = 31;
/// Size of symbol plus 32-bit addend.
pub const R_X86_64_SIZE32: u64 = 32;
/// Size of symbol plus 64-bit addend.
pub const R_X86_64_SIZE64: u64 = 33;
/// GOT offset for TLS descriptor..
pub const R_X86_64_GOTPC32_TLSDESC: u64 = 34;
/// Marker for call through TLS descriptor..
pub const R_X86_64_TLSDESC_CALL: u64 = 35;
/// TLS descriptor..
pub const R_X86_64_TLSDESC: u64 = 36;
/// Adjust indirectly by program base.
pub const R_X86_64_IRELATIVE: u64 = 37;
/// 64-bit adjust by program base.
pub const R_X86_64_RELATIVE64: u64 = 38;
pub const R_X86_64_NUM: u64 = 39;

#[inline]
pub fn type_to_str(typ: u64) -> &'static str {
    match typ {
        R_X86_64_NONE => "NONE",
        R_X86_64_64 => "64",
        R_X86_64_PC32 => "PC32",
        R_X86_64_GOT32 => "GOT32",
        R_X86_64_PLT32 => "PLT32",
        R_X86_64_COPY => "COPY",
        R_X86_64_GLOB_DAT => "GLOB_DAT",
        R_X86_64_JUMP_SLOT => "JUMP_SLOT",
        R_X86_64_RELATIVE => "RELATIVE",
        R_X86_64_GOTPCREL => "GOTPCREL",
        R_X86_64_32 => "32",
        R_X86_64_32S => "32S",
        R_X86_64_16 => "16",
        R_X86_64_PC16 => "PC16",
        R_X86_64_8 => "8",
        R_X86_64_PC8 => "PC8",
        R_X86_64_DTPMOD64 => "DTPMOD64",
        R_X86_64_DTPOFF64 => "DTPOFF64",
        R_X86_64_TPOFF64 => "TPOFF64",
        R_X86_64_TLSGD => "TLSGD",
        R_X86_64_TLSLD => "TLSLD",
        R_X86_64_DTPOFF32 => "DTPOFF32",
        R_X86_64_GOTTPOFF => "GOTTPOFF",
        R_X86_64_TPOFF32 => "TPOFF32",
        R_X86_64_PC64 => "PC64",
        R_X86_64_GOTOFF64 => "GOTOFF64",
        R_X86_64_GOTPC32 => "GOTPC32",
        R_X86_64_GOT64 => "GOT64",
        R_X86_64_GOTPCREL64 => "GOTPCREL64",
        R_X86_64_GOTPC64 => "GOTPC64",
        R_X86_64_GOTPLT64 => "GOTPLT64",
        R_X86_64_PLTOFF64 => "PLTOFF64",
        R_X86_64_SIZE32 => "SIZE32",
        R_X86_64_SIZE64 => "SIZE64",
        R_X86_64_GOTPC32_TLSDESC => "GOTPC32_TLSDESC",
        R_X86_64_TLSDESC_CALL => "TLSDESC_CALL",
        R_X86_64_TLSDESC => "TLSDESC",
        R_X86_64_IRELATIVE => "IRELATIVE",
        R_X86_64_RELATIVE64 => "RELATIVE64",
        _ => "UNKNOWN_RELA_TYPE",
    }
}

macro_rules! elf_rela_impure_impl { ($from_fd_endian:item) => {

        #[cfg(not(feature = "pure"))]
        pub use self::impure::*;

        #[cfg(not(feature = "pure"))]
        mod impure {

            use super::*;

            use std::fs::File;
            use std::io::Seek;
            use std::io::SeekFrom::Start;
            use std::io;
            use std::fmt;
            use std::slice;

            impl fmt::Debug for Rela {
                fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                    let sym = r_sym(self.r_info);
                    let typ = r_type(self.r_info);
                    write!(f,
                           "r_offset: {:x} {} @ {} r_addend: {:x}",
                           self.r_offset,
                           type_to_str(typ as u64),
                           sym,
                           self.r_addend)
                }
            }

    /// Gets the rela entries given a rela u64 and the _size_ of the rela section in the binary, in bytes.  Works for regular rela and the pltrela table.
    /// Assumes the pointer is valid and can safely return a slice of memory pointing to the relas because:
    /// 1. `rela` points to memory received from the kernel (i.e., it loaded the executable), _or_
    /// 2. The binary has already been mmapped (i.e., it's a `SharedObject`), and hence it's safe to return a slice of that memory.
    /// 3. Or if you obtained the pointer in some other lawful manner
            pub unsafe fn from_raw<'a>(ptr: *const Rela, size: usize) -> &'a [Rela] {
                slice::from_raw_parts(ptr, size / SIZEOF_RELA)
            }

            #[cfg(feature = "no_endian_fd")]
            pub fn from_fd(fd: &mut File, offset: usize, size: usize, _: bool) -> io::Result<Vec<Rela>> {
                use std::io::Read;
                let count = size / SIZEOF_RELA;
                let mut bytes = vec![0u8; size];
                try!(fd.seek(Start(offset as u64)));
                try!(fd.read(&mut bytes));
                let bytes = unsafe { slice::from_raw_parts(bytes.as_ptr() as *mut Rela, count) };
                let mut res = Vec::with_capacity(count);
                res.extend_from_slice(bytes);
                Ok(res)
            }

            #[cfg(not(feature = "no_endian_fd"))]
            $from_fd_endian
        }
    };}
<|endoftext|>


<commit_before>// Copyright (c) 2015, Sam Payson
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
// associated documentation files (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, publish, distribute,
// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// TODO: Remove this when things stabilize.
#![allow(dead_code)]

use encoding::Quantifier::*;

#[derive(Copy,Eq,PartialEq)]
pub enum Type {
	Int8  = 0,
	Int16 = 1,
	Int32 = 2,
	Int64 = 3,

	UInt8  = 4,
	UInt16 = 5,
	UInt32 = 6,
	UInt64 = 7,

	Fixed32 = 8,
	Fixed64 = 9,

	Float32 = 10,
	Float64 = 11,

	Bytes  = 12,
	String = 13,

	Bool = 14,

	Enum = 15,

	FirstUnused = 16,
}

// The Quantifier type gives the multiplicity of a field. A Required field has exactly 1 element, an
// Optional field has 0 or 1 elements, and a Repeated field has 0 or more elements.
#[derive(Debug,Copy,PartialEq,Eq)]
pub enum Quantifier {
	Required = 0,
	Optional = 1,
	Repeated = 2,
}

#[derive(PartialEq,Eq)]
pub struct FieldEncoding {
	// Integer id of this field within its containing record.
	pub id: u64,

	// Name of this field in the .rex file, not used in the encoding.
	pub name: String,

	// Is this field Required, Optional (opt), or Repeated (rep)?
	pub quant: Quantifier,

	// Type of this field.
	pub typ: usize,

    // The bounds field is the product of all bounds in an array field. So for example, the field
    //
    //     1 matrix : [3][3]float32
    //
    // would have a bounds field of 3*3 = 9.
    //
    // The bounds field is not present for non-array types.
	pub bounds: Option<usize>,
}

#[derive(PartialEq,Eq)]
pub struct RecordEncoding {
	// Name of the record type in the .rex file, not used in the encoding.
	pub name: String,

	// Required fields of this record type, sorted by id.
	pub req_fields: Vec<FieldEncoding>,

	// Optional and repeated fields of this record type, sorted by id.
	pub opt_rep_fields: Vec<FieldEncoding>,
}

// A CompleteEncoding provides all of the information necessary to parse a particular record type
// (and every record type that it can contain).
#[derive(PartialEq,Eq)]
pub struct CompleteEncoding {
	// The record type that this CompleteEncoding describes.
	pub target:  RecordEncoding,

	// Encodings for all dependencies of target. If a field has a type (t >= Type::FirstUnused),
	// then a RecordEncoding for that type is at depends[t - Type::FirstUnused].
	pub depends: Vec<RecordEncoding>,
}

// These are indices into COMPLETE_ENC.depends, below. See docs for that field on the
// CompleteEncoding type.
const FIELD_ENCODING_TYP:  usize = 0 + (Type::FirstUnused as usize);
const RECORD_ENCODING_TYP: usize = 1 + (Type::FirstUnused as usize);

lazy_static! {

	// I apologize in advance for the confusing-ness of this comment.
	//
	// Encodings for records are themselves encoded, so we need to solve the chicken/egg problem in
	// order to be able to interpret the encodings of encodings :3.
	//
	// We do this by providing a pre-decoded encoding for encodings. That's what this lovely
	// structure is.
	pub static ref COMPLETE_ENC: CompleteEncoding = CompleteEncoding {
		target: RecordEncoding {
			name: "CompleteEncoding".to_string(),
			req_fields: vec![

				FieldEncoding {
					id:     1,
					name:   "target".to_string(),
					quant:  Required,
					typ:    RECORD_ENCODING_TYP,
					bounds: None
				},
			],

			opt_rep_fields: vec![

				FieldEncoding {
					id:     2,
					quant:  Repeated,
					name:   "depends".to_string(),
					typ:    RECORD_ENCODING_TYP,
					bounds: None
				},
			],
		},

		depends: vec![

			RecordEncoding {
				name: "FieldEncoding".to_string(),
				req_fields: vec![

					FieldEncoding {
						id:     1,
						name:   "id".to_string(),
						quant:  Required,
						typ:    Type::UInt64 as usize,
						bounds: None
					},

					FieldEncoding {
						id:     2,
						name:   "name".to_string(),
						quant:  Required,
						typ:    Type::String as usize,
						bounds: None
					},

					FieldEncoding {
						id:     3,
						name:   "quant".to_string(),
						quant:  Required,
						typ:    Type::Enum as usize,
						bounds: None
					},

					FieldEncoding {
						id:     4,
						name:   "typ".to_string(),
						quant:  Required,
						typ:    Type::Enum as usize,
						bounds: None
					},

					FieldEncoding {
						id:     5,
						name:   "bounds".to_string(),
						quant:  Required,
						typ:    Type::UInt64 as usize,
						bounds: None
					},
				],
				opt_rep_fields: vec![]
			},

			RecordEncoding {
				name: "RecordEncoding".to_string(),
				req_fields: vec![
					FieldEncoding {
						id:     1,
						name:   "name".to_string(),
						quant:  Required,
						typ:    Type::String as usize,
						bounds: None
					},
				],

				opt_rep_fields: vec![
					FieldEncoding {
						id:     2,
						name:   "req_fields".to_string(),
						quant:  Repeated,
						typ:    FIELD_ENCODING_TYP,
						bounds: None
					},

					FieldEncoding {
						id:     3,
						name:   "opt_rep_fields".to_string(),
						quant:  Repeated,
						typ:    FIELD_ENCODING_TYP,
						bounds: None
					},
				],
			},
		],
	};
}
<commit_msg>Added methods to sort encodings properly.<commit_after>// Copyright (c) 2015, Sam Payson
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
// associated documentation files (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, publish, distribute,
// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// TODO: Remove this when things stabilize.
#![allow(dead_code)]

use encoding::Quantifier::*;

#[derive(Copy,Eq,PartialEq)]
pub enum Type {
	Int8  = 0,
	Int16 = 1,
	Int32 = 2,
	Int64 = 3,

	UInt8  = 4,
	UInt16 = 5,
	UInt32 = 6,
	UInt64 = 7,

	Fixed32 = 8,
	Fixed64 = 9,

	Float32 = 10,
	Float64 = 11,

	Bytes  = 12,
	String = 13,

	Bool = 14,

	Enum = 15,

	FirstUnused = 16,
}

// The Quantifier type gives the multiplicity of a field. A Required field has exactly 1 element, an
// Optional field has 0 or 1 elements, and a Repeated field has 0 or more elements.
#[derive(Debug,Copy,PartialEq,Eq)]
pub enum Quantifier {
	Required = 0,
	Optional = 1,
	Repeated = 2,
}

#[derive(PartialEq,Eq)]
pub struct FieldEncoding {
	// Integer id of this field within its containing record.
	pub id: u64,

	// Name of this field in the .rex file, not used in the encoding.
	pub name: String,

	// Is this field Required, Optional (opt), or Repeated (rep)?
	pub quant: Quantifier,

	// Type of this field.
	pub typ: usize,

    // The bounds field is the product of all bounds in an array field. So for example, the field
    //
    //     1 matrix : [3][3]float32
    //
    // would have a bounds field of 3*3 = 9.
    //
    // The bounds field is not present for non-array types.
	pub bounds: Option<usize>,
}

#[derive(PartialEq,Eq)]
pub struct RecordEncoding {
	// Name of the record type in the .rex file, not used in the encoding.
	pub name: String,

	// Required fields of this record type, sorted by id.
	pub req_fields: Vec<FieldEncoding>,

	// Optional and repeated fields of this record type, sorted by id.
	pub opt_rep_fields: Vec<FieldEncoding>,
}

impl RecordEncoding {
	fn sort_fields(&mut self) {
		self.req_fields.sort_by(|a, b| a.id.cmp(&b.id));
		self.opt_rep_fields.sort_by(|a, b| a.id.cmp(&b.id));
	}
}

// A CompleteEncoding provides all of the information necessary to parse a particular record type
// (and every record type that it can contain).
#[derive(PartialEq,Eq)]
pub struct CompleteEncoding {
	// The record type that this CompleteEncoding describes.
	pub target:  RecordEncoding,

	// Encodings for all dependencies of target. If a field has a type (t >= Type::FirstUnused),
	// then a RecordEncoding for that type is at depends[t - Type::FirstUnused].
	pub depends: Vec<RecordEncoding>,
}

impl CompleteEncoding {
	pub fn sort_fields(&mut self) {
		self.target.sort_fields();

		for dep in self.depends.iter_mut() {
			dep.sort_fields();
		}
	}
}

// These are indices into COMPLETE_ENC.depends, below. See docs for that field on the
// CompleteEncoding type.
const FIELD_ENCODING_TYP:  usize = 0 + (Type::FirstUnused as usize);
const RECORD_ENCODING_TYP: usize = 1 + (Type::FirstUnused as usize);

lazy_static! {

	// I apologize in advance for the confusing-ness of this comment.
	//
	// Encodings for records are themselves encoded, so we need to solve the chicken/egg problem in
	// order to be able to interpret the encodings of encodings :3.
	//
	// We do this by providing a pre-decoded encoding for encodings. That's what this lovely
	// structure is.
	pub static ref COMPLETE_ENC: CompleteEncoding = CompleteEncoding {
		target: RecordEncoding {
			name: "CompleteEncoding".to_string(),
			req_fields: vec![

				FieldEncoding {
					id:     1,
					name:   "target".to_string(),
					quant:  Required,
					typ:    RECORD_ENCODING_TYP,
					bounds: None
				},
			],

			opt_rep_fields: vec![

				FieldEncoding {
					id:     2,
					quant:  Repeated,
					name:   "depends".to_string(),
					typ:    RECORD_ENCODING_TYP,
					bounds: None
				},
			],
		},

		depends: vec![

			RecordEncoding {
				name: "FieldEncoding".to_string(),
				req_fields: vec![

					FieldEncoding {
						id:     1,
						name:   "id".to_string(),
						quant:  Required,
						typ:    Type::UInt64 as usize,
						bounds: None
					},

					FieldEncoding {
						id:     2,
						name:   "name".to_string(),
						quant:  Required,
						typ:    Type::String as usize,
						bounds: None
					},

					FieldEncoding {
						id:     3,
						name:   "quant".to_string(),
						quant:  Required,
						typ:    Type::Enum as usize,
						bounds: None
					},

					FieldEncoding {
						id:     4,
						name:   "typ".to_string(),
						quant:  Required,
						typ:    Type::Enum as usize,
						bounds: None
					},

					FieldEncoding {
						id:     5,
						name:   "bounds".to_string(),
						quant:  Required,
						typ:    Type::UInt64 as usize,
						bounds: None
					},
				],
				opt_rep_fields: vec![]
			},

			RecordEncoding {
				name: "RecordEncoding".to_string(),
				req_fields: vec![
					FieldEncoding {
						id:     1,
						name:   "name".to_string(),
						quant:  Required,
						typ:    Type::String as usize,
						bounds: None
					},
				],

				opt_rep_fields: vec![
					FieldEncoding {
						id:     2,
						name:   "req_fields".to_string(),
						quant:  Repeated,
						typ:    FIELD_ENCODING_TYP,
						bounds: None
					},

					FieldEncoding {
						id:     3,
						name:   "opt_rep_fields".to_string(),
						quant:  Repeated,
						typ:    FIELD_ENCODING_TYP,
						bounds: None
					},
				],
			},
		],
	};
}
<|endoftext|>


<commit_before>use time;
use regex::Regex;

use esr_errors::Result;
//use std::result::Result as StdResult;

// Get ISO 8601-formatted string from crate API dates
pub(crate) fn crate_to_iso8601(cr_date: &str) -> String {
    match (cr_date.rfind('.'), cr_date.rfind('+'), cr_date.rfind('Z')) {
        // Do nothing
        (None, _, Some(_))    => String::from(cr_date),
        // Crate format without micro seconds
        (None, Some(i), None) => {
            String::from(&cr_date[0..i]) + "Z"
        },
        // Crate format with micro seconds
        (Some(i), Some(_), None) => String::from(&cr_date[0..i]) + "Z",
        _ => unreachable!(),
    }
}

fn date_sec(date: &str) -> Result<f64> {
    let date_tm = time::strptime(date, "%FT%TZ")?;
    Ok(date_tm.to_timespec().sec as f64)
}

pub(crate) fn age_in_months(date: &str) -> Result<f64> {
    let date = date_sec(date)?;
    let curr_date = time::get_time().sec as f64;
    let age = (curr_date - date) / (3600.0 * 24.0 * 30.5);
    Ok(age)
}

pub(crate) fn span_in_months(date1: &str, date2: &str) -> Result<f64> {
    let date1 = date_sec(date1)?;
    let date2 = date_sec(date2)?;
    let span = (date2 - date1).abs() / (3600.0 * 24.0 * 30.5);
    Ok(span)
}

//pub(crate) fn github_re() -> Result<&'static Regex> {
pub(crate) fn github_re() -> &'static Result<Regex> {
    lazy_static! {
        static ref RE: Result<Regex> =
            Ok(Regex::new(r"(.+://github.com/|@|^)(.+?/.+?)(.git|/|$).*")?);
    }
    &RE
}

pub(crate) fn github_repo(repo: &str) -> Option<String> {
    match *github_re() {
        Ok(ref re) => {
            match re.captures(repo) {
                Some(ref cap) if cap.len() >= 3 => Some(String::from(&cap[2])),
                _ => None,
            }
        },
        _ => None,
    }
}
<commit_msg>esr_util: More robust regex to find github path<commit_after>use time;
use regex::Regex;

use esr_errors::Result;
//use std::result::Result as StdResult;

// Get ISO 8601-formatted string from crate API dates
pub(crate) fn crate_to_iso8601(cr_date: &str) -> String {
    match (cr_date.rfind('.'), cr_date.rfind('+'), cr_date.rfind('Z')) {
        // Do nothing
        (None, _, Some(_))    => String::from(cr_date),
        // Crate format without micro seconds
        (None, Some(i), None) => {
            String::from(&cr_date[0..i]) + "Z"
        },
        // Crate format with micro seconds
        (Some(i), Some(_), None) => String::from(&cr_date[0..i]) + "Z",
        _ => unreachable!(),
    }
}

fn date_sec(date: &str) -> Result<f64> {
    let date_tm = time::strptime(date, "%FT%TZ")?;
    Ok(date_tm.to_timespec().sec as f64)
}

pub(crate) fn age_in_months(date: &str) -> Result<f64> {
    let date = date_sec(date)?;
    let curr_date = time::get_time().sec as f64;
    let age = (curr_date - date) / (3600.0 * 24.0 * 30.5);
    Ok(age)
}

pub(crate) fn span_in_months(date1: &str, date2: &str) -> Result<f64> {
    let date1 = date_sec(date1)?;
    let date2 = date_sec(date2)?;
    let span = (date2 - date1).abs() / (3600.0 * 24.0 * 30.5);
    Ok(span)
}

//pub(crate) fn github_re() -> Result<&'static Regex> {
pub(crate) fn github_re() -> &'static Result<Regex> {
    lazy_static! {
        static ref RE: Result<Regex> =
            Ok(Regex::new(r"(.+://github.com/|@|^)([^:/]+?/[^:/]+?)(.git$|$|/.*)")?);
    }
    &RE
}

pub(crate) fn github_repo(repo: &str) -> Option<String> {
    match *github_re() {
        Ok(ref re) => {
            match re.captures(repo) {
                Some(ref cap) if cap.len() >= 3 => Some(String::from(&cap[2])),
                _ => None,
            }
        },
        _ => None,
    }
}
<|endoftext|>