<commit_before>mod error;
pub use self::error::Error;

pub type Result<T> = std::result::Result<T, Error>;

#[derive(Clone, Copy)]
pub struct ViewHandle(u64);

#[derive(Default, Clone, Copy)]
pub struct Rect {
    pub x: f32,
    pub y: f32,
    pub width: f32,
    pub height: f32,
}

impl Rect {
    pub fn new(x: f32, y: f32, width: f32, height: f32) -> Rect {
        Rect {
            x: x,
            y: y,
            width: width,
            height: height
        }
    }
}

#[derive(Clone)]
pub struct View {
    pub handle: ViewHandle,
    pub rect: Rect
}

impl View {
    fn new(view_handle: ViewHandle) -> View {
        View {
            handle: view_handle,
            rect: Rect::default(),
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub enum Direction {
    Vertical,
    Horizontal,
    Full,
}

#[derive(Clone)]
pub struct Container {
    pub views: Vec<View>,
}

pub struct Split {
    /// left/top slipit
    pub left: Option<Box<Split>>,
    /// right/bottom split
    pub right: Option<Box<Split>>,
    /// left/top views
    pub left_views: Container,
    /// right/top views
    pub right_views: Container,
    /// ratioage value of how much of each side that is visible. 1.0 = right/bottom fully visible
    pub ratio: f32,
    /// Direction of the split
    pub direction: Direction,
}

impl Split {
    pub fn new(direction: Direction) -> Split {
        Split {
            left: None,
            right: None,
            left_views: Container { views: Vec::new() },
            right_views: Container { views: Vec::new() },
            ratio: 0.0,
            direction: direction,
        }
    }

    pub fn no_split(&mut self, direction: Direction, view_handle: ViewHandle) -> bool {
        self.direction = direction;

        if self.left_views.views.len() == 0 {
            self.left_views.views.push(View::new(view_handle));
            self.ratio = 0.5;
            return true;
        }

        if self.right_views.views.len() == 0 {
            self.right_views.views.push(View::new(view_handle));
            self.ratio = 0.5;
            return true;
        }

        false
    }

    pub fn split_left(&mut self, view_handle: ViewHandle, direction: Direction) {
        if Self::no_split(self, direction, view_handle) {
            return;
        } else {
            let mut split = Box::new(Split::new(direction));
            split.right_views = self.left_views.clone();
            split.left_views.views.push(View::new(view_handle));
            self.left = Some(split);
            self.right_views.views.clear();
        }

        /*
        // this case only happens when a view happen to be fullscreen
        if self.left_top.len() == 1 && self.right_bottom.len() == 0 {
            self.direction = direction;
            self.ratio = 0.5;
            self.right_bottom.push(View::new(view_handle));
        } else {

        }
        */
    }

    pub fn split_right(&mut self, view_handle: ViewHandle, direction: Direction) {
        if Self::no_split(self, direction, view_handle) {
            return;
        } else {
            let mut split = Box::new(Split::new(direction));
            split.left_views = self.right_views.clone();
            split.right_views.views.push(View::new(view_handle));
            self.right = Some(split);
            self.left_views.views.clear();
        }
    }
}

pub struct Workspace {
    pub split: Option<Box<Split>>,
    pub rect: Rect,
    /// border size of the windows (in pixels)
    pub window_border: f32,
}

impl Workspace {
    /// Construct a new workspace. The rect has to be y >= 0, x >= 0, width > 0 and height > 0
    pub fn new(rect: Rect) -> Result<Workspace> {
        if rect.x < 0.0 {
            return Err(Error::IllegalSize("x has to be non-negative".to_owned()));
        }

        if rect.y < 0.0 {
            return Err(Error::IllegalSize("y has to be non-negative".to_owned()));
        }

        if rect.width <= 0.0 {
            return Err(Error::IllegalSize("width has to be larger than 0.0".to_owned()));
        }

        if rect.height <= 0.0 {
            return Err(Error::IllegalSize("height has to be larger than 0.0".to_owned()));
        }

        Ok(Workspace {
            split: None,
            rect: rect,
            window_border: 4.0,
        })
    }

    /// This code gets called when the top split is None. This mean that the view will be
    /// set to fullscreen as there are no other splits to be done
    fn split_new(&mut self, view_handle: ViewHandle) {
        let mut split = Box::new(Split::new(Direction::Full));
        split.ratio = 1.0;
        split.left_views.views.push(View::new(view_handle));
        self.split = Some(split);
    }

    pub fn split_top(&mut self, view_handle: ViewHandle, direction: Direction) {
        if let Some(ref mut split) = self.split {
            split.split_left(view_handle, direction);
        } else {
            Self::split_new(self, view_handle);
        }
    }

    pub fn calc_horizontal_sizing(rect: Rect, ratio: f32) -> (Rect, Rect) {
        let h = rect.height * ratio;

        let rect_top = Rect::new(rect.x, rect.y, rect.width, h);
        let rect_bottom = Rect::new(rect.x, rect.y + h, rect.width, rect.height - h);

        (rect_top, rect_bottom)
    }

    pub fn calc_vertical_sizing(rect: Rect, ratio: f32) -> (Rect, Rect) {
        let w = rect.width * ratio;

        let rect_left = Rect::new(rect.x, rect.y, w, rect.height);
        let rect_right = Rect::new(rect.x + w, rect.y, rect.width - w, rect.height);

        (rect_left, rect_right)
    }

    fn recursive_update(current_split: &mut Split, rect: Rect, level: usize) {
        let rects = match current_split.direction {
            Direction::Vertical => Self::calc_vertical_sizing(rect, current_split.ratio),
            Direction::Horizontal => Self::calc_horizontal_sizing(rect, current_split.ratio),
            Direction::Full => (rect, rect),
        };

        if let Some(ref mut split) = current_split.left {
            Self::recursive_update(split, rects.0, level + 1);
        }

        if let Some(ref mut split) = current_split.right {
            Self::recursive_update(split, rects.1, level + 1);
        }

        for view in &mut current_split.left_views.views {
            println!("level {} left  - x: {} y: {} w: {} h: {}", level, rects.0.x, rects.0.y, rects.0.width, rects.0.height);
            view.rect = rects.0;
        }

        for view in &mut current_split.right_views.views {
            println!("level {} right - x: {} y: {} w: {} h: {}", level, rects.1.x, rects.1.y, rects.1.width, rects.1.height);
            view.rect = rects.1;
        }
    }

    pub fn update(&mut self) {
        let rect = self.rect.clone();
        if let Some(ref mut split) = self.split {
            Self::recursive_update(split, rect, 0);
        }
    }

    /*
    fn split_view_handle_recursive(&mut self, direction: Direction, view_handle: ViewHandle) {
    }

    pub fn split_by_view_handle(&mut self) {
        if let Some(ref mut split) = self.split {
            Self::split_view_handle_recursive(split, rect, 0);
        }
    }
    */
}

#[cfg(test)]
mod test {
    use {Workspace, Rect, ViewHandle, Direction};

    fn check_range(inv: f32, value: f32, delta: f32) -> bool {
        (inv - value).abs() < delta
    }

    #[test]
    fn test_validate_x_less_than_zero() {
        assert_eq!(Workspace::new(Rect::new(-0.1, 0.0, 1.0, 1.0)).is_err(), true);
    }

    #[test]
    fn test_validate_y_less_than_zero() {
        assert_eq!(Workspace::new(Rect::new(0.0, -0.1, 1.0, 1.0)).is_err(), true);
    }

    #[test]
    fn test_validate_width_zero() {
        assert_eq!(Workspace::new(Rect::new(0.0, 0.0, 0.0, 1.0)).is_err(), true);
    }

    #[test]
    fn test_validate_height_zero() {
        assert_eq!(Workspace::new(Rect::new(0.0, 0.0, 1.0, 0.0)).is_err(), true);
    }

    #[test]
    fn test_validate_width_less_than_zero() {
        assert_eq!(Workspace::new(Rect::new(0.0, 0.0, -1.0, 0.0)).is_err(), true);
    }

    #[test]
    fn test_validate_height_less_than_zero() {
        assert_eq!(Workspace::new(Rect::new(0.0, 0.0, 0.0, -1.0)).is_err(), true);
    }

    #[test]
    fn test_validate_workspace_ok() {
        assert_eq!(Workspace::new(Rect::new(0.0, 0.0, 1024.0, 1024.0)).is_ok(), true);
    }

    #[test]
    fn test_split_top() {
        let mut ws = Workspace::new(Rect::new(0.0, 0.0, 1024.0, 1024.0)).unwrap();
        ws.split_top(ViewHandle(1), Direction::Vertical);

        assert_eq!(ws.split.is_some(), true);
        let split = ws.split.unwrap();

        assert_eq!(split.left_views.views.len(), 1);
    }

    #[test]
    fn test_split_top_2() {
        let mut ws = Workspace::new(Rect::new(0.0, 0.0, 1024.0, 1024.0)).unwrap();
        ws.split_top(ViewHandle(1), Direction::Vertical);
        ws.split_top(ViewHandle(2), Direction::Vertical);

        //ws.update();
        //assert_eq!(1, 0);

        assert_eq!(ws.split.is_some(), true);
        let split = ws.split.unwrap();

        assert_eq!(split.right_views.views.len(), 1);
        assert_eq!(split.left_views.views.len(), 1);
        assert_eq!(check_range(split.ratio, 0.5, 0.01), true);

    }

    #[test]
    fn test_calc_rect_horz_half() {
        let rects = Workspace::calc_horizontal_sizing(Rect::new(0.0, 0.0, 1024.0, 1024.0), 0.5);

        assert_eq!(check_range(rects.0.x, 0.0, 0.001), true);
        assert_eq!(check_range(rects.0.y, 0.0, 0.001), true);
        assert_eq!(check_range(rects.0.width, 1024.0, 0.001), true);
        assert_eq!(check_range(rects.0.height, 512.0, 0.001), true);

        assert_eq!(check_range(rects.1.x, 0.0, 0.001), true);
        assert_eq!(check_range(rects.1.y, 512.0, 0.001), true);
        assert_eq!(check_range(rects.1.width, 1024.0, 0.001), true);
        assert_eq!(check_range(rects.1.height, 512.0, 0.001), true);
    }

    #[test]
    fn test_calc_rect_horz_25_per() {
        let rects = Workspace::calc_horizontal_sizing(Rect::new(0.0, 0.0, 1024.0, 1024.0), 0.25);

        assert_eq!(check_range(rects.0.x, 0.0, 0.001), true);
        assert_eq!(check_range(rects.0.y, 0.0, 0.001), true);
        assert_eq!(check_range(rects.0.width, 1024.0, 0.001), true);
        assert_eq!(check_range(rects.0.height, 256.0, 0.001), true);

        assert_eq!(check_range(rects.1.x, 0.0, 0.001), true);
        assert_eq!(check_range(rects.1.y, 256.0, 0.001), true);
        assert_eq!(check_range(rects.1.width, 1024.0, 0.001), true);
        assert_eq!(check_range(rects.1.height, 768.0, 0.001), true);
    }

    #[test]
    fn test_calc_rect_horz_25_per_2() {
        let rects = Workspace::calc_horizontal_sizing(Rect::new(16.0, 32.0, 512.0, 1024.0), 0.25);

        assert_eq!(check_range(rects.0.x, 16.0, 0.001), true);
        assert_eq!(check_range(rects.0.y, 32.0, 0.001), true);
        assert_eq!(check_range(rects.0.width, 512.0, 0.001), true);
        assert_eq!(check_range(rects.0.height, 256.0, 0.001), true);

        assert_eq!(check_range(rects.1.x, 16.0, 0.001), true);
        assert_eq!(check_range(rects.1.y, 288.0, 0.001), true);
        assert_eq!(check_range(rects.1.width, 512.0, 0.001), true);
        assert_eq!(check_range(rects.1.height, 768.0, 0.001), true);
    }
}
<commit_msg>Moved updating to Split<commit_after>mod error;
pub use self::error::Error;

pub type Result<T> = std::result::Result<T, Error>;

#[derive(Clone, Copy)]
pub struct ViewHandle(u64);

#[derive(Default, Clone, Copy)]
pub struct Rect {
    pub x: f32,
    pub y: f32,
    pub width: f32,
    pub height: f32,
}

impl Rect {
    pub fn new(x: f32, y: f32, width: f32, height: f32) -> Rect {
        Rect {
            x: x,
            y: y,
            width: width,
            height: height
        }
    }
}

#[derive(Clone)]
pub struct View {
    pub handle: ViewHandle,
    pub rect: Rect
}

impl View {
    fn new(view_handle: ViewHandle) -> View {
        View {
            handle: view_handle,
            rect: Rect::default(),
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub enum Direction {
    Vertical,
    Horizontal,
    Full,
}

#[derive(Clone)]
pub struct Container {
    pub views: Vec<View>,
}

pub struct Split {
    /// left/top slipit
    pub left: Option<Box<Split>>,
    /// right/bottom split
    pub right: Option<Box<Split>>,
    /// left/top views
    pub left_views: Container,
    /// right/top views
    pub right_views: Container,
    /// ratioage value of how much of each side that is visible. 1.0 = right/bottom fully visible
    pub ratio: f32,
    /// Direction of the split
    pub direction: Direction,
}

impl Split {
    pub fn new(direction: Direction) -> Split {
        Split {
            left: None,
            right: None,
            left_views: Container { views: Vec::new() },
            right_views: Container { views: Vec::new() },
            ratio: 0.0,
            direction: direction,
        }
    }

    pub fn no_split(&mut self, direction: Direction, view_handle: ViewHandle) -> bool {
        self.direction = direction;

        if self.left_views.views.len() == 0 {
            self.left_views.views.push(View::new(view_handle));
            self.ratio = 0.5;
            return true;
        }

        if self.right_views.views.len() == 0 {
            self.right_views.views.push(View::new(view_handle));
            self.ratio = 0.5;
            return true;
        }

        false
    }

    pub fn split_left(&mut self, view_handle: ViewHandle, direction: Direction) {
        if Self::no_split(self, direction, view_handle) {
            return;
        } else {
            let mut split = Box::new(Split::new(direction));
            split.right_views = self.left_views.clone();
            split.left_views.views.push(View::new(view_handle));
            self.left = Some(split);
            self.right_views.views.clear();
        }

        /*
        // this case only happens when a view happen to be fullscreen
        if self.left_top.len() == 1 && self.right_bottom.len() == 0 {
            self.direction = direction;
            self.ratio = 0.5;
            self.right_bottom.push(View::new(view_handle));
        } else {

        }
        */
    }

    pub fn split_right(&mut self, view_handle: ViewHandle, direction: Direction) {
        if Self::no_split(self, direction, view_handle) {
            return;
        } else {
            let mut split = Box::new(Split::new(direction));
            split.left_views = self.right_views.clone();
            split.right_views.views.push(View::new(view_handle));
            self.right = Some(split);
            self.left_views.views.clear();
        }
    }

    pub fn calc_horizontal_sizing(rect: Rect, ratio: f32) -> (Rect, Rect) {
        let h = rect.height * ratio;

        let rect_top = Rect::new(rect.x, rect.y, rect.width, h);
        let rect_bottom = Rect::new(rect.x, rect.y + h, rect.width, rect.height - h);

        (rect_top, rect_bottom)
    }

    pub fn calc_vertical_sizing(rect: Rect, ratio: f32) -> (Rect, Rect) {
        let w = rect.width * ratio;

        let rect_left = Rect::new(rect.x, rect.y, w, rect.height);
        let rect_right = Rect::new(rect.x + w, rect.y, rect.width - w, rect.height);

        (rect_left, rect_right)
    }

    fn recursive_update(&mut self, rect: Rect, level: usize) {
        let rects = match self.direction {
            Direction::Vertical => Self::calc_vertical_sizing(rect, self.ratio),
            Direction::Horizontal => Self::calc_horizontal_sizing(rect, self.ratio),
            Direction::Full => (rect, rect),
        };

        if let Some(ref mut split) = self.left {
            Self::recursive_update(split, rects.0, level + 1);
        }

        if let Some(ref mut split) = self.right {
            Self::recursive_update(split, rects.1, level + 1);
        }

        for view in &mut self.left_views.views {
            println!("level {} left  - x: {} y: {} w: {} h: {}", level, rects.0.x, rects.0.y, rects.0.width, rects.0.height);
            view.rect = rects.0;
        }

        for view in &mut self.right_views.views {
            println!("level {} right - x: {} y: {} w: {} h: {}", level, rects.1.x, rects.1.y, rects.1.width, rects.1.height);
            view.rect = rects.1;
        }
    }

}

pub struct Workspace {
    pub split: Option<Box<Split>>,
    pub rect: Rect,
    /// border size of the windows (in pixels)
    pub window_border: f32,
}

impl Workspace {
    /// Construct a new workspace. The rect has to be y >= 0, x >= 0, width > 0 and height > 0
    pub fn new(rect: Rect) -> Result<Workspace> {
        if rect.x < 0.0 {
            return Err(Error::IllegalSize("x has to be non-negative".to_owned()));
        }

        if rect.y < 0.0 {
            return Err(Error::IllegalSize("y has to be non-negative".to_owned()));
        }

        if rect.width <= 0.0 {
            return Err(Error::IllegalSize("width has to be larger than 0.0".to_owned()));
        }

        if rect.height <= 0.0 {
            return Err(Error::IllegalSize("height has to be larger than 0.0".to_owned()));
        }

        Ok(Workspace {
            split: None,
            rect: rect,
            window_border: 4.0,
        })
    }

    /// This code gets called when the top split is None. This mean that the view will be
    /// set to fullscreen as there are no other splits to be done
    fn split_new(&mut self, view_handle: ViewHandle) {
        let mut split = Box::new(Split::new(Direction::Full));
        split.ratio = 1.0;
        split.left_views.views.push(View::new(view_handle));
        self.split = Some(split);
    }

    pub fn split_top(&mut self, view_handle: ViewHandle, direction: Direction) {
        if let Some(ref mut split) = self.split {
            split.split_left(view_handle, direction);
        } else {
            Self::split_new(self, view_handle);
        }
    }

    pub fn update(&mut self) {
        let rect = self.rect.clone();
        if let Some(ref mut split) = self.split {
            split.recursive_update(rect, 0);
        }
    }

    /*
    fn split_view_handle_recursive(&mut self, direction: Direction, view_handle: ViewHandle) {
    }

    pub fn split_by_view_handle(&mut self) {
        if let Some(ref mut split) = self.split {
            Self::split_view_handle_recursive(split, rect, 0);
        }
    }
    */
}

#[cfg(test)]
mod test {
    use {Split, Workspace, Rect, ViewHandle, Direction};

    fn check_range(inv: f32, value: f32, delta: f32) -> bool {
        (inv - value).abs() < delta
    }

    #[test]
    fn test_validate_x_less_than_zero() {
        assert_eq!(Workspace::new(Rect::new(-0.1, 0.0, 1.0, 1.0)).is_err(), true);
    }

    #[test]
    fn test_validate_y_less_than_zero() {
        assert_eq!(Workspace::new(Rect::new(0.0, -0.1, 1.0, 1.0)).is_err(), true);
    }

    #[test]
    fn test_validate_width_zero() {
        assert_eq!(Workspace::new(Rect::new(0.0, 0.0, 0.0, 1.0)).is_err(), true);
    }

    #[test]
    fn test_validate_height_zero() {
        assert_eq!(Workspace::new(Rect::new(0.0, 0.0, 1.0, 0.0)).is_err(), true);
    }

    #[test]
    fn test_validate_width_less_than_zero() {
        assert_eq!(Workspace::new(Rect::new(0.0, 0.0, -1.0, 0.0)).is_err(), true);
    }

    #[test]
    fn test_validate_height_less_than_zero() {
        assert_eq!(Workspace::new(Rect::new(0.0, 0.0, 0.0, -1.0)).is_err(), true);
    }

    #[test]
    fn test_validate_workspace_ok() {
        assert_eq!(Workspace::new(Rect::new(0.0, 0.0, 1024.0, 1024.0)).is_ok(), true);
    }

    #[test]
    fn test_split_top() {
        let mut ws = Workspace::new(Rect::new(0.0, 0.0, 1024.0, 1024.0)).unwrap();
        ws.split_top(ViewHandle(1), Direction::Vertical);

        assert_eq!(ws.split.is_some(), true);
        let split = ws.split.unwrap();

        assert_eq!(split.left_views.views.len(), 1);
    }

    #[test]
    fn test_split_top_2() {
        let mut ws = Workspace::new(Rect::new(0.0, 0.0, 1024.0, 1024.0)).unwrap();
        ws.split_top(ViewHandle(1), Direction::Vertical);
        ws.split_top(ViewHandle(2), Direction::Vertical);

        //ws.update();
        //assert_eq!(1, 0);

        assert_eq!(ws.split.is_some(), true);
        let split = ws.split.unwrap();

        assert_eq!(split.right_views.views.len(), 1);
        assert_eq!(split.left_views.views.len(), 1);
        assert_eq!(check_range(split.ratio, 0.5, 0.01), true);

    }

    #[test]
    fn test_calc_rect_horz_half() {
        let rects = Split::calc_horizontal_sizing(Rect::new(0.0, 0.0, 1024.0, 1024.0), 0.5);

        assert_eq!(check_range(rects.0.x, 0.0, 0.001), true);
        assert_eq!(check_range(rects.0.y, 0.0, 0.001), true);
        assert_eq!(check_range(rects.0.width, 1024.0, 0.001), true);
        assert_eq!(check_range(rects.0.height, 512.0, 0.001), true);

        assert_eq!(check_range(rects.1.x, 0.0, 0.001), true);
        assert_eq!(check_range(rects.1.y, 512.0, 0.001), true);
        assert_eq!(check_range(rects.1.width, 1024.0, 0.001), true);
        assert_eq!(check_range(rects.1.height, 512.0, 0.001), true);
    }

    #[test]
    fn test_calc_rect_horz_25_per() {
        let rects = Split::calc_horizontal_sizing(Rect::new(0.0, 0.0, 1024.0, 1024.0), 0.25);

        assert_eq!(check_range(rects.0.x, 0.0, 0.001), true);
        assert_eq!(check_range(rects.0.y, 0.0, 0.001), true);
        assert_eq!(check_range(rects.0.width, 1024.0, 0.001), true);
        assert_eq!(check_range(rects.0.height, 256.0, 0.001), true);

        assert_eq!(check_range(rects.1.x, 0.0, 0.001), true);
        assert_eq!(check_range(rects.1.y, 256.0, 0.001), true);
        assert_eq!(check_range(rects.1.width, 1024.0, 0.001), true);
        assert_eq!(check_range(rects.1.height, 768.0, 0.001), true);
    }

    #[test]
    fn test_calc_rect_horz_25_per_2() {
        let rects = Split::calc_horizontal_sizing(Rect::new(16.0, 32.0, 512.0, 1024.0), 0.25);

        assert_eq!(check_range(rects.0.x, 16.0, 0.001), true);
        assert_eq!(check_range(rects.0.y, 32.0, 0.001), true);
        assert_eq!(check_range(rects.0.width, 512.0, 0.001), true);
        assert_eq!(check_range(rects.0.height, 256.0, 0.001), true);

        assert_eq!(check_range(rects.1.x, 16.0, 0.001), true);
        assert_eq!(check_range(rects.1.y, 288.0, 0.001), true);
        assert_eq!(check_range(rects.1.width, 512.0, 0.001), true);
        assert_eq!(check_range(rects.1.height, 768.0, 0.001), true);
    }
}
<|endoftext|>


<commit_before>#![feature(libc, int_uint, core, unique, std_misc)]

extern crate libc;

use std::ptr::{self, Unique};
use std::ffi::{CStr,CString};
use std::default::Default;
use std::mem::transmute;
use std::slice::SliceExt;
use std::raw::Slice;
use std::ops::Deref;
use std::str;
mod raw;

/// An iterator over devices that pcap is aware about on the system.
pub struct Devices {
    orig: Unique<raw::Struct_pcap_if>,
    device: Unique<raw::Struct_pcap_if>
}

impl Devices {
    /// Construct a new `Devices` iterator by internally using `pcap_findalldevs()`
    pub fn list_all() -> Result<Devices, String> {
        unsafe {
            let mut errbuf = [0i8; 256];
            let mut dev_buf: *mut raw::Struct_pcap_if = ptr::null_mut();

            match raw::pcap_findalldevs(&mut dev_buf, errbuf.as_mut_ptr()) {
                0 => {
                    Ok(Devices {
                        orig: Unique::new(dev_buf),
                        device: Unique::new(dev_buf)
                    })
                },
                _ => {
                    Err(
                        cstr_to_string(errbuf.as_ptr()).unwrap()
                    )
                }
            }
        }
    }
}

impl Drop for Devices {
    fn drop(&mut self) {
        unsafe {
            raw::pcap_freealldevs(*self.orig);
        }
    }
}

#[derive(Debug)]
/// A network device as constructed with `Device::new()` or returned from `Devices::list_all()`.
pub struct Device {
    pub name: String,
    pub desc: Option<String>
}

impl Device {
    /// Create a new device with a given name, useful when you know the name of the device already.
    pub fn new(name: &str) -> Device {
        Device {
            name: name.to_string(),
            desc: None
        }
    }

    /// Open a `Capture` for this network device.
    pub fn open(&self) -> Result<Capture, String> {
        let name = CString::new(&self.name[..]).unwrap();
        let mut errbuf = [0i8; 256];

        unsafe {
            let pcap_t = raw::pcap_open_live(name.as_ptr(), 65535, 1, 0, errbuf.as_mut_ptr());
            if pcap_t.is_null() {
                Err(
                    cstr_to_string(errbuf.as_ptr()).unwrap()
                )
            } else {
                Ok(Capture{
                    handle: Unique::new(pcap_t)
                })
            }
        }
    }
}

/// A packet obtained from a `Capture`.
///
/// This can be dereferenced to access the raw packet data.
pub struct Packet {
    data: Vec<u8>
}

impl Deref for Packet {
    type Target = [u8];

    fn deref<'a>(&'a self) -> &'a [u8] {
        self.data.as_slice()
    }
}

/// An iterator over packets obtained from a `Capture` in realtime.
pub struct Packets<'a> {
    capture: &'a Capture
}

impl<'a> Iterator for Packets<'a> {
    type Item = Packet;

    fn next(&mut self) -> Option<Packet> {
        unsafe {
            let mut header: *mut raw::Struct_pcap_pkthdr = ptr::null_mut();
            let mut packet: *const libc::c_uchar = ptr::null_mut();
            match raw::pcap_next_ex(*self.capture.handle, &mut header, &mut packet) {
                1 => {
                    // packet was read without issue
                    let packet = transmute::<_, &[u8]>(Slice {
                        data: packet,
                        len: (*header).len as usize
                    });

                    Some(Packet {
                        data: packet.to_vec()
                    })
                },
                _ => {
                    None
                }
            }
        }
    }
}

/// This represents an open capture handle obtained by calling `.open()` on a `Device`.
/// Internally it represents a `pcap_t` handle obtained by `pcap_open_live()`.
pub struct Capture {
    handle: Unique<raw::pcap_t>
}

impl Capture {
    /// Returns an iterator over packets received on this capture handle.
    pub fn listen<'a>(&'a mut self) -> Packets<'a> {
        Packets {
            capture: &*self
        }
    }

    /// Adds a filter to the capture using the given BPF program string. Internally
    /// this is compiled using `pcap_compile()`.
    ///
    /// See http://biot.com/capstats/bpf.html for more information about this syntax.
    pub fn filter(&mut self, program: &str) -> Result<(), String> {
        let program = CString::new(program).unwrap();

        unsafe {
            let mut bpf_program: raw::Struct_bpf_program = Default::default();

            if -1 == raw::pcap_compile(*self.handle, &mut bpf_program, program.as_ptr(), 0, 0) {
                return Err(cstr_to_string(raw::pcap_geterr(*self.handle)).unwrap());
            }

            if -1 == raw::pcap_setfilter(*self.handle, &mut bpf_program) {
                raw::pcap_freecode(&mut bpf_program);
                return Err(cstr_to_string(raw::pcap_geterr(*self.handle)).unwrap());
            }

            raw::pcap_freecode(&mut bpf_program);
            Ok(())
        }
    }
}

impl Drop for Capture {
    fn drop(&mut self) {
        unsafe {
            raw::pcap_close(*self.handle)
        }
    }
}

impl Iterator for Devices {
    type Item = Device;

    fn next(&mut self) -> Option<Device> {
        if self.device.is_null() {
            None
        } else {
            unsafe {
                let ret = Device {
                    name: cstr_to_string(self.device.get().name).unwrap(),
                    desc: {
                        if !self.device.get().description.is_null() {
                            Some(cstr_to_string(self.device.get().description).unwrap())
                        } else {
                            None
                        }
                    }
                };
                self.device = Unique::new(self.device.get().next);

                Some(ret)
            }
        }
    }
}

fn cstr_to_string(ptr: *const libc::c_char) -> Result<String, str::Utf8Error> {
    Ok(try!(str::from_utf8(unsafe{CStr::from_ptr(ptr)}.to_bytes())).to_string())
}<commit_msg>Remove std_misc feature gate<commit_after>#![feature(libc, int_uint, core, unique)]

extern crate libc;

use std::ptr::{self, Unique};
use std::ffi::{CStr,CString};
use std::default::Default;
use std::mem::transmute;
use std::slice::SliceExt;
use std::raw::Slice;
use std::ops::Deref;
use std::str;
mod raw;

/// An iterator over devices that pcap is aware about on the system.
pub struct Devices {
    orig: Unique<raw::Struct_pcap_if>,
    device: Unique<raw::Struct_pcap_if>
}

impl Devices {
    /// Construct a new `Devices` iterator by internally using `pcap_findalldevs()`
    pub fn list_all() -> Result<Devices, String> {
        unsafe {
            let mut errbuf = [0i8; 256];
            let mut dev_buf: *mut raw::Struct_pcap_if = ptr::null_mut();

            match raw::pcap_findalldevs(&mut dev_buf, errbuf.as_mut_ptr()) {
                0 => {
                    Ok(Devices {
                        orig: Unique::new(dev_buf),
                        device: Unique::new(dev_buf)
                    })
                },
                _ => {
                    Err(
                        cstr_to_string(errbuf.as_ptr()).unwrap()
                    )
                }
            }
        }
    }
}

impl Drop for Devices {
    fn drop(&mut self) {
        unsafe {
            raw::pcap_freealldevs(*self.orig);
        }
    }
}

#[derive(Debug)]
/// A network device as constructed with `Device::new()` or returned from `Devices::list_all()`.
pub struct Device {
    pub name: String,
    pub desc: Option<String>
}

impl Device {
    /// Create a new device with a given name, useful when you know the name of the device already.
    pub fn new(name: &str) -> Device {
        Device {
            name: name.to_string(),
            desc: None
        }
    }

    /// Open a `Capture` for this network device.
    pub fn open(&self) -> Result<Capture, String> {
        let name = CString::new(&self.name[..]).unwrap();
        let mut errbuf = [0i8; 256];

        unsafe {
            let pcap_t = raw::pcap_open_live(name.as_ptr(), 65535, 1, 0, errbuf.as_mut_ptr());
            if pcap_t.is_null() {
                Err(
                    cstr_to_string(errbuf.as_ptr()).unwrap()
                )
            } else {
                Ok(Capture{
                    handle: Unique::new(pcap_t)
                })
            }
        }
    }
}

/// A packet obtained from a `Capture`.
///
/// This can be dereferenced to access the raw packet data.
pub struct Packet {
    data: Vec<u8>
}

impl Deref for Packet {
    type Target = [u8];

    fn deref<'a>(&'a self) -> &'a [u8] {
        self.data.as_slice()
    }
}

/// An iterator over packets obtained from a `Capture` in realtime.
pub struct Packets<'a> {
    capture: &'a Capture
}

impl<'a> Iterator for Packets<'a> {
    type Item = Packet;

    fn next(&mut self) -> Option<Packet> {
        unsafe {
            let mut header: *mut raw::Struct_pcap_pkthdr = ptr::null_mut();
            let mut packet: *const libc::c_uchar = ptr::null_mut();
            match raw::pcap_next_ex(*self.capture.handle, &mut header, &mut packet) {
                1 => {
                    // packet was read without issue
                    let packet = transmute::<_, &[u8]>(Slice {
                        data: packet,
                        len: (*header).len as usize
                    });

                    Some(Packet {
                        data: packet.to_vec()
                    })
                },
                _ => {
                    None
                }
            }
        }
    }
}

/// This represents an open capture handle obtained by calling `.open()` on a `Device`.
/// Internally it represents a `pcap_t` handle obtained by `pcap_open_live()`.
pub struct Capture {
    handle: Unique<raw::pcap_t>
}

impl Capture {
    /// Returns an iterator over packets received on this capture handle.
    pub fn listen<'a>(&'a mut self) -> Packets<'a> {
        Packets {
            capture: &*self
        }
    }

    /// Adds a filter to the capture using the given BPF program string. Internally
    /// this is compiled using `pcap_compile()`.
    ///
    /// See http://biot.com/capstats/bpf.html for more information about this syntax.
    pub fn filter(&mut self, program: &str) -> Result<(), String> {
        let program = CString::new(program).unwrap();

        unsafe {
            let mut bpf_program: raw::Struct_bpf_program = Default::default();

            if -1 == raw::pcap_compile(*self.handle, &mut bpf_program, program.as_ptr(), 0, 0) {
                return Err(cstr_to_string(raw::pcap_geterr(*self.handle)).unwrap());
            }

            if -1 == raw::pcap_setfilter(*self.handle, &mut bpf_program) {
                raw::pcap_freecode(&mut bpf_program);
                return Err(cstr_to_string(raw::pcap_geterr(*self.handle)).unwrap());
            }

            raw::pcap_freecode(&mut bpf_program);
            Ok(())
        }
    }
}

impl Drop for Capture {
    fn drop(&mut self) {
        unsafe {
            raw::pcap_close(*self.handle)
        }
    }
}

impl Iterator for Devices {
    type Item = Device;

    fn next(&mut self) -> Option<Device> {
        if self.device.is_null() {
            None
        } else {
            unsafe {
                let ret = Device {
                    name: cstr_to_string(self.device.get().name).unwrap(),
                    desc: {
                        if !self.device.get().description.is_null() {
                            Some(cstr_to_string(self.device.get().description).unwrap())
                        } else {
                            None
                        }
                    }
                };
                self.device = Unique::new(self.device.get().next);

                Some(ret)
            }
        }
    }
}

fn cstr_to_string(ptr: *const libc::c_char) -> Result<String, str::Utf8Error> {
    Ok(try!(str::from_utf8(unsafe{CStr::from_ptr(ptr)}.to_bytes())).to_string())
}<|endoftext|>


<commit_before>// Copyright 2014-2016 Johannes Köster.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

//! A classical, flexible, q-gram index implementation.
//!
//! # Example
//!
//! ```
//! use bio::alphabets;
//! use bio::data_structures::qgram_index;
//!
//! let text = b"ACGGCTGAGATGAT";
//! let alphabet = alphabets::dna::alphabet();
//! let q = 3;
//! let qgram_index = qgram_index::QGramIndex::new(q, text, &alphabet);
//!
//! let pattern = b"GCTG";
//! let matches = qgram_index.matches(pattern, 1);
//! assert_eq!(
//!     matches,
//!     [qgram_index::Match {
//!         pattern: qgram_index::Interval { start: 0, stop: 4 },
//!         text: qgram_index::Interval { start: 3, stop: 7 },
//!         count: 2
//!     }]
//! );
//! ```

use std::cmp;
use std::collections;
use std::collections::hash_map::Entry;

use crate::alphabets::{Alphabet, RankTransform};
use crate::utils;

/// A classical, flexible, q-gram index implementation.
#[derive(Serialize, Deserialize)]
pub struct QGramIndex {
    q: u32,
    address: Vec<usize>,
    pos: Vec<usize>,
    ranks: RankTransform,
}

impl QGramIndex {
    /// Create a new q-gram index.
    /// The q has to be smaller than b / log2(|A|) with |A| being the alphabet size and b the number
    /// bits with the `usize` data type.
    pub fn new<'a, T, I>(q: u32, text: T, alphabet: &Alphabet) -> Self
    where
        I: Iterator<Item = &'a u8> + ExactSizeIterator + Clone,
        T: IntoIterator<Item = &'a u8, IntoIter = I> + Sized,
    {
        QGramIndex::with_max_count(q, text, alphabet, std::usize::MAX)
    }

    /// Create a new q-gram index, only considering q-grams that occur at most `max_count` times.
    /// The q has to be smaller than b / log2(|A|) with |A| being the alphabet size and b the number
    /// bits with the `usize` data type.
    pub fn with_max_count<'a, T, I>(q: u32, text: T, alphabet: &Alphabet, max_count: usize) -> Self
    where
        I: Iterator<Item = &'a u8> + ExactSizeIterator + Clone,
        T: IntoIterator<Item = &'a u8, IntoIter = I> + Sized,
    {
        let text = text.into_iter();
        let ranks = RankTransform::new(alphabet);

        let qgram_count = alphabet.len().pow(q as u32);
        let mut address = vec![0; qgram_count + 1];
        let mut pos = vec![0; text.len()];

        for qgram in ranks.qgrams(q, text.clone()) {
            address[qgram] += 1;
        }

        for a in address.iter_mut().skip(1) {
            if *a > max_count {
                // mask qgram
                *a = 0;
            }
        }

        utils::prescan(&mut address, 0, |a, b| a + b);

        {
            let mut offset = vec![0; qgram_count];
            for (i, qgram) in ranks.qgrams(q, text).enumerate() {
                let a = address[qgram as usize];
                if address[qgram as usize + 1] - a != 0 {
                    // if not masked, insert positions
                    pos[a + offset[qgram as usize]] = i;
                    offset[qgram as usize] += 1;
                }
            }
        }

        QGramIndex {
            q,
            address,
            pos,
            ranks,
        }
    }

    /// The used q.
    pub fn q(&self) -> u32 {
        self.q
    }

    /// Return text positions with matching q-gram. Complexity O(1).
    pub fn qgram_matches(&self, qgram: usize) -> &[usize] {
        &self.pos[self.address[qgram]..self.address[qgram + 1]]
    }

    /// Return matches of the given pattern.
    /// Complexity O(m + k) for pattern of length m and k being the number of matching q-grams.
    pub fn matches(&self, pattern: &[u8], min_count: usize) -> Vec<Match> {
        let q = self.q as usize;
        let mut diagonals = collections::HashMap::new();
        for (i, qgram) in self.ranks.qgrams(self.q, pattern).enumerate() {
            for &p in self.qgram_matches(qgram) {
                let diagonal = p - i;
                match diagonals.entry(diagonal) {
                    Entry::Vacant(v) => {
                        v.insert(Match {
                            pattern: Interval {
                                start: i,
                                stop: i + q,
                            },
                            text: Interval {
                                start: p,
                                stop: p + q,
                            },
                            count: 1,
                        });
                    }
                    Entry::Occupied(mut o) => {
                        let m = o.get_mut();
                        m.pattern.stop = i + q;
                        m.text.stop = p + q;
                        m.count += 1;
                    }
                }
            }
        }
        diagonals
            .into_iter()
            .filter_map(|(_, m)| if m.count >= min_count { Some(m) } else { None })
            .collect()
    }

    /// Return exact matches (substrings) of the given pattern.
    /// Complexity O(m + k) for pattern of length m and k being the number of matching q-grams.
    pub fn exact_matches(&self, pattern: &[u8]) -> Vec<ExactMatch> {
        let q = self.q as usize;
        let mut diagonals = collections::HashMap::new();
        let mut matches = Vec::new();

        for (i, qgram) in self.ranks.qgrams(self.q, pattern).enumerate() {
            for &p in self.qgram_matches(qgram) {
                let diagonal = p as i32 - i as i32;
                match diagonals.entry(diagonal) {
                    Entry::Vacant(v) => {
                        v.insert(ExactMatch {
                            pattern: Interval {
                                start: i,
                                stop: i + q,
                            },
                            text: Interval {
                                start: p,
                                stop: p + q,
                            },
                        });
                    }
                    Entry::Occupied(mut o) => {
                        let m = o.get_mut();
                        if m.pattern.stop - q + 1 != i {
                            // discontinue match
                            matches.push(*m);
                            // start new match
                            m.pattern.start = i;
                            m.text.start = p;
                        }
                        m.pattern.stop = i + q;
                        m.text.stop = p + q;
                    }
                }
            }
        }
        for (_, m) in diagonals {
            matches.push(m);
        }

        matches
    }
}

/// An interval, consisting of start and stop position (the latter exclusive).
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub struct Interval {
    pub start: usize,
    pub stop: usize,
}

impl Interval {
    /// Get the text within the given interval.
    pub fn get<'a>(&self, text: &'a [u8]) -> &'a [u8] {
        &text[self.start..self.stop]
    }
}

/// A match between the pattern and the text.
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub struct Match {
    pub pattern: Interval,
    pub text: Interval,
    pub count: usize,
}

impl cmp::Ord for Match {
    fn cmp(&self, other: &Self) -> cmp::Ordering {
        self.count.cmp(&other.count)
    }
}

impl cmp::PartialOrd for Match {
    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
        Some(self.cmp(other))
    }
}

/// An exact match between the pattern and the text.
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct ExactMatch {
    pub pattern: Interval,
    pub text: Interval,
}

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

    fn setup() -> (&'static [u8], alphabets::Alphabet) {
        let text = b"ACGGCTGAGATGAT";
        let alphabet = alphabets::dna::alphabet();

        (text, alphabet)
    }

    #[test]
    fn test_qgram_matches() {
        let (text, alphabet) = setup();
        let q = 3;
        let qgram_index = QGramIndex::new(q, text, &alphabet);

        let ranks = alphabets::RankTransform::new(&alphabet);

        let qgram = ranks.qgrams(q, b"TGA").next().unwrap();

        let matches = qgram_index.qgram_matches(qgram);
        assert_eq!(matches, [5, 10]);
    }

    #[test]
    fn test_matches() {
        let (text, alphabet) = setup();
        let q = 3;
        let qgram_index = QGramIndex::new(q, text, &alphabet);

        let pattern = b"GCTG";
        let matches = qgram_index.matches(pattern, 1);
        assert_eq!(
            matches,
            [Match {
                pattern: Interval { start: 0, stop: 4 },
                text: Interval { start: 3, stop: 7 },
                count: 2,
            }]
        );
    }

    #[test]
    fn test_exact_matches() {
        let (text, alphabet) = setup();
        let q = 3;
        let qgram_index = QGramIndex::new(q, text, &alphabet);

        let pattern = b"GCTGA";
        let exact_matches = qgram_index.exact_matches(pattern);
        assert!(exact_matches.len() == 2);
        for m in exact_matches {
            assert_eq!(m.pattern.get(pattern), m.text.get(text));
        }
    }

    #[test]
    fn test_exact_matches_self() {
        let (text, alphabet) = setup();
        let q = 3;
        let qgram_index = QGramIndex::new(q, text, &alphabet);

        let exact_matches = qgram_index.exact_matches(text);
        assert!(!exact_matches.is_empty());
    }

    #[test]
    fn test_iterator() {
        let (text, alphabet) = setup();
        let q = 3;
        let qgram_index = QGramIndex::new(q, text.iter(), &alphabet);

        let exact_matches = qgram_index.exact_matches(text);
        assert!(exact_matches.len() >= 1);
    }

    #[test]
    #[cfg(feature = "nightly")]
    fn test_serde() {
        use serde::{Deserialize, Serialize};
        fn impls_serde_traits<S: Serialize + Deserialize>() {}

        impls_serde_traits::<QGramIndex>();
    }
}
<commit_msg>fix: Make QGramIndex use less memory; fix bug; improve tests&docs (#471)<commit_after>// Copyright 2014-2016 Johannes Köster.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

//! A classical, flexible, q-gram index implementation.
//!
//! # Example
//!
//! ```
//! use bio::alphabets;
//! use bio::data_structures::qgram_index;
//!
//! let text = b"ACGGCTGAGATGAT";
//! let alphabet = alphabets::dna::alphabet();
//! let q = 3;
//! let qgram_index = qgram_index::QGramIndex::new(q, text, &alphabet);
//!
//! let pattern = b"GCTG";
//! let matches = qgram_index.matches(pattern, 1);
//! assert_eq!(
//!     matches,
//!     [qgram_index::Match {
//!         pattern: qgram_index::Interval { start: 0, stop: 4 },
//!         text: qgram_index::Interval { start: 3, stop: 7 },
//!         count: 2
//!     }]
//! );
//! ```

use std::cmp;
use std::collections;
use std::collections::hash_map::Entry;

use crate::alphabets::{Alphabet, RankTransform};
use crate::utils;

/// A classical, flexible, q-gram index implementation.
///
/// Uses |alphabet|^q + k words of memory, where k is the number of q-grams in the text with count at most `max_count` (if specified).
#[derive(Serialize, Deserialize)]
pub struct QGramIndex {
    q: u32,
    // For each q-gram, the position in `pos` where positions for this q-gram are stored.
    address: Vec<usize>,
    // The positions in `text` where each q-gram occurs.
    pos: Vec<usize>,
    ranks: RankTransform,
}

impl QGramIndex {
    /// Create a new q-gram index.
    /// The q has to be smaller than b / log2(|A|) with |A| being the alphabet size and b the number
    /// bits with the `usize` data type.
    pub fn new<'a, T, I>(q: u32, text: T, alphabet: &Alphabet) -> Self
    where
        I: Iterator<Item = &'a u8> + ExactSizeIterator + Clone,
        T: IntoIterator<Item = &'a u8, IntoIter = I> + Sized,
    {
        QGramIndex::with_max_count(q, text, alphabet, std::usize::MAX)
    }

    /// Create a new q-gram index, only considering q-grams that occur at most `max_count` times.
    /// The q has to be smaller than b / log2(|A|) with |A| being the alphabet size and b the number
    /// bits with the `usize` data type.
    pub fn with_max_count<'a, T, I>(q: u32, text: T, alphabet: &Alphabet, max_count: usize) -> Self
    where
        I: Iterator<Item = &'a u8> + ExactSizeIterator + Clone,
        T: IntoIterator<Item = &'a u8, IntoIter = I> + Sized,
    {
        let text = text.into_iter();
        let ranks = RankTransform::new(alphabet);

        let qgram_count = alphabet.len().pow(q as u32);
        let mut address = vec![0; qgram_count + 1];

        for qgram in ranks.qgrams(q, text.clone()) {
            address[qgram] += 1;
        }

        for a in address.iter_mut() {
            if *a > max_count {
                // mask qgram
                *a = 0;
            }
        }

        utils::prescan(&mut address, 0, |a, b| a + b);

        // Address has at least size 1, so unwrap is fine.
        let mut pos = vec![0; *address.last().unwrap()];

        {
            let mut offset = vec![0; qgram_count];
            for (i, qgram) in ranks.qgrams(q, text).enumerate() {
                let a = address[qgram as usize];
                if address[qgram as usize + 1] - a != 0 {
                    // if not masked, insert positions
                    pos[a + offset[qgram as usize]] = i;
                    offset[qgram as usize] += 1;
                }
            }
        }

        QGramIndex {
            q,
            address,
            pos,
            ranks,
        }
    }

    /// The used q.
    pub fn q(&self) -> u32 {
        self.q
    }

    /// Return text positions with matching q-gram. Complexity O(1).
    pub fn qgram_matches(&self, qgram: usize) -> &[usize] {
        &self.pos[self.address[qgram]..self.address[qgram + 1]]
    }

    /// Return matches of the given pattern, matching in at least `min_count` q-grams.
    /// Complexity O(m + k) for pattern of length m and k being the number of matching q-grams.
    ///
    /// A match is a substring of `pattern` and a corresponding substring of the text that share at least `min_count` q-grams.
    pub fn matches(&self, pattern: &[u8], min_count: usize) -> Vec<Match> {
        let q = self.q as usize;
        let mut diagonals = collections::HashMap::new();
        for (i, qgram) in self.ranks.qgrams(self.q, pattern).enumerate() {
            for &p in self.qgram_matches(qgram) {
                let diagonal = p - i;
                match diagonals.entry(diagonal) {
                    Entry::Vacant(v) => {
                        v.insert(Match {
                            pattern: Interval {
                                start: i,
                                stop: i + q,
                            },
                            text: Interval {
                                start: p,
                                stop: p + q,
                            },
                            count: 1,
                        });
                    }
                    Entry::Occupied(mut o) => {
                        let m = o.get_mut();
                        m.pattern.stop = i + q;
                        m.text.stop = p + q;
                        m.count += 1;
                    }
                }
            }
        }
        diagonals
            .into_iter()
            .filter_map(|(_, m)| if m.count >= min_count { Some(m) } else { None })
            .collect()
    }

    /// Return exact matches (substrings) of the given pattern.
    /// Complexity O(m + k) for pattern of length m and k being the number of matching q-grams.
    ///
    /// An exact match is a substring of `pattern` occurring in the text of length at least `q`.
    pub fn exact_matches(&self, pattern: &[u8]) -> Vec<ExactMatch> {
        let q = self.q as usize;
        let mut diagonals = collections::HashMap::new();
        let mut matches = Vec::new();

        for (i, qgram) in self.ranks.qgrams(self.q, pattern).enumerate() {
            for &p in self.qgram_matches(qgram) {
                let diagonal = p as i32 - i as i32;
                match diagonals.entry(diagonal) {
                    Entry::Vacant(v) => {
                        v.insert(ExactMatch {
                            pattern: Interval {
                                start: i,
                                stop: i + q,
                            },
                            text: Interval {
                                start: p,
                                stop: p + q,
                            },
                        });
                    }
                    Entry::Occupied(mut o) => {
                        let m = o.get_mut();
                        if m.pattern.stop - q + 1 != i {
                            // discontinue match
                            matches.push(*m);
                            // start new match
                            m.pattern.start = i;
                            m.text.start = p;
                        }
                        m.pattern.stop = i + q;
                        m.text.stop = p + q;
                    }
                }
            }
        }
        for (_, m) in diagonals {
            matches.push(m);
        }

        matches
    }
}

/// An interval, consisting of start and stop position (the latter exclusive).
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub struct Interval {
    pub start: usize,
    pub stop: usize,
}

impl Interval {
    /// Get the text within the given interval.
    pub fn get<'a>(&self, text: &'a [u8]) -> &'a [u8] {
        &text[self.start..self.stop]
    }
}

/// A match between the pattern and the text.
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub struct Match {
    pub pattern: Interval,
    pub text: Interval,
    pub count: usize,
}

impl cmp::Ord for Match {
    fn cmp(&self, other: &Self) -> cmp::Ordering {
        self.count.cmp(&other.count)
    }
}

impl cmp::PartialOrd for Match {
    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
        Some(self.cmp(other))
    }
}

/// An exact match between the pattern and the text.
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct ExactMatch {
    pub pattern: Interval,
    pub text: Interval,
}

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

    fn setup() -> (&'static [u8], alphabets::Alphabet) {
        let text = b"ACGGCTGAGATGAT";
        let alphabet = alphabets::dna::alphabet();

        (text, alphabet)
    }

    #[test]
    fn test_qgram_matches() {
        let (text, alphabet) = setup();
        let q = 3;
        let qgram_index = QGramIndex::new(q, text, &alphabet);

        let ranks = alphabets::RankTransform::new(&alphabet);

        let qgram = ranks.qgrams(q, b"TGA").next().unwrap();

        let matches = qgram_index.qgram_matches(qgram);
        assert_eq!(matches, [5, 10]);
    }

    #[test]
    fn test_qgram_with_max_count() {
        let (text, alphabet) = setup();
        let q = 3;
        let qgram_index = QGramIndex::with_max_count(q, text, &alphabet, 1);

        let ranks = alphabets::RankTransform::new(&alphabet);

        let qgram = ranks.qgrams(q, b"TGA").next().unwrap();

        // Should be pruned because the count of 2 is larger than the max_count of 1.
        let matches = qgram_index.qgram_matches(qgram);
        assert_eq!(matches, []);
    }

    #[test]
    fn test_qgram_with_max_count_index_0() {
        let (_, alphabet) = setup();
        let text = b"AAAAA";
        let q = 3;
        let qgram_index = QGramIndex::with_max_count(q, text, &alphabet, 1);

        let ranks = alphabets::RankTransform::new(&alphabet);

        let qgram = ranks.qgrams(q, b"AAA").next().unwrap();

        // Should be pruned because the count of 3 is larger than the max_count of 1.
        let matches = qgram_index.qgram_matches(qgram);
        assert_eq!(matches, []);
    }

    #[test]
    fn test_qgram_sizeof_pos() {
        let (_, alphabet) = setup();
        let text = b"AAAAA";
        let q = 3;
        let qgram_index = QGramIndex::new(q, text, &alphabet);

        let ranks = alphabets::RankTransform::new(&alphabet);

        let qgram = ranks.qgrams(q, b"AAA").next().unwrap();

        // Should be pruned because the count of 3 is larger than the max_count of 1.
        let matches = qgram_index.qgram_matches(qgram);
        assert_eq!(matches, [0, 1, 2]);
    }

    #[test]
    fn test_matches() {
        let (text, alphabet) = setup();
        let q = 3;
        let qgram_index = QGramIndex::new(q, text, &alphabet);

        // A fully matching pattern.
        let pattern = b"GCTG";
        let matches = qgram_index.matches(pattern, 1);
        assert_eq!(
            matches,
            [Match {
                pattern: Interval { start: 0, stop: 4 },
                text: Interval { start: 3, stop: 7 },
                count: 2,
            }]
        );

        // A pattern that matches in one position on two disjoint q-grams.
        let pattern = b"GCTAAGA";
        let matches = qgram_index.matches(pattern, 2);
        assert_eq!(
            matches,
            [Match {
                pattern: Interval { start: 0, stop: 7 },
                text: Interval { start: 3, stop: 10 },
                count: 2,
            }]
        );
    }

    #[test]
    fn test_exact_matches() {
        let (text, alphabet) = setup();
        let q = 3;
        let qgram_index = QGramIndex::new(q, text, &alphabet);

        let pattern = b"GCTGA";
        let exact_matches = qgram_index.exact_matches(pattern);
        assert!(exact_matches.len() == 2);
        for m in exact_matches {
            assert_eq!(m.pattern.get(pattern), m.text.get(text));
        }

        // A pattern that matches in one position on two disjoint q-grams.
        let pattern = b"GCTAAGA";
        let matches = qgram_index.exact_matches(pattern);
        assert_eq!(
            matches,
            [
                ExactMatch {
                    pattern: Interval { start: 0, stop: 3 },
                    text: Interval { start: 3, stop: 6 }
                },
                ExactMatch {
                    pattern: Interval { start: 4, stop: 7 },
                    text: Interval { start: 7, stop: 10 }
                }
            ]
        );
    }

    #[test]
    fn test_exact_matches_self() {
        let (text, alphabet) = setup();
        let q = 3;
        let qgram_index = QGramIndex::new(q, text, &alphabet);

        let exact_matches = qgram_index.exact_matches(text);
        assert!(!exact_matches.is_empty());
    }

    #[test]
    fn test_iterator() {
        let (text, alphabet) = setup();
        let q = 3;
        let qgram_index = QGramIndex::new(q, text.iter(), &alphabet);

        let exact_matches = qgram_index.exact_matches(text);
        assert!(exact_matches.len() >= 1);
    }

    #[test]
    #[cfg(feature = "nightly")]
    fn test_serde() {
        use serde::{Deserialize, Serialize};
        fn impls_serde_traits<S: Serialize + Deserialize>() {}

        impls_serde_traits::<QGramIndex>();
    }
}
<|endoftext|>


<commit_before>extern crate regex;

use std::ascii::*;
use regex::Regex;

fn to_class_case<'a>(non_class_case_string: String) -> String {
    let mut result:String = "".to_string();
    let mut new_word: bool = true;

    for character in non_class_case_string.chars() {
        if character.to_string() == "_" {
            new_word = true;
        } else if new_word {
            result = format!("{}{}", result, character.to_ascii_uppercase());
            new_word = false;
        } else {
            result = format!("{}{}", result, character.to_ascii_lowercase());
        }
    }
    return result
}

fn to_camel_case<'a>(non_camelized_string: String) -> String {
    let mut result:String = "".to_string();
    let mut new_word: bool = false;

    for character in non_camelized_string.chars() {
        if character.to_string() == "_" {
            new_word = true;
        } else if new_word {
            result = format!("{}{}", result, character.to_ascii_uppercase());
            new_word = false;
        } else {
            result = format!("{}{}", result, character.to_ascii_lowercase());
        }
    }
    return result
}

fn to_snake_case<'a>(non_snake_case_string: String) -> String {
    let mut result:String = "".to_string();
    let mut first_character: bool = true;
    for character in non_snake_case_string.chars() {
        if character == character.to_ascii_uppercase() && !first_character {
            result = format!("{}_{}", result, character.to_ascii_lowercase());
        } else {
            result = format!("{}{}", result, character.to_ascii_lowercase());
            first_character = false;
        }
    }
    return result
}

fn to_kebab_case<'a>(non_kebab_case_string: String) -> String {
    let mut result:String = "".to_string();
    let mut first_character: bool = true;
    for character in non_kebab_case_string.chars() {
        if character == character.to_ascii_uppercase() && !first_character {
            result = format!("{}-{}", result, character.to_ascii_lowercase());
        } else {
            result = format!("{}{}", result, character.to_ascii_lowercase());
            first_character = false;
        }
    }
    return result
}

fn is_class_case<'a>(test_string: String) -> bool{
    let class_matcher = Regex::new(r"(^[A-Z])([^-|^_]*[a-z]+)").unwrap();
    let mut is_class_case = false;
    if class_matcher.is_match(test_string.as_ref()){
        is_class_case = true;
    }
    return is_class_case;
}

fn is_camel_case<'a>(test_string: String) -> bool{
    let camel_matcher = Regex::new(r"(^|[A-Z])([^-|^_]*[a-z]+)").unwrap();
    let kebab_snake_matcher = Regex::new(r"[-|_]").unwrap();
    let mut is_camel_case = false;
    if camel_matcher.is_match(test_string.as_ref())
        && !kebab_snake_matcher.is_match(test_string.as_ref())
        && !is_class_case(test_string){
            is_camel_case = true;
        }
    return is_camel_case;
}

fn is_snake_case<'a>(test_string: String) -> bool{
    let snake_matcher = Regex::new(r"(?:[^-]?=^|[_])([a-z]+)").unwrap();
    let upcase_matcher = Regex::new(r"[A-Z]").unwrap();
    let mut is_snake_case = false;
    if snake_matcher.is_match(test_string.as_ref())
        && !upcase_matcher.is_match(test_string.as_ref()) {
            is_snake_case = true;
        }
    return is_snake_case;
}

fn is_kebab_case<'a>(test_string: String) -> bool{
    let kebab_matcher = Regex::new(r"(?:[^_]?=^|[-])([a-z]+)").unwrap();
    let upcase_matcher = Regex::new(r"[A-Z]").unwrap();
    let mut is_kebab_case = false;
    if kebab_matcher.is_match(test_string.as_ref())
        && !upcase_matcher.is_match(test_string.as_ref()) {
            is_kebab_case = true;
        }
    return is_kebab_case;
}

#[test] #[allow(non_snake_case)]
fn returns_truthy_value_for_is_class_case_when_class() {
    let mock_string: String = "DataMapperIsAReallyReallyLongString".to_string();
    let asserted_bool: bool = is_class_case(mock_string);
    println!("{}",asserted_bool);
    assert!(asserted_bool == true);
}

#[test] #[allow(non_snake_case)]
fn returns_falsey_value_for_is_class_case_when_kebab() {
    let mock_string: String = "data-mapper-string-that-is-really-really-long".to_string();
    let asserted_bool: bool = is_class_case(mock_string);
    println!("{}",asserted_bool);
    assert!(asserted_bool == false);
}

#[test] #[allow(non_snake_case)]
fn returns_falsey_value_for_is_class_case_when_camel() {
    let mock_string: String = "dataMapperIsAReallyReallyLongString".to_string();
    let asserted_bool: bool = is_class_case(mock_string);
    println!("{}",asserted_bool);
    assert!(asserted_bool == false);
}

#[test] #[allow(non_snake_case)]
fn returns_falsey_value_for_is_class_case_when_snake() {
    let mock_string: String = "data_mapper_string_that_is_really_really_long".to_string();
    let asserted_bool: bool = is_class_case(mock_string);
    assert!(asserted_bool == false);
}

#[test] #[allow(non_snake_case)]
fn returns_falsey_value_for_is_camel_case_when_kebab() {
    let mock_string: String = "data-mapper-string-that-is-really-really-long".to_string();
    let asserted_bool: bool = is_camel_case(mock_string);
    println!("{}",asserted_bool);
    assert!(asserted_bool == false);
}

#[test] #[allow(non_snake_case)]
fn returns_falsey_value_for_is_camel_case_when_class() {
    let mock_string: String = "DataMapperIsAReallyReallyLongString".to_string();
    let asserted_bool: bool = is_camel_case(mock_string);
    println!("{}",asserted_bool);
    assert!(asserted_bool == false);
}

#[test] #[allow(non_snake_case)]
fn returns_truthy_value_for_is_camel_case_when_camel() {
    let mock_string: String = "dataMapperIsAReallyReallyLongString".to_string();
    let asserted_bool: bool = is_camel_case(mock_string);
    println!("{}",asserted_bool);
    assert!(asserted_bool == true);
}

#[test] #[allow(non_snake_case)]
fn returns_falsey_value_for_is_camel_case_when_snake() {
    let mock_string: String = "data_mapper_string_that_is_really_really_long".to_string();
    let asserted_bool: bool = is_camel_case(mock_string);
    assert!(asserted_bool == false);
}

#[test] #[allow(non_snake_case)]
fn returns_truthy_value_for_is_kebab_case_when_kebab() {
    let mock_string: String = "data-mapper-string-that-is-really-really-long".to_string();
    let asserted_bool: bool = is_kebab_case(mock_string);
    println!("{}",asserted_bool);
    assert!(asserted_bool == true);
}

#[test] #[allow(non_snake_case)]
fn returns_falsey_value_for_is_kebab_case_when_class() {
    let mock_string: String = "DataMapperIsAReallyReallyLongString".to_string();
    let asserted_bool: bool = is_kebab_case(mock_string);
    println!("{}",asserted_bool);
    assert!(asserted_bool == false);
}

#[test] #[allow(non_snake_case)]
fn returns_falsey_value_for_is_kebab_case_when_camel() {
    let mock_string: String = "dataMapperIsAReallyReallyLongString".to_string();
    let asserted_bool: bool = is_kebab_case(mock_string);
    println!("{}",asserted_bool);
    assert!(asserted_bool == false);
}

#[test] #[allow(non_snake_case)]
fn returns_falsey_value_for_is_kebab_case_when_snake() {
    let mock_string: String = "data_mapper_string_that_is_really_really_long".to_string();
    let asserted_bool: bool = is_kebab_case(mock_string);
    assert!(asserted_bool == false);
}

#[test] #[allow(non_snake_case)]
fn returns_falsey_value_for_is_snake_case_when_kebab() {
    let mock_string: String = "data-mapper-string-that-is-really-really-long".to_string();
    let asserted_bool: bool = is_snake_case(mock_string);
    println!("{}",asserted_bool);
    assert!(asserted_bool == false);
}

#[test] #[allow(non_snake_case)]
fn returns_falsey_value_for_is_snake_case_when_class() {
    let mock_string: String = "DataMapperIsAReallyReallyLongString".to_string();
    let asserted_bool: bool = is_snake_case(mock_string);
    println!("{}",asserted_bool);
    assert!(asserted_bool == false);
}

#[test] #[allow(non_snake_case)]
fn returns_falsey_value_for_is_snake_case_when_camel() {
    let mock_string: String = "dataMapperIsAReallyReallyLongString".to_string();
    let asserted_bool: bool = is_snake_case(mock_string);
    println!("{}",asserted_bool);
    assert!(asserted_bool == false);
}

#[test] #[allow(non_snake_case)]
fn returns_truthy_value_for_is_snake_case_when_snake() {
    let mock_string: String = "data_mapper_string_that_is_really_really_long".to_string();
    let asserted_bool: bool = is_snake_case(mock_string);
    assert!(asserted_bool == true);
}

#[test] #[allow(non_snake_case)]
fn ClassCase_data_mapper_as_DataMapper() {
    let mock_string: String = "data_mapper".to_string();
    let expected_string: String = "DataMapper".to_string();
    let asserted_string: String = to_class_case(mock_string);
    assert!(asserted_string == expected_string);
}

#[test] #[allow(non_snake_case)]
fn camelize_data_mapper_as_dataMapper() {
    let mock_string: String = "data_mapper".to_string();
    let expected_string: String = "dataMapper".to_string();
    let asserted_string: String = to_camel_case(mock_string);
    assert!(asserted_string == expected_string);
}

#[test] #[allow(non_snake_case)]
fn snake_case_dataMapper_as_data_mapper() {
    let mock_string: String = "DataMapper".to_string();
    let expected_string: String = "data_mapper".to_string();
    let asserted_string: String = to_snake_case(mock_string);
    assert!(asserted_string == expected_string);
}

// #[test] #[allow(non_snake_case)]
// fn kebab_case_data_mapper_as_data_mapper() {
//     let mock_string: String = "data_mapper".to_string();
//     let expected_string: String = "data-mapper".to_string();
//     let asserted_string: String = to_kebab_case(mock_string);
//     assert!(asserted_string == expected_string);
// }

#[test] #[allow(non_snake_case)]
fn kebab_case_dataMapper_as_data_mapper() {
    let mock_string: String = "DataMapper".to_string();
    let expected_string: String = "data-mapper".to_string();
    let asserted_string: String = to_kebab_case(mock_string);
    assert!(asserted_string == expected_string);
}
<commit_msg>Fixing up kebab case. Still needs to deal with the ClassCase instance.<commit_after>extern crate regex;

use std::ascii::*;
use regex::Regex;

fn to_class_case<'a>(non_class_case_string: String) -> String {
    let mut result:String = "".to_string();
    let mut new_word: bool = true;

    for character in non_class_case_string.chars() {
        if character.to_string() == "_" {
            new_word = true;
        } else if new_word {
            result = format!("{}{}", result, character.to_ascii_uppercase());
            new_word = false;
        } else {
            result = format!("{}{}", result, character.to_ascii_lowercase());
        }
    }
    return result
}

fn to_camel_case<'a>(non_camelized_string: String) -> String {
    let mut result:String = "".to_string();
    let mut new_word: bool = false;

    for character in non_camelized_string.chars() {
        if character.to_string() == "_" {
            new_word = true;
        } else if new_word {
            result = format!("{}{}", result, character.to_ascii_uppercase());
            new_word = false;
        } else {
            result = format!("{}{}", result, character.to_ascii_lowercase());
        }
    }
    return result
}

fn to_snake_case<'a>(non_snake_case_string: String) -> String {
    let mut result:String = "".to_string();
    let mut first_character: bool = true;
    for character in non_snake_case_string.chars() {
        if character == character.to_ascii_uppercase() && !first_character {
            result = format!("{}_{}", result, character.to_ascii_lowercase());
        } else {
            result = format!("{}{}", result, character.to_ascii_lowercase());
            first_character = false;
        }
    }
    return result
}

fn to_kebab_case<'a>(non_kebab_case_string: String) -> String {
    let mut kebab_string: String = non_kebab_case_string.clone();
    if is_camel_case(non_kebab_case_string.clone()) {
        kebab_string = to_kebab_from_camel(non_kebab_case_string.clone());
    }else if is_snake_case(non_kebab_case_string.clone()) {
        kebab_string = to_kebab_from_snake(non_kebab_case_string.clone());
    }
    return kebab_string;
}

fn to_kebab_from_camel<'a>(non_kebab_case_string: String) -> String {
    let mut result:String = "".to_string();
    let mut first_character: bool = true;
    for character in non_kebab_case_string.chars() {
        if character == character.to_ascii_uppercase() && !first_character {
            result = format!("{}-{}", result, character.to_ascii_lowercase());
        } else {
            result = format!("{}{}", result, character.to_ascii_lowercase());
            first_character = false;
        }
    }
    return result
}
fn to_kebab_from_snake<'a>(non_kebab_case_string: String) -> String {
    return non_kebab_case_string.replace("_", "-");
}

fn is_class_case<'a>(test_string: String) -> bool{
    let class_matcher = Regex::new(r"(^[A-Z])([^-|^_]*[a-z]+)").unwrap();
    let mut is_class_case = false;
    if class_matcher.is_match(test_string.as_ref()){
        is_class_case = true;
    }
    return is_class_case;
}

fn is_camel_case<'a>(test_string: String) -> bool{
    let camel_matcher = Regex::new(r"(^|[A-Z])([^-|^_]*[a-z]+)").unwrap();
    let kebab_snake_matcher = Regex::new(r"[-|_]").unwrap();
    let mut is_camel_case = false;
    if camel_matcher.is_match(test_string.as_ref())
        && !kebab_snake_matcher.is_match(test_string.as_ref())
        && !is_class_case(test_string){
            is_camel_case = true;
        }
    return is_camel_case;
}

fn is_snake_case<'a>(test_string: String) -> bool{
    let snake_matcher = Regex::new(r"(?:[^-]?=^|[_])([a-z]+)").unwrap();
    let upcase_matcher = Regex::new(r"[A-Z]").unwrap();
    let mut is_snake_case = false;
    if snake_matcher.is_match(test_string.as_ref())
        && !upcase_matcher.is_match(test_string.as_ref()) {
            is_snake_case = true;
        }
    return is_snake_case;
}

fn is_kebab_case<'a>(test_string: String) -> bool{
    let kebab_matcher = Regex::new(r"(?:[^_]?=^|[-])([a-z]+)").unwrap();
    let upcase_matcher = Regex::new(r"[A-Z]").unwrap();
    let mut is_kebab_case = false;
    if kebab_matcher.is_match(test_string.as_ref())
        && !upcase_matcher.is_match(test_string.as_ref()) {
            is_kebab_case = true;
        }
    return is_kebab_case;
}

#[test] #[allow(non_snake_case)]
fn returns_truthy_value_for_is_class_case_when_class() {
    let mock_string: String = "DataMapperIsAReallyReallyLongString".to_string();
    let asserted_bool: bool = is_class_case(mock_string);
    println!("{}",asserted_bool);
    assert!(asserted_bool == true);
}

#[test] #[allow(non_snake_case)]
fn returns_falsey_value_for_is_class_case_when_kebab() {
    let mock_string: String = "data-mapper-string-that-is-really-really-long".to_string();
    let asserted_bool: bool = is_class_case(mock_string);
    println!("{}",asserted_bool);
    assert!(asserted_bool == false);
}

#[test] #[allow(non_snake_case)]
fn returns_falsey_value_for_is_class_case_when_camel() {
    let mock_string: String = "dataMapperIsAReallyReallyLongString".to_string();
    let asserted_bool: bool = is_class_case(mock_string);
    println!("{}",asserted_bool);
    assert!(asserted_bool == false);
}

#[test] #[allow(non_snake_case)]
fn returns_falsey_value_for_is_class_case_when_snake() {
    let mock_string: String = "data_mapper_string_that_is_really_really_long".to_string();
    let asserted_bool: bool = is_class_case(mock_string);
    assert!(asserted_bool == false);
}

#[test] #[allow(non_snake_case)]
fn returns_falsey_value_for_is_camel_case_when_kebab() {
    let mock_string: String = "data-mapper-string-that-is-really-really-long".to_string();
    let asserted_bool: bool = is_camel_case(mock_string);
    println!("{}",asserted_bool);
    assert!(asserted_bool == false);
}

#[test] #[allow(non_snake_case)]
fn returns_falsey_value_for_is_camel_case_when_class() {
    let mock_string: String = "DataMapperIsAReallyReallyLongString".to_string();
    let asserted_bool: bool = is_camel_case(mock_string);
    println!("{}",asserted_bool);
    assert!(asserted_bool == false);
}

#[test] #[allow(non_snake_case)]
fn returns_truthy_value_for_is_camel_case_when_camel() {
    let mock_string: String = "dataMapperIsAReallyReallyLongString".to_string();
    let asserted_bool: bool = is_camel_case(mock_string);
    println!("{}",asserted_bool);
    assert!(asserted_bool == true);
}

#[test] #[allow(non_snake_case)]
fn returns_falsey_value_for_is_camel_case_when_snake() {
    let mock_string: String = "data_mapper_string_that_is_really_really_long".to_string();
    let asserted_bool: bool = is_camel_case(mock_string);
    assert!(asserted_bool == false);
}

#[test] #[allow(non_snake_case)]
fn returns_truthy_value_for_is_kebab_case_when_kebab() {
    let mock_string: String = "data-mapper-string-that-is-really-really-long".to_string();
    let asserted_bool: bool = is_kebab_case(mock_string);
    println!("{}",asserted_bool);
    assert!(asserted_bool == true);
}

#[test] #[allow(non_snake_case)]
fn returns_falsey_value_for_is_kebab_case_when_class() {
    let mock_string: String = "DataMapperIsAReallyReallyLongString".to_string();
    let asserted_bool: bool = is_kebab_case(mock_string);
    println!("{}",asserted_bool);
    assert!(asserted_bool == false);
}

#[test] #[allow(non_snake_case)]
fn returns_falsey_value_for_is_kebab_case_when_camel() {
    let mock_string: String = "dataMapperIsAReallyReallyLongString".to_string();
    let asserted_bool: bool = is_kebab_case(mock_string);
    println!("{}",asserted_bool);
    assert!(asserted_bool == false);
}

#[test] #[allow(non_snake_case)]
fn returns_falsey_value_for_is_kebab_case_when_snake() {
    let mock_string: String = "data_mapper_string_that_is_really_really_long".to_string();
    let asserted_bool: bool = is_kebab_case(mock_string);
    assert!(asserted_bool == false);
}

#[test] #[allow(non_snake_case)]
fn returns_falsey_value_for_is_snake_case_when_kebab() {
    let mock_string: String = "data-mapper-string-that-is-really-really-long".to_string();
    let asserted_bool: bool = is_snake_case(mock_string);
    println!("{}",asserted_bool);
    assert!(asserted_bool == false);
}

#[test] #[allow(non_snake_case)]
fn returns_falsey_value_for_is_snake_case_when_class() {
    let mock_string: String = "DataMapperIsAReallyReallyLongString".to_string();
    let asserted_bool: bool = is_snake_case(mock_string);
    println!("{}",asserted_bool);
    assert!(asserted_bool == false);
}

#[test] #[allow(non_snake_case)]
fn returns_falsey_value_for_is_snake_case_when_camel() {
    let mock_string: String = "dataMapperIsAReallyReallyLongString".to_string();
    let asserted_bool: bool = is_snake_case(mock_string);
    println!("{}",asserted_bool);
    assert!(asserted_bool == false);
}

#[test] #[allow(non_snake_case)]
fn returns_truthy_value_for_is_snake_case_when_snake() {
    let mock_string: String = "data_mapper_string_that_is_really_really_long".to_string();
    let asserted_bool: bool = is_snake_case(mock_string);
    assert!(asserted_bool == true);
}

#[test] #[allow(non_snake_case)]
fn ClassCase_data_mapper_as_DataMapper() {
    let mock_string: String = "data_mapper".to_string();
    let expected_string: String = "DataMapper".to_string();
    let asserted_string: String = to_class_case(mock_string);
    assert!(asserted_string == expected_string);
}

#[test] #[allow(non_snake_case)]
fn camelize_data_mapper_as_dataMapper() {
    let mock_string: String = "data_mapper".to_string();
    let expected_string: String = "dataMapper".to_string();
    let asserted_string: String = to_camel_case(mock_string);
    assert!(asserted_string == expected_string);
}

#[test] #[allow(non_snake_case)]
fn snake_case_dataMapper_as_data_mapper() {
    let mock_string: String = "DataMapper".to_string();
    let expected_string: String = "data_mapper".to_string();
    let asserted_string: String = to_snake_case(mock_string);
    assert!(asserted_string == expected_string);
}

#[test] #[allow(non_snake_case)]
fn kebab_case_data_mapper_as_data_mapper() {
    let mock_string: String = "data_mapper".to_string();
    let expected_string: String = "data-mapper".to_string();
    let asserted_string: String = to_kebab_case(mock_string);
    assert!(asserted_string == expected_string);
}

#[test] #[allow(non_snake_case)]
fn kebab_case_dataMapper_as_data_mapper() {
    let mock_string: String = "dataMapper".to_string();
    let expected_string: String = "data-mapper".to_string();
    let asserted_string: String = to_kebab_case(mock_string);
    assert!(asserted_string == expected_string);
}
<|endoftext|>


<commit_before>extern crate mio;

use std::net::SocketAddr;
use std::net::Shutdown;

use mio::buf::RingBuf;
use mio::tcp::{TcpListener, TcpStream};
use mio::{EventLoop, Buf,  Handler, Token, ReadHint, NonBlock, IntoNonBlock,
    Interest, PollOpt, TryWrite, TryRead};

pub struct Transport {
    sock_client : NonBlock<TcpStream>,
    rx_buf      : RingBuf,
    sock_server : NonBlock<TcpStream>,
    tx_buf      : RingBuf,
}

pub struct RoxyServer {
    src     : SocketAddr,
    dst     : SocketAddr,
    srv     : Option<NonBlock<TcpListener>>,
    trans   : Option<Transport>,
}

impl RoxyServer {
    pub fn new(src: SocketAddr, dst: SocketAddr) ->  RoxyServer {
        RoxyServer {
            src: src,
            dst: dst,
            srv: None,
            trans: None,
        }
    }

    pub fn run(&mut self) {
        let mut el = EventLoop::new().unwrap();
        let srv = TcpListener::bind(&self.src).unwrap();

        el.register(&srv, Token(0)).unwrap();
        self.srv = Some(srv.into_non_block().unwrap());

        el.run(self).unwrap();

    }
}

const PROXY: Token = Token(0);
const CLIENT: Token = Token(1);
const SERVER: Token = Token(2);

#[inline]
fn readable () -> Interest {
    Interest::readable() | Interest::hinted() | Interest::hup()
}

impl Handler for RoxyServer {
    type Timeout = u64;
    type Message = u32;

    fn readable(&mut self, el: &mut EventLoop<Self>, token: Token, hint: ReadHint) {
        println!("{:?} is readable", token);
        match token {
            PROXY => {
                let sock_client = self.srv.as_mut().unwrap().accept().unwrap().unwrap();
                let sock_server = TcpStream::connect(self.dst).unwrap().into_non_block().unwrap();

                el.register_opt(&sock_client, CLIENT, readable(), PollOpt::level()).unwrap();
                el.register_opt(&sock_server, SERVER, readable(), PollOpt::level()).unwrap();

                self.trans = Some(Transport {
                    sock_client : sock_client,
                    rx_buf : RingBuf::new(4096),
                    sock_server : sock_server,
                    tx_buf : RingBuf::new(4096),

                });
            },

            CLIENT => {
                let trans = self.trans.as_mut().unwrap();
                let sock_client = &mut trans.sock_client;
                let sock_server = &mut trans.sock_server;
                let rx_buf = &mut trans.rx_buf;

                sock_client.read(rx_buf).unwrap();

                if Buf::has_remaining(rx_buf) {
                    el.reregister(sock_server, SERVER, Interest::all(), PollOpt::level()).unwrap();
                }

                if hint.is_hup() {
                    sock_client.shutdown(Shutdown::Both).ok().expect("fail to close client socket");
                    sock_server.shutdown(Shutdown::Both).unwrap();
                    el.deregister(sock_client).unwrap();
                    el.deregister(sock_server).unwrap();
                    println!("client close");
                }
            },

            SERVER => {
                let trans = self.trans.as_mut().unwrap();
                let sock_client = &mut trans.sock_client;
                let sock_server = &mut trans.sock_server;
                let tx_buf = &mut trans.tx_buf;
                sock_server.read(tx_buf).unwrap();

                if Buf::has_remaining(tx_buf) {
                    el.reregister(sock_client, CLIENT, Interest::all(), PollOpt::level()).unwrap();
                }

                if hint.is_hup() {
                    sock_client.shutdown(Shutdown::Both).unwrap();
                    sock_server.shutdown(Shutdown::Both).unwrap();
                    el.deregister(sock_client).unwrap();
                    el.deregister(sock_server).unwrap();
                    println!("server close");
                }
            },
            _ => panic!("oops"),
        }
    }

    fn writable(&mut self, el: &mut EventLoop<Self>, token: Token) {
        println!("{:?} is writable", token);
        match token {
            CLIENT => {
                let trans = self.trans.as_mut().unwrap();
                let sock_client = &mut trans.sock_client;
                let tx_buf = &mut trans.tx_buf;
                sock_client.write(tx_buf).unwrap();

                if !Buf::has_remaining(tx_buf) {
                    el.reregister(sock_client, CLIENT, readable(), PollOpt::level()).unwrap();
                }
            },
            SERVER => {
                let trans = self.trans.as_mut().unwrap();
                let sock_server = &mut trans.sock_server;
                let rx_buf = &mut trans.rx_buf;
                sock_server.write(rx_buf).unwrap();

                if !Buf::has_remaining(rx_buf) {
                    el.reregister(sock_server, SERVER, readable(), PollOpt::level()).unwrap();
                }
            },
            _ => panic!("oops"),
        };
    }
}
<commit_msg>support multi client connections to single server<commit_after>extern crate mio;

use std::net::SocketAddr;
use std::net::Shutdown;
use std::rc::Rc;
use std::cell::RefCell;

use mio::buf::RingBuf;
use mio::util::Slab;
use mio::tcp::{TcpListener, TcpStream};
use mio::{EventLoop, Buf,  Handler, Token, ReadHint, NonBlock, IntoNonBlock,
    Interest, PollOpt, TryWrite, TryRead};

struct Transport {
    client_sock  : NonBlock<TcpStream>,
    client_token : Token,
    server_sock  : NonBlock<TcpStream>,
    server_token : Token,
    rx_buf       : RingBuf,
    tx_buf       : RingBuf,
}

impl Transport {
    fn new(client_sock: NonBlock<TcpStream>, server_sock: NonBlock<TcpStream>) -> Transport {
        Transport{
            client_sock : client_sock,
            client_token : Token(0),
            server_sock : server_sock,
            server_token: Token(0),
            rx_buf : RingBuf::new(4096),
            tx_buf :RingBuf::new(4096),
        }
    }

    fn client_readable(&mut self, el: &mut EventLoop<RoxyServer>, hint: ReadHint) {
        println!("client {:?} is readable", self.client_token);
        self.client_sock.read(&mut self.rx_buf).unwrap();

        if Buf::has_remaining(&self.rx_buf) {
            el.reregister(&self.server_sock, self.server_token, Interest::all(), PollOpt::level()).unwrap();
        }

        if hint.is_hup() {
            self.client_sock.shutdown(Shutdown::Both);
            self.server_sock.shutdown(Shutdown::Both);
            el.deregister(&self.client_sock).unwrap();
            el.deregister(&self.server_sock).unwrap();
        }
    }

    fn client_writable(&mut self, el: &mut EventLoop<RoxyServer>) {
        println!("client {:?} is writable", self.client_token);
        self.client_sock.write(&mut self.tx_buf).unwrap();

        if !Buf::has_remaining(&self.tx_buf) {
            el.reregister(&self.client_sock, self.client_token, readable(), PollOpt::level()).unwrap();
        }
    }

    fn server_readable(&mut self, el: &mut EventLoop<RoxyServer>, hint: ReadHint) {
        println!("server {:?} is readable", self.server_token);
        self.server_sock.read(&mut self.tx_buf).unwrap();

        if Buf::has_remaining(&self.tx_buf) {
            el.reregister(&self.client_sock, self.client_token, Interest::all(), PollOpt::level()).unwrap();
        }

        if hint.is_hup() {
            self.client_sock.shutdown(Shutdown::Both).unwrap();
            self.server_sock.shutdown(Shutdown::Both).unwrap();
            el.deregister(&self.client_sock).unwrap();
            el.deregister(&self.server_sock).unwrap();
        }
    }

    fn server_writable(&mut self, el: &mut EventLoop<RoxyServer>) {
        println!("server {:?} is writable", self.server_token);
        self.server_sock.read(&mut self.tx_buf).unwrap();
        self.server_sock.write(&mut self.rx_buf).unwrap();

        if !Buf::has_remaining(&self.rx_buf) {
            el.reregister(&self.server_sock, self.server_token, readable(), PollOpt::level()).unwrap();
        }
    }

    fn readable(&mut self, el: &mut EventLoop<RoxyServer>, token: Token, hint: ReadHint) {
        match token {
            c if token == self.client_token => self.client_readable(el, hint),
            d if token == self.server_token => self.server_readable(el, hint),
            _ => panic!("what's the hell?"),
        }
    }

    fn writable(&mut self, el: &mut EventLoop<RoxyServer>, token: Token) {
        match token {
            c if token == self.client_token => self.client_writable(el),
            d if token == self.server_token => self.server_writable(el),
            _ => panic!("what's the hell?"),
        }
    }
}

pub struct RoxyServer {
    src        : SocketAddr,
    dst        : SocketAddr,
    srv        : Option<NonBlock<TcpListener>>,
    trans      : Option<Transport>,
    transports : Slab<Rc<RefCell<Transport>>>,
}

impl RoxyServer {
    pub fn new(src: SocketAddr, dst: SocketAddr) ->  RoxyServer {
        RoxyServer {
            src: src,
            dst: dst,
            srv: None,
            trans: None,
            transports: Slab::new_starting_at(Token(1024), 4096),
        }
    }

    pub fn run(&mut self) {
        let mut el = EventLoop::new().unwrap();
        let srv = TcpListener::bind(&self.src).unwrap();

        el.register(&srv, Token(0)).unwrap();
        self.srv = Some(srv.into_non_block().unwrap());

        el.run(self).unwrap();

    }
}

const PROXY: Token = Token(0);
const CLIENT: Token = Token(1);
const SERVER: Token = Token(2);

#[inline]
fn readable () -> Interest {
    Interest::readable() | Interest::hinted() | Interest::hup()
}

impl Handler for RoxyServer {
    type Timeout = u64;
    type Message = u32;

    fn readable(&mut self, el: &mut EventLoop<Self>, token: Token, hint: ReadHint) {
        match token {
            PROXY => {
                let client_sock = self.srv.as_mut().unwrap().accept().unwrap().unwrap();
                let server_sock = TcpStream::connect(self.dst).unwrap().into_non_block().unwrap();

                let ref_trans = Rc::new(RefCell::new(Transport::new(client_sock, server_sock)));
                let client_token = self.transports.insert(ref_trans.clone()).ok().expect("slab is full");
                let server_token = self.transports.insert(ref_trans.clone()).ok().expect("slab is full");

                let mut trans = self.transports[client_token].borrow_mut();
                trans.client_token = client_token;
                trans.server_token = server_token;

                el.register_opt(&trans.client_sock, client_token, readable(), PollOpt::level()).unwrap();
                el.register_opt(&trans.server_sock, server_token, readable(), PollOpt::level()).unwrap();
            },

            _ => {
                let mut trans = self.transports[token].borrow_mut();
                trans.readable(el, token, hint);
            }

        }
    }

    fn writable(&mut self, el: &mut EventLoop<Self>, token: Token) {
        let mut trans = self.transports[token].borrow_mut();
        trans.writable(el, token);
    }
}
<|endoftext|>


<commit_before>#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]

include!(concat!(env!("OUT_DIR"), "/bindings.rs"));

#[cfg(test)]
mod tests {
    use super::*;
    use std::mem;
    use std::ptr;
    use std::ffi::CString;
    use std::ffi::CStr;
    use std::net::{TcpListener, TcpStream};

    #[test]
    fn constants() {
        assert_eq!(
            CUPS_FORMAT_TEXT,
            CString::new("text/plain").unwrap().as_bytes_with_nul()
        );
        assert_eq!(
            CUPS_JOBID_CURRENT,
            0
        );
    }

    #[test]
    fn list_printers() {
        unsafe {
            let mut dests: *mut cups_dest_t = mem::zeroed();
            let num_dests = cupsGetDests(&mut dests as *mut _);
            std::slice::from_raw_parts(dests, num_dests as usize);
            cupsFreeDests(num_dests, dests);
        }
    }

    #[test]
    fn printer_info() {
        unsafe {
            let mut dests: *mut cups_dest_t = mem::zeroed();
            let num_dests = cupsGetDests(&mut dests as *mut _);
            let destinations = std::slice::from_raw_parts(dests, num_dests as usize);

            for destination in destinations {
                let c_printer_name = CStr::from_ptr((*destination).name);
                let printer_name = c_printer_name.to_string_lossy();

                let c_make_and_model = cupsGetOption(
                    CString::new("printer-make-and-model").unwrap().as_ptr(),
                    destination.num_options,
                    destination.options
                );
                let make_and_model = CStr::from_ptr(c_make_and_model).to_string_lossy();
                println!("{} ({})", printer_name, make_and_model);
                /*
                // This prints a real page.
                let job_id: i32 = cupsPrintFile(
                    destination.name,
                    CString::new("/System/Library/CoreServices/SystemVersion.plist")
                        .unwrap()
                        .as_ptr(),
                    CString::new("Test print")
                        .unwrap()
                        .as_ptr(),
                    destination.num_options,
                    destination.options
                );
                println!("{}", job_id);
                */

            }

            cupsFreeDests(num_dests, dests);
        }
    }
}
<commit_msg>Add default printer test<commit_after>#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]

include!(concat!(env!("OUT_DIR"), "/bindings.rs"));

#[cfg(test)]
mod tests {
    use super::*;
    use std::mem;
    use std::ptr;
    use std::ffi::CString;
    use std::ffi::CStr;

    #[test]
    fn constants() {
        assert_eq!(
            CUPS_FORMAT_TEXT,
            CString::new("text/plain").unwrap().as_bytes_with_nul()
        );
        assert_eq!(
            CUPS_JOBID_CURRENT,
            0
        );
    }

    #[test]
    fn list_printers() {
        unsafe {
            let mut dests: *mut cups_dest_t = mem::zeroed();
            let num_dests = cupsGetDests(&mut dests as *mut _);
            std::slice::from_raw_parts(dests, num_dests as usize);
            cupsFreeDests(num_dests, dests);
        }
    }

    #[test]
    fn default_printer() {
        unsafe {
            let mut dests: *mut cups_dest_t = mem::zeroed();
            let num_dests = cupsGetDests(&mut dests as *mut _);
            cupsGetDest(ptr::null(), ptr::null(), num_dests, dests);
            cupsFreeDests(num_dests, dests);
        }
    }

    #[test]
    fn printer_info() {
        unsafe {
            let mut dests: *mut cups_dest_t = mem::zeroed();
            let num_dests = cupsGetDests(&mut dests as *mut _);
            let destinations = std::slice::from_raw_parts(dests, num_dests as usize);

            for destination in destinations {
                let c_printer_name = CStr::from_ptr((*destination).name);
                let printer_name = c_printer_name.to_string_lossy();

                let c_make_and_model = cupsGetOption(
                    CString::new("printer-make-and-model").unwrap().as_ptr(),
                    destination.num_options,
                    destination.options
                );
                let make_and_model = CStr::from_ptr(c_make_and_model).to_string_lossy();
                println!("{} ({})", printer_name, make_and_model);
                /*
                // This prints a real page.
                let job_id: i32 = cupsPrintFile(
                    destination.name,
                    CString::new("/System/Library/CoreServices/SystemVersion.plist")
                        .unwrap()
                        .as_ptr(),
                    CString::new("Test print")
                        .unwrap()
                        .as_ptr(),
                    destination.num_options,
                    destination.options
                );
                println!("{}", job_id);
                */

            }
            cupsFreeDests(num_dests, dests);
        }
    }
}
<|endoftext|>