<commit_before>use alloc::boxed::Box;

use collections::string::String;

use core::ops::DerefMut;

use common::event::{Event, KeyEvent, MouseEvent, QuitEvent};
use common::queue::Queue;
use common::scheduler;

use super::color::Color;
use super::display::Display;
use super::point::Point;
use super::size::Size;

/// A window
pub struct Window {
    /// The position of the window
    pub point: Point,
    /// The size of the window
    pub size: Size,
    /// The title of the window
    pub title: String,
    /// The content of the window
    pub content: Box<Display>,
    /// The color of the window title
    pub title_color: Color,
    /// The color of the border
    pub border_color: Color,
    /// Is the window focused?
    pub focused: bool,
    /// Is the window minimized?
    pub minimized: bool,
    dragging: bool,
    last_mouse_event: MouseEvent,
    events: Queue<Event>,
    ptr: *mut Window,
}

impl Window {
    /// Create a new window
    pub fn new(point: Point, size: Size, title: String) -> Box<Self> {
        let mut ret = box Window {
            point: point,
            size: size,
            title: title,
            content: Display::new(size.width, size.height),
            title_color: Color::new(255, 255, 255),
            border_color: Color::alpha(64, 64, 64, 128),
            focused: false,
            minimized: false,
            dragging: false,
            last_mouse_event: MouseEvent {
                x: 0,
                y: 0,
                left_button: false,
                right_button: false,
                middle_button: false,
            },
            events: Queue::new(),
            ptr: 0 as *mut Window,
        };

        unsafe {
            ret.ptr = ret.deref_mut();

            if ret.ptr as usize > 0 {
                (*::session_ptr).add_window(ret.ptr);
            }
        }

        ret
    }

    /// Poll the window (new)
    pub fn poll(&mut self) -> Option<Event> {
        let event_option;
        unsafe {
            let reenable = scheduler::start_no_ints();
            event_option = self.events.pop();
            scheduler::end_no_ints(reenable);
        }

        return event_option;
    }

    /// Redraw the window
    pub fn redraw(&mut self) {
        unsafe {
            let reenable = scheduler::start_no_ints();
            self.content.flip();
            (*::session_ptr).redraw = true;
            scheduler::end_no_ints(reenable);
        }
    }

    /// Draw the window using a `Display`
    pub fn draw(&mut self, display: &Display) {
        if self.focused {
            self.border_color = Color::alpha(128, 128, 128, 192);
        } else {
            self.border_color = Color::alpha(64, 64, 64, 128);
        }

        if self.minimized {
            self.title_color = Color::new(0, 0, 0);
        } else {
            self.title_color = Color::new(255, 255, 255);

            display.rect(Point::new(self.point.x - 2, self.point.y - 18),
                         Size::new(self.size.width + 4, 18),
                         self.border_color);

            let mut cursor = Point::new(self.point.x, self.point.y - 17);
            for c in self.title.chars() {
                if cursor.x + 8 <= self.point.x + self.size.width as isize {
                    display.char(cursor, c, self.title_color);
                }
                cursor.x += 8;
            }

            display.rect(Point::new(self.point.x - 2, self.point.y),
                         Size::new(2, self.size.height),
                         self.border_color);
            display.rect(Point::new(self.point.x - 2,
                                    self.point.y + self.size.height as isize),
                         Size::new(self.size.width + 4, 2),
                         self.border_color);
            display.rect(Point::new(self.point.x + self.size.width as isize,
                                    self.point.y),
                         Size::new(2, self.size.height),
                         self.border_color);

            unsafe {
                let reenable = scheduler::start_no_ints();
                display.image(self.point,
                              self.content.onscreen as *const u32,
                              Size::new(self.content.width, self.content.height));
                scheduler::end_no_ints(reenable);
            }
        }
    }

    /// Called on key press
    pub fn on_key(&mut self, key_event: KeyEvent) {
        unsafe {
            let reenable = scheduler::start_no_ints();
            self.events.push(key_event.to_event());
            scheduler::end_no_ints(reenable);
        }
    }

    fn on_window_decoration(&self, x: isize, y: isize) -> bool {
        !self.minimized && x >= -2 &&
            x < self.size.width as isize + 4 &&
            y >= -18 &&
            y < self.size.height as isize + 2
    }

    /// Called on mouse movement
    pub fn on_mouse(&mut self, orig_mouse_event: MouseEvent, allow_catch: bool) -> bool {
        let mut mouse_event = orig_mouse_event;

        mouse_event.x -= self.point.x;
        mouse_event.y -= self.point.y;

        let mut caught = false;

        if allow_catch {
            if mouse_event.left_button {
                if self.on_window_decoration(mouse_event.x, mouse_event.y) {
                    caught = true;
                }

                if !self.last_mouse_event.left_button && mouse_event.x >= -2 &&
                   mouse_event.x < self.size.width as isize + 4 &&
                   mouse_event.y >= -18 && mouse_event.y < 0 {
                    self.dragging = true;
                    caught = true;
                }
            } else {
                self.dragging = false;
            }

            if mouse_event.right_button {
                if self.on_window_decoration(mouse_event.x, mouse_event.y) {
                    caught = true;
                }

                if !self.last_mouse_event.right_button && mouse_event.x >= -2 &&
                   mouse_event.x < self.size.width as isize + 4 &&
                   mouse_event.y >= -18 && mouse_event.y < 0 {
                    self.minimized = !self.minimized;
                    caught = true;
                }
            }

            if mouse_event.middle_button {
                if self.on_window_decoration(mouse_event.x, mouse_event.y) {
                    unsafe {
                        let reenable = scheduler::start_no_ints();
                        self.events.push(QuitEvent.to_event());
                        scheduler::end_no_ints(reenable);
                    }
                }
            }

            if self.dragging {
                self.point.x += orig_mouse_event.x - self.last_mouse_event.x;
                self.point.y += orig_mouse_event.y - self.last_mouse_event.y;
                caught = true;
            }
        } else {
            self.dragging = false;
        }

        self.last_mouse_event = orig_mouse_event;

        if caught && !self.dragging {
            unsafe {
                let reenable = scheduler::start_no_ints();
                self.events.push(mouse_event.to_event());
                scheduler::end_no_ints(reenable);
            }
        }

        caught
    }
}

impl Drop for Window {
    fn drop(&mut self) {
        unsafe {
            if self.ptr as usize > 0 {
                (*::session_ptr).remove_window(self.ptr);
            }
        }
    }
}
<commit_msg>Fix mouse events<commit_after>use alloc::boxed::Box;

use collections::string::String;

use core::ops::DerefMut;

use common::event::{Event, KeyEvent, MouseEvent, QuitEvent};
use common::queue::Queue;
use common::scheduler;

use super::color::Color;
use super::display::Display;
use super::point::Point;
use super::size::Size;

/// A window
pub struct Window {
    /// The position of the window
    pub point: Point,
    /// The size of the window
    pub size: Size,
    /// The title of the window
    pub title: String,
    /// The content of the window
    pub content: Box<Display>,
    /// The color of the window title
    pub title_color: Color,
    /// The color of the border
    pub border_color: Color,
    /// Is the window focused?
    pub focused: bool,
    /// Is the window minimized?
    pub minimized: bool,
    dragging: bool,
    last_mouse_event: MouseEvent,
    events: Queue<Event>,
    ptr: *mut Window,
}

impl Window {
    /// Create a new window
    pub fn new(point: Point, size: Size, title: String) -> Box<Self> {
        let mut ret = box Window {
            point: point,
            size: size,
            title: title,
            content: Display::new(size.width, size.height),
            title_color: Color::new(255, 255, 255),
            border_color: Color::alpha(64, 64, 64, 128),
            focused: false,
            minimized: false,
            dragging: false,
            last_mouse_event: MouseEvent {
                x: 0,
                y: 0,
                left_button: false,
                right_button: false,
                middle_button: false,
            },
            events: Queue::new(),
            ptr: 0 as *mut Window,
        };

        unsafe {
            ret.ptr = ret.deref_mut();

            if ret.ptr as usize > 0 {
                (*::session_ptr).add_window(ret.ptr);
            }
        }

        ret
    }

    /// Poll the window (new)
    pub fn poll(&mut self) -> Option<Event> {
        let event_option;
        unsafe {
            let reenable = scheduler::start_no_ints();
            event_option = self.events.pop();
            scheduler::end_no_ints(reenable);
        }

        return event_option;
    }

    /// Redraw the window
    pub fn redraw(&mut self) {
        unsafe {
            let reenable = scheduler::start_no_ints();
            self.content.flip();
            (*::session_ptr).redraw = true;
            scheduler::end_no_ints(reenable);
        }
    }

    /// Draw the window using a `Display`
    pub fn draw(&mut self, display: &Display) {
        if self.focused {
            self.border_color = Color::alpha(128, 128, 128, 192);
        } else {
            self.border_color = Color::alpha(64, 64, 64, 128);
        }

        if self.minimized {
            self.title_color = Color::new(0, 0, 0);
        } else {
            self.title_color = Color::new(255, 255, 255);

            display.rect(Point::new(self.point.x - 2, self.point.y - 18),
                         Size::new(self.size.width + 4, 18),
                         self.border_color);

            let mut cursor = Point::new(self.point.x, self.point.y - 17);
            for c in self.title.chars() {
                if cursor.x + 8 <= self.point.x + self.size.width as isize {
                    display.char(cursor, c, self.title_color);
                }
                cursor.x += 8;
            }

            display.rect(Point::new(self.point.x - 2, self.point.y),
                         Size::new(2, self.size.height),
                         self.border_color);
            display.rect(Point::new(self.point.x - 2,
                                    self.point.y + self.size.height as isize),
                         Size::new(self.size.width + 4, 2),
                         self.border_color);
            display.rect(Point::new(self.point.x + self.size.width as isize,
                                    self.point.y),
                         Size::new(2, self.size.height),
                         self.border_color);

            unsafe {
                let reenable = scheduler::start_no_ints();
                display.image(self.point,
                              self.content.onscreen as *const u32,
                              Size::new(self.content.width, self.content.height));
                scheduler::end_no_ints(reenable);
            }
        }
    }

    /// Called on key press
    pub fn on_key(&mut self, key_event: KeyEvent) {
        unsafe {
            let reenable = scheduler::start_no_ints();
            self.events.push(key_event.to_event());
            scheduler::end_no_ints(reenable);
        }
    }

    fn on_window_decoration(&self, x: isize, y: isize) -> bool {
        !self.minimized && x >= -2 &&
            x < self.size.width as isize + 4 &&
            y >= -18 &&
            y < 0
    }

    fn on_window_body(&self, x: isize, y: isize) -> bool {
        !self.minimized && x >= 0 &&
            x < self.size.width as isize &&
            y >= 0 &&
            y < self.size.height as isize
    }

    /// Called on mouse movement
    pub fn on_mouse(&mut self, orig_mouse_event: MouseEvent, allow_catch: bool) -> bool {
        let mut mouse_event = orig_mouse_event;

        mouse_event.x -= self.point.x;
        mouse_event.y -= self.point.y;

        let mut caught = false;

        if allow_catch {
            if mouse_event.left_button {
                if self.on_window_body(mouse_event.x, mouse_event.y) {
                    caught = true;
                }else if self.on_window_decoration(mouse_event.x, mouse_event.y) {
                    caught = true;
                    if !self.last_mouse_event.left_button {
                        self.dragging = true;
                    }
                }
            } else {
                self.dragging = false;
            }

            if mouse_event.right_button {
                if self.on_window_body(mouse_event.x, mouse_event.y) {
                    caught = true;
                }else if self.on_window_decoration(mouse_event.x, mouse_event.y) {
                    caught = true;
                    if !self.last_mouse_event.right_button {
                        self.minimized = !self.minimized;
                    }
                }
            }

            if mouse_event.middle_button {
                if self.on_window_body(mouse_event.x, mouse_event.y) {
                    caught = true;
                }else if self.on_window_decoration(mouse_event.x, mouse_event.y) {
                    caught = true;
                    unsafe {
                        let reenable = scheduler::start_no_ints();
                        self.events.push(QuitEvent.to_event());
                        scheduler::end_no_ints(reenable);
                    }
                }
            }

            if self.dragging {
                self.point.x += orig_mouse_event.x - self.last_mouse_event.x;
                self.point.y += orig_mouse_event.y - self.last_mouse_event.y;
                caught = true;
            }
        } else {
            self.dragging = false;
        }

        self.last_mouse_event = orig_mouse_event;

        if (caught && !self.dragging) || self.on_window_body(mouse_event.x, mouse_event.y) {
            unsafe {
                let reenable = scheduler::start_no_ints();
                self.events.push(mouse_event.to_event());
                scheduler::end_no_ints(reenable);
            }
        }

        caught
    }
}

impl Drop for Window {
    fn drop(&mut self) {
        unsafe {
            if self.ptr as usize > 0 {
                (*::session_ptr).remove_window(self.ptr);
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::ptr::null_mut;
use std::io::Error;

use widestring::WideCString;
use winapi::{LPCWSTR, BOOL, PSID, PSID_NAME_USE, LPDWORD, SID_NAME_USE};
use winapi::winerror::*;

use super::sid::Sid;

extern "system" {
    fn LookupAccountNameW(lpSystemName: LPCWSTR,
                          lpAccountName: LPCWSTR,
                          Sid: PSID,
                          cbSid: LPDWORD,
                          ReferencedDomainName: LPCWSTR,
                          cchReferencedDomainName: LPDWORD,
                          peUse: PSID_NAME_USE)
                          -> BOOL;
}

pub struct Account {
    pub name: String,
    pub system_name: Option<String>,
    pub domain: String,
    pub account_type: SID_NAME_USE,
    pub sid: Sid,
}

impl Account {
    pub fn from_name(name: &str) -> Option<Account> {
        lookup_account(name, None)
    }

    pub fn from_name_and_system(name: &str, system_name: &str) -> Option<Account> {
        lookup_account(name, Some(system_name.to_string()))
    }
}

fn lookup_account(name: &str, system_name: Option<String>) -> Option<Account> {
    let mut sid_size: u32 = 0;
    let mut domain_size: u32 = 0;
    let wide = WideCString::from_str(name).unwrap();
    unsafe {
        LookupAccountNameW(null_mut(),
                           wide.as_ptr(),
                           null_mut(),
                           &mut sid_size as LPDWORD,
                           null_mut(),
                           &mut domain_size as LPDWORD,
                           null_mut())
    };
    match Error::last_os_error().raw_os_error().unwrap() as u32 {
        ERROR_INSUFFICIENT_BUFFER => {}
        ERROR_NONE_MAPPED => return None,
        _ => {
            panic!("Error while looking up account for {}: {}",
                   name,
                   Error::last_os_error())
        }
    }

    let mut sid: Vec<u8> = Vec::with_capacity(sid_size as usize);
    let mut domain: Vec<u16> = Vec::with_capacity(domain_size as usize);
    let mut sid_type = SID_NAME_USE(0);

    let ret = unsafe {
        LookupAccountNameW(null_mut(),
                           wide.as_ptr(),
                           sid.as_mut_ptr() as PSID,
                           &mut sid_size as LPDWORD,
                           domain.as_mut_ptr(),
                           &mut domain_size as LPDWORD,
                           &mut sid_type as PSID_NAME_USE)
    };
    if ret == 0 {
        panic!("Failed to retrieve SID for {}: {}",
               name,
               Error::last_os_error());
    }
    unsafe {
        domain.set_len(domain_size as usize);
        sid.set_len(sid_size as usize);
    }
    let domain_str = WideCString::from_vec(domain).unwrap().to_string_lossy();
    Some(Account {
        name: name.to_string(),
        system_name: system_name,
        domain: domain_str,
        account_type: sid_type,
        sid: Sid { raw: sid },
    })
}

#[cfg(test)]
mod tests {
    use std::env;

    use winapi::winnt::{SidTypeUser, SidTypeWellKnownGroup};

    use super::*;

    #[test]
    fn real_account_returns_some() {
        assert_eq!(Account::from_name("Administrator").is_some(), true)
    }

    #[test]
    fn bogus_account_returns_none() {
        assert_eq!(Account::from_name("bogus").is_none(), true)
    }

    #[test]
    fn user_account_returns_user_type() {
        let acct_type = Account::from_name("Administrator").unwrap().account_type;
        assert_eq!(acct_type, SidTypeUser)
    }

    #[test]
    fn local_user_account_returns_local_machine_as_domain() {
        let acct_domain = Account::from_name("Administrator").unwrap().domain;
        assert_eq!(acct_domain, env::var("COMPUTERNAME").unwrap())
    }

    #[test]
    fn well_known_group_account_returns_correct_type() {
        let acct_type = Account::from_name("Everyone").unwrap().account_type;
        assert_eq!(acct_type, SidTypeWellKnownGroup)
    }

    #[test]
    fn well_known_group_account_has_well_known_sid() {
        let sid = Account::from_name("Everyone").unwrap().sid;
        assert_eq!(sid.to_string(), "S-1-1-0")
    }

    #[test]
    fn mixing_case_returns_same_account() {
        let lower_sid = Account::from_name("administrator").unwrap().sid;
        let upper_sid = Account::from_name("ADMINISTRATOR").unwrap().sid;
        assert_eq!(lower_sid.to_string(), upper_sid.to_string())
    }
}
<commit_msg>update to rustfmt 0.8.0<commit_after>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::ptr::null_mut;
use std::io::Error;

use widestring::WideCString;
use winapi::{LPCWSTR, BOOL, PSID, PSID_NAME_USE, LPDWORD, SID_NAME_USE};
use winapi::winerror::*;

use super::sid::Sid;

extern "system" {
    fn LookupAccountNameW(lpSystemName: LPCWSTR,
                          lpAccountName: LPCWSTR,
                          Sid: PSID,
                          cbSid: LPDWORD,
                          ReferencedDomainName: LPCWSTR,
                          cchReferencedDomainName: LPDWORD,
                          peUse: PSID_NAME_USE)
                          -> BOOL;
}

pub struct Account {
    pub name: String,
    pub system_name: Option<String>,
    pub domain: String,
    pub account_type: SID_NAME_USE,
    pub sid: Sid,
}

impl Account {
    pub fn from_name(name: &str) -> Option<Account> {
        lookup_account(name, None)
    }

    pub fn from_name_and_system(name: &str, system_name: &str) -> Option<Account> {
        lookup_account(name, Some(system_name.to_string()))
    }
}

fn lookup_account(name: &str, system_name: Option<String>) -> Option<Account> {
    let mut sid_size: u32 = 0;
    let mut domain_size: u32 = 0;
    let wide = WideCString::from_str(name).unwrap();
    unsafe {
        LookupAccountNameW(null_mut(),
                           wide.as_ptr(),
                           null_mut(),
                           &mut sid_size as LPDWORD,
                           null_mut(),
                           &mut domain_size as LPDWORD,
                           null_mut())
    };
    match Error::last_os_error().raw_os_error().unwrap() as u32 {
        ERROR_INSUFFICIENT_BUFFER => {}
        ERROR_NONE_MAPPED => return None,
        _ => {
            panic!("Error while looking up account for {}: {}",
                   name,
                   Error::last_os_error())
        }
    }

    let mut sid: Vec<u8> = Vec::with_capacity(sid_size as usize);
    let mut domain: Vec<u16> = Vec::with_capacity(domain_size as usize);
    let mut sid_type = SID_NAME_USE(0);

    let ret = unsafe {
        LookupAccountNameW(null_mut(),
                           wide.as_ptr(),
                           sid.as_mut_ptr() as PSID,
                           &mut sid_size as LPDWORD,
                           domain.as_mut_ptr(),
                           &mut domain_size as LPDWORD,
                           &mut sid_type as PSID_NAME_USE)
    };
    if ret == 0 {
        panic!("Failed to retrieve SID for {}: {}",
               name,
               Error::last_os_error());
    }
    unsafe {
        domain.set_len(domain_size as usize);
        sid.set_len(sid_size as usize);
    }
    let domain_str = WideCString::from_vec(domain).unwrap().to_string_lossy();
    Some(Account {
             name: name.to_string(),
             system_name: system_name,
             domain: domain_str,
             account_type: sid_type,
             sid: Sid { raw: sid },
         })
}

#[cfg(test)]
mod tests {
    use std::env;

    use winapi::winnt::{SidTypeUser, SidTypeWellKnownGroup};

    use super::*;

    #[test]
    fn real_account_returns_some() {
        assert_eq!(Account::from_name("Administrator").is_some(), true)
    }

    #[test]
    fn bogus_account_returns_none() {
        assert_eq!(Account::from_name("bogus").is_none(), true)
    }

    #[test]
    fn user_account_returns_user_type() {
        let acct_type = Account::from_name("Administrator").unwrap().account_type;
        assert_eq!(acct_type, SidTypeUser)
    }

    #[test]
    fn local_user_account_returns_local_machine_as_domain() {
        let acct_domain = Account::from_name("Administrator").unwrap().domain;
        assert_eq!(acct_domain, env::var("COMPUTERNAME").unwrap())
    }

    #[test]
    fn well_known_group_account_returns_correct_type() {
        let acct_type = Account::from_name("Everyone").unwrap().account_type;
        assert_eq!(acct_type, SidTypeWellKnownGroup)
    }

    #[test]
    fn well_known_group_account_has_well_known_sid() {
        let sid = Account::from_name("Everyone").unwrap().sid;
        assert_eq!(sid.to_string(), "S-1-1-0")
    }

    #[test]
    fn mixing_case_returns_same_account() {
        let lower_sid = Account::from_name("administrator").unwrap().sid;
        let upper_sid = Account::from_name("ADMINISTRATOR").unwrap().sid;
        assert_eq!(lower_sid.to_string(), upper_sid.to_string())
    }
}
<|endoftext|>


<commit_before>
/// Parses a SPIR-V document.
pub fn parse_spirv(data: &[u8]) -> Result<Spirv, ParseError> {
    if data.len() < 20 {
        return Err(ParseError::MissingHeader);
    }

    // we need to determine whether we are in big endian order or little endian order depending
    // on the magic number at the start of the file
    let data = if data[0] == 0x07 && data[1] == 0x23 && data[2] == 0x02 && data[3] == 0x03 {
        // big endian
        data.chunks(4).map(|c| {
            ((c[0] as u32) << 24) | ((c[1] as u32) << 16) | ((c[2] as u32) << 8) | c[3] as u32
        }).collect::<Vec<_>>()

    } else if data[3] == 0x07 && data[2] == 0x23 && data[1] == 0x02 && data[0] == 0x03 {
        // little endian
        data.chunks(4).map(|c| {
            ((c[3] as u32) << 24) | ((c[2] as u32) << 16) | ((c[1] as u32) << 8) | c[0] as u32
        }).collect::<Vec<_>>()

    } else {
        return Err(ParseError::MissingHeader);
    };

    parse_u32s(&data)
}

/// Parses a SPIR-V document from a list of u32s.
///
/// Endianess has already been handled.
fn parse_u32s(i: &[u32]) -> Result<Spirv, ParseError> {
    if i.len() < 5 {
        return Err(ParseError::MissingHeader);
    }

    if i[0] != 0x07230203 {
        return Err(ParseError::WrongHeader);
    }

    let version = (((i[1] & 0x00ff0000) >> 16) as u8, ((i[1] & 0x0000ff00) >> 8) as u8);

    let instructions = {
        let mut ret = Vec::new();
        let mut i = &i[5..];
        while i.len() >= 1 {
            let (instruction, rest) = try!(parse_instruction(i));
            ret.push(instruction);
            i = rest;
        }
        ret
    };

    Ok(Spirv {
        version: version,
        bound: i[3],
        instructions: instructions,
    })
}

/// Error that can happen when parsing.
#[derive(Debug, Clone)]
pub enum ParseError {
    MissingHeader,
    WrongHeader,
    IncompleteInstruction,
    UnknownCapability(u32),
    UnknownExecutionModel(u32),
}

#[derive(Debug, Clone)]
pub struct Spirv {
    pub version: (u8, u8),
    pub bound: u32,
    pub instructions: Vec<Instruction>,
}

#[derive(Debug, Clone)]
pub enum Instruction {
    Unknown(u16, Vec<u32>),
    Nop,
    ExtInstImport { result_id: u32, name: String },
    EntryPoint { execution: ExecutionModel, id: u32, name: String, interface: Vec<u32> },
    Capability(Capability),
    FunctionEnd,
    Kill,
    Return,
}

fn parse_instruction(i: &[u32]) -> Result<(Instruction, &[u32]), ParseError> {
    assert!(i.len() >= 1);

    let word_count = (i[0] >> 16) as usize;
    assert!(word_count >= 1);
    let opcode = (i[0] & 0xffff) as u16;

    if i.len() < word_count {
        return Err(ParseError::IncompleteInstruction);
    }

    let opcode = try!(decode_instruction(opcode, &i[1 .. word_count]));
    Ok((opcode, &i[word_count..]))
}

fn decode_instruction(opcode: u16, operands: &[u32]) -> Result<Instruction, ParseError> {
    Ok(match opcode {
        0 => Instruction::Nop,
        11 => Instruction::ExtInstImport {
            result_id: operands[0],
            name: parse_string(&operands[1..]).0
        },
        15 => {
            let (n, r) = parse_string(&operands[2..]);
            Instruction::EntryPoint {
                execution: try!(ExecutionModel::from_num(operands[0])),
                id: operands[1],
                name: n,
                interface: r.to_owned(),
            }
        },
        17 => Instruction::Capability(try!(Capability::from_num(operands[0]))),
        56 => Instruction::FunctionEnd,
        252 => Instruction::Kill,
        253 => Instruction::Return,
        _ => Instruction::Unknown(opcode, operands.to_owned()),
    })
}

fn parse_string(data: &[u32]) -> (String, &[u32]) {
    let bytes = data.iter().flat_map(|&n| {
        let b1 = (n & 0xff) as u8;
        let b2 = ((n >> 8) & 0xff) as u8;
        let b3 = ((n >> 16) & 0xff) as u8;
        let b4 = ((n >> 24) & 0xff) as u8;
        vec![b1, b2, b3, b4].into_iter()
    }).take_while(|&b| b != 0).collect::<Vec<u8>>();

    let r = 1 + bytes.len() / 4;
    let s = String::from_utf8(bytes).expect("Shader content is not UTF-8");

    (s, &data[r..])
}

#[derive(Debug, Clone)]
pub enum Capability {
    Matrix,
    Shader,
    Geometry,
    Tessellation,
    Addresses,
    Linkage,
    Kernel,
    Vector16,
    Float16Buffer,
    Float16,
    Float64,
    Int64,
    Int64Atomics,
    ImageBasic,
    ImageReadWrite,
    ImageMipmap,
    Pipes,
    Groups,
    DeviceEnqueue,
    LiteralSampler,
    AtomicStorage,
    Int16,
    TessellationPointSize,
    GeometryPointSize,
    ImageGatherExtended,
    StorageImageMultisample,
    UniformBufferArrayDynamicIndexing,
    SampledImageArrayDynamicIndexing,
    StorageBufferArrayDynamicIndexing,
    StorageImageArrayDynamicIndexing,
    ClipDistance,
    CullDistance,
    ImageCubeArray,
    SampleRateShading,
    ImageRect,
    SampledRect,
    GenericPointer,
    Int8,
    InputAttachment,
    SparseResidency,
    MinLod,
    Sampled1D,
    Image1D,
    SampledCubeArray,
    SampledBuffer,
    ImageBuffer,
    ImageMSArray,
    StorageImageExtendedFormats,
    ImageQuery,
    DerivativeControl,
    InterpolationFunction,
    TransformFeedback,
    GeometryStreams,
    StorageImageReadWithoutFormat,
    StorageImageWriteWithoutFormat,
}

impl Capability {
    fn from_num(num: u32) -> Result<Capability, ParseError> {
        match num {
            0 => Ok(Capability::Matrix),
            1 => Ok(Capability::Shader),
            2 => Ok(Capability::Geometry),
            3 => Ok(Capability::Tessellation),
            4 => Ok(Capability::Addresses),
            5 => Ok(Capability::Linkage),
            6 => Ok(Capability::Kernel),
            7 => Ok(Capability::Vector16),
            8 => Ok(Capability::Float16Buffer),
            9 => Ok(Capability::Float16),
            10 => Ok(Capability::Float64),
            11 => Ok(Capability::Int64),
            12 => Ok(Capability::Int64Atomics),
            13 => Ok(Capability::ImageBasic),
            14 => Ok(Capability::ImageReadWrite),
            15 => Ok(Capability::ImageMipmap),
            17 => Ok(Capability::Pipes),
            18 => Ok(Capability::Groups),
            19 => Ok(Capability::DeviceEnqueue),
            20 => Ok(Capability::LiteralSampler),
            21 => Ok(Capability::AtomicStorage),
            22 => Ok(Capability::Int16),
            23 => Ok(Capability::TessellationPointSize),
            24 => Ok(Capability::GeometryPointSize),
            25 => Ok(Capability::ImageGatherExtended),
            27 => Ok(Capability::StorageImageMultisample),
            28 => Ok(Capability::UniformBufferArrayDynamicIndexing),
            29 => Ok(Capability::SampledImageArrayDynamicIndexing),
            30 => Ok(Capability::StorageBufferArrayDynamicIndexing),
            31 => Ok(Capability::StorageImageArrayDynamicIndexing),
            32 => Ok(Capability::ClipDistance),
            33 => Ok(Capability::CullDistance),
            34 => Ok(Capability::ImageCubeArray),
            35 => Ok(Capability::SampleRateShading),
            36 => Ok(Capability::ImageRect),
            37 => Ok(Capability::SampledRect),
            38 => Ok(Capability::GenericPointer),
            39 => Ok(Capability::Int8),
            40 => Ok(Capability::InputAttachment),
            41 => Ok(Capability::SparseResidency),
            42 => Ok(Capability::MinLod),
            43 => Ok(Capability::Sampled1D),
            44 => Ok(Capability::Image1D),
            45 => Ok(Capability::SampledCubeArray),
            46 => Ok(Capability::SampledBuffer),
            47 => Ok(Capability::ImageBuffer),
            48 => Ok(Capability::ImageMSArray),
            49 => Ok(Capability::StorageImageExtendedFormats),
            50 => Ok(Capability::ImageQuery),
            51 => Ok(Capability::DerivativeControl),
            52 => Ok(Capability::InterpolationFunction),
            53 => Ok(Capability::TransformFeedback),
            54 => Ok(Capability::GeometryStreams),
            55 => Ok(Capability::StorageImageReadWithoutFormat),
            56 => Ok(Capability::StorageImageWriteWithoutFormat),
            _ => Err(ParseError::UnknownCapability(num)),
        }
    }
}

#[derive(Debug, Clone)]
pub enum ExecutionModel {
    Vertex,
    TessellationControl,
    TessellationEvaluation,
    Geometry,
    Fragment,
    GLCompute,
    Kernel,
}

impl ExecutionModel {
    fn from_num(num: u32) -> Result<ExecutionModel, ParseError> {
        match num {
            0 => Ok(ExecutionModel::Vertex),
            1 => Ok(ExecutionModel::TessellationControl),
            2 => Ok(ExecutionModel::TessellationEvaluation),
            3 => Ok(ExecutionModel::Geometry),
            4 => Ok(ExecutionModel::Fragment),
            5 => Ok(ExecutionModel::GLCompute),
            6 => Ok(ExecutionModel::Kernel),
            _ => Err(ParseError::UnknownExecutionModel(num)),
        }
    }
}

#[cfg(test)]
mod test {
    use parse;

    #[test]
    fn test() {
        let data = include_bytes!("../tests/frag.spv");
        parse::parse_spirv(data).unwrap();
    }
}
<commit_msg>More SPIR-V parsing<commit_after>
/// Parses a SPIR-V document.
pub fn parse_spirv(data: &[u8]) -> Result<Spirv, ParseError> {
    if data.len() < 20 {
        return Err(ParseError::MissingHeader);
    }

    // we need to determine whether we are in big endian order or little endian order depending
    // on the magic number at the start of the file
    let data = if data[0] == 0x07 && data[1] == 0x23 && data[2] == 0x02 && data[3] == 0x03 {
        // big endian
        data.chunks(4).map(|c| {
            ((c[0] as u32) << 24) | ((c[1] as u32) << 16) | ((c[2] as u32) << 8) | c[3] as u32
        }).collect::<Vec<_>>()

    } else if data[3] == 0x07 && data[2] == 0x23 && data[1] == 0x02 && data[0] == 0x03 {
        // little endian
        data.chunks(4).map(|c| {
            ((c[3] as u32) << 24) | ((c[2] as u32) << 16) | ((c[1] as u32) << 8) | c[0] as u32
        }).collect::<Vec<_>>()

    } else {
        return Err(ParseError::MissingHeader);
    };

    parse_u32s(&data)
}

/// Parses a SPIR-V document from a list of u32s.
///
/// Endianess has already been handled.
fn parse_u32s(i: &[u32]) -> Result<Spirv, ParseError> {
    if i.len() < 5 {
        return Err(ParseError::MissingHeader);
    }

    if i[0] != 0x07230203 {
        return Err(ParseError::WrongHeader);
    }

    let version = (((i[1] & 0x00ff0000) >> 16) as u8, ((i[1] & 0x0000ff00) >> 8) as u8);

    let instructions = {
        let mut ret = Vec::new();
        let mut i = &i[5..];
        while i.len() >= 1 {
            let (instruction, rest) = try!(parse_instruction(i));
            ret.push(instruction);
            i = rest;
        }
        ret
    };

    Ok(Spirv {
        version: version,
        bound: i[3],
        instructions: instructions,
    })
}

/// Error that can happen when parsing.
#[derive(Debug, Clone)]
pub enum ParseError {
    MissingHeader,
    WrongHeader,
    IncompleteInstruction,
    UnknownCapability(u32),
    UnknownExecutionModel(u32),
    UnknownStorageClass(u32),
}

#[derive(Debug, Clone)]
pub struct Spirv {
    pub version: (u8, u8),
    pub bound: u32,
    pub instructions: Vec<Instruction>,
}

#[derive(Debug, Clone)]
pub enum Instruction {
    Unknown(u16, Vec<u32>),
    Nop,
    Name { target_id: u32, name: String },
    MemberName { target_id: u32, member: u32, name: String },
    ExtInstImport { result_id: u32, name: String },
    EntryPoint { execution: ExecutionModel, id: u32, name: String, interface: Vec<u32> },
    Capability(Capability),
    TypeVoid { result_id: u32 },
    TypeBool { result_id: u32 },
    TypeInt { result_id: u32, width: u32, signedness: bool },
    TypeFloat { result_id: u32, width: u32 },
    TypeVector { result_id: u32, component_id: u32, count: u32 },
    TypeArray { result_id: u32, type_id: u32, length_id: u32 },
    TypeRuntimeArray { result_id: u32, type_id: u32 },
    TypeStruct { result_id: u32, member_types: Vec<u32> },
    TypeOpaque { result_id: u32, name: String },
    TypePointer { result_id: u32, storage_class: StorageClass, type_id: u32 },
    FunctionEnd,
    Variable { result_type_id: u32, result_id: u32, storage_class: StorageClass, initializer: Option<u32> },
    Kill,
    Return,
}

fn parse_instruction(i: &[u32]) -> Result<(Instruction, &[u32]), ParseError> {
    assert!(i.len() >= 1);

    let word_count = (i[0] >> 16) as usize;
    assert!(word_count >= 1);
    let opcode = (i[0] & 0xffff) as u16;

    if i.len() < word_count {
        return Err(ParseError::IncompleteInstruction);
    }

    let opcode = try!(decode_instruction(opcode, &i[1 .. word_count]));
    Ok((opcode, &i[word_count..]))
}

fn decode_instruction(opcode: u16, operands: &[u32]) -> Result<Instruction, ParseError> {
    Ok(match opcode {
        0 => Instruction::Nop,
        5 => Instruction::Name { target_id: operands[0], name: parse_string(&operands[1..]).0 },
        6 => Instruction::MemberName { target_id: operands[0], member: operands[1], name: parse_string(&operands[2..]).0 },
        11 => Instruction::ExtInstImport {
            result_id: operands[0],
            name: parse_string(&operands[1..]).0
        },
        15 => {
            let (n, r) = parse_string(&operands[2..]);
            Instruction::EntryPoint {
                execution: try!(ExecutionModel::from_num(operands[0])),
                id: operands[1],
                name: n,
                interface: r.to_owned(),
            }
        },
        17 => Instruction::Capability(try!(Capability::from_num(operands[0]))),
        19 => Instruction::TypeVoid { result_id: operands[0] },
        20 => Instruction::TypeBool { result_id: operands[0] },
        21 => Instruction::TypeInt { result_id: operands[0], width: operands[1], signedness: operands[2] != 0 },
        22 => Instruction::TypeFloat { result_id: operands[0], width: operands[1] },
        23 => Instruction::TypeVector { result_id: operands[0], component_id: operands[1], count: operands[2] },
        28 => Instruction::TypeArray { result_id: operands[0], type_id: operands[1], length_id: operands[2] },
        29 => Instruction::TypeRuntimeArray { result_id: operands[0], type_id: operands[1] },
        30 => Instruction::TypeStruct { result_id: operands[0], member_types: operands[1..].to_owned() },
        31 => Instruction::TypeOpaque { result_id: operands[0], name: parse_string(&operands[1..]).0 },
        32 => Instruction::TypePointer { result_id: operands[0], storage_class: try!(StorageClass::from_num(operands[1])), type_id: operands[2] },
        56 => Instruction::FunctionEnd,
        59 => Instruction::Variable {
            result_type_id: operands[0], result_id: operands[1],
            storage_class: try!(StorageClass::from_num(operands[2])),
            initializer: operands.get(3).map(|&v| v)
        },
        252 => Instruction::Kill,
        253 => Instruction::Return,
        _ => Instruction::Unknown(opcode, operands.to_owned()),
    })
}

fn parse_string(data: &[u32]) -> (String, &[u32]) {
    let bytes = data.iter().flat_map(|&n| {
        let b1 = (n & 0xff) as u8;
        let b2 = ((n >> 8) & 0xff) as u8;
        let b3 = ((n >> 16) & 0xff) as u8;
        let b4 = ((n >> 24) & 0xff) as u8;
        vec![b1, b2, b3, b4].into_iter()
    }).take_while(|&b| b != 0).collect::<Vec<u8>>();

    let r = 1 + bytes.len() / 4;
    let s = String::from_utf8(bytes).expect("Shader content is not UTF-8");

    (s, &data[r..])
}

#[derive(Debug, Clone)]
pub enum Capability {
    Matrix,
    Shader,
    Geometry,
    Tessellation,
    Addresses,
    Linkage,
    Kernel,
    Vector16,
    Float16Buffer,
    Float16,
    Float64,
    Int64,
    Int64Atomics,
    ImageBasic,
    ImageReadWrite,
    ImageMipmap,
    Pipes,
    Groups,
    DeviceEnqueue,
    LiteralSampler,
    AtomicStorage,
    Int16,
    TessellationPointSize,
    GeometryPointSize,
    ImageGatherExtended,
    StorageImageMultisample,
    UniformBufferArrayDynamicIndexing,
    SampledImageArrayDynamicIndexing,
    StorageBufferArrayDynamicIndexing,
    StorageImageArrayDynamicIndexing,
    ClipDistance,
    CullDistance,
    ImageCubeArray,
    SampleRateShading,
    ImageRect,
    SampledRect,
    GenericPointer,
    Int8,
    InputAttachment,
    SparseResidency,
    MinLod,
    Sampled1D,
    Image1D,
    SampledCubeArray,
    SampledBuffer,
    ImageBuffer,
    ImageMSArray,
    StorageImageExtendedFormats,
    ImageQuery,
    DerivativeControl,
    InterpolationFunction,
    TransformFeedback,
    GeometryStreams,
    StorageImageReadWithoutFormat,
    StorageImageWriteWithoutFormat,
}

impl Capability {
    fn from_num(num: u32) -> Result<Capability, ParseError> {
        match num {
            0 => Ok(Capability::Matrix),
            1 => Ok(Capability::Shader),
            2 => Ok(Capability::Geometry),
            3 => Ok(Capability::Tessellation),
            4 => Ok(Capability::Addresses),
            5 => Ok(Capability::Linkage),
            6 => Ok(Capability::Kernel),
            7 => Ok(Capability::Vector16),
            8 => Ok(Capability::Float16Buffer),
            9 => Ok(Capability::Float16),
            10 => Ok(Capability::Float64),
            11 => Ok(Capability::Int64),
            12 => Ok(Capability::Int64Atomics),
            13 => Ok(Capability::ImageBasic),
            14 => Ok(Capability::ImageReadWrite),
            15 => Ok(Capability::ImageMipmap),
            17 => Ok(Capability::Pipes),
            18 => Ok(Capability::Groups),
            19 => Ok(Capability::DeviceEnqueue),
            20 => Ok(Capability::LiteralSampler),
            21 => Ok(Capability::AtomicStorage),
            22 => Ok(Capability::Int16),
            23 => Ok(Capability::TessellationPointSize),
            24 => Ok(Capability::GeometryPointSize),
            25 => Ok(Capability::ImageGatherExtended),
            27 => Ok(Capability::StorageImageMultisample),
            28 => Ok(Capability::UniformBufferArrayDynamicIndexing),
            29 => Ok(Capability::SampledImageArrayDynamicIndexing),
            30 => Ok(Capability::StorageBufferArrayDynamicIndexing),
            31 => Ok(Capability::StorageImageArrayDynamicIndexing),
            32 => Ok(Capability::ClipDistance),
            33 => Ok(Capability::CullDistance),
            34 => Ok(Capability::ImageCubeArray),
            35 => Ok(Capability::SampleRateShading),
            36 => Ok(Capability::ImageRect),
            37 => Ok(Capability::SampledRect),
            38 => Ok(Capability::GenericPointer),
            39 => Ok(Capability::Int8),
            40 => Ok(Capability::InputAttachment),
            41 => Ok(Capability::SparseResidency),
            42 => Ok(Capability::MinLod),
            43 => Ok(Capability::Sampled1D),
            44 => Ok(Capability::Image1D),
            45 => Ok(Capability::SampledCubeArray),
            46 => Ok(Capability::SampledBuffer),
            47 => Ok(Capability::ImageBuffer),
            48 => Ok(Capability::ImageMSArray),
            49 => Ok(Capability::StorageImageExtendedFormats),
            50 => Ok(Capability::ImageQuery),
            51 => Ok(Capability::DerivativeControl),
            52 => Ok(Capability::InterpolationFunction),
            53 => Ok(Capability::TransformFeedback),
            54 => Ok(Capability::GeometryStreams),
            55 => Ok(Capability::StorageImageReadWithoutFormat),
            56 => Ok(Capability::StorageImageWriteWithoutFormat),
            _ => Err(ParseError::UnknownCapability(num)),
        }
    }
}

#[derive(Debug, Clone)]
pub enum ExecutionModel {
    Vertex,
    TessellationControl,
    TessellationEvaluation,
    Geometry,
    Fragment,
    GLCompute,
    Kernel,
}

impl ExecutionModel {
    fn from_num(num: u32) -> Result<ExecutionModel, ParseError> {
        match num {
            0 => Ok(ExecutionModel::Vertex),
            1 => Ok(ExecutionModel::TessellationControl),
            2 => Ok(ExecutionModel::TessellationEvaluation),
            3 => Ok(ExecutionModel::Geometry),
            4 => Ok(ExecutionModel::Fragment),
            5 => Ok(ExecutionModel::GLCompute),
            6 => Ok(ExecutionModel::Kernel),
            _ => Err(ParseError::UnknownExecutionModel(num)),
        }
    }
}

#[derive(Debug, Clone)]
pub enum StorageClass {
    UniformConstant,
    Input,
    Uniform,
    Output,
    Workgroup,
    CrossWorkgroup,
    Private,
    Function,
    Generic,
    PushConstant,
    AtomicCounter,
    Image,
}

impl StorageClass {
    fn from_num(num: u32) -> Result<StorageClass, ParseError> {
        match num {
            0 => Ok(StorageClass::UniformConstant),
            1 => Ok(StorageClass::Input),
            2 => Ok(StorageClass::Uniform),
            3 => Ok(StorageClass::Output),
            4 => Ok(StorageClass::Workgroup),
            5 => Ok(StorageClass::CrossWorkgroup),
            6 => Ok(StorageClass::Private),
            7 => Ok(StorageClass::Function),
            8 => Ok(StorageClass::Generic),
            9 => Ok(StorageClass::PushConstant),
            10 => Ok(StorageClass::AtomicCounter),
            11 => Ok(StorageClass::Image),
            _ => Err(ParseError::UnknownStorageClass(num)),
        }
    }
}

#[cfg(test)]
mod test {
    use parse;

    #[test]
    fn test() {
        let data = include_bytes!("../examples/example.spv");
        println!("{:?}", parse::parse_spirv(data).unwrap());
    }
}
<|endoftext|>


<commit_before>use std::sync::Arc;
use std::default::Default;
use graphics::{self, DrawState, ImageSize, Graphics};
use glium::{Display, Surface, Texture2d, Texture, Program, VertexBuffer,
            DrawParameters, BlendingFunction, LinearBlendingFactor};
use glium::index::{NoIndices, PrimitiveType};
use shader_version::Shaders;
use shader_version::glsl::GLSL;

use shader;
use OpenGL;

#[derive(Clone)]
pub struct DrawTexture {
    texture: Arc<Texture2d>,
}

impl DrawTexture {
    pub fn new(texture: Texture2d) -> DrawTexture {
        DrawTexture { texture: Arc::new(texture) }
    }
}

impl ImageSize for DrawTexture {
    fn get_size(&self) -> (u32, u32) {
        let ref tex = self.texture;
        (tex.get_width(), tex.get_height().unwrap())
    }
}

#[derive(Copy, Clone)]
struct PlainVertex {
    position: [f32; 2],
}

implement_vertex!(PlainVertex, position);

#[derive(Copy, Clone)]
struct TexturedVertex {
    position: [f32; 2],
    texcoord: [f32; 2],
}

implement_vertex!(TexturedVertex, position, texcoord);

pub struct Glium2d {
    next_plain_buffer: u32,
    plain_buffer1: VertexBuffer<PlainVertex>,
    plain_buffer2: VertexBuffer<PlainVertex>,
    next_textured_buffer: u32,
    textured_buffer1: VertexBuffer<TexturedVertex>,
    textured_buffer2: VertexBuffer<TexturedVertex>,
    shader_texture: Program,
    shader_color: Program,
}

impl Glium2d {
    pub fn new(opengl: OpenGL, display: &Display) -> Glium2d {
        // FIXME: create empty buffers when glium supports them
        let plain_data = ::std::iter::repeat(PlainVertex { position: [0.0, 0.0] })
                                .take(graphics::BACK_END_MAX_VERTEX_COUNT).collect::<Vec<_>>();
        let textured_data = ::std::iter::repeat(TexturedVertex { position: [0.0, 0.0], texcoord: [0.0, 0.0] })
                                .take(graphics::BACK_END_MAX_VERTEX_COUNT).collect::<Vec<_>>();

        Glium2d {
            next_plain_buffer: 0,
            plain_buffer1: VertexBuffer::new(display, plain_data.clone()),
            plain_buffer2: VertexBuffer::new(display, plain_data),
            next_textured_buffer: 0,
            textured_buffer1: VertexBuffer::new(display, textured_data.clone()),
            textured_buffer2: VertexBuffer::new(display, textured_data),
            shader_texture:
                Program::from_source(display,
                                     Shaders::new().set(GLSL::_1_20, shader::VS_TEXTURED_120)
                                                   .set(GLSL::_1_50, shader::VS_TEXTURED_150)
                                                   .get(opengl.to_GLSL()).unwrap(),
                                     Shaders::new().set(GLSL::_1_20, shader::FS_TEXTURED_120)
                                                   .set(GLSL::_1_50, shader::FS_TEXTURED_150)
                                                   .get(opengl.to_GLSL()).unwrap(),
                                     None).ok().expect("failed to initialize textured shader"),
            shader_color:
                Program::from_source(display,
                                     Shaders::new().set(GLSL::_1_20, shader::VS_COLORED_120)
                                                   .set(GLSL::_1_50, shader::VS_COLORED_150)
                                                   .get(opengl.to_GLSL()).unwrap(),
                                     Shaders::new().set(GLSL::_1_20, shader::FS_COLORED_120)
                                                   .set(GLSL::_1_50, shader::FS_COLORED_150)
                                                   .get(opengl.to_GLSL()).unwrap(),
                                     None).ok().expect("failed to initialize colored shader"),
        }
    }
}

pub struct GliumGraphics<'d, 's, S: 's> {
    system: &'d mut Glium2d,
    surface: &'s mut S,
}

impl<'d, 's, S> GliumGraphics<'d, 's, S> {
    pub fn new(system: &'d mut Glium2d, surface: &'s mut S)
               -> GliumGraphics<'d, 's, S> {
        GliumGraphics {
            system: system,
            surface: surface,
        }
    }
}

/// Implemented by all graphics back-ends.
impl<'d, 's, S: Surface> Graphics for GliumGraphics<'d, 's, S> {
    type Texture = DrawTexture;

    /// Clears background with a color.
    fn clear(&mut self, color: [f32; 4]) {
        let [r, g, b, a] = color;
        self.surface.clear_color(r, g, b, a);
    }

    /// Renders list of 2d triangles.
    fn tri_list<F>(
        &mut self,
        _draw_state: &DrawState,
        color: &[f32; 4],
        mut f: F
    )
        where F: FnMut(&mut FnMut(&[f32]))
    {
        f(&mut |vertices: &[f32]| {
            let slice = match self.system.next_plain_buffer {
                0 => {
                    self.system.next_plain_buffer = 1;
                    self.system.plain_buffer1.slice(0, vertices.len() / 2).unwrap()
                },
                1 => {
                    self.system.next_plain_buffer = 0;
                    self.system.plain_buffer2.slice(0, vertices.len() / 2).unwrap()
                },
                _ => unreachable!()
            };

            slice.write({
                (0..vertices.len() / 2)
                    .map(|i| PlainVertex {
                        position: [vertices[2 * i], vertices[2 * i + 1]],
                    })
                    .collect()
            });

            self.surface.draw(
                slice,
                &NoIndices(PrimitiveType::TrianglesList),
                &self.system.shader_color,
                &uniform! { color: *color },
                &DrawParameters {
                    blending_function: Some(BlendingFunction::Addition {
                        source: LinearBlendingFactor::SourceAlpha,
                        destination: LinearBlendingFactor::OneMinusSourceAlpha,
                    }),
                    .. Default::default()
                },
            )
            .ok()
            .expect("failed to draw triangle list");
        })
    }

    /// Renders list of 2d triangles.
    ///
    /// A texture coordinate is assigned per vertex.
    /// The texture coordinates refers to the current texture.
    fn tri_list_uv<F>(
        &mut self,
        _draw_state: &DrawState,
        color: &[f32; 4],
        texture: &DrawTexture,
        mut f: F
    )
        where F: FnMut(&mut FnMut(&[f32], &[f32]))
    {
        use std::cmp::min;

        f(&mut |vertices: &[f32], texture_coords: &[f32]| {
            let len = min(vertices.len(), texture_coords.len()) / 2;

            let slice = match self.system.next_textured_buffer {
                0 => {
                    self.system.next_textured_buffer = 1;
                    self.system.textured_buffer1.slice(0, len).unwrap()
                },
                1 => {
                    self.system.next_textured_buffer = 0;
                    self.system.textured_buffer2.slice(0, len).unwrap()
                },
                _ => unreachable!()
            };

            slice.write({
                (0..len)
                    .map(|i| TexturedVertex {
                        position: [vertices[2 * i], vertices[2 * i + 1]],
                        // FIXME: The `1.0 - ...` is because of a wrong convention
                        texcoord: [texture_coords[2 * i], 1.0 - texture_coords[2 * i + 1]],
                    })
                    .collect()
            });

            let texture = &*(texture.texture);
            self.surface.draw(
                slice,
                &NoIndices(PrimitiveType::TrianglesList),
                &self.system.shader_texture,
                &uniform! {
                    color: *color,
                    s_texture: texture
                },
                &DrawParameters {
                    blending_function: Some(BlendingFunction::Addition {
                        source: LinearBlendingFactor::SourceAlpha,
                        destination: LinearBlendingFactor::OneMinusSourceAlpha,
                    }),
                    .. Default::default()
                },
            )
            .ok()
            .expect("failed to draw triangle list");
        })
    }
}
<commit_msg>Added `clear_stencil`<commit_after>use std::sync::Arc;
use std::default::Default;
use graphics::{self, DrawState, ImageSize, Graphics};
use glium::{Display, Surface, Texture2d, Texture, Program, VertexBuffer,
            DrawParameters, BlendingFunction, LinearBlendingFactor};
use glium::index::{NoIndices, PrimitiveType};
use shader_version::Shaders;
use shader_version::glsl::GLSL;

use shader;
use OpenGL;

#[derive(Clone)]
pub struct DrawTexture {
    texture: Arc<Texture2d>,
}

impl DrawTexture {
    pub fn new(texture: Texture2d) -> DrawTexture {
        DrawTexture { texture: Arc::new(texture) }
    }
}

impl ImageSize for DrawTexture {
    fn get_size(&self) -> (u32, u32) {
        let ref tex = self.texture;
        (tex.get_width(), tex.get_height().unwrap())
    }
}

#[derive(Copy, Clone)]
struct PlainVertex {
    position: [f32; 2],
}

implement_vertex!(PlainVertex, position);

#[derive(Copy, Clone)]
struct TexturedVertex {
    position: [f32; 2],
    texcoord: [f32; 2],
}

implement_vertex!(TexturedVertex, position, texcoord);

pub struct Glium2d {
    next_plain_buffer: u32,
    plain_buffer1: VertexBuffer<PlainVertex>,
    plain_buffer2: VertexBuffer<PlainVertex>,
    next_textured_buffer: u32,
    textured_buffer1: VertexBuffer<TexturedVertex>,
    textured_buffer2: VertexBuffer<TexturedVertex>,
    shader_texture: Program,
    shader_color: Program,
}

impl Glium2d {
    pub fn new(opengl: OpenGL, display: &Display) -> Glium2d {
        // FIXME: create empty buffers when glium supports them
        let plain_data = ::std::iter::repeat(PlainVertex { position: [0.0, 0.0] })
                                .take(graphics::BACK_END_MAX_VERTEX_COUNT).collect::<Vec<_>>();
        let textured_data = ::std::iter::repeat(TexturedVertex { position: [0.0, 0.0], texcoord: [0.0, 0.0] })
                                .take(graphics::BACK_END_MAX_VERTEX_COUNT).collect::<Vec<_>>();

        Glium2d {
            next_plain_buffer: 0,
            plain_buffer1: VertexBuffer::new(display, plain_data.clone()),
            plain_buffer2: VertexBuffer::new(display, plain_data),
            next_textured_buffer: 0,
            textured_buffer1: VertexBuffer::new(display, textured_data.clone()),
            textured_buffer2: VertexBuffer::new(display, textured_data),
            shader_texture:
                Program::from_source(display,
                                     Shaders::new().set(GLSL::_1_20, shader::VS_TEXTURED_120)
                                                   .set(GLSL::_1_50, shader::VS_TEXTURED_150)
                                                   .get(opengl.to_GLSL()).unwrap(),
                                     Shaders::new().set(GLSL::_1_20, shader::FS_TEXTURED_120)
                                                   .set(GLSL::_1_50, shader::FS_TEXTURED_150)
                                                   .get(opengl.to_GLSL()).unwrap(),
                                     None).ok().expect("failed to initialize textured shader"),
            shader_color:
                Program::from_source(display,
                                     Shaders::new().set(GLSL::_1_20, shader::VS_COLORED_120)
                                                   .set(GLSL::_1_50, shader::VS_COLORED_150)
                                                   .get(opengl.to_GLSL()).unwrap(),
                                     Shaders::new().set(GLSL::_1_20, shader::FS_COLORED_120)
                                                   .set(GLSL::_1_50, shader::FS_COLORED_150)
                                                   .get(opengl.to_GLSL()).unwrap(),
                                     None).ok().expect("failed to initialize colored shader"),
        }
    }
}

pub struct GliumGraphics<'d, 's, S: 's> {
    system: &'d mut Glium2d,
    surface: &'s mut S,
}

impl<'d, 's, S> GliumGraphics<'d, 's, S> {
    pub fn new(system: &'d mut Glium2d, surface: &'s mut S)
               -> GliumGraphics<'d, 's, S> {
        GliumGraphics {
            system: system,
            surface: surface,
        }
    }
}

/// Implemented by all graphics back-ends.
impl<'d, 's, S: Surface> Graphics for GliumGraphics<'d, 's, S> {
    type Texture = DrawTexture;

    /// Clears background with a color.
    fn clear(&mut self, color: [f32; 4]) {
        let [r, g, b, a] = color;
        self.surface.clear_color(r, g, b, a);
    }

    fn clear_stencil(&mut self, value: u8) {
        self.surface.clear_stencil(value as i32);
    }

    /// Renders list of 2d triangles.
    fn tri_list<F>(
        &mut self,
        _draw_state: &DrawState,
        color: &[f32; 4],
        mut f: F
    )
        where F: FnMut(&mut FnMut(&[f32]))
    {
        f(&mut |vertices: &[f32]| {
            let slice = match self.system.next_plain_buffer {
                0 => {
                    self.system.next_plain_buffer = 1;
                    self.system.plain_buffer1.slice(0, vertices.len() / 2).unwrap()
                },
                1 => {
                    self.system.next_plain_buffer = 0;
                    self.system.plain_buffer2.slice(0, vertices.len() / 2).unwrap()
                },
                _ => unreachable!()
            };

            slice.write({
                (0..vertices.len() / 2)
                    .map(|i| PlainVertex {
                        position: [vertices[2 * i], vertices[2 * i + 1]],
                    })
                    .collect()
            });

            self.surface.draw(
                slice,
                &NoIndices(PrimitiveType::TrianglesList),
                &self.system.shader_color,
                &uniform! { color: *color },
                &DrawParameters {
                    blending_function: Some(BlendingFunction::Addition {
                        source: LinearBlendingFactor::SourceAlpha,
                        destination: LinearBlendingFactor::OneMinusSourceAlpha,
                    }),
                    .. Default::default()
                },
            )
            .ok()
            .expect("failed to draw triangle list");
        })
    }

    /// Renders list of 2d triangles.
    ///
    /// A texture coordinate is assigned per vertex.
    /// The texture coordinates refers to the current texture.
    fn tri_list_uv<F>(
        &mut self,
        _draw_state: &DrawState,
        color: &[f32; 4],
        texture: &DrawTexture,
        mut f: F
    )
        where F: FnMut(&mut FnMut(&[f32], &[f32]))
    {
        use std::cmp::min;

        f(&mut |vertices: &[f32], texture_coords: &[f32]| {
            let len = min(vertices.len(), texture_coords.len()) / 2;

            let slice = match self.system.next_textured_buffer {
                0 => {
                    self.system.next_textured_buffer = 1;
                    self.system.textured_buffer1.slice(0, len).unwrap()
                },
                1 => {
                    self.system.next_textured_buffer = 0;
                    self.system.textured_buffer2.slice(0, len).unwrap()
                },
                _ => unreachable!()
            };

            slice.write({
                (0..len)
                    .map(|i| TexturedVertex {
                        position: [vertices[2 * i], vertices[2 * i + 1]],
                        // FIXME: The `1.0 - ...` is because of a wrong convention
                        texcoord: [texture_coords[2 * i], 1.0 - texture_coords[2 * i + 1]],
                    })
                    .collect()
            });

            let texture = &*(texture.texture);
            self.surface.draw(
                slice,
                &NoIndices(PrimitiveType::TrianglesList),
                &self.system.shader_texture,
                &uniform! {
                    color: *color,
                    s_texture: texture
                },
                &DrawParameters {
                    blending_function: Some(BlendingFunction::Addition {
                        source: LinearBlendingFactor::SourceAlpha,
                        destination: LinearBlendingFactor::OneMinusSourceAlpha,
                    }),
                    .. Default::default()
                },
            )
            .ok()
            .expect("failed to draw triangle list");
        })
    }
}
<|endoftext|>


<commit_before>
use std::ffi;
use std::ffi::AsOsStr;
use std::convert::AsRef;
use std::path::Path;

pub mod record;
pub mod header;

use htslib;
use bcf::header::HeaderView;

pub use bcf::header::Header;
pub use bcf::record::Record;

pub struct Reader {
    inner: *mut htslib::vcf::htsFile,
    pub header: HeaderView,
}

impl Reader {
   pub fn new<P: AsRef<Path>>(path: &P) -> Self {
        let htsfile = bcf_open(path, b"r");
        let header = unsafe { htslib::vcf::bcf_hdr_read(htsfile) };
        Reader { inner: htsfile, header: HeaderView::new(header) }
    }

    pub fn read(&self, record: &mut record::Record) -> Result<(), ReadError> {
        match unsafe { htslib::vcf::bcf_read(self.inner, self.header.inner, record.inner) } {
            0  => {
                record.header = self.header.inner;
                Ok(())
            },
            -1 => Err(ReadError::NoMoreRecord),
            _  => Err(ReadError::Invalid),
        }
    }

    pub fn records(&self) -> Records {
        Records { reader: self }
    }
}

impl Drop for Reader {
    fn drop(&mut self) {
        unsafe {
            htslib::vcf::bcf_hdr_destroy(self.header.inner);
            htslib::vcf::hts_close(self.inner);
        }
    }
}

pub struct Writer {
    inner: *mut htslib::vcf::htsFile,
    pub header: HeaderView,
}

impl Writer {
    pub fn new<P: AsRef<Path>>(path: &P, header: &Header) -> Self {
        let htsfile = bcf_open(path, b"w");
        unsafe { htslib::vcf::bcf_hdr_write(htsfile, header.inner) };
        Writer { inner: htsfile, header: HeaderView::new(unsafe { htslib::vcf::bcf_hdr_dup(header.inner) }) }
    }

    pub fn write(&mut self, record: &record::Record) -> Result<(), ()> {
        if unsafe { htslib::vcf::bcf_write(self.inner, self.header.inner, record.inner) } == -1 {
            Err(())
        }
        else {
            Ok(())
        }
    }
}

impl Drop for Writer {
    fn drop(&mut self) {
        unsafe {
            htslib::vcf::bcf_hdr_destroy(self.header.inner);
            htslib::vcf::hts_close(self.inner);
        }
    }
}

pub struct Records<'a> {
    reader: &'a Reader,
}

impl<'a> Iterator for Records<'a> {
    type Item = Result<record::Record, ReadError>;

    fn next(&mut self) -> Option<Result<record::Record, ReadError>> {
        let mut record = record::Record::new();
        match self.reader.read(&mut record) {
            Err(ReadError::NoMoreRecord) => None,
            Err(e)                       => Some(Err(e)),
            Ok(())                       => Some(Ok(record)),
        }
    }
}

/// Wrapper for opening a BCF file.
fn bcf_open<P: AsRef<Path>>(path: &P, mode: &[u8]) -> *mut htslib::vcf::htsFile {
    unsafe {
        htslib::vcf::hts_open(
            path.as_ref().as_os_str().to_cstring().unwrap().as_ptr(),
            ffi::CString::new(mode).unwrap().as_ptr()
        )
    }
}

pub enum ReadError {
    Invalid,
    NoMoreRecord,
}

#[cfg(test)]
mod tests {
    extern crate tempdir;
    use super::*;
    use std::path::Path;

    fn _test_read<P: AsRef<Path>>(path: &P) {
        let bcf = Reader::new(path);
        assert_eq!(bcf.header.samples(), [b"NA12878.subsample-0.25-0"]);

        for (i, rec) in bcf.records().enumerate() {
            let mut record = rec.ok().expect("Error reading record.");

            assert_eq!(record.rid().expect("Error reading rid."), 0);
            assert_eq!(record.pos(), 10021 + i as u32);
            assert_eq!(record.qual(), 0f32);
            assert_eq!(record.info(b"MQ0F").float().ok().expect("Error reading info."), [1.0]);
            if i == 59 {
                assert_eq!(record.info(b"SGB").float().ok().expect("Error reading info."), [-0.379885]);
            }
            // the artificial "not observed" allele is present in each record.
            assert_eq!(record.alleles().iter().last().unwrap(), b"<X>");

            let mut fmt = record.format(b"PL");
            let pl = fmt.integer().ok().expect("Error reading format.");
            assert_eq!(pl.len(), 1);
            if i == 59 {
                assert_eq!(pl[0].len(), 6);
            }
            else {
                assert_eq!(pl[0].len(), 3);
            }
        }
    }

    #[test]
    fn test_read() {
        _test_read(&"test.bcf");
    }

    #[test]
    fn test_write() {
        let bcf = Reader::new(&"test.bcf");
        let tmp = tempdir::TempDir::new("rust-htslib").ok().expect("Cannot create temp dir");
        let bcfpath = tmp.path().join("test.bcf");
        println!("{:?}", bcfpath);
        {
            let header = Header::with_template(&bcf.header);
            let mut writer = Writer::new(&bcfpath, &header);
            for record in bcf.records() {
                writer.write(&record.ok().expect("Error reading record.")).ok().expect("Error writing record");
            }
        }
        {
            _test_read(&bcfpath);
        }
        tmp.close().ok().expect("Failed to delete temp dir");
    }
}
<commit_msg>Control output for bcf writer: any combination of bcf, vcf, compressed and uncompressed is possible now.<commit_after>
use std::ffi;
use std::ffi::AsOsStr;
use std::convert::AsRef;
use std::path::Path;

pub mod record;
pub mod header;

use htslib;
use bcf::header::HeaderView;

pub use bcf::header::Header;
pub use bcf::record::Record;

pub struct Reader {
    inner: *mut htslib::vcf::htsFile,
    pub header: HeaderView,
}

impl Reader {
   pub fn new<P: AsRef<Path>>(path: &P) -> Self {
        let htsfile = bcf_open(path, b"r");
        let header = unsafe { htslib::vcf::bcf_hdr_read(htsfile) };
        Reader { inner: htsfile, header: HeaderView::new(header) }
    }

    pub fn read(&self, record: &mut record::Record) -> Result<(), ReadError> {
        match unsafe { htslib::vcf::bcf_read(self.inner, self.header.inner, record.inner) } {
            0  => {
                record.header = self.header.inner;
                Ok(())
            },
            -1 => Err(ReadError::NoMoreRecord),
            _  => Err(ReadError::Invalid),
        }
    }

    pub fn records(&self) -> Records {
        Records { reader: self }
    }
}

impl Drop for Reader {
    fn drop(&mut self) {
        unsafe {
            htslib::vcf::bcf_hdr_destroy(self.header.inner);
            htslib::vcf::hts_close(self.inner);
        }
    }
}

pub struct Writer {
    inner: *mut htslib::vcf::htsFile,
    pub header: HeaderView,
}

impl Writer {
    pub fn new<P: AsRef<Path>>(path: &P, header: &Header, uncompressed: bool, vcf: bool) -> Self {
        let mode: &[u8] = match (uncompressed, vcf) {
            (true, true)   => b"w",
            (false, true)  => b"wz",
            (true, false)  => b"wu",
            (false, false) => b"wb",
        };

        let htsfile = bcf_open(path, mode);
        unsafe { htslib::vcf::bcf_hdr_write(htsfile, header.inner) };
        Writer { inner: htsfile, header: HeaderView::new(unsafe { htslib::vcf::bcf_hdr_dup(header.inner) }) }
    }

    pub fn write(&mut self, record: &record::Record) -> Result<(), ()> {
        if unsafe { htslib::vcf::bcf_write(self.inner, self.header.inner, record.inner) } == -1 {
            Err(())
        }
        else {
            Ok(())
        }
    }
}

impl Drop for Writer {
    fn drop(&mut self) {
        unsafe {
            htslib::vcf::bcf_hdr_destroy(self.header.inner);
            htslib::vcf::hts_close(self.inner);
        }
    }
}

pub struct Records<'a> {
    reader: &'a Reader,
}

impl<'a> Iterator for Records<'a> {
    type Item = Result<record::Record, ReadError>;

    fn next(&mut self) -> Option<Result<record::Record, ReadError>> {
        let mut record = record::Record::new();
        match self.reader.read(&mut record) {
            Err(ReadError::NoMoreRecord) => None,
            Err(e)                       => Some(Err(e)),
            Ok(())                       => Some(Ok(record)),
        }
    }
}

/// Wrapper for opening a BCF file.
fn bcf_open<P: AsRef<Path>>(path: &P, mode: &[u8]) -> *mut htslib::vcf::htsFile {
    unsafe {
        htslib::vcf::hts_open(
            path.as_ref().as_os_str().to_cstring().unwrap().as_ptr(),
            ffi::CString::new(mode).unwrap().as_ptr()
        )
    }
}

pub enum ReadError {
    Invalid,
    NoMoreRecord,
}

#[cfg(test)]
mod tests {
    extern crate tempdir;
    use super::*;
    use std::path::Path;

    fn _test_read<P: AsRef<Path>>(path: &P) {
        let bcf = Reader::new(path);
        assert_eq!(bcf.header.samples(), [b"NA12878.subsample-0.25-0"]);

        for (i, rec) in bcf.records().enumerate() {
            let mut record = rec.ok().expect("Error reading record.");

            assert_eq!(record.rid().expect("Error reading rid."), 0);
            assert_eq!(record.pos(), 10021 + i as u32);
            assert_eq!(record.qual(), 0f32);
            assert_eq!(record.info(b"MQ0F").float().ok().expect("Error reading info."), [1.0]);
            if i == 59 {
                assert_eq!(record.info(b"SGB").float().ok().expect("Error reading info."), [-0.379885]);
            }
            // the artificial "not observed" allele is present in each record.
            assert_eq!(record.alleles().iter().last().unwrap(), b"<X>");

            let mut fmt = record.format(b"PL");
            let pl = fmt.integer().ok().expect("Error reading format.");
            assert_eq!(pl.len(), 1);
            if i == 59 {
                assert_eq!(pl[0].len(), 6);
            }
            else {
                assert_eq!(pl[0].len(), 3);
            }
        }
    }

    #[test]
    fn test_read() {
        _test_read(&"test.bcf");
    }

    #[test]
    fn test_write() {
        let bcf = Reader::new(&"test.bcf");
        let tmp = tempdir::TempDir::new("rust-htslib").ok().expect("Cannot create temp dir");
        let bcfpath = tmp.path().join("test.bcf");
        println!("{:?}", bcfpath);
        {
            let header = Header::with_template(&bcf.header);
            let mut writer = Writer::new(&bcfpath, &header, false, false);
            for record in bcf.records() {
                writer.write(&record.ok().expect("Error reading record.")).ok().expect("Error writing record");
            }
        }
        {
            _test_read(&bcfpath);
        }
        tmp.close().ok().expect("Failed to delete temp dir");
    }
}
<|endoftext|>


<commit_before>use clap::{self, Arg, App, SubCommand, AppSettings};
use std::io::{self, Write};
use std::process;
use std::path::PathBuf;
use std::error;
use std::result;
use std::fmt;
use mprovision as mp;

#[derive(Debug, PartialEq)]
pub enum Command {
    List(ListParams),
    ShowUuid(String, Option<PathBuf>),
    ShowPath(PathBuf),
    Cleanup(Option<PathBuf>),
}

#[derive(Debug, Default, PartialEq)]
pub struct ListParams {
    pub filter: Option<String>,
    pub expires_in_days: Option<i64>,
    pub directory: Option<PathBuf>,
}

pub type Result = result::Result<Command, Error>;

#[derive(Debug)]
pub enum Error {
    Lib(mp::Error),
    Clap(clap::Error),
    Custom(String),
}

impl Error {
    pub fn exit(&self) -> ! {
        match *self {
            Error::Clap(ref e) => e.exit(),
            _ => {
                writeln!(&mut io::stderr(), "{}", self).unwrap();
                process::exit(1);
            }
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Lib(ref e) => e.description(),
            Error::Clap(ref e) => e.description(),
            Error::Custom(ref e) => e,
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::Lib(ref e) => Some(e),
            Error::Clap(ref e) => Some(e),
            Error::Custom(_) => None,
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Lib(ref e) => error::Error::description(e).fmt(f),
            Error::Clap(ref e) => error::Error::description(e).fmt(f),
            Error::Custom(ref e) => e.fmt(f),
        }
    }
}

impl From<mp::Error> for Error {
    fn from(e: mp::Error) -> Self {
        Error::Lib(e)
    }
}

impl From<clap::Error> for Error {
    fn from(e: clap::Error) -> Self {
        Error::Clap(e)
    }
}

pub fn parse<I, S>(args: I) -> Result
    where I: IntoIterator<Item = S>,
          S: Clone,
          ::std::ffi::OsString: From<S>
{
    let app = App::new("mprovision")
        .about("A tool that helps iOS developers to manage mobileprovision files.")
        .subcommand(SubCommand::with_name("list")
            .about("Lists provisioning profiles")
            .setting(AppSettings::DisableVersion)
            .arg(Arg::with_name("TEXT")
                .long("--filter")
                .required(false)
                .empty_values(false)
                .takes_value(true))
            .arg(Arg::with_name("DAYS")
                .long("--expires-in-days")
                .required(false)
                .empty_values(false)
                .takes_value(true))
            .arg(Arg::with_name("DIRECTORY")
                .required(false)
                .empty_values(false)
                .takes_value(true)))
        .subcommand(SubCommand::with_name("cleanup")
            .about("Removes expired provisioning profiles")
            .setting(AppSettings::DisableVersion)
            .arg(Arg::with_name("DIRECTORY")
                .required(false)
                .empty_values(false)
                .takes_value(true)))
        .subcommand(SubCommand::with_name("show")
            .about("Shows details of a provisioning profile")
            .setting(AppSettings::DisableVersion)
            .arg(Arg::with_name("UUID")
                .long("--uuid")
                .required(true)
                .empty_values(false)
                .takes_value(true)
                .conflicts_with("PATH"))
            .arg(Arg::with_name("DIRECTORY")
                .required(false)
                .empty_values(false)
                .takes_value(true)
                .conflicts_with("PATH"))
            .arg(Arg::with_name("PATH")
                .long("--path")
                .required(true)
                .empty_values(false)
                .takes_value(true)
                .required(true)));

    let matches = app.get_matches_from_safe(args)?;
    if let Some(list_matches) = matches.subcommand_matches("list") {
        let mut params = ListParams::default();
        params.filter = list_matches.value_of("TEXT").map(|text| text.to_string());
        if let Some(days) = list_matches.value_of("DAYS")
            .and_then(|days| days.parse::<i64>().ok()) {
            if days < 0 || days > 365 {
                return Err(Error::Custom("DAYS should be between 0 and 365".to_string()));
            }
            params.expires_in_days = Some(days);
        }
        params.directory = list_matches.value_of("DIRECTORY").map(|dir| dir.into());
        Ok(Command::List(params))
    } else if let Some(show_matches) = matches.subcommand_matches("show") {
        if let Some(uuid) = show_matches.value_of("UUID").map(|uuid| uuid.to_string()) {
            let directory = show_matches.value_of("DIRECTORY").map(|dir| dir.into());
            Ok(Command::ShowUuid(uuid, directory))
        } else {
            let path = show_matches.value_of("PATH").map(|path| path.into()).unwrap();
            Ok(Command::ShowPath(path))
        }
    } else if let Some(cleanup_matches) = matches.subcommand_matches("cleanup") {
        let directory = cleanup_matches.value_of("DIRECTORY").map(|dir| dir.into());
        Ok(Command::Cleanup(directory))
    } else {
        Err(Error::Custom("Command isn't implemented".to_string()))
    }
}

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

    #[test]
    fn list_command() {
        expect!(parse(&["mprovision", "list"]))
            .to(be_ok().value(Command::List(ListParams::default())));

        expect!(parse(&["mprovision", "list", "."])).to(be_ok().value(Command::List(ListParams {
            filter: None,
            expires_in_days: None,
            directory: Some(".".into()),
        })));

        expect!(parse(&["mprovision", "list", "--filter", "abc"]))
            .to(be_ok().value(Command::List(ListParams {
                filter: Some("abc".to_string()),
                expires_in_days: None,
                directory: None,
            })));

        expect!(parse(&["mprovision", "list", "--filter", ""])).to(be_err());

        expect!(parse(&["mprovision", "list", "--expires-in-days", "3"]))
            .to(be_ok().value(Command::List(ListParams {
                filter: None,
                expires_in_days: Some(3),
                directory: None,
            })));

        expect!(parse(&["mprovision", "list", "--expires-in-days", "-3"])).to(be_err());
        expect!(parse(&["mprovision", "list", "--expires-in-days", "366"])).to(be_err());

        expect!(parse(&["mprovision", "list", "--filter", "abc", "--expires-in-days", "3", "."]))
            .to(be_ok().value(Command::List(ListParams {
                filter: Some("abc".to_string()),
                expires_in_days: Some(3),
                directory: Some(".".into()),
            })));
    }

    #[test]
    fn show_uuid_command() {
        expect!(parse(&["mprovision", "show", "--uuid", "abcd"]))
            .to(be_ok().value(Command::ShowUuid("abcd".to_string(), None)));

        expect!(parse(&["mprovision", "show", "--uuid", "abcd", "."]))
            .to(be_ok().value(Command::ShowUuid("abcd".to_string(), Some(".".into()))));

        expect!(parse(&["mprovision", "show", "."])).to(be_err());
    }

    #[test]
    fn show_path_command() {
        expect!(parse(&["mprovision", "show", "--path", "file.mprovision"]))
            .to(be_ok().value(Command::ShowPath("file.mprovision".into())));

        expect!(parse(&["mprovision", "show", "--path", "file.mprovision", "."])).to(be_err());
    }

    #[test]
    fn cleanup_command() {
        expect!(parse(&["mprovision", "cleanup"])).to(be_ok().value(Command::Cleanup(None)));

        expect!(parse(&["mprovision", "cleanup", "."]))
            .to(be_ok().value(Command::Cleanup(Some(".".into()))));
    }
}
<commit_msg>Add RemoveUuid command<commit_after>use clap::{self, Arg, App, SubCommand, AppSettings};
use std::io::{self, Write};
use std::process;
use std::path::PathBuf;
use std::error;
use std::result;
use std::fmt;
use mprovision as mp;

#[derive(Debug, PartialEq)]
pub enum Command {
    List(ListParams),
    ShowUuid(String, Option<PathBuf>),
    ShowPath(PathBuf),
    RemoveUuid(String, Option<PathBuf>),
    Cleanup(Option<PathBuf>),
}

#[derive(Debug, Default, PartialEq)]
pub struct ListParams {
    pub filter: Option<String>,
    pub expires_in_days: Option<i64>,
    pub directory: Option<PathBuf>,
}

pub type Result = result::Result<Command, Error>;

#[derive(Debug)]
pub enum Error {
    Lib(mp::Error),
    Clap(clap::Error),
    Custom(String),
}

impl Error {
    pub fn exit(&self) -> ! {
        match *self {
            Error::Clap(ref e) => e.exit(),
            _ => {
                writeln!(&mut io::stderr(), "{}", self).unwrap();
                process::exit(1);
            }
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Lib(ref e) => e.description(),
            Error::Clap(ref e) => e.description(),
            Error::Custom(ref e) => e,
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::Lib(ref e) => Some(e),
            Error::Clap(ref e) => Some(e),
            Error::Custom(_) => None,
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Lib(ref e) => error::Error::description(e).fmt(f),
            Error::Clap(ref e) => error::Error::description(e).fmt(f),
            Error::Custom(ref e) => e.fmt(f),
        }
    }
}

impl From<mp::Error> for Error {
    fn from(e: mp::Error) -> Self {
        Error::Lib(e)
    }
}

impl From<clap::Error> for Error {
    fn from(e: clap::Error) -> Self {
        Error::Clap(e)
    }
}

pub fn parse<I, S>(args: I) -> Result
    where I: IntoIterator<Item = S>,
          S: Clone,
          ::std::ffi::OsString: From<S>
{
    let app = App::new("mprovision")
        .about("A tool that helps iOS developers to manage mobileprovision files.")
        .subcommand(SubCommand::with_name("list")
            .about("Lists provisioning profiles")
            .setting(AppSettings::DisableVersion)
            .arg(Arg::with_name("TEXT")
                .long("--filter")
                .required(false)
                .empty_values(false)
                .takes_value(true))
            .arg(Arg::with_name("DAYS")
                .long("--expires-in-days")
                .required(false)
                .empty_values(false)
                .takes_value(true))
            .arg(Arg::with_name("DIRECTORY")
                .required(false)
                .empty_values(false)
                .takes_value(true)))
        .subcommand(SubCommand::with_name("cleanup")
            .about("Removes expired provisioning profiles")
            .setting(AppSettings::DisableVersion)
            .arg(Arg::with_name("DIRECTORY")
                .required(false)
                .empty_values(false)
                .takes_value(true)))
        .subcommand(SubCommand::with_name("show")
            .about("Shows details of a provisioning profile")
            .setting(AppSettings::DisableVersion)
            .arg(Arg::with_name("UUID")
                .long("--uuid")
                .required(true)
                .empty_values(false)
                .takes_value(true)
                .conflicts_with("PATH"))
            .arg(Arg::with_name("DIRECTORY")
                .required(false)
                .empty_values(false)
                .takes_value(true)
                .conflicts_with("PATH"))
            .arg(Arg::with_name("PATH")
                .long("--path")
                .required(true)
                .empty_values(false)
                .takes_value(true)
                .required(true)))
        .subcommand(SubCommand::with_name("remove")
            .about("Removes a provisioning profile")
            .setting(AppSettings::DisableVersion)
            .arg(Arg::with_name("UUID")
                .long("--uuid")
                .required(true)
                .empty_values(false)
                .takes_value(true))
            .arg(Arg::with_name("DIRECTORY")
                .required(false)
                .empty_values(false)
                .takes_value(true)));

    let matches = app.get_matches_from_safe(args)?;
    if let Some(list_matches) = matches.subcommand_matches("list") {
        let mut params = ListParams::default();
        params.filter = list_matches.value_of("TEXT").map(|text| text.to_string());
        if let Some(days) = list_matches.value_of("DAYS")
            .and_then(|days| days.parse::<i64>().ok()) {
            if days < 0 || days > 365 {
                return Err(Error::Custom("DAYS should be between 0 and 365".to_string()));
            }
            params.expires_in_days = Some(days);
        }
        params.directory = list_matches.value_of("DIRECTORY").map(|dir| dir.into());
        Ok(Command::List(params))
    } else if let Some(show_matches) = matches.subcommand_matches("show") {
        if let Some(uuid) = show_matches.value_of("UUID").map(|uuid| uuid.to_string()) {
            let directory = show_matches.value_of("DIRECTORY").map(|dir| dir.into());
            Ok(Command::ShowUuid(uuid, directory))
        } else {
            let path = show_matches.value_of("PATH").map(|path| path.into()).unwrap();
            Ok(Command::ShowPath(path))
        }
    } else if let Some(remove_matches) = matches.subcommand_matches("remove") {
        let uuid = remove_matches.value_of("UUID").map(|uuid| uuid.to_string()).unwrap();
        let directory = remove_matches.value_of("DIRECTORY").map(|dir| dir.into());
        Ok(Command::RemoveUuid(uuid, directory))
    } else if let Some(cleanup_matches) = matches.subcommand_matches("cleanup") {
        let directory = cleanup_matches.value_of("DIRECTORY").map(|dir| dir.into());
        Ok(Command::Cleanup(directory))
    } else {
        Err(Error::Custom("Command isn't implemented".to_string()))
    }
}

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

    #[test]
    fn list_command() {
        expect!(parse(&["mprovision", "list"]))
            .to(be_ok().value(Command::List(ListParams::default())));

        expect!(parse(&["mprovision", "list", "."])).to(be_ok().value(Command::List(ListParams {
            filter: None,
            expires_in_days: None,
            directory: Some(".".into()),
        })));

        expect!(parse(&["mprovision", "list", "--filter", "abc"]))
            .to(be_ok().value(Command::List(ListParams {
                filter: Some("abc".to_string()),
                expires_in_days: None,
                directory: None,
            })));

        expect!(parse(&["mprovision", "list", "--filter", ""])).to(be_err());

        expect!(parse(&["mprovision", "list", "--expires-in-days", "3"]))
            .to(be_ok().value(Command::List(ListParams {
                filter: None,
                expires_in_days: Some(3),
                directory: None,
            })));

        expect!(parse(&["mprovision", "list", "--expires-in-days", "-3"])).to(be_err());
        expect!(parse(&["mprovision", "list", "--expires-in-days", "366"])).to(be_err());

        expect!(parse(&["mprovision", "list", "--filter", "abc", "--expires-in-days", "3", "."]))
            .to(be_ok().value(Command::List(ListParams {
                filter: Some("abc".to_string()),
                expires_in_days: Some(3),
                directory: Some(".".into()),
            })));
    }

    #[test]
    fn show_uuid_command() {
        expect!(parse(&["mprovision", "show", "--uuid", "abcd"]))
            .to(be_ok().value(Command::ShowUuid("abcd".to_string(), None)));

        expect!(parse(&["mprovision", "show", "--uuid", "abcd", "."]))
            .to(be_ok().value(Command::ShowUuid("abcd".to_string(), Some(".".into()))));

        expect!(parse(&["mprovision", "show", "."])).to(be_err());
    }

    #[test]
    fn show_path_command() {
        expect!(parse(&["mprovision", "show", "--path", "file.mprovision"]))
            .to(be_ok().value(Command::ShowPath("file.mprovision".into())));

        expect!(parse(&["mprovision", "show", "--path", "file.mprovision", "."])).to(be_err());
    }

    #[test]
    fn remove_uuid_command() {
        expect!(parse(&["mprovision", "remove", "--uuid", "abcd"]))
            .to(be_ok().value(Command::RemoveUuid("abcd".to_string(), None)));

        expect!(parse(&["mprovision", "remove", "--uuid", "abcd", "."]))
            .to(be_ok().value(Command::RemoveUuid("abcd".to_string(), Some(".".into()))));

        expect!(parse(&["mprovision", "remove", "."])).to(be_err());
    }

    #[test]
    fn cleanup_command() {
        expect!(parse(&["mprovision", "cleanup"])).to(be_ok().value(Command::Cleanup(None)));

        expect!(parse(&["mprovision", "cleanup", "."]))
            .to(be_ok().value(Command::Cleanup(Some(".".into()))));
    }
}
<|endoftext|>


<commit_before>//! Minimal REPL

#![feature(plugin)]
#![plugin(docopt_macros)]

extern crate docopt;
extern crate rustc_serialize;
extern crate term;
extern crate lea_compiler as compiler;
extern crate lea_parser as parser;
extern crate lea_vm as vm;
extern crate lea;

use parser::span::DummyTerm;
use compiler::{CompileConfig, FnData};
use vm::mem::DefaultGc;
use vm::error::VmResult;
use vm::function::FunctionProto;
use vm::value::Value;
use vm::vm::VM;

use std::io::{self, stdin, stderr, Write, BufRead};

docopt!(Args derive Debug, "
The interactive Lea REPL

Usage:
    lea [ --help | --version ]

Options:
    -h, --help          Show this help.
    -v, --version       Show the version of the Lea package.
");

/// Opens a terminal that writes to stderr. If stderr couldn't be opened as a terminal, creates a
/// `DummyTerm` that writes to stderr instead.
fn stderr_term() -> Box<term::StderrTerminal> {
    term::stderr().unwrap_or_else(|| Box::new(DummyTerm(io::stderr())))
}

/// Compiles a piece of code (expression or block of statements). Prints all errors / warnings to
/// stderr, using color if stderr is a terminal.
fn compile(code: &str, filename: &str) -> io::Result<Option<FnData>> {
    let mut fmt_target = stderr_term();
    let fmt_target = &mut *fmt_target;

    // try to compile an expression first
    let mut result = compiler::compile_expr(code, filename, &CompileConfig::default());
    if result.is_err() {
        result = compiler::compile_str(code, filename, &CompileConfig::default());
    }

    match result {
        Err(e) => {
            try!(e.format(code, filename, fmt_target));
            Ok(None)
        },
        Ok(output) => {
            let warns = output.warns;
            if warns.len() > 0 {
                for w in warns {
                    try!(w.format(code, filename, fmt_target));
                }
            }

            Ok(Some(output.mainproto))
        }
    }
}

/// Creates a VM and GC and executes the given `FnData` object. Prints the returned value or the
/// error thrown.
fn run_fndata(main: FnData) {
    let mut gc = DefaultGc::default();
    let proto = FunctionProto::from_fndata(main, &mut gc);
    let mut vm = VM::with_env(gc, proto, Value::TNil);

    let ret: VmResult = vm.start();
    match ret {
        Ok(vals) => {
            if !vals.is_empty() {
                for (i, val) in vals.into_iter().enumerate() {
                    if i != 0 {
                        print!("\t");
                    }

                    unsafe { val.fmt(io::stdout(), vm.gc()) }.unwrap();
                }
                println!("");
            }
        }
        Err(e) => {
            println!("{:?}", e);
        }
    }
}

fn print_prompt() -> io::Result<()> {
    let mut stdout = io::stdout();
    try!(write!(stdout, "> "));
    try!(stdout.flush());

    Ok(())
}

fn repl() -> io::Result<()> {
    let stdin = io::stdin();
    let stdin = io::BufReader::new(stdin);

    try!(print_prompt());
    for input in stdin.lines() {
        let input = try!(input);
        if let Some(fndata) = try!(compile(&input, "<repl>")) {
            run_fndata(fndata);
        }

        try!(print_prompt());
    }

    Ok(())
}

#[allow(dead_code)]     // TODO write tests
fn main() {
    match Args::docopt().decode::<Args>() {
        Err(e) => {
            println!("{}", e);
        }
        Ok(args) => {
            match args {
                Args { flag_version: true, .. } => {
                    println!("Lea {}", lea::version_str());
                }
                Args { .. } => {
                    match repl() {
                        Err(e) => println!("{}", e),
                        Ok(_) => unreachable!(),
                    }
                }
            }
        }
    }
}
<commit_msg>repl: ignore "Ok" result from repl fn<commit_after>//! Minimal REPL

#![feature(plugin)]
#![plugin(docopt_macros)]

extern crate docopt;
extern crate rustc_serialize;
extern crate term;
extern crate lea_compiler as compiler;
extern crate lea_parser as parser;
extern crate lea_vm as vm;
extern crate lea;

use parser::span::DummyTerm;
use compiler::{CompileConfig, FnData};
use vm::mem::DefaultGc;
use vm::error::VmResult;
use vm::function::FunctionProto;
use vm::value::Value;
use vm::vm::VM;

use std::io::{self, stdin, stderr, Write, BufRead};

docopt!(Args derive Debug, "
The interactive Lea REPL

Usage:
    lea [ --help | --version ]

Options:
    -h, --help          Show this help.
    -v, --version       Show the version of the Lea package.
");

/// Opens a terminal that writes to stderr. If stderr couldn't be opened as a terminal, creates a
/// `DummyTerm` that writes to stderr instead.
fn stderr_term() -> Box<term::StderrTerminal> {
    term::stderr().unwrap_or_else(|| Box::new(DummyTerm(io::stderr())))
}

/// Compiles a piece of code (expression or block of statements). Prints all errors / warnings to
/// stderr, using color if stderr is a terminal.
fn compile(code: &str, filename: &str) -> io::Result<Option<FnData>> {
    let mut fmt_target = stderr_term();
    let fmt_target = &mut *fmt_target;

    // try to compile an expression first
    let mut result = compiler::compile_expr(code, filename, &CompileConfig::default());
    if result.is_err() {
        result = compiler::compile_str(code, filename, &CompileConfig::default());
    }

    match result {
        Err(e) => {
            try!(e.format(code, filename, fmt_target));
            Ok(None)
        },
        Ok(output) => {
            let warns = output.warns;
            if warns.len() > 0 {
                for w in warns {
                    try!(w.format(code, filename, fmt_target));
                }
            }

            Ok(Some(output.mainproto))
        }
    }
}

/// Creates a VM and GC and executes the given `FnData` object. Prints the returned value or the
/// error thrown.
fn run_fndata(main: FnData) {
    let mut gc = DefaultGc::default();
    let proto = FunctionProto::from_fndata(main, &mut gc);
    let mut vm = VM::with_env(gc, proto, Value::TNil);

    let ret: VmResult = vm.start();
    match ret {
        Ok(vals) => {
            if !vals.is_empty() {
                for (i, val) in vals.into_iter().enumerate() {
                    if i != 0 {
                        print!("\t");
                    }

                    unsafe { val.fmt(io::stdout(), vm.gc()) }.unwrap();
                }
                println!("");
            }
        }
        Err(e) => {
            println!("{:?}", e);
        }
    }
}

fn print_prompt() -> io::Result<()> {
    let mut stdout = io::stdout();
    try!(write!(stdout, "> "));
    try!(stdout.flush());

    Ok(())
}

fn repl() -> io::Result<()> {
    let stdin = io::stdin();
    let stdin = io::BufReader::new(stdin);

    try!(print_prompt());
    for input in stdin.lines() {
        let input = try!(input);
        if let Some(fndata) = try!(compile(&input, "<repl>")) {
            run_fndata(fndata);
        }

        try!(print_prompt());
    }

    Ok(())
}

#[allow(dead_code)]     // TODO write tests
fn main() {
    match Args::docopt().decode::<Args>() {
        Err(e) => {
            println!("{}", e);
        }
        Ok(args) => {
            match args {
                Args { flag_version: true, .. } => {
                    println!("Lea {}", lea::version_str());
                }
                Args { .. } => {
                    match repl() {
                        Err(e) => println!("{}", e),
                        Ok(_) => {}
                    }
                }
            }
        }
    }
}
<|endoftext|>