<commit_before>//! This reader composes frames of bytes started with a 4 byte frame header indicating the size of
//! the buffer. An exact size buffer will be allocated once the 4 byte frame header is received.

use std::io::{self, Read, Error, ErrorKind};
use std::collections::VecDeque;
use std::mem;

#[derive(Debug)]
pub struct FrameReader {
    frames: Frames
}

impl FrameReader {
    pub fn new(max_frame_size: u32) -> FrameReader {
        FrameReader {
            frames: Frames::new(max_frame_size)
        }
    }

    pub fn read<T: Read>(&mut self, reader: &mut T) -> io::Result<usize> {
        self.frames.read(reader)
    }

    pub fn iter_mut(&mut self) -> Iter {
        Iter {
            frames: &mut self.frames
        }
    }
}

pub struct Iter<'a> {
    frames: &'a mut Frames
}

impl<'a> Iterator for Iter<'a> {
    type Item = Vec<u8>;

    fn next(&mut self) -> Option<Vec<u8>> {
        self.frames.completed_frames.pop_front()
    }
}

#[derive(Debug)]
struct Frames {
    max_frame_size: u32,
    bytes_read: usize,
    header: [u8; 4],
    reading_header: bool,
    current: Vec<u8>,
    completed_frames: VecDeque<Vec<u8>>
}

impl Frames {
    pub fn new(max_frame_size: u32) -> Frames {
        Frames {
            max_frame_size: max_frame_size,
            bytes_read: 0,
            header: [0; 4],
            reading_header: true,
            current: Vec::with_capacity(0),
            completed_frames: VecDeque::new()
        }
    }

    /// Will read as much data as possible and build up frames to be retrieved from the iterator.
    ///
    /// Will stop reading when 0 bytes are retrieved from the latest call to `do_read` or the error
    /// kind is io::ErrorKind::WouldBlock.
    ///
    /// Returns an error or the total amount of bytes read.
    fn read<T: Read>(&mut self, reader: &mut T) -> io::Result<usize> {
        let mut total_bytes_read = 0;
        loop {
            match self.do_read(reader) {
                Ok(0) => {
                    if total_bytes_read == 0 {
                        return Err(Error::new(ErrorKind::UnexpectedEof, "Read 0 bytes"));
                    }
                    return Ok(total_bytes_read);
                },
                Ok(bytes_read) => {
                    total_bytes_read += bytes_read;
                },
                Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => return Ok(total_bytes_read),
                Err(e) => return Err(e)
            }
        }
    }

    fn do_read<T: Read>(&mut self, reader: &mut T) -> io::Result<usize> {
        if self.reading_header {
            self.read_header(reader)
        } else {
            self.read_value(reader)
        }
    }

    // TODO: Return an error if size is greater than max_frame_size
    fn read_header<T: Read>(&mut self, reader: &mut T) -> io::Result<usize> {
        let bytes_read = try!(reader.read(&mut self.header[self.bytes_read..]));
        self.bytes_read += bytes_read;
        if self.bytes_read == 4 {
           let len = unsafe { u32::from_be(mem::transmute(self.header)) };
           self.bytes_read = 0;
           self.reading_header = false;
           self.current = Vec::with_capacity(len as usize);
           unsafe { self.current.set_len(len as usize); }
        }
        Ok(bytes_read)
    }

    fn read_value<T: Read>(&mut self, reader: &mut T) -> io::Result<usize> {
        let bytes_read = try!(reader.read(&mut self.current[self.bytes_read..]));
        self.bytes_read += bytes_read;
        if self.bytes_read == self.current.len() {
           self.completed_frames.push_back(mem::replace(&mut self.current, Vec::new()));
           self.bytes_read = 0;
           self.reading_header = true;
        }
        Ok(bytes_read)
    }
}

#[cfg(test)]
mod tests {
    use std::{mem, thread};
    use std::io::Cursor;
    use std::io::Write;
    use std::net::{TcpListener, TcpStream};
    use super::FrameReader;

    #[test]
    fn partial_and_complete_reads() {
        let buf1 = String::from("Hello World").into_bytes();
        let buf2 = String::from("Hi.").into_bytes();
        let header1: [u8; 4] = unsafe { mem::transmute((buf1.len() as u32).to_be()) };
        let header2: [u8; 4] = unsafe { mem::transmute((buf2.len() as u32).to_be()) };

        let mut reader = FrameReader::new(1024);

        // Write a partial header
        let mut header = Cursor::new(&header1[0..2]);
        let bytes_read = reader.read(&mut header).unwrap();
        assert_eq!(2, bytes_read);
        assert_eq!(None, reader.iter_mut().next());

        // Complete writing just the header
        let mut header = Cursor::new(&header1[2..]);
        let bytes_read = reader.read(&mut header).unwrap();
        assert_eq!(2, bytes_read);
        assert_eq!(None, reader.iter_mut().next());

        // Write a partial value
        let mut data = Cursor::new(&buf1[0..5]);
        let bytes_read = reader.read(&mut data).unwrap();
        assert_eq!(5, bytes_read);
        assert_eq!(None, reader.iter_mut().next());

        // Complete writing the first value
        let mut data = Cursor::new(&buf1[5..]);
        let bytes_read = reader.read(&mut data).unwrap();
        assert_eq!(6, bytes_read);
        let val = reader.iter_mut().next().unwrap();
        assert_eq!(buf1, val);

        // Write an entire header and value
        let mut data = Cursor::new(Vec::with_capacity(7));
        assert_eq!(4, data.write(&header2).unwrap());
        assert_eq!(3, data.write(&buf2).unwrap());
        data.set_position(0);
        let bytes_read = reader.read(&mut data).unwrap();
        assert_eq!(7, bytes_read);
        assert_eq!(buf2, reader.iter_mut().next().unwrap());
    }

    const IP: &'static str = "127.0.0.1:5003";
    /// Test that we never get an io error, but instead get Ok(0) when the call to read would block
    #[test]
    fn would_block() {
        let listener = TcpListener::bind(IP).unwrap();
        let h = thread::spawn(move || {
            for stream in listener.incoming() {
                if let Ok(mut conn) = stream {
                    conn.set_nonblocking(true).unwrap();
                    let mut reader = FrameReader::new(1024);
                    let result = reader.read(&mut conn);
                    assert_matches!(result, Ok(0));
                    return;
                }
            }
        });

        let _ = TcpStream::connect(IP).unwrap();
        h.join().unwrap();
    }
}
<commit_msg>Fix periodically failing test<commit_after>//! This reader composes frames of bytes started with a 4 byte frame header indicating the size of
//! the buffer. An exact size buffer will be allocated once the 4 byte frame header is received.

use std::io::{self, Read, Error, ErrorKind};
use std::collections::VecDeque;
use std::mem;

#[derive(Debug)]
pub struct FrameReader {
    frames: Frames
}

impl FrameReader {
    pub fn new(max_frame_size: u32) -> FrameReader {
        FrameReader {
            frames: Frames::new(max_frame_size)
        }
    }

    pub fn read<T: Read>(&mut self, reader: &mut T) -> io::Result<usize> {
        self.frames.read(reader)
    }

    pub fn iter_mut(&mut self) -> Iter {
        Iter {
            frames: &mut self.frames
        }
    }
}

pub struct Iter<'a> {
    frames: &'a mut Frames
}

impl<'a> Iterator for Iter<'a> {
    type Item = Vec<u8>;

    fn next(&mut self) -> Option<Vec<u8>> {
        self.frames.completed_frames.pop_front()
    }
}

#[derive(Debug)]
struct Frames {
    max_frame_size: u32,
    bytes_read: usize,
    header: [u8; 4],
    reading_header: bool,
    current: Vec<u8>,
    completed_frames: VecDeque<Vec<u8>>
}

impl Frames {
    pub fn new(max_frame_size: u32) -> Frames {
        Frames {
            max_frame_size: max_frame_size,
            bytes_read: 0,
            header: [0; 4],
            reading_header: true,
            current: Vec::with_capacity(0),
            completed_frames: VecDeque::new()
        }
    }

    /// Will read as much data as possible and build up frames to be retrieved from the iterator.
    ///
    /// Will stop reading when 0 bytes are retrieved from the latest call to `do_read` or the error
    /// kind is io::ErrorKind::WouldBlock.
    ///
    /// Returns an error or the total amount of bytes read.
    fn read<T: Read>(&mut self, reader: &mut T) -> io::Result<usize> {
        let mut total_bytes_read = 0;
        loop {
            match self.do_read(reader) {
                Ok(0) => {
                    if total_bytes_read == 0 {
                        return Err(Error::new(ErrorKind::UnexpectedEof, "Read 0 bytes"));
                    }
                    return Ok(total_bytes_read);
                },
                Ok(bytes_read) => {
                    total_bytes_read += bytes_read;
                },
                Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => return Ok(total_bytes_read),
                Err(e) => return Err(e)
            }
        }
    }

    fn do_read<T: Read>(&mut self, reader: &mut T) -> io::Result<usize> {
        if self.reading_header {
            self.read_header(reader)
        } else {
            self.read_value(reader)
        }
    }

    // TODO: Return an error if size is greater than max_frame_size
    fn read_header<T: Read>(&mut self, reader: &mut T) -> io::Result<usize> {
        let bytes_read = try!(reader.read(&mut self.header[self.bytes_read..]));
        self.bytes_read += bytes_read;
        if self.bytes_read == 4 {
           let len = unsafe { u32::from_be(mem::transmute(self.header)) };
           self.bytes_read = 0;
           self.reading_header = false;
           self.current = Vec::with_capacity(len as usize);
           unsafe { self.current.set_len(len as usize); }
        }
        Ok(bytes_read)
    }

    fn read_value<T: Read>(&mut self, reader: &mut T) -> io::Result<usize> {
        let bytes_read = try!(reader.read(&mut self.current[self.bytes_read..]));
        self.bytes_read += bytes_read;
        if self.bytes_read == self.current.len() {
           self.completed_frames.push_back(mem::replace(&mut self.current, Vec::new()));
           self.bytes_read = 0;
           self.reading_header = true;
        }
        Ok(bytes_read)
    }
}

#[cfg(test)]
mod tests {
    use std::{mem, thread};
    use std::io::Cursor;
    use std::io::Write;
    use std::net::{TcpListener, TcpStream};
    use super::FrameReader;

    #[test]
    fn partial_and_complete_reads() {
        let buf1 = String::from("Hello World").into_bytes();
        let buf2 = String::from("Hi.").into_bytes();
        let header1: [u8; 4] = unsafe { mem::transmute((buf1.len() as u32).to_be()) };
        let header2: [u8; 4] = unsafe { mem::transmute((buf2.len() as u32).to_be()) };

        let mut reader = FrameReader::new(1024);

        // Write a partial header
        let mut header = Cursor::new(&header1[0..2]);
        let bytes_read = reader.read(&mut header).unwrap();
        assert_eq!(2, bytes_read);
        assert_eq!(None, reader.iter_mut().next());

        // Complete writing just the header
        let mut header = Cursor::new(&header1[2..]);
        let bytes_read = reader.read(&mut header).unwrap();
        assert_eq!(2, bytes_read);
        assert_eq!(None, reader.iter_mut().next());

        // Write a partial value
        let mut data = Cursor::new(&buf1[0..5]);
        let bytes_read = reader.read(&mut data).unwrap();
        assert_eq!(5, bytes_read);
        assert_eq!(None, reader.iter_mut().next());

        // Complete writing the first value
        let mut data = Cursor::new(&buf1[5..]);
        let bytes_read = reader.read(&mut data).unwrap();
        assert_eq!(6, bytes_read);
        let val = reader.iter_mut().next().unwrap();
        assert_eq!(buf1, val);

        // Write an entire header and value
        let mut data = Cursor::new(Vec::with_capacity(7));
        assert_eq!(4, data.write(&header2).unwrap());
        assert_eq!(3, data.write(&buf2).unwrap());
        data.set_position(0);
        let bytes_read = reader.read(&mut data).unwrap();
        assert_eq!(7, bytes_read);
        assert_eq!(buf2, reader.iter_mut().next().unwrap());
    }

    const IP: &'static str = "127.0.0.1:5003";
    /// Test that we never get an io error, but instead get Ok(0) when the call to read would block
    #[test]
    fn would_block() {
        let listener = TcpListener::bind(IP).unwrap();
        let h = thread::spawn(move || {
            for stream in listener.incoming() {
                if let Ok(mut conn) = stream {
                    conn.set_nonblocking(true).unwrap();
                    let mut reader = FrameReader::new(1024);
                    let result = reader.read(&mut conn);
                    assert_matches!(result, Ok(0));
                    return;
                }
            }
        });
        // Assign to a variable so the sock isn't dropped early
        // Name it with a preceding underscore so we don't get an unused variable warning
        let _sock = TcpStream::connect(IP).unwrap();
        h.join().unwrap();

    }
}
<|endoftext|>


<commit_before>use engine::Game;
use gui::Console;
use gui::screens;
use util::units::Size;

#[derive(PartialEq)]
pub enum State {
    Running,
    Exited
}

pub struct GUI {
    pub console: Console,
    pub current_screen: Box<Screen + 'static>,
    game: Game,
    state: State,
}

pub trait Screen {
    fn input(&self, &mut Game, &mut Console) -> Option<ScreenChange>;
    fn update(&self, &mut Game, &mut Console) -> Option<ScreenChange>;
    fn render(&self, &mut Game, &mut Console);
}

pub enum ScreenChange {
    AddScreen(Box<Screen + 'static>),
    RemoveScreen,
    ExitGame,
}

impl GUI {
    pub fn new(game: Game, console: Console) -> GUI {
        GUI {
            game: game,
            console: console,
            current_screen: screens::GameScreen::new(),
            state: State::Running,
        }
    }

    pub fn size(&self) -> Size {
        self.console.size()
    }

    pub fn run(&mut self) {
        while !self.console.window_closed() && !self.exited() {
            self.render();
            self.update();
            self.handle_input();
        }
    }

    fn handle_input(&mut self) {
        if let Some(ScreenChange::ExitGame) = self.current_screen.input(&mut self.game, &mut self.console) {
            self.state = State::Exited;
        }
    }

    fn update(&mut self) {
        self.current_screen.update(&mut self.game, &mut self.console);
    }

    fn render(&mut self) {
        self.console.clear();
        self.current_screen.render(&mut self.game, &mut self.console);
        self.console.flush();
    }

    pub fn exited(&self) -> bool {
        self.state == State::Exited
    }
}
<commit_msg>Make screens a vec and deal with replacement/exiting<commit_after>use engine::Game;
use gui::Console;
use gui::screens;
use util::units::Size;

#[derive(PartialEq)]
pub enum State {
    Running,
    Exited
}

pub struct GUI {
    pub console: Console,
    pub screens: Vec<Box<Screen + 'static>>,
    game: Game,
    state: State,
}

pub trait Screen {
    fn input(&self, &mut Game, &mut Console) -> Option<ScreenChange>;
    fn update(&self, &mut Game, &mut Console) -> Option<ScreenChange>;
    fn render(&self, &mut Game, &mut Console);
}

pub enum ScreenChange {
    AddScreen(Box<Screen + 'static>),
    RemoveScreen,
    ExitGame,
}

impl GUI {
    pub fn new(game: Game, console: Console) -> GUI {
        GUI {
            game: game,
            console: console,
            screens: vec!(screens::GameScreen::new()),
            state: State::Running,
        }
    }

    pub fn size(&self) -> Size {
        self.console.size()
    }

    pub fn run(&mut self) {
        while !self.console.window_closed() && !self.exited() {
            self.render();
            self.update();
            self.handle_input();
        }
    }

    fn handle_input(&mut self) {
        match self.screens[0].input(&mut self.game, &mut self.console) {
            Some(ScreenChange::AddScreen(screen)) => { self.screens.insert(0, screen) },
            Some(ScreenChange::RemoveScreen) => { self.screens.remove(0); }
            Some(ScreenChange::ExitGame) => { self.state = State::Exited },
            None => {}
        }
    }

    fn update(&mut self) {
        self.screens[0].update(&mut self.game, &mut self.console);
    }

    fn render(&mut self) {
        self.console.clear();
        self.screens[0].render(&mut self.game, &mut self.console);
        self.console.flush();
    }

    pub fn exited(&self) -> bool {
        self.state == State::Exited
    }
}
<|endoftext|>


<commit_before>use super::{AssertionFailure, Spec};

use std::collections::HashMap;
use std::fmt::Debug;
use std::hash::Hash;

pub trait HashMapAssertions<'s, K: Hash + Eq, V: PartialEq> {
    fn has_length(&mut self, expected: usize);
    fn is_empty(&mut self);
    fn contains_key(&mut self, expected_key: &K) -> Spec<'s, V>;
    fn does_not_contain_key(&mut self, expected_key: &K);
    fn contains_entry(&mut self, expected_key: &K, expected_value: &V);
    fn does_not_contain_entry(&mut self, expected_key: &K, expected_value: &V);
}

impl<'s, K, V> HashMapAssertions<'s, K, V> for Spec<'s, HashMap<K, V>>
    where K: Hash + Eq + Debug,
          V: PartialEq + Debug
{
    /// Asserts that the length of the subject hashmap is equal to the provided length. The subject
    /// type must be of `HashMap`.
    ///
    /// ```rust,ignore
    /// let mut test_map = HashMap::new();
    /// test_map.insert(1, 1);
    /// test_map.insert(2, 2);
    ///
    /// assert_that(&test_map).has_length(2);
    /// ```
    fn has_length(&mut self, expected: usize) {
        let subject = self.subject;

        if subject.len() != expected {
            AssertionFailure::from_spec(self)
                .with_expected(format!("hashmap to have length <{}>", expected))
                .with_actual(format!("<{}>", subject.len()))
                .fail();
        }
    }

    /// Asserts that the subject hashmap is empty. The subject type must be of `HashMap`.
    ///
    /// ```rust,ignore
    /// let test_map: HashMap<u8, u8> = HashMap::new();
    /// assert_that(&test_map).is_empty();
    /// ```
    fn is_empty(&mut self) {
        let subject = self.subject;

        if !subject.is_empty() {
            AssertionFailure::from_spec(self)
                .with_expected(format!("an empty hashmap"))
                .with_actual(format!("a hashmap with length <{:?}>", subject.len()))
                .fail();
        }
    }

    /// Asserts that the subject hashmap contains the expected key. The subject type must be
    /// of `HashMap`.
    ///
    /// This will return a new `Spec` containing the associated value if the key is present.
    ///
    /// ```rust,ignore
    /// let mut test_map = HashMap::new();
    /// test_map.insert("hello", "hi");
    ///
    /// assert_that(&test_map).contains_key(&"hello");
    /// ```
    fn contains_key(&mut self, expected_key: &K) -> Spec<'s, V> {
        let subject = self.subject;

        if let Some(value) = subject.get(expected_key) {
            return Spec {
                subject: value,
                subject_name: self.subject_name,
                location: self.location.clone(),
                description: self.description,
            };
        }

        let subject_keys: Vec<&K> = subject.keys().collect();

        AssertionFailure::from_spec(self)
            .with_expected(format!("hashmap to contain key <{:?}>", expected_key))
            .with_actual(format!("<{:?}>", subject_keys))
            .fail();

        unreachable!();
    }

    /// Asserts that the subject hashmap does not contain the provided key. The subject type must be
    /// of `HashMap`.
    ///
    /// ```rust,ignore
    /// let mut test_map = HashMap::new();
    /// test_map.insert("hello", "hi");
    ///
    /// assert_that(&test_map).does_not_contain_key(&"hey");
    /// ```
    fn does_not_contain_key(&mut self, expected_key: &K) {
        let subject = self.subject;

        if subject.get(expected_key).is_some() {
            AssertionFailure::from_spec(self)
                .with_expected(format!("hashmap to not contain key <{:?}>", expected_key))
                .with_actual(format!("present in hashmap"))
                .fail();
        }
    }

    /// Asserts that the subject hashmap contains the expected key with the expected value.
    /// The subject type must be of `HashMap`.
    ///
    /// ```rust,ignore
    /// let mut test_map = HashMap::new();
    /// test_map.insert("hello", "hi");
    ///
    /// assert_that(&test_map).contains_entry(&"hello", &"hi");
    /// ```
    fn contains_entry(&mut self, expected_key: &K, expected_value: &V) {
        let expected_message = format!("hashmap containing key <{:?}> with value <{:?}>",
                                       expected_key,
                                       expected_value);
        let subject = self.subject;

        if let Some(value) = subject.get(expected_key) {
            if value.eq(expected_value) {
                return;
            }

            AssertionFailure::from_spec(self)
                .with_expected(expected_message)
                .with_actual(format!("key <{:?}> with value <{:?}> instead", expected_key, value))
                .fail();

            unreachable!();
        }

        let subject_keys: Vec<&K> = subject.keys().collect();

        AssertionFailure::from_spec(self)
            .with_expected(expected_message)
            .with_actual(format!("no matching key, keys are <{:?}>", subject_keys))
            .fail();

    }

    /// Asserts that the subject hashmap does not contains the provided key and value.
    /// The subject type must be of `HashMap`.
    ///
    /// ```rust,ignore
    /// let mut test_map = HashMap::new();
    /// test_map.insert("hello", "hi");
    ///
    /// assert_that(&test_map).does_not_contain_entry(&"hello", &"hey");
    /// ```
    fn does_not_contain_entry(&mut self, expected_key: &K, expected_value: &V) {
        let subject = self.subject;

        if let Some(value) = subject.get(expected_key) {
            if !value.eq(expected_value) {
                return;
            }

            AssertionFailure::from_spec(self)
                .with_expected(format!("hashmap to not contain key <{:?}> with value <{:?}>",
                                       expected_key,
                                       expected_value))
                .with_actual(format!("present in hashmap"))
                .fail();
        }
    }
}

#[cfg(test)]
mod tests {

    use super::super::prelude::*;

    use std::collections::HashMap;

    #[test]
    fn should_not_panic_if_hashmap_length_matches_expected() {
        let mut test_map = HashMap::new();
        test_map.insert(1, 1);
        test_map.insert(2, 2);

        assert_that(&test_map).has_length(2);
    }

    #[test]
    #[should_panic(expected = "\n\texpected: hashmap to have length <1>\n\t but was: <2>")]
    fn should_panic_if_hashmap_length_does_not_match_expected() {
        let mut test_map = HashMap::new();
        test_map.insert(1, 1);
        test_map.insert(2, 2);

        assert_that(&test_map).has_length(1);
    }

    #[test]
    fn should_not_panic_if_hashmap_was_expected_to_be_empty_and_is() {
        let test_map: HashMap<u8, u8> = HashMap::new();
        assert_that(&test_map).is_empty();
    }

    #[test]
    #[should_panic(expected = "\n\texpected: an empty hashmap\
                   \n\t but was: a hashmap with length <1>")]
    fn should_panic_if_hashmap_was_expected_to_be_empty_and_is_not() {
        let mut test_map = HashMap::new();
        test_map.insert(1, 1);

        assert_that(&test_map).is_empty();
    }

    #[test]
    fn should_not_panic_if_hashmap_contains_key() {
        let mut test_map = HashMap::new();
        test_map.insert("hello", "hi");

        assert_that(&test_map).contains_key(&"hello");
    }

    #[test]
    // Unfortunately the order of the keys can change. Doesn't seem to make sense to sort them
    // just for the sake of checking the panic message.
    #[should_panic]
    fn should_not_panic_if_hashmap_does_not_contain_key() {
        let mut test_map = HashMap::new();
        test_map.insert("hi", "hi");
        test_map.insert("hey", "hey");

        assert_that(&test_map).contains_key(&"hello");
    }

    #[test]
    fn should_be_able_to_chain_value_from_contains_key() {
        let mut test_map = HashMap::new();
        test_map.insert("hello", "hi");

        assert_that(&test_map).contains_key(&"hello").is_equal_to(&"hi");
    }

    #[test]
    fn should_not_panic_if_hashmap_does_not_contain_key_when_expected() {
        let mut test_map = HashMap::new();
        test_map.insert("hello", "hi");

        assert_that(&test_map).does_not_contain_key(&"hey");
    }

    #[test]
    #[should_panic(expected = "\n\texpected: hashmap to not contain key <\"hello\">\
                   \n\t but was: present in hashmap")]
    fn should_panic_if_hashmap_does_contain_key_when_not_expected() {
        let mut test_map = HashMap::new();
        test_map.insert("hello", "hi");

        assert_that(&test_map).does_not_contain_key(&"hello");
    }

    #[test]
    fn should_not_panic_if_hashmap_contains_entry() {
        let mut test_map = HashMap::new();
        test_map.insert("hello", "hi");

        assert_that(&test_map).contains_entry(&"hello", &"hi");
    }

    #[test]
    #[should_panic(expected = "\n\texpected: hashmap containing key <\"hey\"> with value <\"hi\">\
                   \n\t but was: no matching key, keys are <[\"hello\"]>")]
    fn should_panic_if_hashmap_contains_entry_without_key() {
        let mut test_map = HashMap::new();
        test_map.insert("hello", "hi");

        assert_that(&test_map).contains_entry(&"hey", &"hi");
    }

    #[test]
    #[should_panic(expected = "\n\texpected: hashmap containing key <\"hi\"> with value <\"hey\">\
                   \n\t but was: key <\"hi\"> with value <\"hello\"> instead")]
    fn should_panic_if_hashmap_contains_entry_with_different_value() {
        let mut test_map = HashMap::new();
        test_map.insert("hi", "hello");

        assert_that(&test_map).contains_entry(&"hi", &"hey");
    }

    #[test]
    fn should_not_panic_if_hashmap_does_not_contain_entry_if_expected() {
        let mut test_map = HashMap::new();
        test_map.insert("hello", "hi");

        assert_that(&test_map).does_not_contain_entry(&"hey", &"hi");
    }

    #[test]
    fn should_not_panic_if_hashmap_contains_entry_with_different_value_if_expected() {
        let mut test_map = HashMap::new();
        test_map.insert("hi", "hello");

        assert_that(&test_map).does_not_contain_entry(&"hi", &"hey");
    }

    #[test]
    #[should_panic(expected = "\n\texpected: hashmap to not contain key <\"hello\"> \
    with value <\"hi\">\
                   \n\t but was: present in hashmap")]
    fn should_panic_if_hashmap_contains_entry_if_not_expected() {
        let mut test_map = HashMap::new();
        test_map.insert("hello", "hi");

        assert_that(&test_map).does_not_contain_entry(&"hello", &"hi");
    }
}
<commit_msg>Add Borrow trait on assertions for hashmap<commit_after>use super::{AssertionFailure, Spec};

use std::borrow::Borrow;
use std::collections::HashMap;
use std::fmt::Debug;
use std::hash::Hash;

pub trait HashMapAssertions<'s, K: Hash + Eq, V: PartialEq> {
    fn has_length(&mut self, expected: usize);
    fn is_empty(&mut self);
    fn contains_key<E: Borrow<K>>(&mut self, expected_key: E) -> Spec<'s, V>;
    fn does_not_contain_key<E: Borrow<K>>(&mut self, expected_key: E);
    fn contains_entry<E: Borrow<K>, F: Borrow<V>>(&mut self, expected_key: E, expected_value: F);
    fn does_not_contain_entry<E: Borrow<K>, F: Borrow<V>>(&mut self,
                                                          expected_key: E,
                                                          expected_value: F);
}

impl<'s, K, V> HashMapAssertions<'s, K, V> for Spec<'s, HashMap<K, V>>
    where K: Hash + Eq + Debug,
          V: PartialEq + Debug
{
    /// Asserts that the length of the subject hashmap is equal to the provided length. The subject
    /// type must be of `HashMap`.
    ///
    /// ```rust,ignore
    /// let mut test_map = HashMap::new();
    /// test_map.insert(1, 1);
    /// test_map.insert(2, 2);
    ///
    /// assert_that(&test_map).has_length(2);
    /// ```
    fn has_length(&mut self, expected: usize) {
        let subject = self.subject;

        if subject.len() != expected {
            AssertionFailure::from_spec(self)
                .with_expected(format!("hashmap to have length <{}>", expected))
                .with_actual(format!("<{}>", subject.len()))
                .fail();
        }
    }

    /// Asserts that the subject hashmap is empty. The subject type must be of `HashMap`.
    ///
    /// ```rust,ignore
    /// let test_map: HashMap<u8, u8> = HashMap::new();
    /// assert_that(&test_map).is_empty();
    /// ```
    fn is_empty(&mut self) {
        let subject = self.subject;

        if !subject.is_empty() {
            AssertionFailure::from_spec(self)
                .with_expected(format!("an empty hashmap"))
                .with_actual(format!("a hashmap with length <{:?}>", subject.len()))
                .fail();
        }
    }

    /// Asserts that the subject hashmap contains the expected key. The subject type must be
    /// of `HashMap`.
    ///
    /// This will return a new `Spec` containing the associated value if the key is present.
    ///
    /// ```rust,ignore
    /// let mut test_map = HashMap::new();
    /// test_map.insert("hello", "hi");
    ///
    /// assert_that(&test_map).contains_key(&"hello");
    /// ```
    fn contains_key<E: Borrow<K>>(&mut self, expected_key: E) -> Spec<'s, V> {
        let subject = self.subject;
        let borrowed_expected_key = expected_key.borrow();

        if let Some(value) = subject.get(borrowed_expected_key) {
            return Spec {
                subject: value,
                subject_name: self.subject_name,
                location: self.location.clone(),
                description: self.description,
            };
        }

        let subject_keys: Vec<&K> = subject.keys().collect();

        AssertionFailure::from_spec(self)
            .with_expected(format!("hashmap to contain key <{:?}>", borrowed_expected_key))
            .with_actual(format!("<{:?}>", subject_keys))
            .fail();

        unreachable!();
    }

    /// Asserts that the subject hashmap does not contain the provided key. The subject type must be
    /// of `HashMap`.
    ///
    /// ```rust,ignore
    /// let mut test_map = HashMap::new();
    /// test_map.insert("hello", "hi");
    ///
    /// assert_that(&test_map).does_not_contain_key(&"hey");
    /// ```
    fn does_not_contain_key<E: Borrow<K>>(&mut self, expected_key: E) {
        let subject = self.subject;
        let borrowed_expected_key = expected_key.borrow();

        if subject.get(borrowed_expected_key).is_some() {
            AssertionFailure::from_spec(self)
                .with_expected(format!("hashmap to not contain key <{:?}>", borrowed_expected_key))
                .with_actual(format!("present in hashmap"))
                .fail();
        }
    }

    /// Asserts that the subject hashmap contains the expected key with the expected value.
    /// The subject type must be of `HashMap`.
    ///
    /// ```rust,ignore
    /// let mut test_map = HashMap::new();
    /// test_map.insert("hello", "hi");
    ///
    /// assert_that(&test_map).contains_entry(&"hello", &"hi");
    /// ```
    fn contains_entry<E: Borrow<K>, F: Borrow<V>>(&mut self, expected_key: E, expected_value: F) {
        let subject = self.subject;
        let borrowed_expected_key = expected_key.borrow();
        let borrowed_expected_value = expected_value.borrow();

        let expected_message = format!("hashmap containing key <{:?}> with value <{:?}>",
                                       borrowed_expected_key,
                                       borrowed_expected_value);

        if let Some(value) = subject.get(borrowed_expected_key) {
            if value.eq(borrowed_expected_value) {
                return;
            }

            AssertionFailure::from_spec(self)
                .with_expected(expected_message)
                .with_actual(format!("key <{:?}> with value <{:?}> instead",
                                     borrowed_expected_key,
                                     value))
                .fail();

            unreachable!();
        }

        let subject_keys: Vec<&K> = subject.keys().collect();

        AssertionFailure::from_spec(self)
            .with_expected(expected_message)
            .with_actual(format!("no matching key, keys are <{:?}>", subject_keys))
            .fail();

    }

    /// Asserts that the subject hashmap does not contains the provided key and value.
    /// The subject type must be of `HashMap`.
    ///
    /// ```rust,ignore
    /// let mut test_map = HashMap::new();
    /// test_map.insert("hello", "hi");
    ///
    /// assert_that(&test_map).does_not_contain_entry(&"hello", &"hey");
    /// ```
    fn does_not_contain_entry<E: Borrow<K>, F: Borrow<V>>(&mut self,
                                                          expected_key: E,
                                                          expected_value: F) {
        let subject = self.subject;
        let borrowed_expected_key = expected_key.borrow();
        let borrowed_expected_value = expected_value.borrow();

        if let Some(value) = subject.get(borrowed_expected_key) {
            if !value.eq(borrowed_expected_value) {
                return;
            }

            AssertionFailure::from_spec(self)
                .with_expected(format!("hashmap to not contain key <{:?}> with value <{:?}>",
                                       borrowed_expected_key,
                                       borrowed_expected_value))
                .with_actual(format!("present in hashmap"))
                .fail();
        }
    }
}

#[cfg(test)]
mod tests {

    use super::super::prelude::*;

    use std::collections::HashMap;

    #[test]
    fn should_not_panic_if_hashmap_length_matches_expected() {
        let mut test_map = HashMap::new();
        test_map.insert(1, 1);
        test_map.insert(2, 2);

        assert_that(&test_map).has_length(2);
    }

    #[test]
    #[should_panic(expected = "\n\texpected: hashmap to have length <1>\n\t but was: <2>")]
    fn should_panic_if_hashmap_length_does_not_match_expected() {
        let mut test_map = HashMap::new();
        test_map.insert(1, 1);
        test_map.insert(2, 2);

        assert_that(&test_map).has_length(1);
    }

    #[test]
    fn should_not_panic_if_hashmap_was_expected_to_be_empty_and_is() {
        let test_map: HashMap<u8, u8> = HashMap::new();
        assert_that(&test_map).is_empty();
    }

    #[test]
    #[should_panic(expected = "\n\texpected: an empty hashmap\
                   \n\t but was: a hashmap with length <1>")]
    fn should_panic_if_hashmap_was_expected_to_be_empty_and_is_not() {
        let mut test_map = HashMap::new();
        test_map.insert(1, 1);

        assert_that(&test_map).is_empty();
    }

    #[test]
    fn contains_key_should_allow_multiple_borrow_forms() {
        let mut test_map = HashMap::new();
        test_map.insert("hello", "hi");

        assert_that(&test_map).contains_key("hello");
        assert_that(&test_map).contains_key(&mut "hello");
        assert_that(&test_map).contains_key(&"hello");
    }

    #[test]
    fn should_not_panic_if_hashmap_contains_key() {
        let mut test_map = HashMap::new();
        test_map.insert("hello", "hi");

        assert_that(&test_map).contains_key(&"hello");
    }

    #[test]
    // Unfortunately the order of the keys can change. Doesn't seem to make sense to sort them
    // just for the sake of checking the panic message.
    #[should_panic]
    fn should_not_panic_if_hashmap_does_not_contain_key() {
        let mut test_map = HashMap::new();
        test_map.insert("hi", "hi");
        test_map.insert("hey", "hey");

        assert_that(&test_map).contains_key(&"hello");
    }

    #[test]
    fn should_be_able_to_chain_value_from_contains_key() {
        let mut test_map = HashMap::new();
        test_map.insert("hello", "hi");

        assert_that(&test_map).contains_key(&"hello").is_equal_to(&"hi");
    }

    #[test]
    fn does_not_contain_key_should_allow_multiple_borrow_forms() {
        let mut test_map = HashMap::new();
        test_map.insert("hello", "hi");

        assert_that(&test_map).does_not_contain_key("hey");
        assert_that(&test_map).does_not_contain_key(&mut "hey");
        assert_that(&test_map).does_not_contain_key(&"hey");
    }

    #[test]
    fn should_not_panic_if_hashmap_does_not_contain_key_when_expected() {
        let mut test_map = HashMap::new();
        test_map.insert("hello", "hi");

        assert_that(&test_map).does_not_contain_key(&"hey");
    }

    #[test]
    #[should_panic(expected = "\n\texpected: hashmap to not contain key <\"hello\">\
                   \n\t but was: present in hashmap")]
    fn should_panic_if_hashmap_does_contain_key_when_not_expected() {
        let mut test_map = HashMap::new();
        test_map.insert("hello", "hi");

        assert_that(&test_map).does_not_contain_key(&"hello");
    }

    #[test]
    fn contains_entry_should_allow_multiple_borrow_forms() {
        let mut test_map = HashMap::new();
        test_map.insert("hello", "hi");

        assert_that(&test_map).contains_entry("hello", "hi");
        assert_that(&test_map).contains_entry(&mut "hello", &mut "hi");
        assert_that(&test_map).contains_entry("hello", &mut "hi");
        assert_that(&test_map).contains_entry(&"hello", &"hi");
    }

    #[test]
    fn should_not_panic_if_hashmap_contains_entry() {
        let mut test_map = HashMap::new();
        test_map.insert("hello", "hi");

        assert_that(&test_map).contains_entry(&"hello", &"hi");
    }

    #[test]
    #[should_panic(expected = "\n\texpected: hashmap containing key <\"hey\"> with value <\"hi\">\
                   \n\t but was: no matching key, keys are <[\"hello\"]>")]
    fn should_panic_if_hashmap_contains_entry_without_key() {
        let mut test_map = HashMap::new();
        test_map.insert("hello", "hi");

        assert_that(&test_map).contains_entry(&"hey", &"hi");
    }

    #[test]
    #[should_panic(expected = "\n\texpected: hashmap containing key <\"hi\"> with value <\"hey\">\
                   \n\t but was: key <\"hi\"> with value <\"hello\"> instead")]
    fn should_panic_if_hashmap_contains_entry_with_different_value() {
        let mut test_map = HashMap::new();
        test_map.insert("hi", "hello");

        assert_that(&test_map).contains_entry(&"hi", &"hey");
    }

    #[test]
    fn should_not_panic_if_hashmap_does_not_contain_entry_if_expected() {
        let mut test_map = HashMap::new();
        test_map.insert("hello", "hi");

        assert_that(&test_map).does_not_contain_entry(&"hey", &"hi");
    }

    #[test]
    fn should_not_panic_if_hashmap_contains_entry_with_different_value_if_expected() {
        let mut test_map = HashMap::new();
        test_map.insert("hi", "hello");

        assert_that(&test_map).does_not_contain_entry(&"hi", &"hey");
    }

    #[test]
    #[should_panic(expected = "\n\texpected: hashmap to not contain key <\"hello\"> \
    with value <\"hi\">\
                   \n\t but was: present in hashmap")]
    fn should_panic_if_hashmap_contains_entry_if_not_expected() {
        let mut test_map = HashMap::new();
        test_map.insert("hello", "hi");

        assert_that(&test_map).does_not_contain_entry(&"hello", &"hi");
    }
}
<|endoftext|>


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

use io::{self, Error, ErrorKind};
use libc::{self, c_int, gid_t, pid_t, uid_t};
use ptr;

use sys::cvt;
use sys::process::process_common::*;

////////////////////////////////////////////////////////////////////////////////
// Command
////////////////////////////////////////////////////////////////////////////////

impl Command {
    pub fn spawn(&mut self, default: Stdio, needs_stdin: bool)
                 -> io::Result<(Process, StdioPipes)> {
        use sys;

        const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX";

        let envp = self.capture_env();

        if self.saw_nul() {
            return Err(io::Error::new(ErrorKind::InvalidInput,
                                      "nul byte found in provided data"));
        }

        let (ours, theirs) = self.setup_io(default, needs_stdin)?;

        if let Some(ret) = self.posix_spawn(&theirs, envp.as_ref())? {
            return Ok((ret, ours))
        }

        let (input, output) = sys::pipe::anon_pipe()?;

        let pid = unsafe {
            match cvt(libc::fork())? {
                0 => {
                    drop(input);
                    let err = self.do_exec(theirs, envp.as_ref());
                    let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32;
                    let bytes = [
                        (errno >> 24) as u8,
                        (errno >> 16) as u8,
                        (errno >>  8) as u8,
                        (errno >>  0) as u8,
                        CLOEXEC_MSG_FOOTER[0], CLOEXEC_MSG_FOOTER[1],
                        CLOEXEC_MSG_FOOTER[2], CLOEXEC_MSG_FOOTER[3]
                    ];
                    // pipe I/O up to PIPE_BUF bytes should be atomic, and then
                    // we want to be sure we *don't* run at_exit destructors as
                    // we're being torn down regardless
                    assert!(output.write(&bytes).is_ok());
                    libc::_exit(1)
                }
                n => n,
            }
        };

        let mut p = Process { pid: pid, status: None };
        drop(output);
        let mut bytes = [0; 8];

        // loop to handle EINTR
        loop {
            match input.read(&mut bytes) {
                Ok(0) => return Ok((p, ours)),
                Ok(8) => {
                    assert!(combine(CLOEXEC_MSG_FOOTER) == combine(&bytes[4.. 8]),
                            "Validation on the CLOEXEC pipe failed: {:?}", bytes);
                    let errno = combine(&bytes[0.. 4]);
                    assert!(p.wait().is_ok(),
                            "wait() should either return Ok or panic");
                    return Err(Error::from_raw_os_error(errno))
                }
                Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
                Err(e) => {
                    assert!(p.wait().is_ok(),
                            "wait() should either return Ok or panic");
                    panic!("the CLOEXEC pipe failed: {:?}", e)
                },
                Ok(..) => { // pipe I/O up to PIPE_BUF bytes should be atomic
                    assert!(p.wait().is_ok(),
                            "wait() should either return Ok or panic");
                    panic!("short read on the CLOEXEC pipe")
                }
            }
        }

        fn combine(arr: &[u8]) -> i32 {
            let a = arr[0] as u32;
            let b = arr[1] as u32;
            let c = arr[2] as u32;
            let d = arr[3] as u32;

            ((a << 24) | (b << 16) | (c << 8) | (d << 0)) as i32
        }
    }

    pub fn exec(&mut self, default: Stdio) -> io::Error {
        let envp = self.capture_env();

        if self.saw_nul() {
            return io::Error::new(ErrorKind::InvalidInput,
                                  "nul byte found in provided data")
        }

        match self.setup_io(default, true) {
            Ok((_, theirs)) => unsafe { self.do_exec(theirs, envp.as_ref()) },
            Err(e) => e,
        }
    }

    // And at this point we've reached a special time in the life of the
    // child. The child must now be considered hamstrung and unable to
    // do anything other than syscalls really. Consider the following
    // scenario:
    //
    //      1. Thread A of process 1 grabs the malloc() mutex
    //      2. Thread B of process 1 forks(), creating thread C
    //      3. Thread C of process 2 then attempts to malloc()
    //      4. The memory of process 2 is the same as the memory of
    //         process 1, so the mutex is locked.
    //
    // This situation looks a lot like deadlock, right? It turns out
    // that this is what pthread_atfork() takes care of, which is
    // presumably implemented across platforms. The first thing that
    // threads to *before* forking is to do things like grab the malloc
    // mutex, and then after the fork they unlock it.
    //
    // Despite this information, libnative's spawn has been witnessed to
    // deadlock on both macOS and FreeBSD. I'm not entirely sure why, but
    // all collected backtraces point at malloc/free traffic in the
    // child spawned process.
    //
    // For this reason, the block of code below should contain 0
    // invocations of either malloc of free (or their related friends).
    //
    // As an example of not having malloc/free traffic, we don't close
    // this file descriptor by dropping the FileDesc (which contains an
    // allocation). Instead we just close it manually. This will never
    // have the drop glue anyway because this code never returns (the
    // child will either exec() or invoke libc::exit)
    unsafe fn do_exec(
        &mut self,
        stdio: ChildPipes,
        maybe_envp: Option<&CStringArray>
    ) -> io::Error {
        use sys::{self, cvt_r};

        macro_rules! t {
            ($e:expr) => (match $e {
                Ok(e) => e,
                Err(e) => return e,
            })
        }

        if let Some(fd) = stdio.stdin.fd() {
            t!(cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO)));
        }
        if let Some(fd) = stdio.stdout.fd() {
            t!(cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO)));
        }
        if let Some(fd) = stdio.stderr.fd() {
            t!(cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO)));
        }

        if cfg!(not(any(target_os = "l4re"))) {
            if let Some(u) = self.get_gid() {
                t!(cvt(libc::setgid(u as gid_t)));
            }
            if let Some(u) = self.get_uid() {
                // When dropping privileges from root, the `setgroups` call
                // will remove any extraneous groups. If we don't call this,
                // then even though our uid has dropped, we may still have
                // groups that enable us to do super-user things. This will
                // fail if we aren't root, so don't bother checking the
                // return value, this is just done as an optimistic
                // privilege dropping function.
                let _ = libc::setgroups(0, ptr::null());

                t!(cvt(libc::setuid(u as uid_t)));
            }
        }
        if let Some(ref cwd) = *self.get_cwd() {
            t!(cvt(libc::chdir(cwd.as_ptr())));
        }
        if let Some(envp) = maybe_envp {
            *sys::os::environ() = envp.as_ptr();
        }

        // emscripten has no signal support.
        #[cfg(not(any(target_os = "emscripten")))]
        {
            use mem;
            // Reset signal handling so the child process starts in a
            // standardized state. libstd ignores SIGPIPE, and signal-handling
            // libraries often set a mask. Child processes inherit ignored
            // signals and the signal mask from their parent, but most
            // UNIX programs do not reset these things on their own, so we
            // need to clean things up now to avoid confusing the program
            // we're about to run.
            let mut set: libc::sigset_t = mem::uninitialized();
            if cfg!(target_os = "android") {
                // Implementing sigemptyset allow us to support older Android
                // versions. See the comment about Android and sig* functions in
                // process_common.rs
                libc::memset(&mut set as *mut _ as *mut _,
                             0,
                             mem::size_of::<libc::sigset_t>());
            } else {
                t!(cvt(libc::sigemptyset(&mut set)));
            }
            t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &set,
                                         ptr::null_mut())));
            let ret = sys::signal(libc::SIGPIPE, libc::SIG_DFL);
            if ret == libc::SIG_ERR {
                return io::Error::last_os_error()
            }
        }

        for callback in self.get_closures().iter_mut() {
            t!(callback());
        }

        libc::execvp(self.get_argv()[0], self.get_argv().as_ptr());
        io::Error::last_os_error()
    }

    #[cfg(not(any(target_os = "linux", target_os = "macos")))]
    fn posix_spawn(&mut self, _stdio: &ChildPipes, _envp: Option<&CStringArray>)
        -> io::Result<Option<Process>>
    {
        Ok(None)
    }

    #[cfg(any(target_os = "linux", target_os = "macos"))]
    fn posix_spawn(&mut self, stdio: &ChildPipes, envp: Option<&CStringArray>)
        -> io::Result<Option<Process>>
    {
        use mem;
        use sys;

        if self.get_cwd().is_some() ||
            self.get_gid().is_some() ||
            self.get_uid().is_some() ||
            self.get_closures().len() != 0 {
            return Ok(None)
        }

        let mut p = Process { pid: 0, status: None };

        struct PosixSpawnFileActions(libc::posix_spawn_file_actions_t);

        impl Drop for PosixSpawnFileActions {
            fn drop(&mut self) {
                unsafe {
                    libc::posix_spawn_file_actions_destroy(&mut self.0);
                }
            }
        }

        struct PosixSpawnattr(libc::posix_spawnattr_t);

        impl Drop for PosixSpawnattr {
            fn drop(&mut self) {
                unsafe {
                    libc::posix_spawnattr_destroy(&mut self.0);
                }
            }
        }

        unsafe {
            let mut file_actions = PosixSpawnFileActions(mem::uninitialized());
            let mut attrs = PosixSpawnattr(mem::uninitialized());

            libc::posix_spawnattr_init(&mut attrs.0);
            libc::posix_spawn_file_actions_init(&mut file_actions.0);

            if let Some(fd) = stdio.stdin.fd() {
                cvt(libc::posix_spawn_file_actions_adddup2(&mut file_actions.0,
                                                           fd,
                                                           libc::STDIN_FILENO))?;
            }
            if let Some(fd) = stdio.stdout.fd() {
                cvt(libc::posix_spawn_file_actions_adddup2(&mut file_actions.0,
                                                           fd,
                                                           libc::STDOUT_FILENO))?;
            }
            if let Some(fd) = stdio.stderr.fd() {
                cvt(libc::posix_spawn_file_actions_adddup2(&mut file_actions.0,
                                                           fd,
                                                           libc::STDERR_FILENO))?;
            }

            let mut set: libc::sigset_t = mem::uninitialized();
            cvt(libc::sigemptyset(&mut set))?;
            cvt(libc::posix_spawnattr_setsigmask(&mut attrs.0,
                                                 &set))?;
            cvt(libc::sigaddset(&mut set, libc::SIGPIPE))?;
            cvt(libc::posix_spawnattr_setsigdefault(&mut attrs.0,
                                                    &set))?;

            let flags = libc::POSIX_SPAWN_SETSIGDEF |
                libc::POSIX_SPAWN_SETSIGMASK;
            cvt(libc::posix_spawnattr_setflags(&mut attrs.0, flags as _))?;

            let envp = envp.map(|c| c.as_ptr())
                .unwrap_or(sys::os::environ() as *const _);
            let ret = libc::posix_spawnp(
                &mut p.pid,
                self.get_argv()[0],
                &file_actions.0,
                &attrs.0,
                self.get_argv().as_ptr() as *const _,
                envp as *const _,
            );
            if ret == 0 {
                Ok(Some(p))
            } else {
                Err(io::Error::last_os_error())
            }
        }
    }
}

////////////////////////////////////////////////////////////////////////////////
// Processes
////////////////////////////////////////////////////////////////////////////////

/// The unique id of the process (this should never be negative).
pub struct Process {
    pid: pid_t,
    status: Option<ExitStatus>,
}

impl Process {
    pub fn id(&self) -> u32 {
        self.pid as u32
    }

    pub fn kill(&mut self) -> io::Result<()> {
        // If we've already waited on this process then the pid can be recycled
        // and used for another process, and we probably shouldn't be killing
        // random processes, so just return an error.
        if self.status.is_some() {
            Err(Error::new(ErrorKind::InvalidInput,
                           "invalid argument: can't kill an exited process"))
        } else {
            cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(|_| ())
        }
    }

    pub fn wait(&mut self) -> io::Result<ExitStatus> {
        use sys::cvt_r;
        if let Some(status) = self.status {
            return Ok(status)
        }
        let mut status = 0 as c_int;
        cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?;
        self.status = Some(ExitStatus::new(status));
        Ok(ExitStatus::new(status))
    }

    pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
        if let Some(status) = self.status {
            return Ok(Some(status))
        }
        let mut status = 0 as c_int;
        let pid = cvt(unsafe {
            libc::waitpid(self.pid, &mut status, libc::WNOHANG)
        })?;
        if pid == 0 {
            Ok(None)
        } else {
            self.status = Some(ExitStatus::new(status));
            Ok(Some(ExitStatus::new(status)))
        }
    }
}
<commit_msg>Pass proper pointer for envp.<commit_after>// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use io::{self, Error, ErrorKind};
use libc::{self, c_int, gid_t, pid_t, uid_t};
use ptr;

use sys::cvt;
use sys::process::process_common::*;

////////////////////////////////////////////////////////////////////////////////
// Command
////////////////////////////////////////////////////////////////////////////////

impl Command {
    pub fn spawn(&mut self, default: Stdio, needs_stdin: bool)
                 -> io::Result<(Process, StdioPipes)> {
        use sys;

        const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX";

        let envp = self.capture_env();

        if self.saw_nul() {
            return Err(io::Error::new(ErrorKind::InvalidInput,
                                      "nul byte found in provided data"));
        }

        let (ours, theirs) = self.setup_io(default, needs_stdin)?;

        if let Some(ret) = self.posix_spawn(&theirs, envp.as_ref())? {
            return Ok((ret, ours))
        }

        let (input, output) = sys::pipe::anon_pipe()?;

        let pid = unsafe {
            match cvt(libc::fork())? {
                0 => {
                    drop(input);
                    let err = self.do_exec(theirs, envp.as_ref());
                    let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32;
                    let bytes = [
                        (errno >> 24) as u8,
                        (errno >> 16) as u8,
                        (errno >>  8) as u8,
                        (errno >>  0) as u8,
                        CLOEXEC_MSG_FOOTER[0], CLOEXEC_MSG_FOOTER[1],
                        CLOEXEC_MSG_FOOTER[2], CLOEXEC_MSG_FOOTER[3]
                    ];
                    // pipe I/O up to PIPE_BUF bytes should be atomic, and then
                    // we want to be sure we *don't* run at_exit destructors as
                    // we're being torn down regardless
                    assert!(output.write(&bytes).is_ok());
                    libc::_exit(1)
                }
                n => n,
            }
        };

        let mut p = Process { pid: pid, status: None };
        drop(output);
        let mut bytes = [0; 8];

        // loop to handle EINTR
        loop {
            match input.read(&mut bytes) {
                Ok(0) => return Ok((p, ours)),
                Ok(8) => {
                    assert!(combine(CLOEXEC_MSG_FOOTER) == combine(&bytes[4.. 8]),
                            "Validation on the CLOEXEC pipe failed: {:?}", bytes);
                    let errno = combine(&bytes[0.. 4]);
                    assert!(p.wait().is_ok(),
                            "wait() should either return Ok or panic");
                    return Err(Error::from_raw_os_error(errno))
                }
                Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
                Err(e) => {
                    assert!(p.wait().is_ok(),
                            "wait() should either return Ok or panic");
                    panic!("the CLOEXEC pipe failed: {:?}", e)
                },
                Ok(..) => { // pipe I/O up to PIPE_BUF bytes should be atomic
                    assert!(p.wait().is_ok(),
                            "wait() should either return Ok or panic");
                    panic!("short read on the CLOEXEC pipe")
                }
            }
        }

        fn combine(arr: &[u8]) -> i32 {
            let a = arr[0] as u32;
            let b = arr[1] as u32;
            let c = arr[2] as u32;
            let d = arr[3] as u32;

            ((a << 24) | (b << 16) | (c << 8) | (d << 0)) as i32
        }
    }

    pub fn exec(&mut self, default: Stdio) -> io::Error {
        let envp = self.capture_env();

        if self.saw_nul() {
            return io::Error::new(ErrorKind::InvalidInput,
                                  "nul byte found in provided data")
        }

        match self.setup_io(default, true) {
            Ok((_, theirs)) => unsafe { self.do_exec(theirs, envp.as_ref()) },
            Err(e) => e,
        }
    }

    // And at this point we've reached a special time in the life of the
    // child. The child must now be considered hamstrung and unable to
    // do anything other than syscalls really. Consider the following
    // scenario:
    //
    //      1. Thread A of process 1 grabs the malloc() mutex
    //      2. Thread B of process 1 forks(), creating thread C
    //      3. Thread C of process 2 then attempts to malloc()
    //      4. The memory of process 2 is the same as the memory of
    //         process 1, so the mutex is locked.
    //
    // This situation looks a lot like deadlock, right? It turns out
    // that this is what pthread_atfork() takes care of, which is
    // presumably implemented across platforms. The first thing that
    // threads to *before* forking is to do things like grab the malloc
    // mutex, and then after the fork they unlock it.
    //
    // Despite this information, libnative's spawn has been witnessed to
    // deadlock on both macOS and FreeBSD. I'm not entirely sure why, but
    // all collected backtraces point at malloc/free traffic in the
    // child spawned process.
    //
    // For this reason, the block of code below should contain 0
    // invocations of either malloc of free (or their related friends).
    //
    // As an example of not having malloc/free traffic, we don't close
    // this file descriptor by dropping the FileDesc (which contains an
    // allocation). Instead we just close it manually. This will never
    // have the drop glue anyway because this code never returns (the
    // child will either exec() or invoke libc::exit)
    unsafe fn do_exec(
        &mut self,
        stdio: ChildPipes,
        maybe_envp: Option<&CStringArray>
    ) -> io::Error {
        use sys::{self, cvt_r};

        macro_rules! t {
            ($e:expr) => (match $e {
                Ok(e) => e,
                Err(e) => return e,
            })
        }

        if let Some(fd) = stdio.stdin.fd() {
            t!(cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO)));
        }
        if let Some(fd) = stdio.stdout.fd() {
            t!(cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO)));
        }
        if let Some(fd) = stdio.stderr.fd() {
            t!(cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO)));
        }

        if cfg!(not(any(target_os = "l4re"))) {
            if let Some(u) = self.get_gid() {
                t!(cvt(libc::setgid(u as gid_t)));
            }
            if let Some(u) = self.get_uid() {
                // When dropping privileges from root, the `setgroups` call
                // will remove any extraneous groups. If we don't call this,
                // then even though our uid has dropped, we may still have
                // groups that enable us to do super-user things. This will
                // fail if we aren't root, so don't bother checking the
                // return value, this is just done as an optimistic
                // privilege dropping function.
                let _ = libc::setgroups(0, ptr::null());

                t!(cvt(libc::setuid(u as uid_t)));
            }
        }
        if let Some(ref cwd) = *self.get_cwd() {
            t!(cvt(libc::chdir(cwd.as_ptr())));
        }
        if let Some(envp) = maybe_envp {
            *sys::os::environ() = envp.as_ptr();
        }

        // emscripten has no signal support.
        #[cfg(not(any(target_os = "emscripten")))]
        {
            use mem;
            // Reset signal handling so the child process starts in a
            // standardized state. libstd ignores SIGPIPE, and signal-handling
            // libraries often set a mask. Child processes inherit ignored
            // signals and the signal mask from their parent, but most
            // UNIX programs do not reset these things on their own, so we
            // need to clean things up now to avoid confusing the program
            // we're about to run.
            let mut set: libc::sigset_t = mem::uninitialized();
            if cfg!(target_os = "android") {
                // Implementing sigemptyset allow us to support older Android
                // versions. See the comment about Android and sig* functions in
                // process_common.rs
                libc::memset(&mut set as *mut _ as *mut _,
                             0,
                             mem::size_of::<libc::sigset_t>());
            } else {
                t!(cvt(libc::sigemptyset(&mut set)));
            }
            t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &set,
                                         ptr::null_mut())));
            let ret = sys::signal(libc::SIGPIPE, libc::SIG_DFL);
            if ret == libc::SIG_ERR {
                return io::Error::last_os_error()
            }
        }

        for callback in self.get_closures().iter_mut() {
            t!(callback());
        }

        libc::execvp(self.get_argv()[0], self.get_argv().as_ptr());
        io::Error::last_os_error()
    }

    #[cfg(not(any(target_os = "linux", target_os = "macos")))]
    fn posix_spawn(&mut self, _stdio: &ChildPipes, _envp: Option<&CStringArray>)
        -> io::Result<Option<Process>>
    {
        Ok(None)
    }

    #[cfg(any(target_os = "linux", target_os = "macos"))]
    fn posix_spawn(&mut self, stdio: &ChildPipes, envp: Option<&CStringArray>)
        -> io::Result<Option<Process>>
    {
        use mem;
        use sys;

        if self.get_cwd().is_some() ||
            self.get_gid().is_some() ||
            self.get_uid().is_some() ||
            self.get_closures().len() != 0 {
            return Ok(None)
        }

        let mut p = Process { pid: 0, status: None };

        struct PosixSpawnFileActions(libc::posix_spawn_file_actions_t);

        impl Drop for PosixSpawnFileActions {
            fn drop(&mut self) {
                unsafe {
                    libc::posix_spawn_file_actions_destroy(&mut self.0);
                }
            }
        }

        struct PosixSpawnattr(libc::posix_spawnattr_t);

        impl Drop for PosixSpawnattr {
            fn drop(&mut self) {
                unsafe {
                    libc::posix_spawnattr_destroy(&mut self.0);
                }
            }
        }

        unsafe {
            let mut file_actions = PosixSpawnFileActions(mem::uninitialized());
            let mut attrs = PosixSpawnattr(mem::uninitialized());

            libc::posix_spawnattr_init(&mut attrs.0);
            libc::posix_spawn_file_actions_init(&mut file_actions.0);

            if let Some(fd) = stdio.stdin.fd() {
                cvt(libc::posix_spawn_file_actions_adddup2(&mut file_actions.0,
                                                           fd,
                                                           libc::STDIN_FILENO))?;
            }
            if let Some(fd) = stdio.stdout.fd() {
                cvt(libc::posix_spawn_file_actions_adddup2(&mut file_actions.0,
                                                           fd,
                                                           libc::STDOUT_FILENO))?;
            }
            if let Some(fd) = stdio.stderr.fd() {
                cvt(libc::posix_spawn_file_actions_adddup2(&mut file_actions.0,
                                                           fd,
                                                           libc::STDERR_FILENO))?;
            }

            let mut set: libc::sigset_t = mem::uninitialized();
            cvt(libc::sigemptyset(&mut set))?;
            cvt(libc::posix_spawnattr_setsigmask(&mut attrs.0,
                                                 &set))?;
            cvt(libc::sigaddset(&mut set, libc::SIGPIPE))?;
            cvt(libc::posix_spawnattr_setsigdefault(&mut attrs.0,
                                                    &set))?;

            let flags = libc::POSIX_SPAWN_SETSIGDEF |
                libc::POSIX_SPAWN_SETSIGMASK;
            cvt(libc::posix_spawnattr_setflags(&mut attrs.0, flags as _))?;

            let envp = envp.map(|c| c.as_ptr())
                .unwrap_or(*sys::os::environ() as *const _);
            let ret = libc::posix_spawnp(
                &mut p.pid,
                self.get_argv()[0],
                &file_actions.0,
                &attrs.0,
                self.get_argv().as_ptr() as *const _,
                envp as *const _,
            );
            if ret == 0 {
                Ok(Some(p))
            } else {
                Err(io::Error::last_os_error())
            }
        }
    }
}

////////////////////////////////////////////////////////////////////////////////
// Processes
////////////////////////////////////////////////////////////////////////////////

/// The unique id of the process (this should never be negative).
pub struct Process {
    pid: pid_t,
    status: Option<ExitStatus>,
}

impl Process {
    pub fn id(&self) -> u32 {
        self.pid as u32
    }

    pub fn kill(&mut self) -> io::Result<()> {
        // If we've already waited on this process then the pid can be recycled
        // and used for another process, and we probably shouldn't be killing
        // random processes, so just return an error.
        if self.status.is_some() {
            Err(Error::new(ErrorKind::InvalidInput,
                           "invalid argument: can't kill an exited process"))
        } else {
            cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(|_| ())
        }
    }

    pub fn wait(&mut self) -> io::Result<ExitStatus> {
        use sys::cvt_r;
        if let Some(status) = self.status {
            return Ok(status)
        }
        let mut status = 0 as c_int;
        cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?;
        self.status = Some(ExitStatus::new(status));
        Ok(ExitStatus::new(status))
    }

    pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
        if let Some(status) = self.status {
            return Ok(Some(status))
        }
        let mut status = 0 as c_int;
        let pid = cvt(unsafe {
            libc::waitpid(self.pid, &mut status, libc::WNOHANG)
        })?;
        if pid == 0 {
            Ok(None)
        } else {
            self.status = Some(ExitStatus::new(status));
            Ok(Some(ExitStatus::new(status)))
        }
    }
}
<|endoftext|>


<commit_before>use std::ascii::AsciiExt;

#[inline(always)]
pub fn is_transfer_encoding(val: &str) -> bool {
    if val.len() != "transfer-encoding".len() {
        return false;
    }
    for (idx, ch) in val.bytes().enumerate() {
        if b"transfer-encoding"[idx] != ch.to_ascii_lowercase() {
            return false;
        }
    }
    return true;
}

#[inline(always)]
pub fn is_content_length(val: &str) -> bool {
    if val.len() != "content-length".len() {
        return false;
    }
    for (idx, ch) in val.bytes().enumerate() {
        if b"content-length"[idx] != ch.to_ascii_lowercase() {
            return false;
        }
    }
    return true;
}

#[inline(always)]
pub fn is_connection(val: &str) -> bool {
    if val.len() != "connection".len() {
        return false;
    }
    for (idx, ch) in val.bytes().enumerate() {
        if b"connection"[idx] != ch.to_ascii_lowercase() {
            return false;
        }
    }
    return true;
}

#[cfg(test)]
mod test {
    use super::{is_content_length, is_transfer_encoding, is_connection};

    #[test]
    fn test_content_len() {
        assert!(is_content_length("Content-Length"));
        assert!(is_content_length("content-length"));
        assert!(is_content_length("CONTENT-length"));
        assert!(is_content_length("CONTENT-LENGTH"));
    }

    #[test]
    fn test_transfer_encoding() {
        assert!(is_transfer_encoding("Transfer-Encoding"));
        assert!(is_transfer_encoding("transfer-ENCODING"));
        assert!(is_transfer_encoding("TRANSFER-Encoding"));
        assert!(is_transfer_encoding("TRANSFER-ENCODING"));
    }

    #[test]
    fn test_connection() {
        assert!(is_connection("Connection"));
        assert!(is_connection("CONNECTION"));
        assert!(is_connection("ConneCTION"));
        assert!(is_connection("connection"));
    }
}

<commit_msg>The is_chunked and is_close helper functions<commit_after>use std::ascii::AsciiExt;

#[inline(always)]
pub fn is_transfer_encoding(val: &str) -> bool {
    if val.len() != "transfer-encoding".len() {
        return false;
    }
    for (idx, ch) in val.bytes().enumerate() {
        if b"transfer-encoding"[idx] != ch.to_ascii_lowercase() {
            return false;
        }
    }
    return true;
}

#[inline(always)]
pub fn is_content_length(val: &str) -> bool {
    if val.len() != "content-length".len() {
        return false;
    }
    for (idx, ch) in val.bytes().enumerate() {
        if b"content-length"[idx] != ch.to_ascii_lowercase() {
            return false;
        }
    }
    return true;
}

#[inline(always)]
pub fn is_connection(val: &str) -> bool {
    if val.len() != "connection".len() {
        return false;
    }
    for (idx, ch) in val.bytes().enumerate() {
        if b"connection"[idx] != ch.to_ascii_lowercase() {
            return false;
        }
    }
    return true;
}

#[inline(always)]
// header value is byte sequence
// we need case insensitive comparison and strip out of the whitespace
pub fn is_close(val: &[u8]) -> bool {
    if val.len() < "close".len() {
        return false;
    }
    let mut iter = val.iter();
    for (idx, &ch) in iter.by_ref().enumerate() {
        match ch {
            b'\r' | b'\n' | b' ' | b'\t' => continue,
            b'c' | b'C' => {
                if idx + "close".len() > val.len() {
                    return false;
                }
                break;
            }
            _ => return false,
        }
    }
    for (idx, ch) in iter.by_ref().take(4).enumerate() {
        if b"lose"[idx] != ch.to_ascii_lowercase() {
            return false;
        }
    }
    return true;
}

#[inline(always)]
// header value is byte sequence
// we need case insensitive comparison and strip out of the whitespace
pub fn is_chunked(val: &[u8]) -> bool {
    if val.len() < "chunked".len() {
        return false;
    }
    let mut iter = val.iter();
    for (idx, &ch) in iter.by_ref().enumerate() {
        match ch {
            b'\r' | b'\n' | b' ' | b'\t' => continue,
            b'c' | b'C' => {
                if idx + "chunked".len() > val.len() {
                    return false;
                }
                break;
            }
            _ => return false,
        }
    }
    for (idx, ch) in iter.by_ref().take(6).enumerate() {
        if b"hunked"[idx] != ch.to_ascii_lowercase() {
            return false;
        }
    }
    return true;
}

#[cfg(test)]
mod test {
    use super::{is_content_length, is_transfer_encoding, is_connection};
    use super::{is_chunked, is_close};

    #[test]
    fn test_content_len() {
        assert!(is_content_length("Content-Length"));
        assert!(is_content_length("content-length"));
        assert!(is_content_length("CONTENT-length"));
        assert!(is_content_length("CONTENT-LENGTH"));
    }

    #[test]
    fn test_transfer_encoding() {
        assert!(is_transfer_encoding("Transfer-Encoding"));
        assert!(is_transfer_encoding("transfer-ENCODING"));
        assert!(is_transfer_encoding("TRANSFER-Encoding"));
        assert!(is_transfer_encoding("TRANSFER-ENCODING"));
    }

    #[test]
    fn test_connection() {
        assert!(is_connection("Connection"));
        assert!(is_connection("CONNECTION"));
        assert!(is_connection("ConneCTION"));
        assert!(is_connection("connection"));
    }

    #[test]
    fn test_chunked() {
        assert!(is_chunked(b"chunked"));
        assert!(is_chunked(b"Chunked"));
        assert!(is_chunked(b"chuNKED"));
        assert!(is_chunked(b"CHUNKED"));
        assert!(is_chunked(b"   CHUNKED"));
        assert!(is_chunked(b"   CHUNKED  "));
        assert!(is_chunked(b"chunked  "));
    }

    #[test]
    fn test_close() {
        assert!(is_close(b"close"));
        assert!(is_close(b"Close"));
        assert!(is_close(b"clOSE"));
        assert!(is_close(b"CLOSE"));
        assert!(is_close(b" CLOSE"));
        assert!(is_close(b"   close   "));
        assert!(is_close(b"Close   "));
    }
}

<|endoftext|>


<commit_before>use std::marker;

use crate::reflect::runtime_type_box::RuntimeTypeBox;
use crate::reflect::runtime_types::RuntimeType;
use crate::reflect::ReflectValueRef;

/// Dynamic version of `RuntimeType`.
///
/// This is used internally in reflection implementation.
pub trait RuntimeTypeDynamic: Send + Sync + 'static {
    /// Convert to "enum" version
    fn to_box(&self) -> RuntimeTypeBox;

    /// Default value for type
    fn default_value_ref(&self) -> ReflectValueRef;
}

pub(crate) struct RuntimeTypeDynamicImpl<T: RuntimeType>(pub marker::PhantomData<T>);

impl<T: RuntimeType> RuntimeTypeDynamic for RuntimeTypeDynamicImpl<T> {
    fn to_box(&self) -> RuntimeTypeBox {
        T::runtime_type_box()
    }

    fn default_value_ref(&self) -> ReflectValueRef {
        T::default_value_ref()
    }
}
<commit_msg>RuntimeTypeDynamic::default_value_ref is dead code<commit_after>use std::marker;

use crate::reflect::runtime_type_box::RuntimeTypeBox;
use crate::reflect::runtime_types::RuntimeType;

/// Dynamic version of `RuntimeType`.
///
/// This is used internally in reflection implementation.
pub trait RuntimeTypeDynamic: Send + Sync + 'static {
    /// Convert to "enum" version
    fn to_box(&self) -> RuntimeTypeBox;
}

pub(crate) struct RuntimeTypeDynamicImpl<T: RuntimeType>(pub marker::PhantomData<T>);

impl<T: RuntimeType> RuntimeTypeDynamic for RuntimeTypeDynamicImpl<T> {
    fn to_box(&self) -> RuntimeTypeBox {
        T::runtime_type_box()
    }
}
<|endoftext|>


<commit_before>use color::RGB;
use linear::{Direction, Position};
use std::default::Default;

pub struct LightProp {
  pub diff: RGB,
  pub spec: RGB,
  pub gloss: f32
}

pub struct Light<L> {
  pub prop: LightProp,
  pub caracterization: L
}

pub type Dir = Light<Direction>;
pub type Omni = Light<Position>;

impl LightProp {
  pub fn new(diff: RGB, spec: RGB, gloss: f32) -> Self {
    LightProp {
      diff: diff,
      spec: spec,
      gloss: gloss
    }
  }
}

impl Default for LightProp {
  fn default() -> Self {
    LightProp {
      diff: RGB::new(0.6, 0.6, 0.7),
      spec: RGB::new(0.6, 0.6, 0.7),
      gloss: 10.
    }
  }
}

impl<L> Light<L> {
  pub fn new(prop: LightProp, caracterization: L) -> Self {
    Light {
      prop: prop,
      caracterization: caracterization
    }
  }
}
<commit_msg>Using feature instead of characterization; because it’s shorter and simpler to read.<commit_after>use color::RGB;
use linear::{Direction, Position};
use std::default::Default;

pub struct LightProp {
  pub diff: RGB,
  pub spec: RGB,
  pub gloss: f32
}

pub struct Light<L> {
  pub prop: LightProp,
  pub feature: L
}

pub type Dir = Light<Direction>;
pub type Omni = Light<Position>;

impl LightProp {
  pub fn new(diff: RGB, spec: RGB, gloss: f32) -> Self {
    LightProp {
      diff: diff,
      spec: spec,
      gloss: gloss
    }
  }
}

impl Default for LightProp {
  fn default() -> Self {
    LightProp {
      diff: RGB::new(0.6, 0.6, 0.7),
      spec: RGB::new(0.6, 0.6, 0.7),
      gloss: 10.
    }
  }
}

impl<L> Light<L> {
  pub fn new(prop: LightProp, feature: L) -> Self {
    Light {
      prop: prop,
      feature: feature
    }
  }
}
<|endoftext|>


<commit_before>//! List merge requests
//!
//! https://docs.gitlab.com/ce/api/merge_requests.html#list-merge-requests
//!
//! # List merge requests
//!
//! Get all merge requests for this project.
//! The `state` parameter can be used to get only merge requests with a given state (`opened`, `closed`, or `merged`) or all of them (`all`).
//! The pagination parameters `page` and `per_page` can be used to restrict the list of merge requests.
//!
//! ```text
//! GET /projects/ID/merge_requests
//! GET /projects/ID/merge_requests?state=opened
//! GET /projects/ID/merge_requests?state=all
//! GET /projects/ID/merge_requests?iid=42
//! GET /projects/ID/merge_requests?iid[]=42&iid[]=43
//! ```
//!
//! Parameters:
//!
//! - `id` (required) - The ID of a project
//! - `iid` (optional) - Return the request having the given `iid`
//! - `state` (optional) - Return `all` requests or just those that are `merged`, `opened` or `closed`
//! - `order_by` (optional) - Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at`
//! - `sort` (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc`
//!
//!

use serde_json;
use serde_urlencoded;

use BuildQuery;
use Projects;

pub mod single;

#[cfg(feature = "serde_derive")]
include!("serde_types.in.rs");

#[cfg(feature = "serde_codegen")]
include!(concat!(env!("OUT_DIR"), "/merge_requests/serde_types.rs"));

#[derive(Debug, Clone)]
pub struct MergeRequestsLister<'a> {
    gl: &'a ::GitLab,
    id: i64,
    internal: MergeRequestsListerInternal,
}

impl<'a> MergeRequestsLister<'a> {
    pub fn new(gl: &'a ::GitLab, id: i64) -> MergeRequestsLister {
        MergeRequestsLister {
            gl: gl,
            id: id,
            internal: MergeRequestsListerInternal {
                iid: None,
                state: None,
                order_by: None,
                sort: None,
            },
        }
    }

    // pub fn single(self) -> single::ProjectsLister<'a> {
    //     // assert_eq!(self, ProjectsLister::new(self.gl));
    //     single::ProjectsLister::new(self.gl)
    // }

    pub fn iid(&'a mut self, iid: Vec<i64>) -> &'a mut MergeRequestsLister {
        self.internal.iid = Some(iid);
        self
    }
    pub fn state(&'a mut self, state: State) -> &'a mut MergeRequestsLister {
        self.internal.state = Some(state);
        self
    }
    pub fn order_by(&'a mut self, order_by: ListingOrderBy) -> &'a mut MergeRequestsLister {
        self.internal.order_by = Some(order_by);
        self
    }
    fn sort(&'a mut self, sort: ::ListingSort) -> &'a mut MergeRequestsLister {
        self.internal.sort = Some(sort);
        self
    }

    /// Commit the lister: Query GitLab and return a list of projects.
    pub fn list(&self) -> MergeRequests {
        let query = self.build_query();
        debug!("query: {:?}", query);

        let merge_requests: Result<MergeRequests, serde_json::Error> = self.gl.get(&query);

        merge_requests.unwrap()
    }
}

impl<'a> BuildQuery for MergeRequestsLister<'a> {
    fn build_query(&self) -> String {

        // NOTE: Can't use `serde_urlencoded` since it cannot serialize a Vec<T>
        //       See https://github.com/nox/serde_urlencoded/issues/6
        // let encoded = serde_urlencoded::to_string(&self.internal).unwrap();

        let mut query = format!("projects/{}/merge_requests", self.id);

        let amp_char = "&";
        let none_char = "";
        let mut split_char = &none_char;

        // Append a "?" only if at least one of the `Option` is `Some(_)` or if
        // strings contain something.
        query.push_str(match (&self.internal.iid, &self.internal.state, &self.internal.order_by, &self.internal.sort) {
            (&None, &None, &None, &None) => "",
            _ => "?",
        });

        self.internal.iid.as_ref().map(|iid| {
            query.push_str(split_char);
            split_char = &amp_char;

            if iid.len() == 1 {
                query.push_str("iid=");
                query.push_str(&iid[0].to_string());
            } else {
                let mut array_split_char = &none_char;
                for iid in iid {
                    query.push_str(array_split_char);
                    query.push_str("iid[]=");
                    query.push_str(&iid.to_string());
                    array_split_char = &amp_char;
                }
            }
        });

        self.internal.state.map(|state| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("state=");
            query.push_str(match state {
                State::Merged => "merged",
                State::Opened => "opened",
                State::Closed => "closed",
                State::All => "all",
            });
        });

        self.internal.order_by.map(|order_by| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("order_by=");
            query.push_str(match order_by {
                ListingOrderBy::CreatedAt => "created_at",
                ListingOrderBy::UpdatedAt => "updated_at",
            });
        });

        self.internal.sort.map(|sort| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("sort=");
            query.push_str(match sort {
                ::ListingSort::Asc => "asc",
                ::ListingSort::Desc => "desc",
            });
        });

        query
    }
}

#[cfg(test)]
mod tests {
    use BuildQuery;

    const TEST_PROJECT_ID: i64 = 123;

    #[test]
    fn build_query_default() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX");
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/merge_requests", TEST_PROJECT_ID);
        let lister = gl.merge_requests(TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID).build_query();
        assert_eq!(query, expected_string);

        let expected_string = format!("projects/{}/merge_requests", TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_iid() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX");
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/merge_requests?iid=456", TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID).iid(vec![456]).build_query();
        assert_eq!(query, expected_string);

        let expected_string = format!("projects/123/merge_requests?iid[]=456&iid[]=789");
        let query = gl.merge_requests(TEST_PROJECT_ID).iid(vec![456, 789]).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_state() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX");
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/merge_requests?state=opened", TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID).state(::merge_requests::State::Opened).build_query();
        assert_eq!(query, expected_string);

        let expected_string = format!("projects/{}/merge_requests?state=closed", TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID).state(::merge_requests::State::Closed).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_order_by() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX");
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/merge_requests?order_by=created_at", TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID).order_by(::merge_requests::ListingOrderBy::CreatedAt).build_query();
        assert_eq!(query, expected_string);

        let expected_string = format!("projects/{}/merge_requests?order_by=updated_at", TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID).order_by(::merge_requests::ListingOrderBy::UpdatedAt).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_sort() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX");
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/merge_requests?sort=asc", TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID).sort(::ListingSort::Asc).build_query();
        assert_eq!(query, expected_string);

        let expected_string = format!("projects/{}/merge_requests?sort=desc", TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID).sort(::ListingSort::Desc).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_multiple() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX");
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/merge_requests?iid[]=456&iid[]=789&order_by=created_at&sort=asc", TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID)
            .iid(vec![456, 789])
            .sort(::ListingSort::Asc)
            .order_by(::merge_requests::ListingOrderBy::CreatedAt)
            .build_query();
        assert_eq!(query, expected_string);
    }
}
<commit_msg>No need for `serde_urlencoded` until `Vec<T>` can be serialized.<commit_after>//! List merge requests
//!
//! https://docs.gitlab.com/ce/api/merge_requests.html#list-merge-requests
//!
//! # List merge requests
//!
//! Get all merge requests for this project.
//! The `state` parameter can be used to get only merge requests with a given state (`opened`, `closed`, or `merged`) or all of them (`all`).
//! The pagination parameters `page` and `per_page` can be used to restrict the list of merge requests.
//!
//! ```text
//! GET /projects/ID/merge_requests
//! GET /projects/ID/merge_requests?state=opened
//! GET /projects/ID/merge_requests?state=all
//! GET /projects/ID/merge_requests?iid=42
//! GET /projects/ID/merge_requests?iid[]=42&iid[]=43
//! ```
//!
//! Parameters:
//!
//! - `id` (required) - The ID of a project
//! - `iid` (optional) - Return the request having the given `iid`
//! - `state` (optional) - Return `all` requests or just those that are `merged`, `opened` or `closed`
//! - `order_by` (optional) - Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at`
//! - `sort` (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc`
//!
//!

use serde_json;
// use serde_urlencoded;

use BuildQuery;
use Projects;

pub mod single;

#[cfg(feature = "serde_derive")]
include!("serde_types.in.rs");

#[cfg(feature = "serde_codegen")]
include!(concat!(env!("OUT_DIR"), "/merge_requests/serde_types.rs"));

#[derive(Debug, Clone)]
pub struct MergeRequestsLister<'a> {
    gl: &'a ::GitLab,
    id: i64,
    internal: MergeRequestsListerInternal,
}

impl<'a> MergeRequestsLister<'a> {
    pub fn new(gl: &'a ::GitLab, id: i64) -> MergeRequestsLister {
        MergeRequestsLister {
            gl: gl,
            id: id,
            internal: MergeRequestsListerInternal {
                iid: None,
                state: None,
                order_by: None,
                sort: None,
            },
        }
    }

    // pub fn single(self) -> single::ProjectsLister<'a> {
    //     // assert_eq!(self, ProjectsLister::new(self.gl));
    //     single::ProjectsLister::new(self.gl)
    // }

    pub fn iid(&'a mut self, iid: Vec<i64>) -> &'a mut MergeRequestsLister {
        self.internal.iid = Some(iid);
        self
    }
    pub fn state(&'a mut self, state: State) -> &'a mut MergeRequestsLister {
        self.internal.state = Some(state);
        self
    }
    pub fn order_by(&'a mut self, order_by: ListingOrderBy) -> &'a mut MergeRequestsLister {
        self.internal.order_by = Some(order_by);
        self
    }
    fn sort(&'a mut self, sort: ::ListingSort) -> &'a mut MergeRequestsLister {
        self.internal.sort = Some(sort);
        self
    }

    /// Commit the lister: Query GitLab and return a list of projects.
    pub fn list(&self) -> MergeRequests {
        let query = self.build_query();
        debug!("query: {:?}", query);

        let merge_requests: Result<MergeRequests, serde_json::Error> = self.gl.get(&query);

        merge_requests.unwrap()
    }
}

impl<'a> BuildQuery for MergeRequestsLister<'a> {
    fn build_query(&self) -> String {

        // NOTE: Can't use `serde_urlencoded` since it cannot serialize a Vec<T>
        //       See https://github.com/nox/serde_urlencoded/issues/6
        // let encoded = serde_urlencoded::to_string(&self.internal).unwrap();

        let mut query = format!("projects/{}/merge_requests", self.id);

        let amp_char = "&";
        let none_char = "";
        let mut split_char = &none_char;

        // Append a "?" only if at least one of the `Option` is `Some(_)` or if
        // strings contain something.
        query.push_str(match (&self.internal.iid, &self.internal.state, &self.internal.order_by, &self.internal.sort) {
            (&None, &None, &None, &None) => "",
            _ => "?",
        });

        self.internal.iid.as_ref().map(|iid| {
            query.push_str(split_char);
            split_char = &amp_char;

            if iid.len() == 1 {
                query.push_str("iid=");
                query.push_str(&iid[0].to_string());
            } else {
                let mut array_split_char = &none_char;
                for iid in iid {
                    query.push_str(array_split_char);
                    query.push_str("iid[]=");
                    query.push_str(&iid.to_string());
                    array_split_char = &amp_char;
                }
            }
        });

        self.internal.state.map(|state| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("state=");
            query.push_str(match state {
                State::Merged => "merged",
                State::Opened => "opened",
                State::Closed => "closed",
                State::All => "all",
            });
        });

        self.internal.order_by.map(|order_by| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("order_by=");
            query.push_str(match order_by {
                ListingOrderBy::CreatedAt => "created_at",
                ListingOrderBy::UpdatedAt => "updated_at",
            });
        });

        self.internal.sort.map(|sort| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("sort=");
            query.push_str(match sort {
                ::ListingSort::Asc => "asc",
                ::ListingSort::Desc => "desc",
            });
        });

        query
    }
}

#[cfg(test)]
mod tests {
    use BuildQuery;

    const TEST_PROJECT_ID: i64 = 123;

    #[test]
    fn build_query_default() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX");
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/merge_requests", TEST_PROJECT_ID);
        let lister = gl.merge_requests(TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID).build_query();
        assert_eq!(query, expected_string);

        let expected_string = format!("projects/{}/merge_requests", TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_iid() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX");
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/merge_requests?iid=456", TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID).iid(vec![456]).build_query();
        assert_eq!(query, expected_string);

        let expected_string = format!("projects/123/merge_requests?iid[]=456&iid[]=789");
        let query = gl.merge_requests(TEST_PROJECT_ID).iid(vec![456, 789]).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_state() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX");
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/merge_requests?state=opened", TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID).state(::merge_requests::State::Opened).build_query();
        assert_eq!(query, expected_string);

        let expected_string = format!("projects/{}/merge_requests?state=closed", TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID).state(::merge_requests::State::Closed).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_order_by() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX");
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/merge_requests?order_by=created_at", TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID).order_by(::merge_requests::ListingOrderBy::CreatedAt).build_query();
        assert_eq!(query, expected_string);

        let expected_string = format!("projects/{}/merge_requests?order_by=updated_at", TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID).order_by(::merge_requests::ListingOrderBy::UpdatedAt).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_sort() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX");
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/merge_requests?sort=asc", TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID).sort(::ListingSort::Asc).build_query();
        assert_eq!(query, expected_string);

        let expected_string = format!("projects/{}/merge_requests?sort=desc", TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID).sort(::ListingSort::Desc).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_multiple() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX");
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("projects/{}/merge_requests?iid[]=456&iid[]=789&order_by=created_at&sort=asc", TEST_PROJECT_ID);
        let query = gl.merge_requests(TEST_PROJECT_ID)
            .iid(vec![456, 789])
            .sort(::ListingSort::Asc)
            .order_by(::merge_requests::ListingOrderBy::CreatedAt)
            .build_query();
        assert_eq!(query, expected_string);
    }
}
<|endoftext|>