<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(path_ext)]
#![feature(rustc_private)]
#![cfg(not(test))]
#![feature(result_expect)]

#[macro_use]
extern crate log;
extern crate rustfmt;
extern crate toml;

use rustfmt::{WriteMode, run};
use rustfmt::config::Config;

use std::env;
use std::fs::{File, PathExt};
use std::io::{self, Read};
use std::path::PathBuf;
use std::str::FromStr;

// Try to find a project file in the current directory and its parents.
fn lookup_project_file() -> io::Result<PathBuf> {
    let mut current = try!(env::current_dir());
    loop {
        let config_file = current.join("rustfmt.toml");
        if config_file.exists() {
            return Ok(config_file);
        } else {
            current = match current.parent() {
                // if the current directory has no parent, we're done searching
                None => return Err(io::Error::new(io::ErrorKind::NotFound, "config not found")),
                Some(path) => path.to_path_buf(),
            };
        }
    }
}

// Try to find a project file. If it's found, read it.
fn lookup_and_read_project_file() -> io::Result<(PathBuf, String)> {
    let path = try!(lookup_project_file());
    let mut file = try!(File::open(&path));
    let mut toml = String::new();
    try!(file.read_to_string(&mut toml));
    Ok((path, toml))
}

fn main() {
    let (args, write_mode) = determine_params(std::env::args());

    let config = match lookup_and_read_project_file() {
        Ok((path, toml)) => {
            println!("Project config file: {}", path.display());
            Config::from_toml(&toml)
        }
        Err(_) => Default::default(),
    };

    run(args, write_mode, Box::new(config));
    std::process::exit(0);
}

fn determine_params<I>(args: I) -> (Vec<String>, WriteMode)
    where I: Iterator<Item = String>
{
    let prefix = "--write-mode=";
    let mut write_mode = WriteMode::Replace;

    // The NewFile option currently isn't supported because it requires another
    // parameter, but it can be added later.
    let args = args.filter(|arg| {
        if arg.starts_with(prefix) {
            write_mode = FromStr::from_str(&arg[prefix.len()..]).expect("Unrecognized write mode");
            false
        } else {
            true
        }
    }).collect();

    (args, write_mode)
}
<commit_msg>Fix usage of the tool. Provide -h and --help<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(path_ext)]
#![feature(rustc_private)]
#![cfg(not(test))]
#![feature(result_expect)]

#[macro_use]
extern crate log;
extern crate rustfmt;
extern crate toml;

use rustfmt::{WriteMode, run};
use rustfmt::config::Config;

use std::env;
use std::fs::{File, PathExt};
use std::io::{self, Read};
use std::path::PathBuf;
use std::str::FromStr;

// Try to find a project file in the current directory and its parents.
fn lookup_project_file() -> io::Result<PathBuf> {
    let mut current = try!(env::current_dir());
    loop {
        let config_file = current.join("rustfmt.toml");
        if config_file.exists() {
            return Ok(config_file);
        } else {
            current = match current.parent() {
                // if the current directory has no parent, we're done searching
                None => return Err(io::Error::new(io::ErrorKind::NotFound, "config not found")),
                Some(path) => path.to_path_buf(),
            };
        }
    }
}

// Try to find a project file. If it's found, read it.
fn lookup_and_read_project_file() -> io::Result<(PathBuf, String)> {
    let path = try!(lookup_project_file());
    let mut file = try!(File::open(&path));
    let mut toml = String::new();
    try!(file.read_to_string(&mut toml));
    Ok((path, toml))
}

fn main() {
    let (args, write_mode) = determine_params(std::env::args());

    let config = match lookup_and_read_project_file() {
        Ok((path, toml)) => {
            println!("Project config file: {}", path.display());
            Config::from_toml(&toml)
        }
        Err(_) => Default::default(),
    };

    run(args, write_mode, Box::new(config));
    std::process::exit(0);
}

fn usage<S: Into<String>>(reason: S) {
    print!("{}\n\r usage: rustfmt [-h Help] [--write-mode=[true/false]] <file_name>", reason.into());
    std::process::exit(1);
}

fn determine_params<I>(args: I) -> (Vec<String>, WriteMode)
    where I: Iterator<Item = String>
{
    let arg_prefix = "-";
    let write_mode_prefix = "--write-mode=";
    let help_mode = "-h";
    let long_help_mode = "--help";
    let mut write_mode = WriteMode::Replace;

    // The NewFile option currently isn't supported because it requires another
    // parameter, but it can be added later.
    let args:Vec<String> = args.filter(|arg| {
        if arg.starts_with(write_mode_prefix) {
            write_mode = FromStr::from_str(&arg[write_mode_prefix.len()..]).expect("Unrecognized write mode");
            false
        } else if arg.starts_with(help_mode) || arg.starts_with(long_help_mode) {
            usage("");
            false
        } else if arg.starts_with(arg_prefix) {
            usage("Invalid argument");
            false
        } else {
            true
        }
    }).collect();
    if args.len() < 2 {
        usage("Please provide a file to be formatted");
    }

    (args, write_mode)
}
<|endoftext|>


<commit_before>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// xfail-test
struct Obj {
    member: uint
}

pub impl Obj {
    fn boom() -> bool {
        return 1+1 == 2
    }
    fn chirp() {
        self.boom(); //~ ERROR wat
    }
}

fn main() {
    let o = Obj { member: 0 };
    o.chirp();
    1 + 1;
}
<commit_msg>Enable test for #3707<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

struct Obj {
    member: uint
}

pub impl Obj {
    fn boom() -> bool {
        return 1+1 == 2
    }
    fn chirp(&self) {
        self.boom(); //~ ERROR `&Obj` does not implement any method in scope named `boom`
    }
}

fn main() {
    let o = Obj { member: 0 };
    o.chirp();
    1 + 1;
}
<|endoftext|>


<commit_before>/* Copyright 2015 Jordan Miner
 *
 * Licensed under the MIT license <LICENSE or
 * http://opensource.org/licenses/MIT>. This file may not be copied,
 * modified, or distributed except according to those terms.
 */

use std::any::Any;
use std::cell::RefCell;
use std::collections::HashMap;
use std::marker::PhantomData;
use std::mem;
use std::ops::{CoerceUnsized};
use std::panic::{self, RecoverSafe};
use std::rc::Rc;
use std::thread::LocalKey;
use libc::{c_int, c_char};
use iup_sys::*;
use super::{Control, MouseButton, KeyboardMouseStatus};
use super::handle_rc::add_destroy_callback;

pub enum CallbackAction {
    Default,
    // Close is not needed because it is just as easy to call IupExitLoop()
    // and then the API is smaller.
    // Close,
    Ignore,
    Continue,
}

// To avoid letting panics unwind into C stack frames, we need to catch the panic,
// store it away, and check it later when there's no C code on the stack.
thread_local!(
    static PANIC_PAYLOAD: RefCell<Option<Box<Any + Send + 'static>>> = RefCell::new(None)
);

pub fn set_panic_payload(payload: Box<Any + Send + 'static>) {
    PANIC_PAYLOAD.with(|cell|
        *cell.borrow_mut() = Some(payload)
    );
}

pub fn is_panic_pending() -> bool {
    PANIC_PAYLOAD.with(|cell| cell.borrow().is_some())
}

pub fn take_panic_payload() -> Option<Box<Any + Send + 'static>> {
    PANIC_PAYLOAD.with(|cell| cell.borrow_mut().take())
}

// If a callback's documentation does not specify valid return values, then only IUP_DEFAULT is
// supported.

// use LDESTROY_CB instead of DESTROY_CB

// TODO: if Token is public, then I need to make sure that converting from a MapCallbackToken to
// a DestroyToken can't cause unsafety
pub struct Token {
    id: usize,
    ih: *mut Ihandle,
}

macro_rules! callback_token {
    ($name:ident) => {
        pub struct $name(Token);

        impl From<$name> for Token {
            fn from($name(t): $name) -> Token { t }
        }

        impl From<Token> for $name {
            fn from(t: Token) -> $name { $name(t) }
        }
    };
}

pub struct CallbackRegistry<F: ?Sized + 'static, T: Into<Token> + From<Token>> {
    cb_name: &'static str,
    cb_fn: Icallback,
    pub callbacks: Rc<RefCell<HashMap<*mut Ihandle, Vec<(usize, Box<F>)>>>>,
    phantom: PhantomData<*const T>,
}

impl<F: ?Sized, T: Into<Token> + From<Token>> CallbackRegistry<F, T> {
    // Icallback is the most common type of callback, but there are many exceptions. If the
    // callback's signature does not match Icallback, just cast to Icallback.
    pub fn new(cb_name: &'static str, cb_fn: Icallback) -> CallbackRegistry<F, T> {
        let reg = CallbackRegistry {
            cb_name: cb_name,
            cb_fn: cb_fn,
            callbacks: Rc::new(RefCell::new(HashMap::new())),
            phantom: PhantomData,
        };
        // When a control is destroyed, we need to remove all of its callbacks.
        let callbacks = reg.callbacks.clone();
        add_destroy_callback(move |ih| { callbacks.borrow_mut().remove(&ih); });
        reg
    }

    fn add_callback_inner(&self, ih: *mut Ihandle, cb: Box<F>) -> T {
        let mut map = self.callbacks.borrow_mut();
        let cbs = map.entry(ih).or_insert_with(|| Vec::with_capacity(4));
        let id = cbs.last().map(|&(id, _)| id + 1).unwrap_or(0);
        cbs.push((id, cb));

        unsafe {
            IupSetCallback(ih, self.cb_name.as_ptr() as *const i8, self.cb_fn);
        }

        Token { id: id, ih: ih }.into()
    }

    pub fn add_callback<G>(&self, ih: *mut Ihandle, cb: G) -> T
    where Box<G>: CoerceUnsized<Box<F>>
    {
        self.add_callback_inner(ih, Box::new(cb) as Box<F>)
    }

    pub fn remove_callback(&self, ih: *mut Ihandle, token: T) {
        let token: Token = token.into();
        assert!(ih == token.ih, "token used with wrong control");
        let mut map = self.callbacks.borrow_mut();
        if let Some(cbs) = map.get_mut(&token.ih) {
            if let Some(index) = cbs.iter().position(|&(id, _)| id == token.id) {
                cbs.remove(index);
            } else {
                panic!("failed to remove callback");
            }
        }
    }
}

struct AssertRecoverSafeVal<T: ?Sized>(T);

impl<T: ?Sized> RecoverSafe for AssertRecoverSafeVal<T> {}

pub fn with_callbacks<F, G: ?Sized, T>(ih: *mut Ihandle,
                                       reg: &'static LocalKey<CallbackRegistry<G, T>>, f: F)
                                       -> c_int
                                       where F: FnOnce(&mut [(usize, Box<G>)]) -> c_int,
                                             G: 'static,
                                             T: Into<Token> + From<Token> {
    let h = AssertRecoverSafeVal(f);
    let result = panic::recover(move || {
        reg.with(move |reg| {
            if let Some(cbs) = reg.callbacks.borrow_mut().get_mut(&ih) {
                h.0(cbs)
            } else {
                IUP_DEFAULT
            }
        })
    });
    match result {
        Ok(r) => r,
        Err(err) => {
            set_panic_payload(err);
            unsafe { IupExitLoop(); }
            IUP_DEFAULT
        },
    }
}

pub fn simple_callback<T>(ih: *mut Ihandle,
                      reg: &'static LocalKey<CallbackRegistry<FnMut(), T>>)
                      -> c_int where T: Into<Token> + From<Token> {
    with_callbacks(ih, reg, |cbs| {
        for cb in cbs {
            cb.1();
        }
        IUP_DEFAULT
    })
}

pub struct Event<'a, F: ?Sized + 'static, T: 'static + Into<Token> + From<Token>> {
    control: &'a Control,
    reg: &'static LocalKey<CallbackRegistry<F, T>>,
}

impl<'a, F: ?Sized, T: Into<Token> + From<Token>> Event<'a, F, T> {
    pub fn new(control: &'a Control, reg: &'static LocalKey<CallbackRegistry<F, T>>) -> Event<'a, F, T> {
        Event { control: control, reg: reg }
    }

    pub fn add<G>(&self, cb: G) -> T
    where Box<G>: CoerceUnsized<Box<F>>
    {
        self.reg.with(|reg| reg.add_callback_inner(self.control.handle(), Box::new(cb) as Box<F>))
    }

    pub fn remove(&self, token: T) {
        self.reg.with(|reg| reg.remove_callback(self.control.handle(), token))
    }
}

pub trait MenuCommonCallbacks : Control {
    // fn map_event();
    // fn unmap_event();
    // fn destroy_event();
}

callback_token!(EnterWindowCallbackToken);
thread_local!(
    static ENTER_WINDOW_CALLBACKS: CallbackRegistry<FnMut(), EnterWindowCallbackToken> =
        CallbackRegistry::new("ENTERWINDOW_CB", enter_window_cb)
);
extern fn enter_window_cb(ih: *mut Ihandle) -> c_int {
    simple_callback(ih, &ENTER_WINDOW_CALLBACKS)
}

callback_token!(LeaveWindowCallbackToken);
thread_local!(
    static LEAVE_WINDOW_CALLBACKS: CallbackRegistry<FnMut(), LeaveWindowCallbackToken> =
        CallbackRegistry::new("LEAVEWINDOW_CB", leave_window_cb)
);
extern fn leave_window_cb(ih: *mut Ihandle) -> c_int {
    simple_callback(ih, &LEAVE_WINDOW_CALLBACKS)
}

pub trait NonMenuCommonCallbacks : MenuCommonCallbacks {
    // fn get_focus_event();
    // fn kill_focus_event();

    fn enter_window_event<'a>(&'a self) -> Event<'a, FnMut(), EnterWindowCallbackToken>
    where &'a Self: CoerceUnsized<&'a Control> {
        Event::new(self as &Control, &ENTER_WINDOW_CALLBACKS)
    }

    fn leave_window_event<'a>(&'a self) -> Event<'a, FnMut(), LeaveWindowCallbackToken>
    where &'a Self: CoerceUnsized<&'a Control> {
        Event::new(self as &Control, &LEAVE_WINDOW_CALLBACKS)
    }

    // fn k_any_event();
}

#[derive(Clone)]
pub struct ButtonArgs {
    pub button: MouseButton,
    pub pressed: bool,
    pub x: i32,
    pub y: i32,
    pub status: KeyboardMouseStatus,
    _dummy: (),
}

callback_token!(ButtonCallbackToken);
thread_local!(
    static BUTTON_CALLBACKS: CallbackRegistry<FnMut(&ButtonArgs), ButtonCallbackToken> =
        CallbackRegistry::new("BUTTON_CB",  unsafe { mem::transmute::<_, Icallback>(button_cb) })
);
unsafe extern fn button_cb(ih: *mut Ihandle, button: c_int, pressed: c_int, x: c_int, y: c_int, status: *mut c_char) -> c_int {
    // Maybe the callback should be able to return Ignore (and thus this function return
    // IUP_IGNORE). My main hesitation is that IUP's docs state that it is system
    // dependent: "On some controls if IUP_IGNORE is returned the action is ignored (this is
    // system dependent)." Plus, it doesn't seem really useful and is more verbose.
    BUTTON_CALLBACKS.with(|reg| {
        if let Some(cbs) = reg.callbacks.borrow_mut().get_mut(&ih) {
            let args = ButtonArgs {
                button: MouseButton::from_int(button),
                pressed: pressed != 0,
                x: x as i32,
                y: y as i32,
                status: KeyboardMouseStatus::from_cstr(status),
                _dummy: (),
            };
            for cb in cbs {
                cb.1(&args);
            }
        }
    });
    IUP_DEFAULT
}

pub trait ButtonCallback {
    fn button_event<'a>(&'a self) -> Event<'a, FnMut(&ButtonArgs), ButtonCallbackToken>
    where &'a Self: CoerceUnsized<&'a Control> {
        Event::new(self as &Control, &BUTTON_CALLBACKS)
    }
}
<commit_msg>Use with_callbacks for button events<commit_after>/* Copyright 2015 Jordan Miner
 *
 * Licensed under the MIT license <LICENSE or
 * http://opensource.org/licenses/MIT>. This file may not be copied,
 * modified, or distributed except according to those terms.
 */

use std::any::Any;
use std::cell::RefCell;
use std::collections::HashMap;
use std::marker::PhantomData;
use std::mem;
use std::ops::{CoerceUnsized};
use std::panic::{self, RecoverSafe};
use std::rc::Rc;
use std::thread::LocalKey;
use libc::{c_int, c_char};
use iup_sys::*;
use super::{Control, MouseButton, KeyboardMouseStatus};
use super::handle_rc::add_destroy_callback;

pub enum CallbackAction {
    Default,
    // Close is not needed because it is just as easy to call IupExitLoop()
    // and then the API is smaller.
    // Close,
    Ignore,
    Continue,
}

// To avoid letting panics unwind into C stack frames, we need to catch the panic,
// store it away, and check it later when there's no C code on the stack.
thread_local!(
    static PANIC_PAYLOAD: RefCell<Option<Box<Any + Send + 'static>>> = RefCell::new(None)
);

pub fn set_panic_payload(payload: Box<Any + Send + 'static>) {
    PANIC_PAYLOAD.with(|cell|
        *cell.borrow_mut() = Some(payload)
    );
}

pub fn is_panic_pending() -> bool {
    PANIC_PAYLOAD.with(|cell| cell.borrow().is_some())
}

pub fn take_panic_payload() -> Option<Box<Any + Send + 'static>> {
    PANIC_PAYLOAD.with(|cell| cell.borrow_mut().take())
}

// If a callback's documentation does not specify valid return values, then only IUP_DEFAULT is
// supported.

// use LDESTROY_CB instead of DESTROY_CB

// TODO: if Token is public, then I need to make sure that converting from a MapCallbackToken to
// a DestroyToken can't cause unsafety
pub struct Token {
    id: usize,
    ih: *mut Ihandle,
}

macro_rules! callback_token {
    ($name:ident) => {
        pub struct $name(Token);

        impl From<$name> for Token {
            fn from($name(t): $name) -> Token { t }
        }

        impl From<Token> for $name {
            fn from(t: Token) -> $name { $name(t) }
        }
    };
}

pub struct CallbackRegistry<F: ?Sized + 'static, T: Into<Token> + From<Token>> {
    cb_name: &'static str,
    cb_fn: Icallback,
    callbacks: Rc<RefCell<HashMap<*mut Ihandle, Vec<(usize, Box<F>)>>>>,
    phantom: PhantomData<*const T>,
}

impl<F: ?Sized, T: Into<Token> + From<Token>> CallbackRegistry<F, T> {
    // Icallback is the most common type of callback, but there are many exceptions. If the
    // callback's signature does not match Icallback, just cast to Icallback.
    pub fn new(cb_name: &'static str, cb_fn: Icallback) -> CallbackRegistry<F, T> {
        let reg = CallbackRegistry {
            cb_name: cb_name,
            cb_fn: cb_fn,
            callbacks: Rc::new(RefCell::new(HashMap::new())),
            phantom: PhantomData,
        };
        // When a control is destroyed, we need to remove all of its callbacks.
        let callbacks = reg.callbacks.clone();
        add_destroy_callback(move |ih| { callbacks.borrow_mut().remove(&ih); });
        reg
    }

    fn add_callback_inner(&self, ih: *mut Ihandle, cb: Box<F>) -> T {
        let mut map = self.callbacks.borrow_mut();
        let cbs = map.entry(ih).or_insert_with(|| Vec::with_capacity(4));
        let id = cbs.last().map(|&(id, _)| id + 1).unwrap_or(0);
        cbs.push((id, cb));

        unsafe {
            IupSetCallback(ih, self.cb_name.as_ptr() as *const i8, self.cb_fn);
        }

        Token { id: id, ih: ih }.into()
    }

    pub fn add_callback<G>(&self, ih: *mut Ihandle, cb: G) -> T
    where Box<G>: CoerceUnsized<Box<F>>
    {
        self.add_callback_inner(ih, Box::new(cb) as Box<F>)
    }

    pub fn remove_callback(&self, ih: *mut Ihandle, token: T) {
        let token: Token = token.into();
        assert!(ih == token.ih, "token used with wrong control");
        let mut map = self.callbacks.borrow_mut();
        if let Some(cbs) = map.get_mut(&token.ih) {
            if let Some(index) = cbs.iter().position(|&(id, _)| id == token.id) {
                cbs.remove(index);
            } else {
                panic!("failed to remove callback");
            }
        }
    }
}

struct AssertRecoverSafeVal<T: ?Sized>(T);

impl<T: ?Sized> RecoverSafe for AssertRecoverSafeVal<T> {}

pub fn with_callbacks<F, G: ?Sized, T>(ih: *mut Ihandle,
                                       reg: &'static LocalKey<CallbackRegistry<G, T>>, f: F)
                                       -> c_int
                                       where F: FnOnce(&mut [(usize, Box<G>)]) -> c_int,
                                             G: 'static,
                                             T: Into<Token> + From<Token> {
    let h = AssertRecoverSafeVal(f);
    let result = panic::recover(move || {
        reg.with(move |reg| {
            if let Some(cbs) = reg.callbacks.borrow_mut().get_mut(&ih) {
                h.0(cbs)
            } else {
                IUP_DEFAULT
            }
        })
    });
    match result {
        Ok(r) => r,
        Err(err) => {
            set_panic_payload(err);
            unsafe { IupExitLoop(); }
            IUP_DEFAULT
        },
    }
}

pub fn simple_callback<T>(ih: *mut Ihandle,
                      reg: &'static LocalKey<CallbackRegistry<FnMut(), T>>)
                      -> c_int where T: Into<Token> + From<Token> {
    with_callbacks(ih, reg, |cbs| {
        for cb in cbs {
            cb.1();
        }
        IUP_DEFAULT
    })
}

pub struct Event<'a, F: ?Sized + 'static, T: 'static + Into<Token> + From<Token>> {
    control: &'a Control,
    reg: &'static LocalKey<CallbackRegistry<F, T>>,
}

impl<'a, F: ?Sized, T: Into<Token> + From<Token>> Event<'a, F, T> {
    pub fn new(control: &'a Control, reg: &'static LocalKey<CallbackRegistry<F, T>>) -> Event<'a, F, T> {
        Event { control: control, reg: reg }
    }

    pub fn add<G>(&self, cb: G) -> T
    where Box<G>: CoerceUnsized<Box<F>>
    {
        self.reg.with(|reg| reg.add_callback_inner(self.control.handle(), Box::new(cb) as Box<F>))
    }

    pub fn remove(&self, token: T) {
        self.reg.with(|reg| reg.remove_callback(self.control.handle(), token))
    }
}

pub trait MenuCommonCallbacks : Control {
    // fn map_event();
    // fn unmap_event();
    // fn destroy_event();
}

callback_token!(EnterWindowCallbackToken);
thread_local!(
    static ENTER_WINDOW_CALLBACKS: CallbackRegistry<FnMut(), EnterWindowCallbackToken> =
        CallbackRegistry::new("ENTERWINDOW_CB", enter_window_cb)
);
extern fn enter_window_cb(ih: *mut Ihandle) -> c_int {
    simple_callback(ih, &ENTER_WINDOW_CALLBACKS)
}

callback_token!(LeaveWindowCallbackToken);
thread_local!(
    static LEAVE_WINDOW_CALLBACKS: CallbackRegistry<FnMut(), LeaveWindowCallbackToken> =
        CallbackRegistry::new("LEAVEWINDOW_CB", leave_window_cb)
);
extern fn leave_window_cb(ih: *mut Ihandle) -> c_int {
    simple_callback(ih, &LEAVE_WINDOW_CALLBACKS)
}

pub trait NonMenuCommonCallbacks : MenuCommonCallbacks {
    // fn get_focus_event();
    // fn kill_focus_event();

    fn enter_window_event<'a>(&'a self) -> Event<'a, FnMut(), EnterWindowCallbackToken>
    where &'a Self: CoerceUnsized<&'a Control> {
        Event::new(self as &Control, &ENTER_WINDOW_CALLBACKS)
    }

    fn leave_window_event<'a>(&'a self) -> Event<'a, FnMut(), LeaveWindowCallbackToken>
    where &'a Self: CoerceUnsized<&'a Control> {
        Event::new(self as &Control, &LEAVE_WINDOW_CALLBACKS)
    }

    // fn k_any_event();
}

#[derive(Clone)]
pub struct ButtonArgs {
    pub button: MouseButton,
    pub pressed: bool,
    pub x: i32,
    pub y: i32,
    pub status: KeyboardMouseStatus,
    _dummy: (),
}

callback_token!(ButtonCallbackToken);
thread_local!(
    static BUTTON_CALLBACKS: CallbackRegistry<FnMut(&ButtonArgs), ButtonCallbackToken> =
        CallbackRegistry::new("BUTTON_CB",  unsafe { mem::transmute::<_, Icallback>(button_cb) })
);
unsafe extern fn button_cb(ih: *mut Ihandle, button: c_int, pressed: c_int, x: c_int, y: c_int, status: *mut c_char) -> c_int {
    // Maybe the callback should be able to return Ignore (and thus this function return
    // IUP_IGNORE). My main hesitation is that IUP's docs state that it is system
    // dependent: "On some controls if IUP_IGNORE is returned the action is ignored (this is
    // system dependent)." Plus, it doesn't seem really useful and is more verbose.
    with_callbacks(ih, &BUTTON_CALLBACKS, |cbs| {
        let args = ButtonArgs {
            button: MouseButton::from_int(button),
            pressed: pressed != 0,
            x: x as i32,
            y: y as i32,
            status: KeyboardMouseStatus::from_cstr(status),
            _dummy: (),
        };
        for cb in cbs {
            cb.1(&args);
        }
        IUP_DEFAULT
    })
}

pub trait ButtonCallback {
    fn button_event<'a>(&'a self) -> Event<'a, FnMut(&ButtonArgs), ButtonCallbackToken>
    where &'a Self: CoerceUnsized<&'a Control> {
        Event::new(self as &Control, &BUTTON_CALLBACKS)
    }
}
<|endoftext|>


<commit_before>#![crate_name="cargo"]
#![crate_type="rlib"]

#![feature(macro_rules, phase)]
#![feature(default_type_params)]
#![deny(bad_style, unused)]

extern crate libc;
extern crate regex;
extern crate serialize;
extern crate term;
extern crate time;
#[phase(plugin)] extern crate regex_macros;
#[phase(plugin, link)] extern crate log;

extern crate curl;
extern crate docopt;
extern crate flate2;
extern crate git2;
extern crate glob;
extern crate semver;
extern crate tar;
extern crate toml;
extern crate url;
#[cfg(test)] extern crate hamcrest;

use std::os;
use std::io::stdio::{stdout_raw, stderr_raw};
use std::io::{mod, stdout, stderr};
use serialize::{Decoder, Encoder, Decodable, Encodable, json};
use docopt::FlagParser;

use core::{Shell, MultiShell, ShellConfig};
use term::color::{BLACK};

pub use util::{CargoError, CliError, CliResult, human};

macro_rules! some(
    ($e:expr) => (
        match $e {
            Some(e) => e,
            None => return None
        }
    )
)

// Added so that the try! macro below can refer to cargo::util, while
// other external importers of this macro can use it as well.
//
// "Hygiene strikes again" - @acrichton
mod cargo {
    pub use super::util;
}

#[macro_export]
macro_rules! try (
    ($expr:expr) => ({
        use cargo::util::FromError;
        match $expr.map_err(FromError::from_error) {
            Ok(val) => val,
            Err(err) => return Err(err)
        }
    })
)

macro_rules! raw_try (
    ($expr:expr) => ({
        match $expr {
            Ok(val) => val,
            Err(err) => return Err(err)
        }
    })
)

pub mod core;
pub mod ops;
pub mod sources;
pub mod util;

pub trait RepresentsJSON : Decodable<json::Decoder, json::DecoderError> {}
impl<T: Decodable<json::Decoder, json::DecoderError>> RepresentsJSON for T {}

pub fn execute_main<'a,
                    T: FlagParser,
                    U: RepresentsJSON,
                    V: Encodable<json::Encoder<'a>, io::IoError>>(
                        exec: fn(T, U, &mut MultiShell) -> CliResult<Option<V>>,
                        options_first: bool) {
    // see comments below
    off_the_main_thread(proc() {
        process::<V>(|rest, shell| call_main(exec, shell, rest, options_first));
    });
}

pub fn call_main<'a,
        T: FlagParser,
        U: RepresentsJSON,
        V: Encodable<json::Encoder<'a>, io::IoError>>(
            exec: fn(T, U, &mut MultiShell) -> CliResult<Option<V>>,
            shell: &mut MultiShell,
            args: &[String],
            options_first: bool) -> CliResult<Option<V>> {
    let flags = try!(flags_from_args::<T>(args, options_first));
    let json = try!(json_from_stdin::<U>());

    exec(flags, json, shell)
}

pub fn execute_main_without_stdin<'a,
                                  T: FlagParser,
                                  V: Encodable<json::Encoder<'a>, io::IoError>>(
                                      exec: fn(T, &mut MultiShell) -> CliResult<Option<V>>,
                                      options_first: bool) {
    // see comments below
    off_the_main_thread(proc() {
        process::<V>(|rest, shell| call_main_without_stdin(exec, shell, rest,
                                                           options_first));
    });
}

pub fn call_main_without_stdin<'a,
                               T: FlagParser,
                               V: Encodable<json::Encoder<'a>, io::IoError>>(
            exec: fn(T, &mut MultiShell) -> CliResult<Option<V>>,
            shell: &mut MultiShell,
            args: &[String],
            options_first: bool) -> CliResult<Option<V>> {
    let flags = try!(flags_from_args::<T>(args, options_first));
    exec(flags, shell)
}

fn process<'a, V: Encodable<json::Encoder<'a>, io::IoError>>(
               callback: |&[String], &mut MultiShell| -> CliResult<Option<V>>) {
    let mut shell = shell(true);
    let mut args = os::args();
    args.remove(0);
    process_executed(callback(args.as_slice(), &mut shell), &mut shell)
}

pub fn process_executed<'a,
                        T: Encodable<json::Encoder<'a>, io::IoError>>(
                            result: CliResult<Option<T>>,
                            shell: &mut MultiShell) {
    match result {
        Err(e) => handle_error(e, shell),
        Ok(encodable) => {
            encodable.map(|encodable| {
                let encoded = json::encode(&encodable);
                println!("{}", encoded);
            });
        }
    }
}

pub fn shell(verbose: bool) -> MultiShell<'static> {
    let tty = stderr_raw().isatty();
    let stderr = box stderr() as Box<Writer>;

    let config = ShellConfig { color: true, verbose: verbose, tty: tty };
    let err = Shell::create(stderr, config);

    let tty = stdout_raw().isatty();
    let stdout = box stdout() as Box<Writer>;

    let config = ShellConfig { color: true, verbose: verbose, tty: tty };
    let out = Shell::create(stdout, config);

    MultiShell::new(out, err, verbose)
}

pub fn handle_error(err: CliError, shell: &mut MultiShell) {
    log!(4, "handle_error; err={}", err);

    let CliError { error, exit_code, unknown } = err;

    if unknown {
        let _ = shell.error("An unknown error occurred");
    } else if error.to_string().len() > 0 {
        let _ = shell.error(error.to_string());
    }

    if error.cause().is_some() || unknown {
        let _ = shell.concise(|shell| {
            shell.err().say("\nTo learn more, run the command again with --verbose.", BLACK)
        });
    }

    let _ = shell.verbose(|shell| {
        if unknown {
            let _ = shell.error(error.to_string());
        }
        error.detail().map(|detail| {
            let _ = shell.err().say(format!("{}", detail), BLACK);
        });
        error.cause().map(|err| {
            let _ = handle_cause(err, shell);
        });
        Ok(())
      });

    std::os::set_exit_status(exit_code as int);
}

fn handle_cause(err: &CargoError, shell: &mut MultiShell) {
    let _ = shell.err().say("\nCaused by:", BLACK);
    let _ = shell.err().say(format!("  {}", err.description()), BLACK);

    err.cause().map(|e| handle_cause(e, shell));
}

pub fn version() -> String {
    format!("cargo {}", match option_env!("CFG_VERSION") {
        Some(s) => s.to_string(),
        None => format!("{}.{}.{}{}",
                        env!("CARGO_PKG_VERSION_MAJOR"),
                        env!("CARGO_PKG_VERSION_MINOR"),
                        env!("CARGO_PKG_VERSION_PATCH"),
                        option_env!("CARGO_PKG_VERSION_PRE").unwrap_or(""))
    })
}

fn flags_from_args<T: FlagParser>(args: &[String],
                                  options_first: bool) -> CliResult<T> {
    let args = args.iter().map(|a| a.as_slice()).collect::<Vec<&str>>();
    let config = docopt::Config {
        options_first: options_first,
        help: true,
        version: Some(version()),
    };
    FlagParser::parse_args(config, args.as_slice()).map_err(|e| {
        let code = if e.fatal() {1} else {0};
        CliError::from_error(e, code)
    })
}

fn json_from_stdin<T: RepresentsJSON>() -> CliResult<T> {
    let mut reader = io::stdin();
    let input = try!(reader.read_to_string().map_err(|_| {
        CliError::new("Standard in did not exist or was not UTF-8", 1)
    }));

    let json = try!(json::from_str(input.as_slice()).map_err(|_| {
        CliError::new("Could not parse standard in as JSON", 1)
    }));
    let mut decoder = json::Decoder::new(json);

    Decodable::decode(&mut decoder).map_err(|_| {
        CliError::new("Could not process standard in as input", 1)
    })
}

// Seems curious to run cargo off the main thread, right? Well do I have a story
// for you. Turns out rustdoc does a similar thing, and already has a good
// explanation [1] though, so I'll just point you over there.
//
// [1]: https://github.com/rust-lang/rust/blob/85fd37f/src/librustdoc/lib.rs#L92-L122
fn off_the_main_thread(p: proc():Send) {
    let (tx, rx) = channel();
    spawn(proc() { p(); tx.send(()); });
    if rx.recv_opt().is_err() {
        std::os::set_exit_status(std::rt::DEFAULT_ERROR_CODE);
    }
}
<commit_msg>Remote `unused` lint<commit_after>#![crate_name="cargo"]
#![crate_type="rlib"]

#![feature(macro_rules, phase)]
#![feature(default_type_params)]
#![deny(bad_style)]

extern crate libc;
extern crate regex;
extern crate serialize;
extern crate term;
extern crate time;
#[phase(plugin)] extern crate regex_macros;
#[phase(plugin, link)] extern crate log;

extern crate curl;
extern crate docopt;
extern crate flate2;
extern crate git2;
extern crate glob;
extern crate semver;
extern crate tar;
extern crate toml;
extern crate url;
#[cfg(test)] extern crate hamcrest;

use std::os;
use std::io::stdio::{stdout_raw, stderr_raw};
use std::io::{mod, stdout, stderr};
use serialize::{Decoder, Encoder, Decodable, Encodable, json};
use docopt::FlagParser;

use core::{Shell, MultiShell, ShellConfig};
use term::color::{BLACK};

pub use util::{CargoError, CliError, CliResult, human};

macro_rules! some(
    ($e:expr) => (
        match $e {
            Some(e) => e,
            None => return None
        }
    )
)

// Added so that the try! macro below can refer to cargo::util, while
// other external importers of this macro can use it as well.
//
// "Hygiene strikes again" - @acrichton
mod cargo {
    pub use super::util;
}

#[macro_export]
macro_rules! try (
    ($expr:expr) => ({
        use cargo::util::FromError;
        match $expr.map_err(FromError::from_error) {
            Ok(val) => val,
            Err(err) => return Err(err)
        }
    })
)

macro_rules! raw_try (
    ($expr:expr) => ({
        match $expr {
            Ok(val) => val,
            Err(err) => return Err(err)
        }
    })
)

pub mod core;
pub mod ops;
pub mod sources;
pub mod util;

pub trait RepresentsJSON : Decodable<json::Decoder, json::DecoderError> {}
impl<T: Decodable<json::Decoder, json::DecoderError>> RepresentsJSON for T {}

pub fn execute_main<'a,
                    T: FlagParser,
                    U: RepresentsJSON,
                    V: Encodable<json::Encoder<'a>, io::IoError>>(
                        exec: fn(T, U, &mut MultiShell) -> CliResult<Option<V>>,
                        options_first: bool) {
    // see comments below
    off_the_main_thread(proc() {
        process::<V>(|rest, shell| call_main(exec, shell, rest, options_first));
    });
}

pub fn call_main<'a,
        T: FlagParser,
        U: RepresentsJSON,
        V: Encodable<json::Encoder<'a>, io::IoError>>(
            exec: fn(T, U, &mut MultiShell) -> CliResult<Option<V>>,
            shell: &mut MultiShell,
            args: &[String],
            options_first: bool) -> CliResult<Option<V>> {
    let flags = try!(flags_from_args::<T>(args, options_first));
    let json = try!(json_from_stdin::<U>());

    exec(flags, json, shell)
}

pub fn execute_main_without_stdin<'a,
                                  T: FlagParser,
                                  V: Encodable<json::Encoder<'a>, io::IoError>>(
                                      exec: fn(T, &mut MultiShell) -> CliResult<Option<V>>,
                                      options_first: bool) {
    // see comments below
    off_the_main_thread(proc() {
        process::<V>(|rest, shell| call_main_without_stdin(exec, shell, rest,
                                                           options_first));
    });
}

pub fn call_main_without_stdin<'a,
                               T: FlagParser,
                               V: Encodable<json::Encoder<'a>, io::IoError>>(
            exec: fn(T, &mut MultiShell) -> CliResult<Option<V>>,
            shell: &mut MultiShell,
            args: &[String],
            options_first: bool) -> CliResult<Option<V>> {
    let flags = try!(flags_from_args::<T>(args, options_first));
    exec(flags, shell)
}

fn process<'a, V: Encodable<json::Encoder<'a>, io::IoError>>(
               callback: |&[String], &mut MultiShell| -> CliResult<Option<V>>) {
    let mut shell = shell(true);
    let mut args = os::args();
    args.remove(0);
    process_executed(callback(args.as_slice(), &mut shell), &mut shell)
}

pub fn process_executed<'a,
                        T: Encodable<json::Encoder<'a>, io::IoError>>(
                            result: CliResult<Option<T>>,
                            shell: &mut MultiShell) {
    match result {
        Err(e) => handle_error(e, shell),
        Ok(encodable) => {
            encodable.map(|encodable| {
                let encoded = json::encode(&encodable);
                println!("{}", encoded);
            });
        }
    }
}

pub fn shell(verbose: bool) -> MultiShell<'static> {
    let tty = stderr_raw().isatty();
    let stderr = box stderr() as Box<Writer>;

    let config = ShellConfig { color: true, verbose: verbose, tty: tty };
    let err = Shell::create(stderr, config);

    let tty = stdout_raw().isatty();
    let stdout = box stdout() as Box<Writer>;

    let config = ShellConfig { color: true, verbose: verbose, tty: tty };
    let out = Shell::create(stdout, config);

    MultiShell::new(out, err, verbose)
}

pub fn handle_error(err: CliError, shell: &mut MultiShell) {
    log!(4, "handle_error; err={}", err);

    let CliError { error, exit_code, unknown } = err;

    if unknown {
        let _ = shell.error("An unknown error occurred");
    } else if error.to_string().len() > 0 {
        let _ = shell.error(error.to_string());
    }

    if error.cause().is_some() || unknown {
        let _ = shell.concise(|shell| {
            shell.err().say("\nTo learn more, run the command again with --verbose.", BLACK)
        });
    }

    let _ = shell.verbose(|shell| {
        if unknown {
            let _ = shell.error(error.to_string());
        }
        error.detail().map(|detail| {
            let _ = shell.err().say(format!("{}", detail), BLACK);
        });
        error.cause().map(|err| {
            let _ = handle_cause(err, shell);
        });
        Ok(())
      });

    std::os::set_exit_status(exit_code as int);
}

fn handle_cause(err: &CargoError, shell: &mut MultiShell) {
    let _ = shell.err().say("\nCaused by:", BLACK);
    let _ = shell.err().say(format!("  {}", err.description()), BLACK);

    err.cause().map(|e| handle_cause(e, shell));
}

pub fn version() -> String {
    format!("cargo {}", match option_env!("CFG_VERSION") {
        Some(s) => s.to_string(),
        None => format!("{}.{}.{}{}",
                        env!("CARGO_PKG_VERSION_MAJOR"),
                        env!("CARGO_PKG_VERSION_MINOR"),
                        env!("CARGO_PKG_VERSION_PATCH"),
                        option_env!("CARGO_PKG_VERSION_PRE").unwrap_or(""))
    })
}

fn flags_from_args<T: FlagParser>(args: &[String],
                                  options_first: bool) -> CliResult<T> {
    let args = args.iter().map(|a| a.as_slice()).collect::<Vec<&str>>();
    let config = docopt::Config {
        options_first: options_first,
        help: true,
        version: Some(version()),
    };
    FlagParser::parse_args(config, args.as_slice()).map_err(|e| {
        let code = if e.fatal() {1} else {0};
        CliError::from_error(e, code)
    })
}

fn json_from_stdin<T: RepresentsJSON>() -> CliResult<T> {
    let mut reader = io::stdin();
    let input = try!(reader.read_to_string().map_err(|_| {
        CliError::new("Standard in did not exist or was not UTF-8", 1)
    }));

    let json = try!(json::from_str(input.as_slice()).map_err(|_| {
        CliError::new("Could not parse standard in as JSON", 1)
    }));
    let mut decoder = json::Decoder::new(json);

    Decodable::decode(&mut decoder).map_err(|_| {
        CliError::new("Could not process standard in as input", 1)
    })
}

// Seems curious to run cargo off the main thread, right? Well do I have a story
// for you. Turns out rustdoc does a similar thing, and already has a good
// explanation [1] though, so I'll just point you over there.
//
// [1]: https://github.com/rust-lang/rust/blob/85fd37f/src/librustdoc/lib.rs#L92-L122
fn off_the_main_thread(p: proc():Send) {
    let (tx, rx) = channel();
    spawn(proc() { p(); tx.send(()); });
    if rx.recv_opt().is_err() {
        std::os::set_exit_status(std::rt::DEFAULT_ERROR_CODE);
    }
}
<|endoftext|>


<commit_before>use opencl;
use opencl::mem::{Buffer, CLBuffer};

use context::Context;
use matrix::Matrix;
use num::Num;

pub enum ClMatrixMode {
    In,
    Out,
    Mut,
}

pub struct ClMatrix<T: Num> {
    rows: usize,
    columns: usize,
    buffer: CLBuffer<T>,
}

impl<T: Num> ClMatrix<T> {
    pub fn new(ctx: &Context, rows: usize, columns: usize, mode: ClMatrixMode) -> ClMatrix<T> {
        let cl_mem_mode =
            match mode {
                ClMatrixMode::In => { opencl::cl::CL_MEM_READ_ONLY },
                ClMatrixMode::Out => { opencl::cl::CL_MEM_WRITE_ONLY },
                ClMatrixMode::Mut => { opencl::cl::CL_MEM_READ_WRITE},
            };
        ClMatrix {
            rows: rows,
            columns: columns,
            buffer: ctx.ctx.create_buffer(rows*columns, cl_mem_mode),
        }
    }

    pub fn from_matrix(ctx: &Context,
                       matrix: &Matrix<T>,
                       mode: ClMatrixMode) -> ClMatrix<T> {
        let cl_mem_mode =
            match mode {
                ClMatrixMode::In => { opencl::cl::CL_MEM_READ_ONLY },
                ClMatrixMode::Out => { opencl::cl::CL_MEM_WRITE_ONLY },
                ClMatrixMode::Mut => { opencl::cl::CL_MEM_READ_WRITE},
            };
        let cl_matrix =
            ClMatrix {
                rows: matrix.rows(),
                columns: matrix.columns(),
                buffer: ctx.ctx.create_buffer(matrix.rows()*matrix.columns(), cl_mem_mode),
            };

        ctx.queue.write(&cl_matrix.buffer, &&matrix.buffer()[..], ());
        cl_matrix
    }

    pub fn add(&self, ctx: &Context, other: &ClMatrix<T>, output: &ClMatrix<T>) -> Event {
        let kernel = ctx.program.create_kernel(format!("vector_add_{}", T::name()).as_str());

        kernel.set_arg(0, &self.buffer);
        kernel.set_arg(1, &other.buffer);
        kernel.set_arg(2, &output.buffer);

        let event = ctx.queue.enqueue_async_kernel(&kernel, self.buffer.len(), None, ());
        Event(event)
    }

    pub fn sub(&self, ctx: &Context, other: &ClMatrix<T>, output: &ClMatrix<T>) -> Event {
        let kernel = ctx.program.create_kernel(format!("vector_sub_{}", T::name()).as_str());

        kernel.set_arg(0, &self.buffer);
        kernel.set_arg(1, &other.buffer);
        kernel.set_arg(2, &output.buffer);

        let event = ctx.queue.enqueue_async_kernel(&kernel, self.buffer.len(), None, ());
        Event(event)
    }

    pub fn transpose(&self, ctx: &Context, output: &ClMatrix<T>) -> Event {
        let kernel = ctx.program.create_kernel(format!("vector_transpose_{}", T::name()).as_str());

        kernel.set_arg(0, &self.buffer);
        kernel.set_arg(1, &output.buffer);
        kernel.set_arg(2, &self.rows);
        kernel.set_arg(3, &self.columns);

        let event = ctx.queue.enqueue_async_kernel(&kernel,
                                                   (self.rows, self.columns),
                                                   None, ());
        Event(event)
    }

    pub fn multiply(&self, ctx: &Context, other: &ClMatrix<T>, output: &ClMatrix<T>) -> Event {
        let kernel = ctx.program.create_kernel(format!("vector_multiply_{}", T::name()).as_str());

        kernel.set_arg(0, &self.buffer);
        kernel.set_arg(1, &other.buffer);
        kernel.set_arg(2, &output.buffer);
        kernel.set_arg(3, &self.columns);
        kernel.set_arg(4, &other.columns);

        let event = ctx.queue.enqueue_async_kernel(&kernel,
                                                   (self.rows, other.columns),
                                                   None, ());
        Event(event)
    }
}

pub struct Event(opencl::hl::Event);

impl Event {
    pub fn get<T: Num>(&self, ctx: &Context, cl_matrix: &ClMatrix<T>) -> Matrix<T> {
        let vec = ctx.queue.get(&cl_matrix.buffer, &self.0);
        Matrix::from_vec(cl_matrix.rows, cl_matrix.columns, vec)
    }
}

#[test]
fn cl_matrix_add() {
    let ref ctx = Context::new();

    let a = Matrix::from_vec(1, 10000, (0..10000).collect());
    let b = Matrix::from_vec(1, 10000, (0..10000).map(|x| x*2).collect());

    let a_cl = ClMatrix::from_matrix(ctx, &a, ClMatrixMode::In);
    let b_cl = ClMatrix::from_matrix(ctx, &b, ClMatrixMode::In);
    let c_cl: ClMatrix<i32> = ClMatrix::new(ctx, 1, 10000, ClMatrixMode::Out);

    let event = a_cl.add(ctx, &b_cl, &c_cl);

    let c = event.get(ctx, &c_cl);

    for i in 0..10000 {
        assert!(c[(0, i)] == a[(0, i)] + b[(0, i)]);
    }
}
<commit_msg>row and column access methods on ClMatrix<commit_after>use opencl;
use opencl::mem::{Buffer, CLBuffer};

use context::Context;
use matrix::Matrix;
use num::Num;

pub enum ClMatrixMode {
    In,
    Out,
    Mut,
}

pub struct ClMatrix<T: Num> {
    rows: usize,
    columns: usize,
    buffer: CLBuffer<T>,
}

impl<T: Num> ClMatrix<T> {
    pub fn new(ctx: &Context, rows: usize, columns: usize, mode: ClMatrixMode) -> ClMatrix<T> {
        let cl_mem_mode =
            match mode {
                ClMatrixMode::In => { opencl::cl::CL_MEM_READ_ONLY },
                ClMatrixMode::Out => { opencl::cl::CL_MEM_WRITE_ONLY },
                ClMatrixMode::Mut => { opencl::cl::CL_MEM_READ_WRITE},
            };
        ClMatrix {
            rows: rows,
            columns: columns,
            buffer: ctx.ctx.create_buffer(rows*columns, cl_mem_mode),
        }
    }

    pub fn from_matrix(ctx: &Context,
                       matrix: &Matrix<T>,
                       mode: ClMatrixMode) -> ClMatrix<T> {
        let cl_mem_mode =
            match mode {
                ClMatrixMode::In => { opencl::cl::CL_MEM_READ_ONLY },
                ClMatrixMode::Out => { opencl::cl::CL_MEM_WRITE_ONLY },
                ClMatrixMode::Mut => { opencl::cl::CL_MEM_READ_WRITE},
            };
        let cl_matrix =
            ClMatrix {
                rows: matrix.rows(),
                columns: matrix.columns(),
                buffer: ctx.ctx.create_buffer(matrix.rows()*matrix.columns(), cl_mem_mode),
            };

        ctx.queue.write(&cl_matrix.buffer, &&matrix.buffer()[..], ());
        cl_matrix
    }

    pub fn rows(&self) -> usize {
        self.rows
    }

    pub fn columns(&self) -> usize {
        self.columns
    }

    pub fn add(&self, ctx: &Context, other: &ClMatrix<T>, output: &ClMatrix<T>) -> Event {
        let kernel = ctx.program.create_kernel(format!("vector_add_{}", T::name()).as_str());

        kernel.set_arg(0, &self.buffer);
        kernel.set_arg(1, &other.buffer);
        kernel.set_arg(2, &output.buffer);

        let event = ctx.queue.enqueue_async_kernel(&kernel, self.buffer.len(), None, ());
        Event(event)
    }

    pub fn sub(&self, ctx: &Context, other: &ClMatrix<T>, output: &ClMatrix<T>) -> Event {
        let kernel = ctx.program.create_kernel(format!("vector_sub_{}", T::name()).as_str());

        kernel.set_arg(0, &self.buffer);
        kernel.set_arg(1, &other.buffer);
        kernel.set_arg(2, &output.buffer);

        let event = ctx.queue.enqueue_async_kernel(&kernel, self.buffer.len(), None, ());
        Event(event)
    }

    pub fn transpose(&self, ctx: &Context, output: &ClMatrix<T>) -> Event {
        let kernel = ctx.program.create_kernel(format!("vector_transpose_{}", T::name()).as_str());

        kernel.set_arg(0, &self.buffer);
        kernel.set_arg(1, &output.buffer);
        kernel.set_arg(2, &self.rows);
        kernel.set_arg(3, &self.columns);

        let event = ctx.queue.enqueue_async_kernel(&kernel,
                                                   (self.rows, self.columns),
                                                   None, ());
        Event(event)
    }

    pub fn multiply(&self, ctx: &Context, other: &ClMatrix<T>, output: &ClMatrix<T>) -> Event {
        let kernel = ctx.program.create_kernel(format!("vector_multiply_{}", T::name()).as_str());

        kernel.set_arg(0, &self.buffer);
        kernel.set_arg(1, &other.buffer);
        kernel.set_arg(2, &output.buffer);
        kernel.set_arg(3, &self.columns);
        kernel.set_arg(4, &other.columns);

        let event = ctx.queue.enqueue_async_kernel(&kernel,
                                                   (self.rows, other.columns),
                                                   None, ());
        Event(event)
    }
}

pub struct Event(opencl::hl::Event);

impl Event {
    pub fn get<T: Num>(&self, ctx: &Context, cl_matrix: &ClMatrix<T>) -> Matrix<T> {
        let vec = ctx.queue.get(&cl_matrix.buffer, &self.0);
        Matrix::from_vec(cl_matrix.rows, cl_matrix.columns, vec)
    }
}

#[test]
fn cl_matrix_add() {
    let ref ctx = Context::new();

    let a = Matrix::from_vec(1, 10000, (0..10000).collect());
    let b = Matrix::from_vec(1, 10000, (0..10000).map(|x| x*2).collect());

    let a_cl = ClMatrix::from_matrix(ctx, &a, ClMatrixMode::In);
    let b_cl = ClMatrix::from_matrix(ctx, &b, ClMatrixMode::In);
    let c_cl: ClMatrix<i32> = ClMatrix::new(ctx, 1, 10000, ClMatrixMode::Out);

    let event = a_cl.add(ctx, &b_cl, &c_cl);

    let c = event.get(ctx, &c_cl);

    for i in 0..10000 {
        assert!(c[(0, i)] == a[(0, i)] + b[(0, i)]);
    }
}
<|endoftext|>


<commit_before>use nom::multispace;
use std::collections::{HashSet, VecDeque};
use std::str;
use std::fmt;

use column::Column;
use common::{binary_comparison_operator, column_identifier, integer_literal, opt_multispace,
             string_literal, value_list, Literal, Operator};

use select::{nested_selection, SelectStatement};

#[derive(Clone, Debug, Hash, PartialEq, Serialize, Deserialize)]
pub enum ConditionBase {
    Field(Column),
    Literal(Literal),
    Placeholder,
    NestedSelect(Box<SelectStatement>),
}

impl fmt::Display for ConditionBase {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ConditionBase::Field(ref col) => write!(f, "{}", col),
            ConditionBase::Literal(ref literal) => write!(f, "{}", literal.to_string()),
            ConditionBase::Placeholder => write!(f, "?"),
            ConditionBase::NestedSelect(ref select) => write!(f, "{}", select),
        }
    }
}

#[derive(Clone, Debug, Hash, PartialEq, Serialize, Deserialize)]
pub struct ConditionTree {
    pub operator: Operator,
    pub left: Box<ConditionExpression>,
    pub right: Box<ConditionExpression>,
}

impl<'a> ConditionTree {
    pub fn contained_columns(&'a self) -> HashSet<&'a Column> {
        let mut s = HashSet::new();
        let mut q = VecDeque::<&'a ConditionTree>::new();
        q.push_back(self);
        while let Some(ref ct) = q.pop_front() {
            match *ct.left.as_ref() {
                ConditionExpression::Base(ConditionBase::Field(ref c)) => {
                    s.insert(c);
                }
                ConditionExpression::LogicalOp(ref ct)
                | ConditionExpression::ComparisonOp(ref ct) => q.push_back(ct),
                _ => (),
            }
            match *ct.right.as_ref() {
                ConditionExpression::Base(ConditionBase::Field(ref c)) => {
                    s.insert(c);
                }
                ConditionExpression::LogicalOp(ref ct)
                | ConditionExpression::ComparisonOp(ref ct) => q.push_back(ct),
                _ => (),
            }
        }
        s
    }
}

impl fmt::Display for ConditionTree {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.left)?;
        write!(f, " {} ", self.operator)?;
        write!(f, "{}", self.right)
    }
}

#[derive(Clone, Debug, Hash, PartialEq, Serialize, Deserialize)]
pub enum ConditionExpression {
    ComparisonOp(ConditionTree),
    LogicalOp(ConditionTree),
    NegationOp(Box<ConditionExpression>),
    Base(ConditionBase),
}

impl fmt::Display for ConditionExpression {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ConditionExpression::ComparisonOp(ref tree) => write!(f, "{}", tree),
            ConditionExpression::LogicalOp(ref tree) => write!(f, "{}", tree),
            ConditionExpression::NegationOp(ref expr) => write!(f, "NOT {}", expr),
            ConditionExpression::Base(ref base) => write!(f, "{}", base),
        }
    }
}

/// Parse a conditional expression into a condition tree structure
named!(pub condition_expr<&[u8], ConditionExpression>,
       alt_complete!(
           do_parse!(
               left: and_expr >>
               opt_multispace >>
               tag_no_case!("or") >>
               multispace >>
               right: condition_expr >>
               (ConditionExpression::LogicalOp(
                   ConditionTree {
                       operator: Operator::Or,
                       left: Box::new(left),
                       right: Box::new(right),
                   }
               ))
           )
       |   and_expr)
);

named!(pub and_expr<&[u8], ConditionExpression>,
       alt_complete!(
           do_parse!(
               left: parenthetical_expr >>
               opt_multispace >>
               tag_no_case!("and") >>
               multispace >>
               right: and_expr >>
               (ConditionExpression::LogicalOp(
                   ConditionTree {
                       operator: Operator::And,
                       left: Box::new(left),
                       right: Box::new(right),
                   }
               ))
           )
       |   parenthetical_expr)
);

named!(pub parenthetical_expr<&[u8], ConditionExpression>,
       alt_complete!(
           delimited!(
               do_parse!(tag!("(") >> opt_multispace >> ()),
               condition_expr,
               do_parse!(opt_multispace >> tag!(")") >> opt_multispace >> ())
            )
       |   not_expr)
);

named!(pub not_expr<&[u8], ConditionExpression>,
       alt_complete!(
           do_parse!(
               tag_no_case!("not") >>
               multispace >>
               right: parenthetical_expr >>
               (ConditionExpression::NegationOp(Box::new(right)))
           )
       |   boolean_primary)
);

named!(boolean_primary<&[u8], ConditionExpression>,
    alt_complete!(
        do_parse!(
            left: predicate >>
            opt_multispace >>
            op: binary_comparison_operator >>
            opt_multispace >>
            right: predicate >>
            (ConditionExpression::ComparisonOp(
                ConditionTree {
                    operator: op,
                    left: Box::new(left),
                    right: Box::new(right),
                }
            ))
        )
        | predicate)
);

named!(predicate<&[u8], ConditionExpression>,
    dbg_dmp!(do_parse!(
        left: simple_expr >>
        op_right: opt!(
            alt_complete!(
                  do_parse!(
                      neg: opt!(preceded!(opt_multispace, tag_no_case!("not"))) >>
                      multispace >>
                      tag_no_case!("in") >>
                      multispace >>
                      sq: nested_selection >>
                      (ConditionExpression::Base(ConditionBase::NestedSelect(Box::new(sq))))
                  )
                | do_parse!(
                      neg: opt!(preceded!(opt_multispace, tag_no_case!("not"))) >>
                      multispace >>
                      tag_no_case!("in") >>
                      multispace >>
                      vl: delimited!(tag!("("), value_list, tag!(")")) >>
                      // XXX(malte): incorrect -- don't just return first element!
                      (ConditionExpression::Base(ConditionBase::Literal(vl[0].clone())))
                  )
            )
        ) >>
        (match op_right {
            Some(right) => ConditionExpression::ComparisonOp(
                ConditionTree {
                    operator: Operator::In,
                    left: Box::new(left),
                    right: Box::new(right),
                }),
            None => left,
        })
    ))
);

named!(simple_expr<&[u8], ConditionExpression>,
    alt_complete!(
            do_parse!(
                tag!("?") >>
                (ConditionExpression::Base(
                    ConditionBase::Placeholder
                ))
            )
        |   do_parse!(
                field: integer_literal >>
                (ConditionExpression::Base(ConditionBase::Literal(field)))
            )
        |   do_parse!(
                field: string_literal >>
                (ConditionExpression::Base(ConditionBase::Literal(field)))
            )
        |   do_parse!(
                field: column_identifier >>
                (ConditionExpression::Base(
                    ConditionBase::Field(field)
                ))
            )
        |   do_parse!(
                select: delimited!(tag!("("), nested_selection, tag!(")")) >>
                (ConditionExpression::Base(
                    ConditionBase::NestedSelect(Box::new(select))
                ))
            )
    )
);

#[cfg(test)]
mod tests {
    use super::*;
    use column::Column;
    use common::{FieldExpression, Literal, Operator};

    fn columns(cols: &[&str]) -> Vec<FieldExpression> {
        cols.iter()
            .map(|c| FieldExpression::Col(Column::from(*c)))
            .collect()
    }

    fn flat_condition_tree(
        op: Operator,
        l: ConditionBase,
        r: ConditionBase,
    ) -> ConditionExpression {
        ConditionExpression::ComparisonOp(ConditionTree {
            operator: op,
            left: Box::new(ConditionExpression::Base(l)),
            right: Box::new(ConditionExpression::Base(r)),
        })
    }

    #[test]
    fn ct_contained_columns() {
        use std::collections::HashSet;

        let cond = "a.foo = ? and b.bar = 42";

        let res = condition_expr(cond.as_bytes());
        let c1 = Column::from("a.foo");
        let c2 = Column::from("b.bar");
        let mut expected_cols = HashSet::new();
        expected_cols.insert(&c1);
        expected_cols.insert(&c2);
        match res.unwrap().1 {
            ConditionExpression::LogicalOp(ct) => {
                assert_eq!(ct.contained_columns(), expected_cols);
            }
            _ => panic!(),
        }
    }

    #[test]
    fn equality_placeholder() {
        let cond = "foo = ?";

        let res = condition_expr(cond.as_bytes());
        assert_eq!(
            res.unwrap().1,
            flat_condition_tree(
                Operator::Equal,
                ConditionBase::Field(Column::from("foo")),
                ConditionBase::Placeholder
            )
        );
    }

    #[test]
    fn equality_literals() {
        let cond1 = "foo = 42";
        let cond2 = "foo = \"hello\"";

        let res1 = condition_expr(cond1.as_bytes());
        assert_eq!(
            res1.unwrap().1,
            flat_condition_tree(
                Operator::Equal,
                ConditionBase::Field(Column::from("foo")),
                ConditionBase::Literal(Literal::Integer(42 as i64))
            )
        );

        let res2 = condition_expr(cond2.as_bytes());
        assert_eq!(
            res2.unwrap().1,
            flat_condition_tree(
                Operator::Equal,
                ConditionBase::Field(Column::from("foo")),
                ConditionBase::Literal(Literal::String(String::from("hello")))
            )
        );
    }

    #[test]
    fn inequality_literals() {
        let cond1 = "foo >= 42";
        let cond2 = "foo <= 5";

        let res1 = condition_expr(cond1.as_bytes());
        assert_eq!(
            res1.unwrap().1,
            flat_condition_tree(
                Operator::GreaterOrEqual,
                ConditionBase::Field(Column::from("foo")),
                ConditionBase::Literal(Literal::Integer(42 as i64))
            )
        );

        let res2 = condition_expr(cond2.as_bytes());
        assert_eq!(
            res2.unwrap().1,
            flat_condition_tree(
                Operator::LessOrEqual,
                ConditionBase::Field(Column::from("foo")),
                ConditionBase::Literal(Literal::Integer(5 as i64))
            )
        );
    }

    #[test]
    fn empty_string_literal() {
        let cond = "foo = ''";

        let res = condition_expr(cond.as_bytes());
        assert_eq!(
            res.unwrap().1,
            flat_condition_tree(
                Operator::Equal,
                ConditionBase::Field(Column::from("foo")),
                ConditionBase::Literal(Literal::String(String::from("")))
            )
        );
    }

    #[test]
    fn parenthesis() {
        let cond = "(foo = ? or bar = 12) and foobar = 'a'";

        use ConditionExpression::*;
        use ConditionBase::*;
        use common::Literal;

        let a = ComparisonOp(ConditionTree {
            operator: Operator::Equal,
            left: Box::new(Base(Field("foo".into()))),
            right: Box::new(Base(Placeholder)),
        });

        let b = ComparisonOp(ConditionTree {
            operator: Operator::Equal,
            left: Box::new(Base(Field("bar".into()))),
            right: Box::new(Base(Literal(Literal::Integer(12.into())))),
        });

        let left = LogicalOp(ConditionTree {
            operator: Operator::Or,
            left: Box::new(a),
            right: Box::new(b),
        });

        let right = ComparisonOp(ConditionTree {
            operator: Operator::Equal,
            left: Box::new(Base(Field("foobar".into()))),
            right: Box::new(Base(Literal(Literal::String("a".into())))),
        });

        let complete = LogicalOp(ConditionTree {
            operator: Operator::And,
            left: Box::new(left),
            right: Box::new(right),
        });

        let res = condition_expr(cond.as_bytes());
        assert_eq!(res.unwrap().1, complete);
    }

    #[test]
    fn order_of_operations() {
        let cond = "foo = ? and bar = 12 or foobar = 'a'";

        use ConditionExpression::*;
        use ConditionBase::*;
        use common::Literal;

        let a = ComparisonOp(ConditionTree {
            operator: Operator::Equal,
            left: Box::new(Base(Field("foo".into()))),
            right: Box::new(Base(Placeholder)),
        });

        let b = ComparisonOp(ConditionTree {
            operator: Operator::Equal,
            left: Box::new(Base(Field("bar".into()))),
            right: Box::new(Base(Literal(Literal::Integer(12.into())))),
        });

        let left = LogicalOp(ConditionTree {
            operator: Operator::And,
            left: Box::new(a),
            right: Box::new(b),
        });

        let right = ComparisonOp(ConditionTree {
            operator: Operator::Equal,
            left: Box::new(Base(Field("foobar".into()))),
            right: Box::new(Base(Literal(Literal::String("a".into())))),
        });

        let complete = LogicalOp(ConditionTree {
            operator: Operator::Or,
            left: Box::new(left),
            right: Box::new(right),
        });

        let res = condition_expr(cond.as_bytes());
        assert_eq!(res.unwrap().1, complete);
    }

    #[test]
    fn negation() {
        let cond = "not bar = 12 or foobar = 'a'";

        use ConditionExpression::*;
        use ConditionBase::*;
        use common::Literal::*;

        let left = NegationOp(Box::new(ComparisonOp(ConditionTree {
            operator: Operator::Equal,
            left: Box::new(Base(Field("bar".into()))),
            right: Box::new(Base(Literal(Integer(12.into())))),
        })));

        let right = ComparisonOp(ConditionTree {
            operator: Operator::Equal,
            left: Box::new(Base(Field("foobar".into()))),
            right: Box::new(Base(Literal(String("a".into())))),
        });

        let complete = LogicalOp(ConditionTree {
            operator: Operator::Or,
            left: Box::new(left),
            right: Box::new(right),
        });

        let res = condition_expr(cond.as_bytes());
        assert_eq!(res.unwrap().1, complete);
    }

    #[test]
    fn nested_select() {
        use select::SelectStatement;
        use table::Table;
        use ConditionBase::*;
        use std::default::Default;

        let cond = "bar in (select col from foo)";

        let res = condition_expr(cond.as_bytes());

        let nested_select = Box::new(SelectStatement {
            tables: vec![Table::from("foo")],
            fields: columns(&["col"]),
            ..Default::default()
        });

        let expected = flat_condition_tree(
            Operator::In,
            Field("bar".into()),
            NestedSelect(nested_select),
        );

        assert_eq!(res.unwrap().1, expected);
    }

    #[test]
    fn and_with_nested_select() {
        use select::SelectStatement;
        use table::Table;
        use ConditionBase::*;
        use std::default::Default;

        let cond = "paperId in (select paperId from PaperConflict) and size > 0";

        let res = condition_expr(cond.as_bytes());

        let nested_select = Box::new(SelectStatement {
            tables: vec![Table::from("PaperConflict")],
            fields: columns(&["paperId"]),
            ..Default::default()
        });

        let left = flat_condition_tree(
            Operator::In,
            Field("paperId".into()),
            NestedSelect(nested_select),
        );

        let right = flat_condition_tree(Operator::Greater, Field("size".into()), Literal(0.into()));

        let expected = ConditionExpression::LogicalOp(ConditionTree {
            left: Box::new(left),
            right: Box::new(right),
            operator: Operator::And,
        });

        assert_eq!(res.unwrap().1, expected);
    }

    #[test]
    fn in_list_of_values() {
        use ConditionBase::*;

        let cond = "bar in (0)";

        let res = condition_expr(cond.as_bytes());

        let expected = flat_condition_tree(Operator::In, Field("bar".into()), Literal(0.into()));

        assert_eq!(res.unwrap().1, expected);
    }
}
<commit_msg>No debugging<commit_after>use nom::multispace;
use std::collections::{HashSet, VecDeque};
use std::str;
use std::fmt;

use column::Column;
use common::{binary_comparison_operator, column_identifier, integer_literal, opt_multispace,
             string_literal, value_list, Literal, Operator};

use select::{nested_selection, SelectStatement};

#[derive(Clone, Debug, Hash, PartialEq, Serialize, Deserialize)]
pub enum ConditionBase {
    Field(Column),
    Literal(Literal),
    Placeholder,
    NestedSelect(Box<SelectStatement>),
}

impl fmt::Display for ConditionBase {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ConditionBase::Field(ref col) => write!(f, "{}", col),
            ConditionBase::Literal(ref literal) => write!(f, "{}", literal.to_string()),
            ConditionBase::Placeholder => write!(f, "?"),
            ConditionBase::NestedSelect(ref select) => write!(f, "{}", select),
        }
    }
}

#[derive(Clone, Debug, Hash, PartialEq, Serialize, Deserialize)]
pub struct ConditionTree {
    pub operator: Operator,
    pub left: Box<ConditionExpression>,
    pub right: Box<ConditionExpression>,
}

impl<'a> ConditionTree {
    pub fn contained_columns(&'a self) -> HashSet<&'a Column> {
        let mut s = HashSet::new();
        let mut q = VecDeque::<&'a ConditionTree>::new();
        q.push_back(self);
        while let Some(ref ct) = q.pop_front() {
            match *ct.left.as_ref() {
                ConditionExpression::Base(ConditionBase::Field(ref c)) => {
                    s.insert(c);
                }
                ConditionExpression::LogicalOp(ref ct)
                | ConditionExpression::ComparisonOp(ref ct) => q.push_back(ct),
                _ => (),
            }
            match *ct.right.as_ref() {
                ConditionExpression::Base(ConditionBase::Field(ref c)) => {
                    s.insert(c);
                }
                ConditionExpression::LogicalOp(ref ct)
                | ConditionExpression::ComparisonOp(ref ct) => q.push_back(ct),
                _ => (),
            }
        }
        s
    }
}

impl fmt::Display for ConditionTree {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.left)?;
        write!(f, " {} ", self.operator)?;
        write!(f, "{}", self.right)
    }
}

#[derive(Clone, Debug, Hash, PartialEq, Serialize, Deserialize)]
pub enum ConditionExpression {
    ComparisonOp(ConditionTree),
    LogicalOp(ConditionTree),
    NegationOp(Box<ConditionExpression>),
    Base(ConditionBase),
}

impl fmt::Display for ConditionExpression {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ConditionExpression::ComparisonOp(ref tree) => write!(f, "{}", tree),
            ConditionExpression::LogicalOp(ref tree) => write!(f, "{}", tree),
            ConditionExpression::NegationOp(ref expr) => write!(f, "NOT {}", expr),
            ConditionExpression::Base(ref base) => write!(f, "{}", base),
        }
    }
}

/// Parse a conditional expression into a condition tree structure
named!(pub condition_expr<&[u8], ConditionExpression>,
       alt_complete!(
           do_parse!(
               left: and_expr >>
               opt_multispace >>
               tag_no_case!("or") >>
               multispace >>
               right: condition_expr >>
               (ConditionExpression::LogicalOp(
                   ConditionTree {
                       operator: Operator::Or,
                       left: Box::new(left),
                       right: Box::new(right),
                   }
               ))
           )
       |   and_expr)
);

named!(pub and_expr<&[u8], ConditionExpression>,
       alt_complete!(
           do_parse!(
               left: parenthetical_expr >>
               opt_multispace >>
               tag_no_case!("and") >>
               multispace >>
               right: and_expr >>
               (ConditionExpression::LogicalOp(
                   ConditionTree {
                       operator: Operator::And,
                       left: Box::new(left),
                       right: Box::new(right),
                   }
               ))
           )
       |   parenthetical_expr)
);

named!(pub parenthetical_expr<&[u8], ConditionExpression>,
       alt_complete!(
           delimited!(
               do_parse!(tag!("(") >> opt_multispace >> ()),
               condition_expr,
               do_parse!(opt_multispace >> tag!(")") >> opt_multispace >> ())
            )
       |   not_expr)
);

named!(pub not_expr<&[u8], ConditionExpression>,
       alt_complete!(
           do_parse!(
               tag_no_case!("not") >>
               multispace >>
               right: parenthetical_expr >>
               (ConditionExpression::NegationOp(Box::new(right)))
           )
       |   boolean_primary)
);

named!(boolean_primary<&[u8], ConditionExpression>,
    alt_complete!(
        do_parse!(
            left: predicate >>
            opt_multispace >>
            op: binary_comparison_operator >>
            opt_multispace >>
            right: predicate >>
            (ConditionExpression::ComparisonOp(
                ConditionTree {
                    operator: op,
                    left: Box::new(left),
                    right: Box::new(right),
                }
            ))
        )
        | predicate)
);

named!(predicate<&[u8], ConditionExpression>,
    do_parse!(
        left: simple_expr >>
        op_right: opt!(
            alt_complete!(
                  do_parse!(
                      neg: opt!(preceded!(opt_multispace, tag_no_case!("not"))) >>
                      multispace >>
                      tag_no_case!("in") >>
                      multispace >>
                      sq: nested_selection >>
                      (ConditionExpression::Base(ConditionBase::NestedSelect(Box::new(sq))))
                  )
                | do_parse!(
                      neg: opt!(preceded!(opt_multispace, tag_no_case!("not"))) >>
                      multispace >>
                      tag_no_case!("in") >>
                      multispace >>
                      vl: delimited!(tag!("("), value_list, tag!(")")) >>
                      // XXX(malte): incorrect -- don't just return first element!
                      (ConditionExpression::Base(ConditionBase::Literal(vl[0].clone())))
                  )
            )
        ) >>
        (match op_right {
            Some(right) => ConditionExpression::ComparisonOp(
                ConditionTree {
                    operator: Operator::In,
                    left: Box::new(left),
                    right: Box::new(right),
                }),
            None => left,
        })
    )
);

named!(simple_expr<&[u8], ConditionExpression>,
    alt_complete!(
            do_parse!(
                tag!("?") >>
                (ConditionExpression::Base(
                    ConditionBase::Placeholder
                ))
            )
        |   do_parse!(
                field: integer_literal >>
                (ConditionExpression::Base(ConditionBase::Literal(field)))
            )
        |   do_parse!(
                field: string_literal >>
                (ConditionExpression::Base(ConditionBase::Literal(field)))
            )
        |   do_parse!(
                field: column_identifier >>
                (ConditionExpression::Base(
                    ConditionBase::Field(field)
                ))
            )
        |   do_parse!(
                select: delimited!(tag!("("), nested_selection, tag!(")")) >>
                (ConditionExpression::Base(
                    ConditionBase::NestedSelect(Box::new(select))
                ))
            )
    )
);

#[cfg(test)]
mod tests {
    use super::*;
    use column::Column;
    use common::{FieldExpression, Literal, Operator};

    fn columns(cols: &[&str]) -> Vec<FieldExpression> {
        cols.iter()
            .map(|c| FieldExpression::Col(Column::from(*c)))
            .collect()
    }

    fn flat_condition_tree(
        op: Operator,
        l: ConditionBase,
        r: ConditionBase,
    ) -> ConditionExpression {
        ConditionExpression::ComparisonOp(ConditionTree {
            operator: op,
            left: Box::new(ConditionExpression::Base(l)),
            right: Box::new(ConditionExpression::Base(r)),
        })
    }

    #[test]
    fn ct_contained_columns() {
        use std::collections::HashSet;

        let cond = "a.foo = ? and b.bar = 42";

        let res = condition_expr(cond.as_bytes());
        let c1 = Column::from("a.foo");
        let c2 = Column::from("b.bar");
        let mut expected_cols = HashSet::new();
        expected_cols.insert(&c1);
        expected_cols.insert(&c2);
        match res.unwrap().1 {
            ConditionExpression::LogicalOp(ct) => {
                assert_eq!(ct.contained_columns(), expected_cols);
            }
            _ => panic!(),
        }
    }

    #[test]
    fn equality_placeholder() {
        let cond = "foo = ?";

        let res = condition_expr(cond.as_bytes());
        assert_eq!(
            res.unwrap().1,
            flat_condition_tree(
                Operator::Equal,
                ConditionBase::Field(Column::from("foo")),
                ConditionBase::Placeholder
            )
        );
    }

    #[test]
    fn equality_literals() {
        let cond1 = "foo = 42";
        let cond2 = "foo = \"hello\"";

        let res1 = condition_expr(cond1.as_bytes());
        assert_eq!(
            res1.unwrap().1,
            flat_condition_tree(
                Operator::Equal,
                ConditionBase::Field(Column::from("foo")),
                ConditionBase::Literal(Literal::Integer(42 as i64))
            )
        );

        let res2 = condition_expr(cond2.as_bytes());
        assert_eq!(
            res2.unwrap().1,
            flat_condition_tree(
                Operator::Equal,
                ConditionBase::Field(Column::from("foo")),
                ConditionBase::Literal(Literal::String(String::from("hello")))
            )
        );
    }

    #[test]
    fn inequality_literals() {
        let cond1 = "foo >= 42";
        let cond2 = "foo <= 5";

        let res1 = condition_expr(cond1.as_bytes());
        assert_eq!(
            res1.unwrap().1,
            flat_condition_tree(
                Operator::GreaterOrEqual,
                ConditionBase::Field(Column::from("foo")),
                ConditionBase::Literal(Literal::Integer(42 as i64))
            )
        );

        let res2 = condition_expr(cond2.as_bytes());
        assert_eq!(
            res2.unwrap().1,
            flat_condition_tree(
                Operator::LessOrEqual,
                ConditionBase::Field(Column::from("foo")),
                ConditionBase::Literal(Literal::Integer(5 as i64))
            )
        );
    }

    #[test]
    fn empty_string_literal() {
        let cond = "foo = ''";

        let res = condition_expr(cond.as_bytes());
        assert_eq!(
            res.unwrap().1,
            flat_condition_tree(
                Operator::Equal,
                ConditionBase::Field(Column::from("foo")),
                ConditionBase::Literal(Literal::String(String::from("")))
            )
        );
    }

    #[test]
    fn parenthesis() {
        let cond = "(foo = ? or bar = 12) and foobar = 'a'";

        use ConditionExpression::*;
        use ConditionBase::*;
        use common::Literal;

        let a = ComparisonOp(ConditionTree {
            operator: Operator::Equal,
            left: Box::new(Base(Field("foo".into()))),
            right: Box::new(Base(Placeholder)),
        });

        let b = ComparisonOp(ConditionTree {
            operator: Operator::Equal,
            left: Box::new(Base(Field("bar".into()))),
            right: Box::new(Base(Literal(Literal::Integer(12.into())))),
        });

        let left = LogicalOp(ConditionTree {
            operator: Operator::Or,
            left: Box::new(a),
            right: Box::new(b),
        });

        let right = ComparisonOp(ConditionTree {
            operator: Operator::Equal,
            left: Box::new(Base(Field("foobar".into()))),
            right: Box::new(Base(Literal(Literal::String("a".into())))),
        });

        let complete = LogicalOp(ConditionTree {
            operator: Operator::And,
            left: Box::new(left),
            right: Box::new(right),
        });

        let res = condition_expr(cond.as_bytes());
        assert_eq!(res.unwrap().1, complete);
    }

    #[test]
    fn order_of_operations() {
        let cond = "foo = ? and bar = 12 or foobar = 'a'";

        use ConditionExpression::*;
        use ConditionBase::*;
        use common::Literal;

        let a = ComparisonOp(ConditionTree {
            operator: Operator::Equal,
            left: Box::new(Base(Field("foo".into()))),
            right: Box::new(Base(Placeholder)),
        });

        let b = ComparisonOp(ConditionTree {
            operator: Operator::Equal,
            left: Box::new(Base(Field("bar".into()))),
            right: Box::new(Base(Literal(Literal::Integer(12.into())))),
        });

        let left = LogicalOp(ConditionTree {
            operator: Operator::And,
            left: Box::new(a),
            right: Box::new(b),
        });

        let right = ComparisonOp(ConditionTree {
            operator: Operator::Equal,
            left: Box::new(Base(Field("foobar".into()))),
            right: Box::new(Base(Literal(Literal::String("a".into())))),
        });

        let complete = LogicalOp(ConditionTree {
            operator: Operator::Or,
            left: Box::new(left),
            right: Box::new(right),
        });

        let res = condition_expr(cond.as_bytes());
        assert_eq!(res.unwrap().1, complete);
    }

    #[test]
    fn negation() {
        let cond = "not bar = 12 or foobar = 'a'";

        use ConditionExpression::*;
        use ConditionBase::*;
        use common::Literal::*;

        let left = NegationOp(Box::new(ComparisonOp(ConditionTree {
            operator: Operator::Equal,
            left: Box::new(Base(Field("bar".into()))),
            right: Box::new(Base(Literal(Integer(12.into())))),
        })));

        let right = ComparisonOp(ConditionTree {
            operator: Operator::Equal,
            left: Box::new(Base(Field("foobar".into()))),
            right: Box::new(Base(Literal(String("a".into())))),
        });

        let complete = LogicalOp(ConditionTree {
            operator: Operator::Or,
            left: Box::new(left),
            right: Box::new(right),
        });

        let res = condition_expr(cond.as_bytes());
        assert_eq!(res.unwrap().1, complete);
    }

    #[test]
    fn nested_select() {
        use select::SelectStatement;
        use table::Table;
        use ConditionBase::*;
        use std::default::Default;

        let cond = "bar in (select col from foo)";

        let res = condition_expr(cond.as_bytes());

        let nested_select = Box::new(SelectStatement {
            tables: vec![Table::from("foo")],
            fields: columns(&["col"]),
            ..Default::default()
        });

        let expected = flat_condition_tree(
            Operator::In,
            Field("bar".into()),
            NestedSelect(nested_select),
        );

        assert_eq!(res.unwrap().1, expected);
    }

    #[test]
    fn and_with_nested_select() {
        use select::SelectStatement;
        use table::Table;
        use ConditionBase::*;
        use std::default::Default;

        let cond = "paperId in (select paperId from PaperConflict) and size > 0";

        let res = condition_expr(cond.as_bytes());

        let nested_select = Box::new(SelectStatement {
            tables: vec![Table::from("PaperConflict")],
            fields: columns(&["paperId"]),
            ..Default::default()
        });

        let left = flat_condition_tree(
            Operator::In,
            Field("paperId".into()),
            NestedSelect(nested_select),
        );

        let right = flat_condition_tree(Operator::Greater, Field("size".into()), Literal(0.into()));

        let expected = ConditionExpression::LogicalOp(ConditionTree {
            left: Box::new(left),
            right: Box::new(right),
            operator: Operator::And,
        });

        assert_eq!(res.unwrap().1, expected);
    }

    #[test]
    fn in_list_of_values() {
        use ConditionBase::*;

        let cond = "bar in (0)";

        let res = condition_expr(cond.as_bytes());

        let expected = flat_condition_tree(Operator::In, Field("bar".into()), Literal(0.into()));

        assert_eq!(res.unwrap().1, expected);
    }
}
<|endoftext|>