<commit_before>use std::f32::consts::PI;
use std::ops::{Add, Index, IndexMut, Mul, Sub};

pub const TAU: f32 = 2.0 * PI;

macro_rules! define_vec {
    ($name:ident, $size:expr) => (
        #[derive(Copy, Clone, Debug, PartialEq)]
        pub struct $name(pub [f32; $size]);

        impl $name {
            pub fn zero() -> Self {
                $name([0.0; $size])
            }

            /// Calculate the square of the length (or norm) of the vector. Slightly faster than
            /// `length`.
            pub fn length_squared(self) -> f32 {
                self.dot(self)
            }

            /// Calculate the the length (or norm) of the vector.
            pub fn length(self) -> f32 {
                self.length_squared().sqrt()
            }

            /// Normalize the vector so that it has the same orientation but a length of 1.
            pub fn normalize(&mut self) {
                let length = self.length();

                for i in 0..$size {
                    self[i] /= length;
                }
            }

            /// Calculate the vector dot product.
            pub fn dot(self, other: Self) -> f32 {
                let mut result = 0.0;

                for i in 0..$size {
                    result += self[i] * other[i];
                }

                result
            }
        }

        impl Index<usize> for $name {
            type Output = f32;

            fn index(&self, i: usize) -> &f32 {
                &self.0[i]
            }
        }

        impl IndexMut<usize> for $name {
            fn index_mut(&mut self, i: usize) -> &mut f32 {
                &mut self.0[i]
            }
        }

        impl Add for $name {
            type Output = Self;

            fn add(self, other: Self) -> Self {
                let mut result = $name::zero();

                for i in 0..$size {
                    result[i] = self[i] + other[i];
                }

                result
            }
        }

        impl Sub for $name {
            type Output = Self;

            fn sub(self, other: Self) -> Self {
                let mut result = $name::zero();

                for i in 0..$size {
                    result[i] = self[i] - other[i];
                }

                result
            }
        }
    );
}

define_vec!(Vec3, 3);
define_vec!(Vec4, 4);

impl Vec3 {
    /// Calculate the vector cross product.
    pub fn cross(self, other: Self) -> Self {
        Vec3([
            self[1] * other[2] - self[2] * other[1],
            self[2] * other[0] - self[0] * other[2],
            self[0] * other[1] - self[1] * other[0],
        ])
    }
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Mat4(pub [[f32; 4]; 4]);

impl Mat4 {
    /// The zero matrix.
    pub fn zero() -> Self {
        Mat4([
            [0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0],
        ])
    }

    /// The identity matrix.
    pub fn identity() -> Self {
        Mat4([
            [1.0, 0.0, 0.0, 0.0],
            [0.0, 1.0, 0.0, 0.0],
            [0.0, 0.0, 1.0, 0.0],
            [0.0, 0.0, 0.0, 1.0],
        ])
    }

    /// Build a matrix representing a scaling by the given factors.
    pub fn scale(x: f32, y: f32, z: f32) -> Self {
        Mat4([
            [x,   0.0, 0.0, 0.0],
            [0.0, y,   0.0, 0.0],
            [0.0, 0.0, z,   0.0],
            [0.0, 0.0, 0.0, 1.0],
        ])
    }

    /// Build a matrix representing a translation.
    pub fn translate(x: f32, y: f32, z: f32) -> Self {
        Mat4([
            [1.0, 0.0, 0.0, x  ],
            [0.0, 1.0, 0.0, y  ],
            [0.0, 0.0, 1.0, z  ],
            [0.0, 0.0, 0.0, 1.0],
        ])
    }

    /// Build a matrix representing a rotation around the X-axis by the given angle (in radians).
    pub fn rotate_x(angle: f32) -> Self {
        let cos = angle.cos();
        let sin = angle.sin();

        Mat4([
            [1.0, 0.0,  0.0, 0.0],
            [0.0, cos, -sin, 0.0],
            [0.0, sin,  cos, 0.0],
            [0.0, 0.0,  0.0, 1.0],
        ])
    }

    /// Build a matrix representing a rotation around the Y-axis by the given angle (in radians).
    pub fn rotate_y(angle: f32) -> Self {
        let cos = angle.cos();
        let sin = angle.sin();

        Mat4([
            [ cos, 0.0, sin, 0.0],
            [ 0.0, 1.0, 0.0, 0.0],
            [-sin, 0.0, cos, 0.0],
            [ 0.0, 0.0, 0.0, 1.0],
        ])
    }

    /// Build a matrix representing a rotation around the Z-axis by the given angle (in radians).
    pub fn rotate_z(angle: f32) -> Self {
        let cos = angle.cos();
        let sin = angle.sin();

        Mat4([
            [cos, -sin, 0.0, 0.0],
            [sin,  cos, 0.0, 0.0],
            [0.0,  0.0, 1.0, 0.0],
            [0.0,  0.0, 0.0, 1.0],
        ])
    }

    /// Build a camera view matrix with the camera at `eye` looking toward `center` with `up` as
    /// the vertical direction.
    pub fn look_at(eye: Vec3, center: Vec3, up: Vec3) -> Self {
        // Set the Z-axis to the unit vector pointing from the center toward the eye (the depth
        // axis).
        let mut z = eye - center;
        z.normalize();

        // Make the Y-axis the vertical direction.
        let mut y = up;

        // Make the X-axis perpendicular to Y and Z, pointing to the right.
        let mut x = y.cross(z);

        // Make the Y-axis perpendicular to Z and X.
        y = z.cross(x);

        // Normalize the axes to unit vectors.
        x.normalize();
        y.normalize();

        // Build the rotation/translation matrix that transforms coordinates to the new coordinate
        // system.
        Mat4([
            [ x[0],        y[0],        z[0],       0.0],
            [ x[1],        y[1],        z[1],       0.0],
            [ x[2],        y[2],        z[2],       0.0],
            [-x.dot(eye), -y.dot(eye), -z.dot(eye), 1.0],
        ])
    }

    /// Build a perspective projection matrix with the given vertical field of view (in radians),
    /// aspect ratio, and Z-axis clipping distances.
    pub fn perspective(fov_y: f32, aspect: f32, z_near: f32, z_far: f32) -> Self {
        assert!(aspect != 0.0);
        assert!(z_near != z_far);

        let f = 1.0 / (fov_y / 2.0).tan();
        let z_diff = z_near - z_far;

        let mut result = Mat4::zero();
        result[0][0] = f / aspect;
        result[1][1] = f;
        result[2][2] = (z_near + z_far) / z_diff;
        result[2][3] = -1.0;
        result[3][2] = (2.0 * z_near * z_far) / z_diff;
        result
    }
}

impl Index<usize> for Mat4 {
    type Output = [f32; 4];

    fn index(&self, i: usize) -> &[f32; 4] {
        &self.0[i]
    }
}

impl IndexMut<usize> for Mat4 {
    fn index_mut(&mut self, i: usize) -> &mut [f32; 4] {
        &mut self.0[i]
    }
}

impl Mul<Mat4> for Mat4 {
    type Output = Mat4;

    fn mul(self, other: Mat4) -> Mat4 {
        let mut result = Mat4::zero();

        for i in 0..4 {
            for j in 0..4 {
                for k in 0..4 {
                    result[i][j] += self[i][k] * other[k][j];
                }
            }
        }

        result
    }
}

impl Mul<Vec4> for Mat4 {
    type Output = Vec4;

    fn mul(self, vec: Vec4) -> Vec4 {
        let mut result = Vec4::zero();

        for i in 0..4 {
            for j in 0..4 {
                result[i] += self[i][j] * vec[j];
            }
        }

        result
    }
}

#[test]
fn test_math() {
    let scale = Mat4::scale(2.0, 2.0, 2.0);
    let trans = Mat4::translate(1.0, 2.0, 3.0);
    let combined = trans * scale;

    let original = Vec4([3.0, 3.0, 3.0, 1.0]);
    let expected = Vec4([7.0, 8.0, 9.0, 1.0]);

    assert_eq!(expected, combined * original);
}
<commit_msg>Switch to column-major matrices to match OpenGL.<commit_after>use std::f32::consts::PI;
use std::ops::{Add, Index, IndexMut, Mul, Sub};

pub const TAU: f32 = 2.0 * PI;

macro_rules! define_vec {
    ($name:ident, $size:expr) => (
        /// A column vector.
        #[derive(Copy, Clone, Debug, PartialEq)]
        pub struct $name(pub [f32; $size]);

        impl $name {
            /// Create a vector with all fields set to zero.
            pub fn zero() -> Self {
                $name([0.0; $size])
            }

            /// Calculate the square of the length (or norm) of the vector. Slightly faster than
            /// `length`.
            pub fn length_squared(self) -> f32 {
                self.dot(self)
            }

            /// Calculate the the length (or norm) of the vector.
            pub fn length(self) -> f32 {
                self.length_squared().sqrt()
            }

            /// Normalize the vector so that it has the same orientation but a length of 1.
            pub fn normalize(&mut self) {
                let length = self.length();

                for i in 0..$size {
                    self[i] /= length;
                }
            }

            /// Calculate the vector dot product.
            pub fn dot(self, other: Self) -> f32 {
                let mut result = 0.0;

                for i in 0..$size {
                    result += self[i] * other[i];
                }

                result
            }
        }

        impl Index<usize> for $name {
            type Output = f32;

            fn index(&self, i: usize) -> &f32 {
                &self.0[i]
            }
        }

        impl IndexMut<usize> for $name {
            fn index_mut(&mut self, i: usize) -> &mut f32 {
                &mut self.0[i]
            }
        }

        impl Add for $name {
            type Output = Self;

            fn add(self, other: Self) -> Self {
                let mut result = $name::zero();

                for i in 0..$size {
                    result[i] = self[i] + other[i];
                }

                result
            }
        }

        impl Sub for $name {
            type Output = Self;

            fn sub(self, other: Self) -> Self {
                let mut result = $name::zero();

                for i in 0..$size {
                    result[i] = self[i] - other[i];
                }

                result
            }
        }
    );
}

define_vec!(Vec3, 3);
define_vec!(Vec4, 4);

impl Vec3 {
    /// Calculate the vector cross product.
    pub fn cross(self, other: Self) -> Self {
        Vec3([
            self[1] * other[2] - self[2] * other[1],
            self[2] * other[0] - self[0] * other[2],
            self[0] * other[1] - self[1] * other[0],
        ])
    }
}

/// A matrix stored in column-major order.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Mat4(pub [[f32; 4]; 4]);

impl Mat4 {
    /// The zero matrix.
    pub fn zero() -> Self {
        Mat4([
            [0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0],
        ])
    }

    /// The identity matrix.
    pub fn identity() -> Self {
        Mat4([
            [1.0, 0.0, 0.0, 0.0],
            [0.0, 1.0, 0.0, 0.0],
            [0.0, 0.0, 1.0, 0.0],
            [0.0, 0.0, 0.0, 1.0],
        ])
    }

    /// Build a matrix representing a scaling by the given factors.
    pub fn scale(x: f32, y: f32, z: f32) -> Self {
        Mat4([
            [x,   0.0, 0.0, 0.0],
            [0.0, y,   0.0, 0.0],
            [0.0, 0.0, z,   0.0],
            [0.0, 0.0, 0.0, 1.0],
        ])
    }

    /// Build a matrix representing a translation.
    pub fn translate(x: f32, y: f32, z: f32) -> Self {
        Mat4([
            [1.0, 0.0, 0.0, 0.0],
            [0.0, 1.0, 0.0, 0.0],
            [0.0, 0.0, 1.0, 0.0],
            [x,   y,   z,   1.0],
        ])
    }

    /// Build a matrix representing a rotation around the X-axis by the given angle (in radians).
    pub fn rotate_x(angle: f32) -> Self {
        let cos = angle.cos();
        let sin = angle.sin();

        Mat4([
            [1.0, 0.0,  0.0, 0.0],
            [0.0, cos, -sin, 0.0],
            [0.0, sin,  cos, 0.0],
            [0.0, 0.0,  0.0, 1.0],
        ])
    }

    /// Build a matrix representing a rotation around the Y-axis by the given angle (in radians).
    pub fn rotate_y(angle: f32) -> Self {
        let cos = angle.cos();
        let sin = angle.sin();

        Mat4([
            [ cos, 0.0, sin, 0.0],
            [ 0.0, 1.0, 0.0, 0.0],
            [-sin, 0.0, cos, 0.0],
            [ 0.0, 0.0, 0.0, 1.0],
        ])
    }

    /// Build a matrix representing a rotation around the Z-axis by the given angle (in radians).
    pub fn rotate_z(angle: f32) -> Self {
        let cos = angle.cos();
        let sin = angle.sin();

        Mat4([
            [cos, -sin, 0.0, 0.0],
            [sin,  cos, 0.0, 0.0],
            [0.0,  0.0, 1.0, 0.0],
            [0.0,  0.0, 0.0, 1.0],
        ])
    }

    /// Build a camera view matrix with the camera at `eye` looking toward `center` with `up` as
    /// the vertical direction.
    pub fn look_at(eye: Vec3, center: Vec3, up: Vec3) -> Self {
        // Set the Z-axis to the unit vector pointing from the center toward the eye (the depth
        // axis).
        let mut z = eye - center;
        z.normalize();

        // Make the Y-axis the vertical direction.
        let mut y = up;

        // Make the X-axis perpendicular to Y and Z, pointing to the right.
        let mut x = y.cross(z);

        // Make the Y-axis perpendicular to Z and X.
        y = z.cross(x);

        // Normalize the axes to unit vectors.
        x.normalize();
        y.normalize();

        // Build the rotation/translation matrix that transforms coordinates to the new coordinate
        // system.
        Mat4([
            [ x[0],        y[0],        z[0],       0.0],
            [ x[1],        y[1],        z[1],       0.0],
            [ x[2],        y[2],        z[2],       0.0],
            [-x.dot(eye), -y.dot(eye), -z.dot(eye), 1.0],
        ])
    }

    /// Build a perspective projection matrix with the given vertical field of view (in radians),
    /// aspect ratio, and Z-axis clipping distances.
    pub fn perspective(fov_y: f32, aspect: f32, z_near: f32, z_far: f32) -> Self {
        assert!(aspect != 0.0);
        assert!(z_near != z_far);

        let f = 1.0 / (fov_y / 2.0).tan();
        let z_diff = z_near - z_far;

        let mut result = Mat4::zero();
        result[0][0] = f / aspect;
        result[1][1] = f;
        result[2][2] = (z_near + z_far) / z_diff;
        result[2][3] = -1.0;
        result[3][2] = (2.0 * z_near * z_far) / z_diff;
        result
    }
}

impl Index<usize> for Mat4 {
    type Output = [f32; 4];

    fn index(&self, i: usize) -> &[f32; 4] {
        &self.0[i]
    }
}

impl IndexMut<usize> for Mat4 {
    fn index_mut(&mut self, i: usize) -> &mut [f32; 4] {
        &mut self.0[i]
    }
}

impl Mul<Mat4> for Mat4 {
    type Output = Mat4;

    fn mul(self, other: Mat4) -> Mat4 {
        let mut result = Mat4::zero();

        for col in 0..4 {
            for row in 0..4 {
                for i in 0..4 {
                    result[col][row] += self[i][row] * other[col][i];
                }
            }
        }

        result
    }
}

impl Mul<Vec4> for Mat4 {
    type Output = Vec4;

    fn mul(self, vec: Vec4) -> Vec4 {
        let mut result = Vec4::zero();

        for col in 0..4 {
            for row in 0..4 {
                result[row] += self[col][row] * vec[col];
            }
        }

        result
    }
}

#[test]
fn test_math() {
    let scale = Mat4::scale(2.0, 2.0, 2.0);
    let trans = Mat4::translate(1.0, 2.0, 3.0);
    let combined = trans * scale;

    let original = Vec4([3.0, 3.0, 3.0, 1.0]);
    let expected = Vec4([7.0, 8.0, 9.0, 1.0]);

    assert_eq!(expected, combined * original);
}
<|endoftext|>


<commit_before>use std::str;

use std::ffi::CStr;

/// A structure that describes the version of the underlying `libusb` library.
#[derive(Debug)]
pub struct LibraryVersion {
    major: u16,
    minor: u16,
    micro: u16,
    nano: u16,
    rc: &'static str,
    describe: &'static str
}

impl LibraryVersion {
    /// Library major version.
    pub fn major(&self) -> u16 {
        self.major
    }

    /// Library minor version.
    pub fn minor(&self) -> u16 {
        self.minor
    }

    /// Library micro version.
    pub fn micro(&self) -> u16 {
        self.micro
    }

    /// Library nano version.
    pub fn nano(&self) -> u16 {
        self.nano
    }

    /// Library release candidate suffix string, e.g., `"-rc4"`.
    pub fn rc(&self) -> Option<&'static str> {
        if self.rc.len() > 0 {
            Some(self.rc)
        }
        else {
            None
        }
    }
}

/// Returns a structure with the version of the running libusb library.
pub fn version() -> LibraryVersion {
    let v = unsafe { ::libusb::libusb_get_version() };

    LibraryVersion {
        major: v.major,
        minor: v.minor,
        micro: v.micro,
        nano: v.nano,
        rc: str::from_utf8(unsafe { CStr::from_ptr(v.rc) }.to_bytes()).unwrap_or(""),
        describe: str::from_utf8(unsafe { CStr::from_ptr(v.describe) }.to_bytes()).unwrap_or("")
    }
}
<commit_msg>avoids extra memory copies when instantiating LibraryVersion<commit_after>use std::ffi::CStr;
use std::fmt;
use std::mem;
use std::str;

/// A structure that describes the version of the underlying `libusb` library.
pub struct LibraryVersion {
    inner: &'static ::libusb::libusb_version,
}

impl LibraryVersion {
    /// Library major version.
    pub fn major(&self) -> u16 {
        self.inner.major
    }

    /// Library minor version.
    pub fn minor(&self) -> u16 {
        self.inner.minor
    }

    /// Library micro version.
    pub fn micro(&self) -> u16 {
        self.inner.micro
    }

    /// Library nano version.
    pub fn nano(&self) -> u16 {
        self.inner.nano
    }

    /// Library release candidate suffix string, e.g., `"-rc4"`.
    pub fn rc(&self) -> Option<&'static str> {
        let cstr = unsafe { CStr::from_ptr(self.inner.rc) };

        match str::from_utf8(cstr.to_bytes()) {
            Ok(s) => {
                if s.len() > 0 {
                    Some(s)
                }
                else {
                    None
                }
            },
            Err(_) => {
                None
            }
        }
    }
}

impl fmt::Debug for LibraryVersion {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        let mut debug = fmt.debug_struct("LibraryVersion");

        debug.field("major", &self.major());
        debug.field("minor", &self.minor());
        debug.field("micro", &self.micro());
        debug.field("nano", &self.nano());
        debug.field("rc", &self.rc());

        debug.finish()
    }
}

/// Returns a structure with the version of the running libusb library.
pub fn version() -> LibraryVersion {
    let version: &'static ::libusb::libusb_version = unsafe {
        mem::transmute(::libusb::libusb_get_version())
    };

    LibraryVersion { inner: version }
}
<|endoftext|>


<commit_before>use {Event, Hints};
use libc;
use std::{mem, ptr};
use std::sync::atomics::AtomicBool;

mod ffi;

pub struct Window {
    display: *mut ffi::Display,
    window: ffi::Window,
    context: ffi::GLXContext,
    should_close: AtomicBool,
    wm_delete_window: ffi::Atom,
}

impl Window {
    pub fn new(dimensions: Option<(uint, uint)>, title: &str, hints: &Hints)
        -> Result<Window, String>
    {
        // calling XOpenDisplay
        let display = unsafe {
            let display = ffi::XOpenDisplay(ptr::null());
            if display.is_null() {
                return Err(format!("XOpenDisplay failed"));
            }
            display
        };

        // TODO: set error handler

        static VISUAL_ATTRIBUTES: [libc::c_int, ..5] = [
            ffi::GLX_RGBA,
            ffi::GLX_DEPTH_SIZE,
            24,
            ffi::GLX_DOUBLEBUFFER,
            0
        ];

        // getting the visual infos
        let visual_infos = unsafe {
            let vi = ffi::glXChooseVisual(display, 0, VISUAL_ATTRIBUTES.as_ptr());
            if vi.is_null() {
                return Err(format!("glXChooseVisual failed"));
            }
            vi
        };

        // getting the root window
        let root = unsafe { ffi::XDefaultRootWindow(display) };

        // creating the color map
        let cmap = unsafe {
            let cmap = ffi::XCreateColormap(display, root,
                (*visual_infos).visual, ffi::AllocNone);
            // TODO: error checking?
            cmap
        };

        // creating
        let mut set_win_attr = {
            let mut swa: ffi::XSetWindowAttributes = unsafe { mem::zeroed() };
            swa.colormap = cmap;
            swa.event_mask = ffi::ExposureMask | ffi::ResizeRedirectMask |
                ffi::VisibilityChangeMask | ffi::KeyPressMask;
            swa
        };

        // finally creating the window
        let window = unsafe {
            let win = ffi::XCreateWindow(display, root, 10, 10, 800, 600,
                0, (*visual_infos).depth, ffi::InputOutput, (*visual_infos).visual,
                ffi::CWColormap | ffi::CWEventMask, &mut set_win_attr);
            win
        };

        // creating window, step 2
        let wm_delete_window = unsafe {
            use std::c_str::ToCStr;

            ffi::XMapWindow(display, window);
            let mut wm_delete_window = ffi::XInternAtom(display,
                "WM_DELETE_WINDOW".to_c_str().as_ptr() as *const libc::c_char, 0);
            ffi::XSetWMProtocols(display, window, &mut wm_delete_window, 1);
            ffi::XStoreName(display, window, mem::transmute(title.as_slice().as_ptr()));
            ffi::XFlush(display);

            wm_delete_window
        };

        // creating GL context
        let context = unsafe {
            ffi::glXCreateContext(display, visual_infos, ptr::null(), 1)
        };

        // returning
        Ok(Window{
            display: display,
            window: window,
            context: context,
            should_close: AtomicBool::new(false),
            wm_delete_window: wm_delete_window,
        })
    }

    pub fn should_close(&self) -> bool {
        use std::sync::atomics::Relaxed;
        self.should_close.load(Relaxed)
    }

    pub fn set_title(&self, title: &str) {
        unsafe {
            ffi::XStoreName(self.display, self.window,
                mem::transmute(title.as_slice().as_ptr()));
        }
    }

    pub fn get_position(&self) -> (uint, uint) {
        unimplemented!()
    }

    pub fn set_position(&self, x: uint, y: uint) {
        unimplemented!()
    }

    pub fn get_size(&self) -> (uint, uint) {
        unimplemented!()
    }

    pub fn set_size(&self, x: uint, y: uint) {
        unimplemented!()
    }

    pub fn poll_events(&self) -> Vec<Event> {
        unimplemented!()
    }

    pub fn wait_events(&self) -> Vec<Event> {
        use std::mem;

        let mut xev = unsafe { mem::uninitialized() };
        unsafe { ffi::XNextEvent(self.display, &mut xev) };

        let mut events = Vec::new();

        match xev.type_ {
            ffi::ClientMessage => {
                use Closed;
                use std::sync::atomics::Relaxed;

                let client_msg: &ffi::XClientMessageEvent = unsafe { mem::transmute(&xev) };

                if client_msg.l[0] == self.wm_delete_window as libc::c_long {
                    self.should_close.store(true, Relaxed);
                    events.push(Closed);
                }
            },

            _ => ()
        }

        events
    }

    pub fn make_current(&self) {
        let res = unsafe { ffi::glXMakeCurrent(self.display, self.window, self.context) };
        if res == 0 {
            fail!("glXMakeCurrent failed");
        }
    }

    pub fn get_proc_address(&self, addr: &str) -> *const () {
        use std::c_str::ToCStr;
        use std::mem;

        unsafe {
            addr.with_c_str(|s| {
                let p = ffi::glXGetProcAddress(mem::transmute(s)) as *const ();
                if !p.is_null() { return p; }
                println!("{}", p);
                p
            })
        }
    }

    pub fn swap_buffers(&self) {
        unsafe { ffi::glXSwapBuffers(self.display, self.window) }
    }
}

impl Drop for Window {
    fn drop(&mut self) {
        unsafe { ffi::glXDestroyContext(self.display, self.context) }
        unsafe { ffi::XDestroyWindow(self.display, self.window) }
        unsafe { ffi::XCloseDisplay(self.display) }
    }
}

/*
  printf( "Getting matching framebuffer configs\n" );
  int fbcount;
  GLXFBConfig* fbc = glXChooseFBConfig(display, DefaultScreen(display), visual_attribs, &fbcount);
  if (!fbc)
  {
    printf( "Failed to retrieve a framebuffer config\n" );
    exit(1);
  }
  printf( "Found %d matching FB configs.\n", fbcount );

  // Pick the FB config/visual with the most samples per pixel
  printf( "Getting XVisualInfos\n" );
  int best_fbc = -1, worst_fbc = -1, best_num_samp = -1, worst_num_samp = 999;

  int i;
  for (i=0; i<fbcount; ++i)
  {
    XVisualInfo *vi = glXGetVisualFromFBConfig( display, fbc[i] );
    if ( vi )
    {
      int samp_buf, samples;
      glXGetFBConfigAttrib( display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf );
      glXGetFBConfigAttrib( display, fbc[i], GLX_SAMPLES       , &samples  );

      printf( "  Matching fbconfig %d, visual ID 0x%2x: SAMPLE_BUFFERS = %d,"
              " SAMPLES = %d\n",
              i, vi -> visualid, samp_buf, samples );

      if ( best_fbc < 0 || samp_buf && samples > best_num_samp )
        best_fbc = i, best_num_samp = samples;
      if ( worst_fbc < 0 || !samp_buf || samples < worst_num_samp )
        worst_fbc = i, worst_num_samp = samples;
    }
    XFree( vi );
  }

  GLXFBConfig bestFbc = fbc[ best_fbc ];

  // Be sure to free the FBConfig list allocated by glXChooseFBConfig()
  XFree( fbc );

  // Get a visual
  XVisualInfo *vi = glXGetVisualFromFBConfig( display, bestFbc );
  printf( "Chosen visual ID = 0x%x\n", vi->visualid );

  printf( "Creating colormap\n" );
  XSetWindowAttributes swa;
  Colormap cmap;
  swa.colormap = cmap = XCreateColormap( display,
                                         RootWindow( display, vi->screen ),
                                         vi->visual, AllocNone );
  swa.background_pixmap = None ;
  swa.border_pixel      = 0;
  swa.event_mask        = StructureNotifyMask;

  printf( "Creating window\n" );
  Window win = XCreateWindow( display, RootWindow( display, vi->screen ),
                              0, 0, 100, 100, 0, vi->depth, InputOutput,
                              vi->visual,
                              CWBorderPixel|CWColormap|CWEventMask, &swa );
  if ( !win )
  {
    printf( "Failed to create window.\n" );
    exit(1);
  }

  // Done with the visual info data
  XFree( vi );

  XStoreName( display, win, "GL 3.0 Window" );

  printf( "Mapping window\n" );
  XMapWindow( display, win );

  // Get the default screen's GLX extension list
  const char *glxExts = glXQueryExtensionsString( display,
                                                  DefaultScreen( display ) );

  // NOTE: It is not necessary to create or make current to a context before
  // calling glXGetProcAddressARB
  glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0;
  glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)
           glXGetProcAddressARB( (const GLubyte *) "glXCreateContextAttribsARB" );

  GLXContext ctx = 0;

  // Install an X error handler so the application won't exit if GL 3.0
  // context allocation fails.
  //
  // Note this error handler is global.  All display connections in all threads
  // of a process use the same error handler, so be sure to guard against other
  // threads issuing X commands while this code is running.
  ctxErrorOccurred = false;
  int (*oldHandler)(Display*, XErrorEvent*) =
      XSetErrorHandler(&ctxErrorHandler);

  // Check for the GLX_ARB_create_context extension string and the function.
  // If either is not present, use GLX 1.3 context creation method.
  if ( !isExtensionSupported( glxExts, "GLX_ARB_create_context" ) ||
       !glXCreateContextAttribsARB )
  {
    printf( "glXCreateContextAttribsARB() not found"
            " ... using old-style GLX context\n" );
    ctx = glXCreateNewContext( display, bestFbc, GLX_RGBA_TYPE, 0, True );
  }

  // If it does, try to get a GL 3.0 context!
  else
  {
    int context_attribs[] =
      {
        GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
        GLX_CONTEXT_MINOR_VERSION_ARB, 0,
        //GLX_CONTEXT_FLAGS_ARB        , GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
        None
      };

    printf( "Creating context\n" );
    ctx = glXCreateContextAttribsARB( display, bestFbc, 0,
                                      True, context_attribs );

    // Sync to ensure any errors generated are processed.
    XSync( display, False );
    if ( !ctxErrorOccurred && ctx )
      printf( "Created GL 3.0 context\n" );
    else
    {
      // Couldn't create GL 3.0 context.  Fall back to old-style 2.x context.
      // When a context version below 3.0 is requested, implementations will
      // return the newest context version compatible with OpenGL versions less
      // than version 3.0.
      // GLX_CONTEXT_MAJOR_VERSION_ARB = 1
      context_attribs[1] = 1;
      // GLX_CONTEXT_MINOR_VERSION_ARB = 0
      context_attribs[3] = 0;

      ctxErrorOccurred = false;

      printf( "Failed to create GL 3.0 context"
              " ... using old-style GLX context\n" );
      ctx = glXCreateContextAttribsARB( display, bestFbc, 0,
                                        True, context_attribs );
    }
  }

  // Sync to ensure any errors generated are processed.
  XSync( display, False );

  // Restore the original error handler
  XSetErrorHandler( oldHandler );

  if ( ctxErrorOccurred || !ctx )
  {
    printf( "Failed to create an OpenGL context\n" );
    exit(1);
  }

  // Verifying that context is a direct context
  if ( ! glXIsDirect ( display, ctx ) )
  {
    printf( "Indirect GLX rendering context obtained\n" );
  }
  else
  {
    printf( "Direct GLX rendering context obtained\n" );
  }

  printf( "Making context current\n" );
  glXMakeCurrent( display, win, ctx );

  glClearColor( 0, 0.5, 1, 1 );
  glClear( GL_COLOR_BUFFER_BIT );
  glXSwapBuffers ( display, win );

*/<commit_msg>Now using dimensions<commit_after>use {Event, Hints};
use libc;
use std::{mem, ptr};
use std::sync::atomics::AtomicBool;

mod ffi;

pub struct Window {
    display: *mut ffi::Display,
    window: ffi::Window,
    context: ffi::GLXContext,
    should_close: AtomicBool,
    wm_delete_window: ffi::Atom,
}

impl Window {
    pub fn new(dimensions: Option<(uint, uint)>, title: &str, hints: &Hints)
        -> Result<Window, String>
    {
        // calling XOpenDisplay
        let display = unsafe {
            let display = ffi::XOpenDisplay(ptr::null());
            if display.is_null() {
                return Err(format!("XOpenDisplay failed"));
            }
            display
        };

        // TODO: set error handler

        static VISUAL_ATTRIBUTES: [libc::c_int, ..5] = [
            ffi::GLX_RGBA,
            ffi::GLX_DEPTH_SIZE,
            24,
            ffi::GLX_DOUBLEBUFFER,
            0
        ];

        // getting the visual infos
        let visual_infos = unsafe {
            let vi = ffi::glXChooseVisual(display, 0, VISUAL_ATTRIBUTES.as_ptr());
            if vi.is_null() {
                return Err(format!("glXChooseVisual failed"));
            }
            vi
        };

        // getting the root window
        let root = unsafe { ffi::XDefaultRootWindow(display) };

        // creating the color map
        let cmap = unsafe {
            let cmap = ffi::XCreateColormap(display, root,
                (*visual_infos).visual, ffi::AllocNone);
            // TODO: error checking?
            cmap
        };

        // creating
        let mut set_win_attr = {
            let mut swa: ffi::XSetWindowAttributes = unsafe { mem::zeroed() };
            swa.colormap = cmap;
            swa.event_mask = ffi::ExposureMask | ffi::ResizeRedirectMask |
                ffi::VisibilityChangeMask | ffi::KeyPressMask;
            swa
        };

        // finally creating the window
        let window = unsafe {
            let dimensions = dimensions.unwrap_or((800, 600));

            let win = ffi::XCreateWindow(display, root, 50, 50, dimensions.val0() as libc::c_uint,
                dimensions.val1() as libc::c_uint, 0, (*visual_infos).depth, ffi::InputOutput,
                (*visual_infos).visual, ffi::CWColormap | ffi::CWEventMask,
                &mut set_win_attr);
            win
        };

        // creating window, step 2
        let wm_delete_window = unsafe {
            use std::c_str::ToCStr;

            ffi::XMapWindow(display, window);
            let mut wm_delete_window = ffi::XInternAtom(display,
                "WM_DELETE_WINDOW".to_c_str().as_ptr() as *const libc::c_char, 0);
            ffi::XSetWMProtocols(display, window, &mut wm_delete_window, 1);
            ffi::XStoreName(display, window, mem::transmute(title.as_slice().as_ptr()));
            ffi::XFlush(display);

            wm_delete_window
        };

        // creating GL context
        let context = unsafe {
            ffi::glXCreateContext(display, visual_infos, ptr::null(), 1)
        };

        // returning
        Ok(Window{
            display: display,
            window: window,
            context: context,
            should_close: AtomicBool::new(false),
            wm_delete_window: wm_delete_window,
        })
    }

    pub fn should_close(&self) -> bool {
        use std::sync::atomics::Relaxed;
        self.should_close.load(Relaxed)
    }

    pub fn set_title(&self, title: &str) {
        unsafe {
            ffi::XStoreName(self.display, self.window,
                mem::transmute(title.as_slice().as_ptr()));
        }
    }

    pub fn get_position(&self) -> (uint, uint) {
        unimplemented!()
    }

    pub fn set_position(&self, x: uint, y: uint) {
        unimplemented!()
    }

    pub fn get_size(&self) -> (uint, uint) {
        unimplemented!()
    }

    pub fn set_size(&self, x: uint, y: uint) {
        unimplemented!()
    }

    pub fn poll_events(&self) -> Vec<Event> {
        unimplemented!()
    }

    pub fn wait_events(&self) -> Vec<Event> {
        use std::mem;

        let mut xev = unsafe { mem::uninitialized() };
        unsafe { ffi::XNextEvent(self.display, &mut xev) };

        let mut events = Vec::new();

        match xev.type_ {
            ffi::ClientMessage => {
                use Closed;
                use std::sync::atomics::Relaxed;

                let client_msg: &ffi::XClientMessageEvent = unsafe { mem::transmute(&xev) };

                if client_msg.l[0] == self.wm_delete_window as libc::c_long {
                    self.should_close.store(true, Relaxed);
                    events.push(Closed);
                }
            },

            _ => ()
        }

        events
    }

    pub fn make_current(&self) {
        let res = unsafe { ffi::glXMakeCurrent(self.display, self.window, self.context) };
        if res == 0 {
            fail!("glXMakeCurrent failed");
        }
    }

    pub fn get_proc_address(&self, addr: &str) -> *const () {
        use std::c_str::ToCStr;
        use std::mem;

        unsafe {
            addr.with_c_str(|s| {
                let p = ffi::glXGetProcAddress(mem::transmute(s)) as *const ();
                if !p.is_null() { return p; }
                println!("{}", p);
                p
            })
        }
    }

    pub fn swap_buffers(&self) {
        unsafe { ffi::glXSwapBuffers(self.display, self.window) }
    }
}

impl Drop for Window {
    fn drop(&mut self) {
        unsafe { ffi::glXDestroyContext(self.display, self.context) }
        unsafe { ffi::XDestroyWindow(self.display, self.window) }
        unsafe { ffi::XCloseDisplay(self.display) }
    }
}

/*
  printf( "Getting matching framebuffer configs\n" );
  int fbcount;
  GLXFBConfig* fbc = glXChooseFBConfig(display, DefaultScreen(display), visual_attribs, &fbcount);
  if (!fbc)
  {
    printf( "Failed to retrieve a framebuffer config\n" );
    exit(1);
  }
  printf( "Found %d matching FB configs.\n", fbcount );

  // Pick the FB config/visual with the most samples per pixel
  printf( "Getting XVisualInfos\n" );
  int best_fbc = -1, worst_fbc = -1, best_num_samp = -1, worst_num_samp = 999;

  int i;
  for (i=0; i<fbcount; ++i)
  {
    XVisualInfo *vi = glXGetVisualFromFBConfig( display, fbc[i] );
    if ( vi )
    {
      int samp_buf, samples;
      glXGetFBConfigAttrib( display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf );
      glXGetFBConfigAttrib( display, fbc[i], GLX_SAMPLES       , &samples  );

      printf( "  Matching fbconfig %d, visual ID 0x%2x: SAMPLE_BUFFERS = %d,"
              " SAMPLES = %d\n",
              i, vi -> visualid, samp_buf, samples );

      if ( best_fbc < 0 || samp_buf && samples > best_num_samp )
        best_fbc = i, best_num_samp = samples;
      if ( worst_fbc < 0 || !samp_buf || samples < worst_num_samp )
        worst_fbc = i, worst_num_samp = samples;
    }
    XFree( vi );
  }

  GLXFBConfig bestFbc = fbc[ best_fbc ];

  // Be sure to free the FBConfig list allocated by glXChooseFBConfig()
  XFree( fbc );

  // Get a visual
  XVisualInfo *vi = glXGetVisualFromFBConfig( display, bestFbc );
  printf( "Chosen visual ID = 0x%x\n", vi->visualid );

  printf( "Creating colormap\n" );
  XSetWindowAttributes swa;
  Colormap cmap;
  swa.colormap = cmap = XCreateColormap( display,
                                         RootWindow( display, vi->screen ),
                                         vi->visual, AllocNone );
  swa.background_pixmap = None ;
  swa.border_pixel      = 0;
  swa.event_mask        = StructureNotifyMask;

  printf( "Creating window\n" );
  Window win = XCreateWindow( display, RootWindow( display, vi->screen ),
                              0, 0, 100, 100, 0, vi->depth, InputOutput,
                              vi->visual,
                              CWBorderPixel|CWColormap|CWEventMask, &swa );
  if ( !win )
  {
    printf( "Failed to create window.\n" );
    exit(1);
  }

  // Done with the visual info data
  XFree( vi );

  XStoreName( display, win, "GL 3.0 Window" );

  printf( "Mapping window\n" );
  XMapWindow( display, win );

  // Get the default screen's GLX extension list
  const char *glxExts = glXQueryExtensionsString( display,
                                                  DefaultScreen( display ) );

  // NOTE: It is not necessary to create or make current to a context before
  // calling glXGetProcAddressARB
  glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0;
  glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)
           glXGetProcAddressARB( (const GLubyte *) "glXCreateContextAttribsARB" );

  GLXContext ctx = 0;

  // Install an X error handler so the application won't exit if GL 3.0
  // context allocation fails.
  //
  // Note this error handler is global.  All display connections in all threads
  // of a process use the same error handler, so be sure to guard against other
  // threads issuing X commands while this code is running.
  ctxErrorOccurred = false;
  int (*oldHandler)(Display*, XErrorEvent*) =
      XSetErrorHandler(&ctxErrorHandler);

  // Check for the GLX_ARB_create_context extension string and the function.
  // If either is not present, use GLX 1.3 context creation method.
  if ( !isExtensionSupported( glxExts, "GLX_ARB_create_context" ) ||
       !glXCreateContextAttribsARB )
  {
    printf( "glXCreateContextAttribsARB() not found"
            " ... using old-style GLX context\n" );
    ctx = glXCreateNewContext( display, bestFbc, GLX_RGBA_TYPE, 0, True );
  }

  // If it does, try to get a GL 3.0 context!
  else
  {
    int context_attribs[] =
      {
        GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
        GLX_CONTEXT_MINOR_VERSION_ARB, 0,
        //GLX_CONTEXT_FLAGS_ARB        , GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
        None
      };

    printf( "Creating context\n" );
    ctx = glXCreateContextAttribsARB( display, bestFbc, 0,
                                      True, context_attribs );

    // Sync to ensure any errors generated are processed.
    XSync( display, False );
    if ( !ctxErrorOccurred && ctx )
      printf( "Created GL 3.0 context\n" );
    else
    {
      // Couldn't create GL 3.0 context.  Fall back to old-style 2.x context.
      // When a context version below 3.0 is requested, implementations will
      // return the newest context version compatible with OpenGL versions less
      // than version 3.0.
      // GLX_CONTEXT_MAJOR_VERSION_ARB = 1
      context_attribs[1] = 1;
      // GLX_CONTEXT_MINOR_VERSION_ARB = 0
      context_attribs[3] = 0;

      ctxErrorOccurred = false;

      printf( "Failed to create GL 3.0 context"
              " ... using old-style GLX context\n" );
      ctx = glXCreateContextAttribsARB( display, bestFbc, 0,
                                        True, context_attribs );
    }
  }

  // Sync to ensure any errors generated are processed.
  XSync( display, False );

  // Restore the original error handler
  XSetErrorHandler( oldHandler );

  if ( ctxErrorOccurred || !ctx )
  {
    printf( "Failed to create an OpenGL context\n" );
    exit(1);
  }

  // Verifying that context is a direct context
  if ( ! glXIsDirect ( display, ctx ) )
  {
    printf( "Indirect GLX rendering context obtained\n" );
  }
  else
  {
    printf( "Direct GLX rendering context obtained\n" );
  }

  printf( "Making context current\n" );
  glXMakeCurrent( display, win, ctx );

  glClearColor( 0, 0.5, 1, 1 );
  glClear( GL_COLOR_BUFFER_BIT );
  glXSwapBuffers ( display, win );

*/<|endoftext|>


<commit_before>use std::sync::Arc;
use std::sync::mpsc::{Receiver, Sender, channel};
use event::{Event, EventArg};
use item::{Item, ItemGroup, MatchedItem, MatchedItemGroup, MatchedRange};
use std::thread;

use getopts;
use score;
use std::io::Write;
use regex::Regex;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;

macro_rules! println_stderr(
    ($($arg:tt)*) => { {
        let r = writeln!(&mut ::std::io::stderr(), $($arg)*);
        r.expect("failed printing to stderr");
    } }
);

#[derive(Clone, Copy)]
enum Algorithm {
    Fuzzy,
    Regex,
    PrefixExact,
    SuffixExact,
    Exact,
    InverseExact,
    InverseSuffixExact,
}

pub struct Matcher {
    tx_result: Sender<(Event, EventArg)>,
    rank_criterion: Vec<RankCriteria>,
    is_exact: bool,
}

impl Matcher {
    pub fn new(tx_result: Sender<(Event, EventArg)>) -> Self {
        Matcher {
            tx_result: tx_result,
            rank_criterion: vec![RankCriteria::Score, RankCriteria::Index, RankCriteria::Begin, RankCriteria::End],
            is_exact: false,
        }
    }

    pub fn parse_options(&mut self, options: &getopts::Matches) {
        if let Some(tie_breaker) = options.opt_str("t") {
            let mut vec = Vec::new();
            for criteria in tie_breaker.split(',') {
                if let Some(c) = parse_criteria(criteria) {
                    vec.push(c);
                }
            }
            self.rank_criterion = vec;
        }

        if options.opt_present("exact") {
            self.is_exact = true;
        }
    }

    pub fn run(&self, rx_item: Receiver<(Event, EventArg)>) {
        let (tx_matcher, rx_matcher) = channel();
        let matcher_restart = Arc::new(AtomicBool::new(false));
        // start a new thread listening for EvMatcherRestart, that means the query had been
        // changed, so that matcher shoudl discard all previous events.
        {
            let matcher_restart = matcher_restart.clone();
            thread::spawn(move || {
                while let Ok((ev, arg)) = rx_item.recv() {
                    match ev {
                        Event::EvMatcherRestart => {
                            matcher_restart.store(true, Ordering::Relaxed);
                            while matcher_restart.load(Ordering::Relaxed) {
                                thread::sleep(Duration::from_millis(10));
                            }

                            tx_matcher.send((ev, arg));
                        }
                        _ => {
                            // pass through all other events
                            tx_matcher.send((ev, arg));
                        }
                    }
                }
            });
        }

        let mut matcher_engine: Option<MatchingEngine> = None;
        let mut num_processed: usize = 0;
        loop {

            if matcher_restart.load(Ordering::Relaxed) {
                while let Ok(_) = rx_matcher.try_recv() {}
                matcher_restart.store(false, Ordering::Relaxed);
            }

            if let Ok((ev, arg)) = rx_matcher.recv_timeout(Duration::from_millis(10)) {
                match ev {
                    Event::EvMatcherNewItem => {
                        let items: ItemGroup = *arg.downcast().unwrap();
                        num_processed += items.len();

                        matcher_engine.as_ref().map(|mat| {
                            let matched_items: MatchedItemGroup = items.into_iter()
                                .map(|item| mat.match_item(item))
                                .filter(Option::is_some)
                                .map(|item| item.unwrap())
                                .collect();
                            let _ = self.tx_result.send((Event::EvModelNewItem, Box::new(matched_items)));
                        });

                        // report the number of processed items
                        let _ = self.tx_result.send((Event::EvModelNotifyProcessed, Box::new(num_processed)));
                    }

                    Event::EvReaderStopped | Event::EvSenderWaiting => {
                        let _ = self.tx_result.send((ev, arg));
                    }
                    Event::EvSenderStopped => {
                        // Since matcher is single threaded, sender stopped means all items are
                        // processed.
                        let _ = self.tx_result.send((Event::EvModelNotifyProcessed, Box::new(num_processed)));
                        let _ = self.tx_result.send((Event::EvMatcherStopped, arg));
                    }

                    Event::EvReaderStarted => { let _ = self.tx_result.send((ev, arg)); }

                    Event::EvMatcherRestart => {
                        num_processed = 0;
                        let query = arg.downcast::<String>().unwrap();

                        // notifiy the model that the query had been changed
                        let _ = self.tx_result.send((Event::EvModelRestart, Box::new(true)));

                        matcher_engine = Some(MatchingEngine::builder(&query, self.is_exact)
                                              .rank(&self.rank_criterion)
                                              .build());
                    }

                    _ => {}
                }
            }
        }
    }

}

type ExactFilter = Box<Fn(&Item, &Option<((usize, usize), (usize, usize))>) -> bool>;

struct MatchingEngine<'a> {
    query: String,
    query_chars: Vec<char>,
    query_lower_chars: Vec<char>,
    query_regex: Option<Regex>,
    rank_criterion: Option<&'a [RankCriteria]>,
    algorithm: Algorithm
}

impl<'a> MatchingEngine<'a> {
    pub fn builder(query: &str, is_exact: bool) -> Self {
        let (algo, query) = if query.starts_with('\'') {
            if is_exact {
                (Algorithm::Fuzzy, &query[1..])
            } else {
                (Algorithm::Exact, &query[1..])
            }
        } else if query.starts_with('^') {
            (Algorithm::PrefixExact, &query[1..])
        } else if query.starts_with('!') {
            if query.ends_with('$') {
                (Algorithm::InverseSuffixExact, &query[1..(query.len()-1)])
            } else {
                (Algorithm::InverseExact, &query[1..])
            }
        } else if query.ends_with('$') {
            (Algorithm::SuffixExact, &query[..(query.len()-1)])
        } else if is_exact {
            (Algorithm::Exact, query)
        } else {
            (Algorithm::Fuzzy, query)
        };

        MatchingEngine {
            query: query.to_string(),
            query_chars: query.chars().collect(),
            query_lower_chars: query.to_lowercase().chars().collect(),
            query_regex:  Regex::new(query).ok(),
            rank_criterion: None,
            algorithm: algo,
        }
    }

    pub fn rank(mut self, rank: &'a [RankCriteria]) -> Self {
        self.rank_criterion = Some(rank);
        self
    }

    pub fn build(self) -> Self {
        self
    }

    pub fn match_item(&self, item: Arc<Item>) -> Option<MatchedItem> {
        match self.algorithm {
            Algorithm::Fuzzy => self.match_item_fuzzy(item),
            Algorithm::Regex => self.match_item_regex(item),
            Algorithm::Exact => {
                self.match_item_exact(item, Box::new(|_, matched_result| {
                    matched_result.is_some()
                }))
            }
            Algorithm::InverseExact => {
                self.match_item_exact(item, Box::new(|_, matched_result| {
                    matched_result.is_none()
                }))
            }
            Algorithm::PrefixExact => {
                self.match_item_exact(item, Box::new(|_, matched_result| {
                    match *matched_result {
                        Some(((start_pos, _), _)) => start_pos == 0,
                        None => false
                    }
                }))
            }
            Algorithm::SuffixExact => {
                self.match_item_exact(item, Box::new(|item, matched_result| {
                    match *matched_result {
                        Some((_, (_, last_pos))) => last_pos == item.get_lower_chars().len(),
                        None => false
                    }
                }))
            }
            Algorithm::InverseSuffixExact => {
                self.match_item_exact(item, Box::new(|item, matched_result| {
                    match *matched_result {
                        Some((_, (_, last_pos))) => last_pos != item.get_lower_chars().len(),
                        None => true
                    }
                }))
            }
        }
    }

    fn build_rank(&self, score: i64, index: i64, begin: i64, end: i64) -> [i64; 4] {
        self.rank_criterion.map(|criterion| {
            let mut rank = [0; 4];
            for (idx, criteria) in criterion.iter().enumerate().take(4) {
                rank[idx] = match *criteria {
                    RankCriteria::Score    => score,
                    RankCriteria::Index    => index,
                    RankCriteria::Begin    => begin,
                    RankCriteria::End      => end,
                    RankCriteria::NegScore => -score,
                    RankCriteria::NegIndex => -index,
                    RankCriteria::NegBegin => -begin,
                    RankCriteria::NegEnd   => -end,
                }
            }
            rank
        }).unwrap_or([0; 4])
    }

    fn match_item_regex(&self, item: Arc<Item>) -> Option<MatchedItem> {
        let mut matched_result = None;
        for &(start, end) in item.get_matching_ranges() {
            if self.query == "" {
                matched_result = Some((0, 0));
                break;
            }

            let source: String = item.get_lower_chars()[start .. end].iter().cloned().collect();
            matched_result = score::regex_match(&source, &self.query_regex);

            if matched_result == None {
                continue;
            }
        }

        if matched_result == None {
            return None;
        }

        let (begin, end) = matched_result.unwrap();

        let score = end - begin;
        let rank = self.build_rank(-score, item.get_index() as i64, begin, end);

        Some(MatchedItem::builder(item)
             .rank(rank)
             .matched_range(MatchedRange::Range(begin as usize, end as usize))
             .build())
    }

    fn match_item_fuzzy(&self, item: Arc<Item>) -> Option<MatchedItem> {
        // iterate over all matching fields:
        let mut matched_result = None;
        for &(start, end) in item.get_matching_ranges() {
            let source = &item.get_lower_chars()[start .. end];

            matched_result = score::fuzzy_match(source, &self.query_chars, &self.query_lower_chars);

            if matched_result == None {
                continue;
            }
        }

        if matched_result == None {
            return None;
        }

        let (score, matched_range) = matched_result.unwrap();

        let begin = *matched_range.get(0).unwrap_or(&0) as i64;
        let end = *matched_range.last().unwrap_or(&0) as i64;

        let rank = self.build_rank(-score, item.get_index() as i64, begin, end);

        Some(MatchedItem::builder(item)
             .rank(rank)
             .matched_range(MatchedRange::Chars(matched_range))
             .build())
    }

    fn match_item_exact(&self, item: Arc<Item>, filter: ExactFilter) -> Option<MatchedItem>{
        let mut matched_result = None;
        for &(start, end) in item.get_matching_ranges() {
            if self.query == "" {
                matched_result = Some(((0, 0), (0, 0)));
                break;
            }

            let chars: Vec<_> = item.get_text().chars().collect();
            let source: String = chars[start .. end].iter().cloned().collect();
            matched_result = score::exact_match(&source, &self.query);

            if matched_result == None {
                continue;
            }
        }

        if !filter(&item, &matched_result){
            return None;
        }

        let (first, _) = matched_result.unwrap_or(((0,0), (0,0)));

        let (begin, end) = first;
        let score = (end - begin) as i64;
        let rank = self.build_rank(-score, item.get_index() as i64, begin as i64, end as i64);

        Some(MatchedItem::builder(item)
             .rank(rank)
             .matched_range(MatchedRange::Range(begin, end))
             .build())
    }
}

#[derive(Debug)]
pub enum RankCriteria {
    Score,
    Index,
    Begin,
    End,
    NegScore,
    NegIndex,
    NegBegin,
    NegEnd,
}

pub fn parse_criteria(text: &str) -> Option<RankCriteria> {
    match text.to_lowercase().as_ref() {
        "score"  => Some(RankCriteria::Score),
        "index"  => Some(RankCriteria::Index),
        "begin"  => Some(RankCriteria::Begin),
        "end"    => Some(RankCriteria::End),
        "-score" => Some(RankCriteria::NegScore),
        "-index" => Some(RankCriteria::NegIndex),
        "-begin" => Some(RankCriteria::NegBegin),
        "-end"   => Some(RankCriteria::NegEnd),
        _ => None,
    }
}
<commit_msg>[matcher] fix: matching incorrect with ranged fields<commit_after>use std::sync::Arc;
use std::sync::mpsc::{Receiver, Sender, channel};
use event::{Event, EventArg};
use item::{Item, ItemGroup, MatchedItem, MatchedItemGroup, MatchedRange};
use std::thread;

use getopts;
use score;
use std::io::Write;
use regex::Regex;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;

macro_rules! println_stderr(
    ($($arg:tt)*) => { {
        let r = writeln!(&mut ::std::io::stderr(), $($arg)*);
        r.expect("failed printing to stderr");
    } }
);

#[derive(Clone, Copy)]
enum Algorithm {
    Fuzzy,
    Regex,
    PrefixExact,
    SuffixExact,
    Exact,
    InverseExact,
    InverseSuffixExact,
}

pub struct Matcher {
    tx_result: Sender<(Event, EventArg)>,
    rank_criterion: Vec<RankCriteria>,
    is_exact: bool,
}

impl Matcher {
    pub fn new(tx_result: Sender<(Event, EventArg)>) -> Self {
        Matcher {
            tx_result: tx_result,
            rank_criterion: vec![RankCriteria::Score, RankCriteria::Index, RankCriteria::Begin, RankCriteria::End],
            is_exact: false,
        }
    }

    pub fn parse_options(&mut self, options: &getopts::Matches) {
        if let Some(tie_breaker) = options.opt_str("t") {
            let mut vec = Vec::new();
            for criteria in tie_breaker.split(',') {
                if let Some(c) = parse_criteria(criteria) {
                    vec.push(c);
                }
            }
            self.rank_criterion = vec;
        }

        if options.opt_present("exact") {
            self.is_exact = true;
        }
    }

    pub fn run(&self, rx_item: Receiver<(Event, EventArg)>) {
        let (tx_matcher, rx_matcher) = channel();
        let matcher_restart = Arc::new(AtomicBool::new(false));
        // start a new thread listening for EvMatcherRestart, that means the query had been
        // changed, so that matcher shoudl discard all previous events.
        {
            let matcher_restart = matcher_restart.clone();
            thread::spawn(move || {
                while let Ok((ev, arg)) = rx_item.recv() {
                    match ev {
                        Event::EvMatcherRestart => {
                            matcher_restart.store(true, Ordering::Relaxed);
                            while matcher_restart.load(Ordering::Relaxed) {
                                thread::sleep(Duration::from_millis(10));
                            }

                            tx_matcher.send((ev, arg));
                        }
                        _ => {
                            // pass through all other events
                            tx_matcher.send((ev, arg));
                        }
                    }
                }
            });
        }

        let mut matcher_engine: Option<MatchingEngine> = None;
        let mut num_processed: usize = 0;
        loop {

            if matcher_restart.load(Ordering::Relaxed) {
                while let Ok(_) = rx_matcher.try_recv() {}
                matcher_restart.store(false, Ordering::Relaxed);
            }

            if let Ok((ev, arg)) = rx_matcher.recv_timeout(Duration::from_millis(10)) {
                match ev {
                    Event::EvMatcherNewItem => {
                        let items: ItemGroup = *arg.downcast().unwrap();
                        num_processed += items.len();

                        matcher_engine.as_ref().map(|mat| {
                            let matched_items: MatchedItemGroup = items.into_iter()
                                .map(|item| mat.match_item(item))
                                .filter(Option::is_some)
                                .map(|item| item.unwrap())
                                .collect();
                            let _ = self.tx_result.send((Event::EvModelNewItem, Box::new(matched_items)));
                        });

                        // report the number of processed items
                        let _ = self.tx_result.send((Event::EvModelNotifyProcessed, Box::new(num_processed)));
                    }

                    Event::EvReaderStopped | Event::EvSenderWaiting => {
                        let _ = self.tx_result.send((ev, arg));
                    }
                    Event::EvSenderStopped => {
                        // Since matcher is single threaded, sender stopped means all items are
                        // processed.
                        let _ = self.tx_result.send((Event::EvModelNotifyProcessed, Box::new(num_processed)));
                        let _ = self.tx_result.send((Event::EvMatcherStopped, arg));
                    }

                    Event::EvReaderStarted => { let _ = self.tx_result.send((ev, arg)); }

                    Event::EvMatcherRestart => {
                        num_processed = 0;
                        let query = arg.downcast::<String>().unwrap();

                        // notifiy the model that the query had been changed
                        let _ = self.tx_result.send((Event::EvModelRestart, Box::new(true)));

                        matcher_engine = Some(MatchingEngine::builder(&query, self.is_exact)
                                              .rank(&self.rank_criterion)
                                              .build());
                    }

                    _ => {}
                }
            }
        }
    }

}

// <Option<(start, end), (start, end)>, item_length> -> Option<(start, end)>
type ExactFilter = Box<Fn(&Option<((usize, usize), (usize, usize))>, usize) -> Option<(usize, usize)>>;

struct MatchingEngine<'a> {
    query: String,
    query_chars: Vec<char>,
    query_lower_chars: Vec<char>,
    query_regex: Option<Regex>,
    rank_criterion: Option<&'a [RankCriteria]>,
    algorithm: Algorithm
}

impl<'a> MatchingEngine<'a> {
    pub fn builder(query: &str, is_exact: bool) -> Self {
        let (algo, query) = if query.starts_with('\'') {
            if is_exact {
                (Algorithm::Fuzzy, &query[1..])
            } else {
                (Algorithm::Exact, &query[1..])
            }
        } else if query.starts_with('^') {
            (Algorithm::PrefixExact, &query[1..])
        } else if query.starts_with('!') {
            if query.ends_with('$') {
                (Algorithm::InverseSuffixExact, &query[1..(query.len()-1)])
            } else {
                (Algorithm::InverseExact, &query[1..])
            }
        } else if query.ends_with('$') {
            (Algorithm::SuffixExact, &query[..(query.len()-1)])
        } else if is_exact {
            (Algorithm::Exact, query)
        } else {
            (Algorithm::Fuzzy, query)
        };

        MatchingEngine {
            query: query.to_string(),
            query_chars: query.chars().collect(),
            query_lower_chars: query.to_lowercase().chars().collect(),
            query_regex:  Regex::new(query).ok(),
            rank_criterion: None,
            algorithm: algo,
        }
    }

    pub fn rank(mut self, rank: &'a [RankCriteria]) -> Self {
        self.rank_criterion = Some(rank);
        self
    }

    pub fn build(self) -> Self {
        self
    }

    pub fn match_item(&self, item: Arc<Item>) -> Option<MatchedItem> {
        match self.algorithm {
            Algorithm::Fuzzy => self.match_item_fuzzy(item),
            Algorithm::Regex => self.match_item_regex(item),
            Algorithm::Exact => {
                self.match_item_exact(item, Box::new(|matched_result, _| {
                    matched_result.map(|(first, _)| first)
                }))
            }
            Algorithm::InverseExact => {
                self.match_item_exact(item, Box::new(|matched_result, _| {
                    if matched_result.is_none() {Some((0, 0))} else {None}
                }))
            }
            Algorithm::PrefixExact => {
                self.match_item_exact(item, Box::new(|matched_result, _| {
                    match *matched_result {
                        Some(((s, e), _)) if s == 0 => Some((s, e)),
                        _ => None,
                    }
                }))
            }
            Algorithm::SuffixExact => {
                self.match_item_exact(item, Box::new(|matched_result, len| {
                    match *matched_result {
                        Some((_, (s, e))) if e == len => Some((s, e)),
                        _ => None,
                    }
                }))
            }
            Algorithm::InverseSuffixExact => {
                self.match_item_exact(item, Box::new(|matched_result, len| {
                    match *matched_result {
                        Some((_, (_, e))) if e != len => None,
                        _ => Some((0, 0))
                    }
                }))
            }
        }
    }

    fn build_rank(&self, score: i64, index: i64, begin: i64, end: i64) -> [i64; 4] {
        self.rank_criterion.map(|criterion| {
            let mut rank = [0; 4];
            for (idx, criteria) in criterion.iter().enumerate().take(4) {
                rank[idx] = match *criteria {
                    RankCriteria::Score    => score,
                    RankCriteria::Index    => index,
                    RankCriteria::Begin    => begin,
                    RankCriteria::End      => end,
                    RankCriteria::NegScore => -score,
                    RankCriteria::NegIndex => -index,
                    RankCriteria::NegBegin => -begin,
                    RankCriteria::NegEnd   => -end,
                }
            }
            rank
        }).unwrap_or([0; 4])
    }

    fn match_item_regex(&self, item: Arc<Item>) -> Option<MatchedItem> {
        let mut matched_result = None;
        for &(start, end) in item.get_matching_ranges() {
            if self.query == "" {
                matched_result = Some((0, 0));
                break;
            }

            let source: String = item.get_lower_chars()[start .. end].iter().cloned().collect();
            matched_result = score::regex_match(&source, &self.query_regex);

            if matched_result == None {
                continue;
            }
        }

        if matched_result == None {
            return None;
        }

        let (begin, end) = matched_result.unwrap();

        let score = end - begin;
        let rank = self.build_rank(-score, item.get_index() as i64, begin, end);

        Some(MatchedItem::builder(item)
             .rank(rank)
             .matched_range(MatchedRange::Range(begin as usize, end as usize))
             .build())
    }

    fn match_item_fuzzy(&self, item: Arc<Item>) -> Option<MatchedItem> {
        // iterate over all matching fields:
        let mut matched_result = None;
        for &(start, end) in item.get_matching_ranges() {
            let source = &item.get_lower_chars()[start .. end];

            matched_result = score::fuzzy_match(source, &self.query_chars, &self.query_lower_chars)
                .map(|(s, vec)| {
                    if start != 0 {
                        (s, vec.iter().map(|x| x + start).collect())
                    } else {
                        (s, vec)
                    }
                });

            if matched_result.is_some() {
                break;
            }
        }

        if matched_result == None {
            return None;
        }

        let (score, matched_range) = matched_result.unwrap();

        let begin = *matched_range.get(0).unwrap_or(&0) as i64;
        let end = *matched_range.last().unwrap_or(&0) as i64;

        let rank = self.build_rank(-score, item.get_index() as i64, begin, end);

        Some(MatchedItem::builder(item)
             .rank(rank)
             .matched_range(MatchedRange::Chars(matched_range))
             .build())
    }

    fn match_item_exact(&self, item: Arc<Item>, filter: ExactFilter) -> Option<MatchedItem>{
        let mut matched_result = None;
        let mut range_start = 0;
        let mut range_end = 0;
        for &(start, end) in item.get_matching_ranges() {
            if self.query == "" {
                matched_result = Some(((0, 0), (0, 0)));
                break;
            }

            let chars: Vec<_> = item.get_text().chars().collect();
            let source: String = chars[start .. end].iter().cloned().collect();
            matched_result = score::exact_match(&source, &self.query);

            if matched_result.is_some() {
                range_start = start;
                range_end = end;
                break;
            }
        }

        let result_range = filter(&matched_result, range_end - range_start);
        if result_range.is_none() {return None;}

        let (begin, end) = result_range.map(|(s, e)| (s + range_start, e + range_start)).unwrap();
        let score = (end - begin) as i64;
        let rank = self.build_rank(-score, item.get_index() as i64, begin as i64, end as i64);

        Some(MatchedItem::builder(item)
             .rank(rank)
             .matched_range(MatchedRange::Range(begin, end))
             .build())
    }
}

#[derive(Debug)]
pub enum RankCriteria {
    Score,
    Index,
    Begin,
    End,
    NegScore,
    NegIndex,
    NegBegin,
    NegEnd,
}

pub fn parse_criteria(text: &str) -> Option<RankCriteria> {
    match text.to_lowercase().as_ref() {
        "score"  => Some(RankCriteria::Score),
        "index"  => Some(RankCriteria::Index),
        "begin"  => Some(RankCriteria::Begin),
        "end"    => Some(RankCriteria::End),
        "-score" => Some(RankCriteria::NegScore),
        "-index" => Some(RankCriteria::NegIndex),
        "-begin" => Some(RankCriteria::NegBegin),
        "-end"   => Some(RankCriteria::NegEnd),
        _ => None,
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::iter::FromIterator;
use std::slice::Iter;
use std::vec;

use document::dom4;

use super::EvaluationContext;

macro_rules! unpack(
    ($enum_name:ident, $name:ident, $wrapper:ident, dom4::$inner:ident) => (
        impl<'d> $enum_name<'d> {
            pub fn $name(self) -> Option<dom4::$inner<'d>> {
                match self {
                    $enum_name::$wrapper(n) => Some(n),
                    _ => None,
                }
            }
        }
    )
);

macro_rules! conversion_trait(
    ($tr_name:ident, $method:ident, $res_type:ident,
        { $(dom4::$leaf_type:ident => Node::$variant:ident),* }
    ) => (
        pub trait $tr_name<'d> {
            fn $method(self) -> $res_type<'d>;
        }

        impl<'d> $tr_name<'d> for $res_type<'d> {
            fn $method(self) -> $res_type<'d> {
                self
            }
        }

        $(impl<'d> $tr_name<'d> for dom4::$leaf_type<'d> {
            fn $method(self) -> $res_type<'d> {
                Node::$variant(self)
            }
        })*
    )
);

#[derive(Copy,Clone,PartialEq,Eq,Hash,Debug)]
pub enum Node<'d> {
    Root(dom4::Root<'d>),
    Element(dom4::Element<'d>),
    Attribute(dom4::Attribute<'d>),
    Text(dom4::Text<'d>),
    Comment(dom4::Comment<'d>),
    ProcessingInstruction(dom4::ProcessingInstruction<'d>),
}

unpack!(Node, root, Root, dom4::Root);
unpack!(Node, element, Element, dom4::Element);
unpack!(Node, attribute, Attribute, dom4::Attribute);
unpack!(Node, text, Text, dom4::Text);
unpack!(Node, comment, Comment, dom4::Comment);
unpack!(Node, processing_instruction, ProcessingInstruction, dom4::ProcessingInstruction);

impl<'d> Node<'d> {
    pub fn document(&self) -> &'d dom4::Document<'d> {
        use self::Node::*;
        match self {
            &Root(n)                  => n.document(),
            &Element(n)               => n.document(),
            &Attribute(n)             => n.document(),
            &Text(n)                  => n.document(),
            &Comment(n)               => n.document(),
            &ProcessingInstruction(n) => n.document(),
        }
    }

    pub fn parent(&self) -> Option<Node<'d>> {
        use self::Node::*;
        match self {
            &Root(_)                  => None,
            &Element(n)               => n.parent().map(|n| n.to_node()),
            &Attribute(n)             => n.parent().map(|n| n.to_node()),
            &Text(n)                  => n.parent().map(|n| n.to_node()),
            &Comment(n)               => n.parent().map(|n| n.to_node()),
            &ProcessingInstruction(n) => n.parent().map(|n| n.to_node()),
        }
    }

    pub fn children(&self) -> Vec<Node<'d>> {
        use self::Node::*;
        match self {
            &Root(n)                  => n.children().iter().map(|n| n.to_node()).collect(),
            &Element(n)               => n.children().iter().map(|n| n.to_node()).collect(),
            &Attribute(_)             => Vec::new(),
            &Text(_)                  => Vec::new(),
            &Comment(_)               => Vec::new(),
            &ProcessingInstruction(_) => Vec::new(),
        }
    }

    pub fn preceding_siblings(&self) -> Vec<Node<'d>> {
        use self::Node::*;
        match self {
            &Root(_)                  => Vec::new(),
            &Element(n)               => n.preceding_siblings().iter().rev().map(|n| n.to_node()).collect(),
            &Attribute(_)             => Vec::new(),
            &Text(n)                  => n.preceding_siblings().iter().rev().map(|n| n.to_node()).collect(),
            &Comment(n)               => n.preceding_siblings().iter().rev().map(|n| n.to_node()).collect(),
            &ProcessingInstruction(n) => n.preceding_siblings().iter().rev().map(|n| n.to_node()).collect(),
        }
    }

    pub fn following_siblings(&self) -> Vec<Node<'d>> {
        use self::Node::*;
        match self {
            &Root(_)                  => Vec::new(),
            &Element(n)               => n.following_siblings().iter().map(|n| n.to_node()).collect(),
            &Attribute(_)             => Vec::new(),
            &Text(n)                  => n.following_siblings().iter().map(|n| n.to_node()).collect(),
            &Comment(n)               => n.following_siblings().iter().map(|n| n.to_node()).collect(),
            &ProcessingInstruction(n) => n.following_siblings().iter().map(|n| n.to_node()).collect(),
        }
    }
}

conversion_trait!(ToNode, to_node, Node, {
    dom4::Root => Node::Root,
    dom4::Element => Node::Element,
    dom4::Attribute => Node::Attribute,
    dom4::Text => Node::Text,
    dom4::Comment => Node::Comment,
    dom4::ProcessingInstruction => Node::ProcessingInstruction
});

impl<'d> ToNode<'d> for dom4::ChildOfRoot<'d> {
    fn to_node(self) -> Node<'d> {
        use self::Node::*;
        match self {
            dom4::ChildOfRoot::Element(n)               => Element(n),
            dom4::ChildOfRoot::Comment(n)               => Comment(n),
            dom4::ChildOfRoot::ProcessingInstruction(n) => ProcessingInstruction(n),
        }
    }
}

impl<'d> ToNode<'d> for dom4::ChildOfElement<'d> {
    fn to_node(self) -> Node<'d> {
        use self::Node::*;
        match self {
            dom4::ChildOfElement::Element(n)               => Element(n),
            dom4::ChildOfElement::Text(n)                  => Text(n),
            dom4::ChildOfElement::Comment(n)               => Comment(n),
            dom4::ChildOfElement::ProcessingInstruction(n) => ProcessingInstruction(n),
        }
    }
}

impl<'d> ToNode<'d> for dom4::ParentOfChild<'d> {
    fn to_node(self) -> Node<'d> {
        use self::Node::*;
        match self {
            dom4::ParentOfChild::Root(n)    => Root(n),
            dom4::ParentOfChild::Element(n) => Element(n),
        }
    }
}

/// A collection of nodes
#[derive(PartialEq,Debug,Clone)]
pub struct Nodeset<'d> {
    nodes: Vec<Node<'d>>,
}

impl<'d> Nodeset<'d> {
    pub fn new() -> Nodeset<'d> {
        Nodeset { nodes: Vec::new() }
    }

    pub fn add<N : ToNode<'d>>(&mut self, node: N) {
        self.nodes.push(node.to_node());
    }

    pub fn iter(&self) -> Iter<Node<'d>> {
        self.nodes.iter()
    }

    pub fn add_nodeset(& mut self, other: &Nodeset<'d>) {
        self.nodes.push_all(&other.nodes);
    }

    pub fn size(&self) -> usize {
        self.nodes.len()
    }

    pub fn into_iter(self) -> vec::IntoIter<Node<'d>> {
        self.nodes.into_iter()
    }

    pub fn document_order_first(&self) -> Option<Node<'d>> {
        let doc = match self.nodes.first() {
            Some(n) => n.document(),
            None => return None,
        };

        let mut idx = 0;
        let mut stack = vec![doc.root().to_node()];
        let mut order: HashMap<Node, usize> = HashMap::new();

        // Rebuilding this each time cannot possibly be performant,
        // but I want to see how widely used this is first before
        // picking an appropriate caching point.

        while let Some(n) = stack.pop() {
            order.insert(n, idx);
            idx += 1;
            let c = n.children();

            stack.extend(c.into_iter().rev());

            if let Node::Element(e) = n {
                // TODO: namespaces
                stack.extend(e.attributes().into_iter().map(|a| a.to_node()));
            }
        }

        self.nodes.iter().min_by(|&&n| order[n]).map(|n| *n)
    }
}

impl<'a, 'd : 'a> FromIterator<EvaluationContext<'a, 'd>> for Nodeset<'d> {
    fn from_iter<T>(iterator: T) -> Nodeset<'d>
        where T: Iterator<Item=EvaluationContext<'a, 'd>>
    {
        let mut ns = Nodeset::new();
        for n in iterator { ns.add(n.node) };
        ns
    }
}

#[cfg(test)]
mod test {
    use document::Package;

    use super::Node::{
        Attribute,
        Comment,
        Element,
        ProcessingInstruction,
        Root,
        Text,
    };
    use super::{Nodeset,ToNode};

    #[test]
    fn nodeset_can_include_all_node_types() {
        let package = Package::new();
        let doc = package.as_document();
        let mut nodes = Nodeset::new();

        let r = doc.root();
        let e = doc.create_element("element");
        let a = e.set_attribute_value("name", "value");
        let t = doc.create_text("text");
        let c = doc.create_comment("comment");
        let p = doc.create_processing_instruction("pi", None);

        nodes.add(r);
        nodes.add(e);
        nodes.add(a);
        nodes.add(t);
        nodes.add(c);
        nodes.add(p);

        let node_vec: Vec<_> = nodes.iter().collect();

        assert_eq!(6, node_vec.len());
        assert_eq!(node_vec[0], &Root(r));
        assert_eq!(node_vec[1], &Element(e));
        assert_eq!(node_vec[2], &Attribute(a));
        assert_eq!(node_vec[3], &Text(t));
        assert_eq!(node_vec[4], &Comment(c));
        assert_eq!(node_vec[5], &ProcessingInstruction(p));
    }

    #[test]
    fn nodesets_can_be_combined() {
        let package = Package::new();
        let doc = package.as_document();

        let mut all_nodes = Nodeset::new();
        let mut nodes1 = Nodeset::new();
        let mut nodes2 = Nodeset::new();

        let e1 = doc.create_element("element1");
        let e2 = doc.create_element("element2");

        all_nodes.add(e1);
        all_nodes.add(e2);

        nodes1.add(e1);
        nodes2.add(e2);

        nodes1.add_nodeset(&nodes2);

        assert_eq!(all_nodes, nodes1);
    }

    #[test]
    fn nodeset_knows_first_node_in_document_order() {
        let package = Package::new();
        let doc = package.as_document();

        let c1 = doc.create_comment("1");
        let c2 = doc.create_comment("2");
        doc.root().append_child(c1);
        doc.root().append_child(c2);

        let nodes = nodeset![c2, c1];

        assert_eq!(Some(c1.to_node()), nodes.document_order_first());
    }

    #[test]
    fn attributes_come_before_children_in_document_order() {
        let package = Package::new();
        let doc = package.as_document();

        let parent = doc.create_element("parent");
        let attr = parent.set_attribute_value("a", "v");
        let child = doc.create_element("child");

        doc.root().append_child(parent);
        parent.append_child(child);

        let nodes = nodeset![child, attr];

        assert_eq!(Some(attr.to_node()), nodes.document_order_first());
    }
}
<commit_msg>Node learned the concept of an expanded-name<commit_after>use std::collections::HashMap;
use std::iter::FromIterator;
use std::slice::Iter;
use std::vec;

use document::QName;
use document::dom4;

use super::EvaluationContext;

macro_rules! unpack(
    ($enum_name:ident, $name:ident, $wrapper:ident, dom4::$inner:ident) => (
        impl<'d> $enum_name<'d> {
            pub fn $name(self) -> Option<dom4::$inner<'d>> {
                match self {
                    $enum_name::$wrapper(n) => Some(n),
                    _ => None,
                }
            }
        }
    )
);

macro_rules! conversion_trait(
    ($tr_name:ident, $method:ident, $res_type:ident,
        { $(dom4::$leaf_type:ident => Node::$variant:ident),* }
    ) => (
        pub trait $tr_name<'d> {
            fn $method(self) -> $res_type<'d>;
        }

        impl<'d> $tr_name<'d> for $res_type<'d> {
            fn $method(self) -> $res_type<'d> {
                self
            }
        }

        $(impl<'d> $tr_name<'d> for dom4::$leaf_type<'d> {
            fn $method(self) -> $res_type<'d> {
                Node::$variant(self)
            }
        })*
    )
);

#[derive(Copy,Clone,PartialEq,Eq,Hash,Debug)]
pub enum Node<'d> {
    Root(dom4::Root<'d>),
    Element(dom4::Element<'d>),
    Attribute(dom4::Attribute<'d>),
    Text(dom4::Text<'d>),
    Comment(dom4::Comment<'d>),
    ProcessingInstruction(dom4::ProcessingInstruction<'d>),
}

unpack!(Node, root, Root, dom4::Root);
unpack!(Node, element, Element, dom4::Element);
unpack!(Node, attribute, Attribute, dom4::Attribute);
unpack!(Node, text, Text, dom4::Text);
unpack!(Node, comment, Comment, dom4::Comment);
unpack!(Node, processing_instruction, ProcessingInstruction, dom4::ProcessingInstruction);

impl<'d> Node<'d> {
    pub fn document(&self) -> &'d dom4::Document<'d> {
        use self::Node::*;
        match self {
            &Root(n)                  => n.document(),
            &Element(n)               => n.document(),
            &Attribute(n)             => n.document(),
            &Text(n)                  => n.document(),
            &Comment(n)               => n.document(),
            &ProcessingInstruction(n) => n.document(),
        }
    }

    pub fn expanded_name(&self) -> Option<QName<'d>> {
        use self::Node::*;
        match self {
            &Root(_)                  => None,
            &Element(n)               => Some(n.name()),
            &Attribute(n)             => Some(n.name()),
            &Text(_)                  => None,
            &Comment(_)               => None,
            &ProcessingInstruction(n) => Some(QName::new(n.target())),
        }
    }

    pub fn parent(&self) -> Option<Node<'d>> {
        use self::Node::*;
        match self {
            &Root(_)                  => None,
            &Element(n)               => n.parent().map(|n| n.to_node()),
            &Attribute(n)             => n.parent().map(|n| n.to_node()),
            &Text(n)                  => n.parent().map(|n| n.to_node()),
            &Comment(n)               => n.parent().map(|n| n.to_node()),
            &ProcessingInstruction(n) => n.parent().map(|n| n.to_node()),
        }
    }

    pub fn children(&self) -> Vec<Node<'d>> {
        use self::Node::*;
        match self {
            &Root(n)                  => n.children().iter().map(|n| n.to_node()).collect(),
            &Element(n)               => n.children().iter().map(|n| n.to_node()).collect(),
            &Attribute(_)             => Vec::new(),
            &Text(_)                  => Vec::new(),
            &Comment(_)               => Vec::new(),
            &ProcessingInstruction(_) => Vec::new(),
        }
    }

    pub fn preceding_siblings(&self) -> Vec<Node<'d>> {
        use self::Node::*;
        match self {
            &Root(_)                  => Vec::new(),
            &Element(n)               => n.preceding_siblings().iter().rev().map(|n| n.to_node()).collect(),
            &Attribute(_)             => Vec::new(),
            &Text(n)                  => n.preceding_siblings().iter().rev().map(|n| n.to_node()).collect(),
            &Comment(n)               => n.preceding_siblings().iter().rev().map(|n| n.to_node()).collect(),
            &ProcessingInstruction(n) => n.preceding_siblings().iter().rev().map(|n| n.to_node()).collect(),
        }
    }

    pub fn following_siblings(&self) -> Vec<Node<'d>> {
        use self::Node::*;
        match self {
            &Root(_)                  => Vec::new(),
            &Element(n)               => n.following_siblings().iter().map(|n| n.to_node()).collect(),
            &Attribute(_)             => Vec::new(),
            &Text(n)                  => n.following_siblings().iter().map(|n| n.to_node()).collect(),
            &Comment(n)               => n.following_siblings().iter().map(|n| n.to_node()).collect(),
            &ProcessingInstruction(n) => n.following_siblings().iter().map(|n| n.to_node()).collect(),
        }
    }
}

conversion_trait!(ToNode, to_node, Node, {
    dom4::Root => Node::Root,
    dom4::Element => Node::Element,
    dom4::Attribute => Node::Attribute,
    dom4::Text => Node::Text,
    dom4::Comment => Node::Comment,
    dom4::ProcessingInstruction => Node::ProcessingInstruction
});

impl<'d> ToNode<'d> for dom4::ChildOfRoot<'d> {
    fn to_node(self) -> Node<'d> {
        use self::Node::*;
        match self {
            dom4::ChildOfRoot::Element(n)               => Element(n),
            dom4::ChildOfRoot::Comment(n)               => Comment(n),
            dom4::ChildOfRoot::ProcessingInstruction(n) => ProcessingInstruction(n),
        }
    }
}

impl<'d> ToNode<'d> for dom4::ChildOfElement<'d> {
    fn to_node(self) -> Node<'d> {
        use self::Node::*;
        match self {
            dom4::ChildOfElement::Element(n)               => Element(n),
            dom4::ChildOfElement::Text(n)                  => Text(n),
            dom4::ChildOfElement::Comment(n)               => Comment(n),
            dom4::ChildOfElement::ProcessingInstruction(n) => ProcessingInstruction(n),
        }
    }
}

impl<'d> ToNode<'d> for dom4::ParentOfChild<'d> {
    fn to_node(self) -> Node<'d> {
        use self::Node::*;
        match self {
            dom4::ParentOfChild::Root(n)    => Root(n),
            dom4::ParentOfChild::Element(n) => Element(n),
        }
    }
}

/// A collection of nodes
#[derive(PartialEq,Debug,Clone)]
pub struct Nodeset<'d> {
    nodes: Vec<Node<'d>>,
}

impl<'d> Nodeset<'d> {
    pub fn new() -> Nodeset<'d> {
        Nodeset { nodes: Vec::new() }
    }

    pub fn add<N : ToNode<'d>>(&mut self, node: N) {
        self.nodes.push(node.to_node());
    }

    pub fn iter(&self) -> Iter<Node<'d>> {
        self.nodes.iter()
    }

    pub fn add_nodeset(& mut self, other: &Nodeset<'d>) {
        self.nodes.push_all(&other.nodes);
    }

    pub fn size(&self) -> usize {
        self.nodes.len()
    }

    pub fn into_iter(self) -> vec::IntoIter<Node<'d>> {
        self.nodes.into_iter()
    }

    pub fn document_order_first(&self) -> Option<Node<'d>> {
        let doc = match self.nodes.first() {
            Some(n) => n.document(),
            None => return None,
        };

        let mut idx = 0;
        let mut stack = vec![doc.root().to_node()];
        let mut order: HashMap<Node, usize> = HashMap::new();

        // Rebuilding this each time cannot possibly be performant,
        // but I want to see how widely used this is first before
        // picking an appropriate caching point.

        while let Some(n) = stack.pop() {
            order.insert(n, idx);
            idx += 1;
            let c = n.children();

            stack.extend(c.into_iter().rev());

            if let Node::Element(e) = n {
                // TODO: namespaces
                stack.extend(e.attributes().into_iter().map(|a| a.to_node()));
            }
        }

        self.nodes.iter().min_by(|&&n| order[n]).map(|n| *n)
    }
}

impl<'a, 'd : 'a> FromIterator<EvaluationContext<'a, 'd>> for Nodeset<'d> {
    fn from_iter<T>(iterator: T) -> Nodeset<'d>
        where T: Iterator<Item=EvaluationContext<'a, 'd>>
    {
        let mut ns = Nodeset::new();
        for n in iterator { ns.add(n.node) };
        ns
    }
}

#[cfg(test)]
mod test {
    use document::Package;

    use super::Node::{
        Attribute,
        Comment,
        Element,
        ProcessingInstruction,
        Root,
        Text,
    };
    use super::{Nodeset,ToNode};

    #[test]
    fn nodeset_can_include_all_node_types() {
        let package = Package::new();
        let doc = package.as_document();
        let mut nodes = Nodeset::new();

        let r = doc.root();
        let e = doc.create_element("element");
        let a = e.set_attribute_value("name", "value");
        let t = doc.create_text("text");
        let c = doc.create_comment("comment");
        let p = doc.create_processing_instruction("pi", None);

        nodes.add(r);
        nodes.add(e);
        nodes.add(a);
        nodes.add(t);
        nodes.add(c);
        nodes.add(p);

        let node_vec: Vec<_> = nodes.iter().collect();

        assert_eq!(6, node_vec.len());
        assert_eq!(node_vec[0], &Root(r));
        assert_eq!(node_vec[1], &Element(e));
        assert_eq!(node_vec[2], &Attribute(a));
        assert_eq!(node_vec[3], &Text(t));
        assert_eq!(node_vec[4], &Comment(c));
        assert_eq!(node_vec[5], &ProcessingInstruction(p));
    }

    #[test]
    fn nodesets_can_be_combined() {
        let package = Package::new();
        let doc = package.as_document();

        let mut all_nodes = Nodeset::new();
        let mut nodes1 = Nodeset::new();
        let mut nodes2 = Nodeset::new();

        let e1 = doc.create_element("element1");
        let e2 = doc.create_element("element2");

        all_nodes.add(e1);
        all_nodes.add(e2);

        nodes1.add(e1);
        nodes2.add(e2);

        nodes1.add_nodeset(&nodes2);

        assert_eq!(all_nodes, nodes1);
    }

    #[test]
    fn nodeset_knows_first_node_in_document_order() {
        let package = Package::new();
        let doc = package.as_document();

        let c1 = doc.create_comment("1");
        let c2 = doc.create_comment("2");
        doc.root().append_child(c1);
        doc.root().append_child(c2);

        let nodes = nodeset![c2, c1];

        assert_eq!(Some(c1.to_node()), nodes.document_order_first());
    }

    #[test]
    fn attributes_come_before_children_in_document_order() {
        let package = Package::new();
        let doc = package.as_document();

        let parent = doc.create_element("parent");
        let attr = parent.set_attribute_value("a", "v");
        let child = doc.create_element("child");

        doc.root().append_child(parent);
        parent.append_child(child);

        let nodes = nodeset![child, attr];

        assert_eq!(Some(attr.to_node()), nodes.document_order_first());
    }
}
<|endoftext|>