<commit_before>use state::{Selectable, Editable, Named};
use std::cmp;
use std::io::{stdout, Stdout, BufWriter, Write, Result};
use std::iter;
use termion::terminal_size;
use termion::input::MouseTerminal;
use termion::screen::AlternateScreen;
use termion::raw::{IntoRawMode, RawTerminal};
use termion::{clear, style, cursor, color};

pub struct View {
    stdout: BufWriter<MouseTerminal<AlternateScreen<RawTerminal<Stdout>>>>,
    message: Option<String>,
    is_prompt: bool,
    line_offset: u16,
}

const TAB_LENGTH: usize = 4;

impl View {
    pub fn new() -> Result<View> {
        let stdout =
            BufWriter::new(MouseTerminal::from(AlternateScreen::from(stdout()
                                                                         .into_raw_mode()
                                                                         .unwrap())));
        Ok(View {
               stdout,
               message: None,
               is_prompt: false,
               line_offset: 0,
           })
    }

    pub fn message(&mut self, message: &str) {
        self.is_prompt = false;
        self.message = Some(String::from(message));
    }

    pub fn prompt(&mut self, prompt: &str, message: &str) {
        self.is_prompt = true;
        let msg = String::from(prompt) + message;
        self.message = Some(msg);
    }

    pub fn quiet(&mut self) {
        self.is_prompt = false;
        self.message = None;
    }

    pub fn center_view(&mut self, line: usize) {
        self.line_offset = match line.checked_sub(self.lines_height() as usize / 2) {
            None => 0,
            Some(val) => val as u16,
        }
    }

    pub fn adjust_view(&mut self, line: usize) {
        let line = line as u16;
        if line < self.line_offset {
            self.line_offset = line;
        } else if line + 1 >= self.line_offset + self.lines_height() {
            self.line_offset = 1 + line - self.lines_height();
        }
    }

    pub fn scroll_view<T: Editable>(&mut self, offset: isize, content: &T) {
        self.line_offset = cmp::min(cmp::max((self.line_offset as isize) + offset, 0),
                                    (content.line_count() as isize) - 1) as
                           u16;
    }

    pub fn render<T>(&mut self, content: &T) -> Result<()>
        where T: Editable + Named + Selectable
    {
        write!(self.stdout, "{}", clear::All)?;
        self.paint_lines(content)?;
        self.paint_status(content)?;
        self.paint_message()?;
        self.paint_cursor(content)?;
        self.stdout.flush()?;
        Ok(())
    }

    pub fn translate_coordinates<T>(&self, content: &T, x: u16, y: u16) -> (usize, usize)
        where T: Editable
    {
        let line = cmp::min((y as isize + self.line_offset as isize - 1) as usize,
                            content.line_count() - 1);
        let visual_col = (cmp::max(0,
                                   x as isize -
                                   self.line_number_width(content.line_count()) as isize -
                                   2)) as usize;
        // find out if we clicked through a tab
        let col = content.iter_line(line)
            .scan(0, |state, x| {
                *state += if x == '\t' { TAB_LENGTH } else { 1 };
                Some(*state)
            })
            .take_while(|&x| x <= visual_col)
            .count();
        (line, col)
    }

    fn paint_message(&mut self) -> Result<()> {
        if let Some(ref message) = self.message {
            let y = self.lines_height() + 1;
            write!(self.stdout, "{}{}", cursor::Goto(1, 1 + y as u16), message)?;
            self.stdout.flush()?;
        }
        Ok(())
    }

    fn paint_cursor<T>(&mut self, content: &T) -> Result<()>
        where T: Editable + Selectable
    {
        // FIXME: don't print the cursor if off screen, though we should in the future for long
        // lines
        if (content.line() as u16) < self.line_offset ||
           content.line() as u16 >= self.line_offset + self.lines_height() ||
           content.col() as u16 >= self.lines_width(content.line_count()) ||
           content.sel().is_some() {
            write!(self.stdout, "{}", cursor::Hide)?;
            return Ok(());
        }

        // in the case of a prompt, the cursor should be drawn in the message line
        let (x, y) = if self.is_prompt {
            (self.message
                 .clone()
                 .unwrap()
                 .chars()
                 .count() as u16,
             self.lines_height() + 1)
        } else {
            let (a, b) = self.cursor_pos(content);
            (a as u16, b as u16)
        };
        try!(write!(self.stdout,
                    "{}{}",
                    cursor::Show,
                    cursor::Goto(1 + x, 1 + y)));
        Ok(())
    }

    fn paint_status<T>(&mut self, content: &T) -> Result<()>
        where T: Editable + Named
    {
        let line = content.line();
        let column = content.col();
        let line_count = content.line_count();
        let advance = ((line + 1) as f64 / line_count as f64 * 100.0).floor();

        let (screen_width, _) = terminal_size().unwrap();
        let empty_line = (0..screen_width).map(|_| ' ').collect::<String>();
        let y = self.lines_height() as u16;

        try!(write!(self.stdout,
                    "{}{}{}{}{}{}",
                    color::Fg(color::White),
                    style::Invert,
                    cursor::Goto(1, 1 + y),
                    empty_line,
                    cursor::Goto(1, 1 + y),
                    content.name()));

        let position_info = format!("{}% {}/{}: {}", advance, line + 1, line_count, column);
        let x = screen_width - position_info.len() as u16;
        try!(write!(self.stdout,
                    "{}{}{}",
                    cursor::Goto(1 + x, 1 + y),
                    position_info,
                    style::Reset));
        Ok(())
    }

    fn paint_lines<T>(&mut self, content: &T) -> Result<()>
        where T: Editable + Selectable
    {
        let line_offset = self.line_offset as usize;
        let lines_height = self.lines_height() as usize;
        let lines_width = self.lines_width(content.line_count()) as usize;
        let line_count = content.line_count();

        let line_start = self.line_number_width(line_count) + 1;

        let mut chars = content.line_index_to_char_index(line_offset);
        for y in 0..cmp::min(lines_height, line_count - line_offset) {
            let line = y + line_offset;
            try!(write!(self.stdout,
                        "{}{}{}{}{}",
                        color::Fg(color::White),
                        cursor::Goto(1, 1 + y as u16),
                        1 + line,
                        style::Reset,
                        cursor::Goto(1 + line_start, 1 + y as u16)));

            let mut line_len: usize = 0;
            for c in content.iter_line(line).take_while(|&x| x != '\n') {
                if line_len > lines_width - 1 {
                    // do nothing but count
                    chars += 1;
                    line_len += 1;
                } else {
                    if content.in_sel(chars) {
                        write!(self.stdout, "{}", style::Invert)?;
                    }
                    if c == '\t' {
                        try!(write!(self.stdout,
                                    "{}",
                                    iter::repeat(" ").take(TAB_LENGTH).collect::<String>()));
                        line_len += TAB_LENGTH;
                    } else {
                        write!(self.stdout, "{}", c)?;
                        line_len += 1;
                    }
                    write!(self.stdout, "{}", style::Reset)?;
                    chars += 1;
                }
            }
            chars += 1;
            if line_len <= 1 && content.in_sel(chars) {
                write!(self.stdout, "{} {}", style::Invert, style::Reset)?;
            }
        }
        Ok(())
    }

    fn cursor_pos<T: Editable>(&self, content: &T) -> (usize, usize) {
        // TODO: column offsetting for long lines
        let line = content.line();
        let first_line = self.line_offset;
        let y = line - first_line as usize;
        // we can't trust the actual column because tabs have variable length
        let visual_col = content.col();
        let column = content.iter_line(line)
            .map(|x| if x == '\t' { TAB_LENGTH } else { 1 })
            .take(visual_col)
            .fold(0, |acc, x| acc + x);
        ((self.line_number_width(content.line_count()) as usize + 1 + column), y)
    }

    fn line_number_width(&self, line_count: usize) -> u16 {
        line_count.to_string().len() as u16
    }

    fn status_height(&self) -> u16 {
        2
    }

    pub fn lines_height(&self) -> u16 {
        let (_, screen_height) = terminal_size().unwrap();
        let incompressible = self.status_height();
        cmp::max(screen_height, incompressible) - incompressible
    }

    pub fn lines_width(&self, line_count: usize) -> u16 {
        let (screen_width, _) = terminal_size().unwrap();
        let incompressible = self.line_number_width(line_count) + 1;
        cmp::max(screen_width, incompressible) - incompressible
    }
}
<commit_msg>remove unstable struct shorthand<commit_after>use state::{Selectable, Editable, Named};
use std::cmp;
use std::io::{stdout, Stdout, BufWriter, Write, Result};
use std::iter;
use termion::terminal_size;
use termion::input::MouseTerminal;
use termion::screen::AlternateScreen;
use termion::raw::{IntoRawMode, RawTerminal};
use termion::{clear, style, cursor, color};

pub struct View {
    stdout: BufWriter<MouseTerminal<AlternateScreen<RawTerminal<Stdout>>>>,
    message: Option<String>,
    is_prompt: bool,
    line_offset: u16,
}

const TAB_LENGTH: usize = 4;

impl View {
    pub fn new() -> Result<View> {
        Ok(View {
            stdout: BufWriter::new(MouseTerminal::from(AlternateScreen::from(stdout()
                                                                         .into_raw_mode()
                                                                         .unwrap()))),
               message: None,
               is_prompt: false,
               line_offset: 0,
           })
    }

    pub fn message(&mut self, message: &str) {
        self.is_prompt = false;
        self.message = Some(String::from(message));
    }

    pub fn prompt(&mut self, prompt: &str, message: &str) {
        self.is_prompt = true;
        let msg = String::from(prompt) + message;
        self.message = Some(msg);
    }

    pub fn quiet(&mut self) {
        self.is_prompt = false;
        self.message = None;
    }

    pub fn center_view(&mut self, line: usize) {
        self.line_offset = match line.checked_sub(self.lines_height() as usize / 2) {
            None => 0,
            Some(val) => val as u16,
        }
    }

    pub fn adjust_view(&mut self, line: usize) {
        let line = line as u16;
        if line < self.line_offset {
            self.line_offset = line;
        } else if line + 1 >= self.line_offset + self.lines_height() {
            self.line_offset = 1 + line - self.lines_height();
        }
    }

    pub fn scroll_view<T: Editable>(&mut self, offset: isize, content: &T) {
        self.line_offset = cmp::min(cmp::max((self.line_offset as isize) + offset, 0),
                                    (content.line_count() as isize) - 1) as
                           u16;
    }

    pub fn render<T>(&mut self, content: &T) -> Result<()>
        where T: Editable + Named + Selectable
    {
        write!(self.stdout, "{}", clear::All)?;
        self.paint_lines(content)?;
        self.paint_status(content)?;
        self.paint_message()?;
        self.paint_cursor(content)?;
        self.stdout.flush()?;
        Ok(())
    }

    pub fn translate_coordinates<T>(&self, content: &T, x: u16, y: u16) -> (usize, usize)
        where T: Editable
    {
        let line = cmp::min((y as isize + self.line_offset as isize - 1) as usize,
                            content.line_count() - 1);
        let visual_col = (cmp::max(0,
                                   x as isize -
                                   self.line_number_width(content.line_count()) as isize -
                                   2)) as usize;
        // find out if we clicked through a tab
        let col = content.iter_line(line)
            .scan(0, |state, x| {
                *state += if x == '\t' { TAB_LENGTH } else { 1 };
                Some(*state)
            })
            .take_while(|&x| x <= visual_col)
            .count();
        (line, col)
    }

    fn paint_message(&mut self) -> Result<()> {
        if let Some(ref message) = self.message {
            let y = self.lines_height() + 1;
            write!(self.stdout, "{}{}", cursor::Goto(1, 1 + y as u16), message)?;
            self.stdout.flush()?;
        }
        Ok(())
    }

    fn paint_cursor<T>(&mut self, content: &T) -> Result<()>
        where T: Editable + Selectable
    {
        // FIXME: don't print the cursor if off screen, though we should in the future for long
        // lines
        if (content.line() as u16) < self.line_offset ||
           content.line() as u16 >= self.line_offset + self.lines_height() ||
           content.col() as u16 >= self.lines_width(content.line_count()) ||
           content.sel().is_some() {
            write!(self.stdout, "{}", cursor::Hide)?;
            return Ok(());
        }

        // in the case of a prompt, the cursor should be drawn in the message line
        let (x, y) = if self.is_prompt {
            (self.message
                 .clone()
                 .unwrap()
                 .chars()
                 .count() as u16,
             self.lines_height() + 1)
        } else {
            let (a, b) = self.cursor_pos(content);
            (a as u16, b as u16)
        };
        try!(write!(self.stdout,
                    "{}{}",
                    cursor::Show,
                    cursor::Goto(1 + x, 1 + y)));
        Ok(())
    }

    fn paint_status<T>(&mut self, content: &T) -> Result<()>
        where T: Editable + Named
    {
        let line = content.line();
        let column = content.col();
        let line_count = content.line_count();
        let advance = ((line + 1) as f64 / line_count as f64 * 100.0).floor();

        let (screen_width, _) = terminal_size().unwrap();
        let empty_line = (0..screen_width).map(|_| ' ').collect::<String>();
        let y = self.lines_height() as u16;

        try!(write!(self.stdout,
                    "{}{}{}{}{}{}",
                    color::Fg(color::White),
                    style::Invert,
                    cursor::Goto(1, 1 + y),
                    empty_line,
                    cursor::Goto(1, 1 + y),
                    content.name()));

        let position_info = format!("{}% {}/{}: {}", advance, line + 1, line_count, column);
        let x = screen_width - position_info.len() as u16;
        try!(write!(self.stdout,
                    "{}{}{}",
                    cursor::Goto(1 + x, 1 + y),
                    position_info,
                    style::Reset));
        Ok(())
    }

    fn paint_lines<T>(&mut self, content: &T) -> Result<()>
        where T: Editable + Selectable
    {
        let line_offset = self.line_offset as usize;
        let lines_height = self.lines_height() as usize;
        let lines_width = self.lines_width(content.line_count()) as usize;
        let line_count = content.line_count();

        let line_start = self.line_number_width(line_count) + 1;

        let mut chars = content.line_index_to_char_index(line_offset);
        for y in 0..cmp::min(lines_height, line_count - line_offset) {
            let line = y + line_offset;
            try!(write!(self.stdout,
                        "{}{}{}{}{}",
                        color::Fg(color::White),
                        cursor::Goto(1, 1 + y as u16),
                        1 + line,
                        style::Reset,
                        cursor::Goto(1 + line_start, 1 + y as u16)));

            let mut line_len: usize = 0;
            for c in content.iter_line(line).take_while(|&x| x != '\n') {
                if line_len > lines_width - 1 {
                    // do nothing but count
                    chars += 1;
                    line_len += 1;
                } else {
                    if content.in_sel(chars) {
                        write!(self.stdout, "{}", style::Invert)?;
                    }
                    if c == '\t' {
                        try!(write!(self.stdout,
                                    "{}",
                                    iter::repeat(" ").take(TAB_LENGTH).collect::<String>()));
                        line_len += TAB_LENGTH;
                    } else {
                        write!(self.stdout, "{}", c)?;
                        line_len += 1;
                    }
                    write!(self.stdout, "{}", style::Reset)?;
                    chars += 1;
                }
            }
            chars += 1;
            if line_len <= 1 && content.in_sel(chars) {
                write!(self.stdout, "{} {}", style::Invert, style::Reset)?;
            }
        }
        Ok(())
    }

    fn cursor_pos<T: Editable>(&self, content: &T) -> (usize, usize) {
        // TODO: column offsetting for long lines
        let line = content.line();
        let first_line = self.line_offset;
        let y = line - first_line as usize;
        // we can't trust the actual column because tabs have variable length
        let visual_col = content.col();
        let column = content.iter_line(line)
            .map(|x| if x == '\t' { TAB_LENGTH } else { 1 })
            .take(visual_col)
            .fold(0, |acc, x| acc + x);
        ((self.line_number_width(content.line_count()) as usize + 1 + column), y)
    }

    fn line_number_width(&self, line_count: usize) -> u16 {
        line_count.to_string().len() as u16
    }

    fn status_height(&self) -> u16 {
        2
    }

    pub fn lines_height(&self) -> u16 {
        let (_, screen_height) = terminal_size().unwrap();
        let incompressible = self.status_height();
        cmp::max(screen_height, incompressible) - incompressible
    }

    pub fn lines_width(&self, line_count: usize) -> u16 {
        let (screen_width, _) = terminal_size().unwrap();
        let incompressible = self.line_number_width(line_count) + 1;
        cmp::max(screen_width, incompressible) - incompressible
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

mod board;
mod sys;

use imgui::{self, ImVec2, Ui};
use imgui_sys;

use model::{Color, FieldCoord, Model};
use self::board::board;
pub use self::sys::run;

pub enum Event {
    Click(FieldCoord),
}

pub fn draw(ui: &Ui, size: (f32, f32), model: &Model) -> Option<Event> {
    unsafe {
        imgui_sys::igPushStyleVar(imgui_sys::ImGuiStyleVar::WindowRounding, 0.0);
    }

    let mut event = None;
    ui.window(im_str!("Coerceo"))
        .size(size, imgui::ImGuiSetCond_Always)
        .position((0.0, 0.0), imgui::ImGuiSetCond_Once)
        .title_bar(false)
        .resizable(false)
        .movable(false)
        .build(|| {
            ui.text(im_str!("Welcome to Coerceo!"));

            event = board(model, &ImVec2::new(600.0, 600.0));

            match model.turn {
                Color::White => ui.text(im_str!("It's white's turn.")),
                Color::Black => ui.text(im_str!("It's black's turn.")),
            }

            ui.text(im_str!("White has {} piece(s) left and {} captured hex(es).", model.white_pieces, model.white_hexes));
            ui.text(im_str!("Black has {} piece(s) left and {} captured hex(es).", model.black_pieces, model.black_hexes));
        });

    unsafe {
        imgui_sys::igPopStyleVar(1);
    }

    event
}
<commit_msg>Add win message<commit_after>/*
 * Copyright (C) 2017 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

mod board;
mod sys;

use imgui::{self, ImVec2, Ui};
use imgui_sys;

use model::{Color, FieldCoord, Model};
use self::board::board;
pub use self::sys::run;

pub enum Event {
    Click(FieldCoord),
}

pub fn draw(ui: &Ui, size: (f32, f32), model: &Model) -> Option<Event> {
    unsafe {
        imgui_sys::igPushStyleVar(imgui_sys::ImGuiStyleVar::WindowRounding, 0.0);
    }

    let mut event = None;
    ui.window(im_str!("Coerceo"))
        .size(size, imgui::ImGuiSetCond_Always)
        .position((0.0, 0.0), imgui::ImGuiSetCond_Once)
        .title_bar(false)
        .resizable(false)
        .movable(false)
        .build(|| {
            ui.text(im_str!("Welcome to Coerceo!"));

            event = board(model, &ImVec2::new(600.0, 600.0));

            if model.white_pieces == 0 {
                ui.text(im_str!("Black wins!"));
            } else if model.black_pieces == 0 {
                ui.text(im_str!("White wins!"));
            } else {
                match model.turn {
                    Color::White => ui.text(im_str!("It's white's turn.")),
                    Color::Black => ui.text(im_str!("It's black's turn.")),
                }

                ui.text(im_str!(
                    "White has {} piece(s) left and {} captured hex(es).",
                    model.white_pieces,
                    model.white_hexes
                ));
                ui.text(im_str!(
                    "Black has {} piece(s) left and {} captured hex(es).",
                    model.black_pieces,
                    model.black_hexes
                ));
            }
        });

    unsafe {
        imgui_sys::igPopStyleVar(1);
    }

    event
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

mod board;
mod board_parts;
mod sys;
mod vec2;

use imgui::{ImGuiCond, ImStr, StyleVar, Ui};

use self::board::board;
pub use self::sys::run;
use self::vec2::Vec2;
use model::{Color, ColorMap, GameType, Model, Player};
use update::Event;

pub fn draw(ui: &Ui, size: (f32, f32), model: &Model) -> Option<Event> {
    let mut event = None;
    let mut window_states = model.window_states.borrow_mut();

    ui.main_menu_bar(|| {
        ui.menu(im_str!("Game")).build(|| {
            ui.menu_item(im_str!("New game")).enabled(false).build();

            ui.menu(im_str!("Laurentius")).build(|| {
                player_options(ui, &mut event, GameType::Laurentius);
            });
            ui.menu(im_str!("Ocius")).build(|| {
                player_options(ui, &mut event, GameType::Ocius);
            });

            ui.separator();

            if ui.menu_item(im_str!("Quit")).build() {
                insert_if_empty(&mut event, Event::Quit);
            }
        });

        ui.menu(im_str!("Computer")).build(|| {
            ui.slider_int(
                im_str!("Search depth"),
                &mut model.ai_search_depth.borrow_mut(),
                1,
                7,
            ).build();
            if ui.is_item_hovered() {
                ui.tooltip_text(
                    "How many moves ahead the computer will search.\nFewer moves is \
                     faster and easier, while more moves is slower and more difficult.",
                );
            }

            ui.menu_item(im_str!("Show debug info"))
                .selected(&mut window_states.ai_debug)
                .build();
        });

        ui.menu(im_str!("Help")).build(|| {
            ui.menu_item(im_str!("How to Play"))
                .selected(&mut window_states.how_to_play)
                .build();
            ui.menu_item(im_str!("About"))
                .selected(&mut window_states.about)
                .build();
        });
    });

    ui.with_style_var(StyleVar::WindowRounding(0.0), || {
        draw_window(ui, size, model, &mut event);
    });

    if window_states.ai_debug {
        ui.window(im_str!("AI Debug Info"))
            .opened(&mut window_states.ai_debug)
            .size((300.0, 600.0), ImGuiCond::FirstUseEver)
            .build(|| {
                if let Ok(debug_info) = model.ai.debug_info.read() {
                    ui.text(debug_info.clone());
                }
            });
    }

    if window_states.how_to_play {
        // TODO: Create an interactive, in-game tutorial to teach the rules of the game
        ui.window(im_str!("How to Play"))
            .opened(&mut window_states.how_to_play)
            .build(|| {
                ui.text(
                    "Unfortunately, there isn't an in-game tutorial. Sorry!\nSee coerceo.com for \
                     the rules of the game.",
                );
            });
    }

    if window_states.about {
        ui.window(im_str!("About"))
            .opened(&mut window_states.about)
            .build(|| {
                ui.text(
                    "Coerceo v1.0.0\n\nAn unofficial clone of a strategic board game.\n\n\
                     Copyright (C) 2017-2018 Ryan Huang\n\nhttps://github.com/NPN/coerceo",
                );
            });
    }

    event
}

fn player_options(ui: &Ui, event: &mut Option<Event>, game_type: GameType) {
    use self::Player::*;
    if ui.menu_item(im_str!("Human vs. Human")).build() {
        insert_if_empty(
            event,
            Event::NewGame(game_type, ColorMap::new(Human, Human)),
        );
    }
    if ui.menu_item(im_str!("Human vs. Computer")).build() {
        insert_if_empty(
            event,
            Event::NewGame(game_type, ColorMap::new(Human, Computer)),
        );
    }
    if ui.menu_item(im_str!("Computer vs. Human")).build() {
        insert_if_empty(
            event,
            Event::NewGame(game_type, ColorMap::new(Computer, Human)),
        );
    }
    if ui.menu_item(im_str!("Computer vs. Computer")).build() {
        insert_if_empty(
            event,
            Event::NewGame(game_type, ColorMap::new(Computer, Computer)),
        );
    }
}

fn draw_window(ui: &Ui, size: (f32, f32), model: &Model, event: &mut Option<Event>) {
    ui.window(im_str!("Coerceo"))
        .size(size, ImGuiCond::Always)
        .position((0.0, 27.0), ImGuiCond::Always)
        .title_bar(false)
        .resizable(false)
        .movable(false)
        .no_bring_to_front_on_focus(true)
        .build(|| {
            ui.text("Welcome to Coerceo!");
            ui.text(format!(
                "{:?} vs. {:?}",
                model.players.white, model.players.black
            ));

            if let Some(click) = board(ui, model, Vec2::new(size.0 - 16.0, size.1 - 232.0)) {
                insert_if_empty(event, click);
            }

            let format_piece_count = |count| match count {
                1 => String::from("1 piece"),
                _ => format!("{} pieces", count),
            };

            let format_hex_count = |count| match count {
                1 => String::from("1 captured tile"),
                _ => format!("{} captured tiles", count),
            };

            let display_vitals = || {
                ui.text(format!(
                    "{:?} has {} and {}.",
                    Color::White,
                    format_piece_count(model.board.pieces(Color::White)),
                    format_hex_count(model.board.hexes(Color::White)),
                ));
                ui.text(format!(
                    "{:?} has {} and {}.",
                    Color::Black,
                    format_piece_count(model.board.pieces(Color::Black)),
                    format_hex_count(model.board.hexes(Color::Black)),
                ));
            };

            let button_size = Vec2::new(155.0, 29.0);
            use model::Outcome::*;
            match model.outcome {
                Win(color) => {
                    ui.text(format!("{:?} wins!", color));
                    display_vitals();
                    if model.can_undo() && ui.button(im_str!("Undo"), button_size) {
                        insert_if_empty(event, Event::Undo);
                    }
                }
                InProgress => {
                    if model.players.white == model.players.black {
                        ui.text(format!("It's {:?}'s turn.", model.board.turn,));
                    } else {
                        ui.text(match model.current_player() {
                            Player::Computer => "Waiting for the computer...",
                            Player::Human => "It's your turn.",
                        });
                    }

                    display_vitals();

                    horz_button_layout(
                        ui,
                        vec![
                            (model.can_undo(), im_str!("Undo"), Event::Undo),
                            (model.can_redo(), im_str!("Redo"), Event::Redo),
                        ],
                        button_size,
                        event,
                    );
                    let is_human_player = model.current_player() == Player::Human;
                    horz_button_layout(
                        ui,
                        vec![
                            (is_human_player, im_str!("Resign"), Event::Resign),
                            (
                                model.board.can_exchange() && is_human_player,
                                if model.exchanging {
                                    im_str!("Stop Exchanging")
                                } else {
                                    im_str!("Exchange")
                                },
                                Event::Exchange,
                            ),
                        ],
                        button_size,
                        event,
                    );
                }
                // Draw cases
                _ => {
                    let message = match model.outcome {
                        DrawStalemate => "It's a draw by stalemate!",
                        DrawThreefoldRepetition => "It's a draw by threefold repetition!",
                        DrawInsufficientMaterial => "It's a draw by insufficient material!",
                        _ => unreachable!(),
                    };
                    ui.text(message);
                    display_vitals();
                    if model.can_undo() && ui.button(im_str!("Undo"), button_size) {
                        insert_if_empty(event, Event::Undo);
                    }
                }
            }
        });
}

fn horz_button_layout(
    ui: &Ui,
    buttons: Vec<(bool, &ImStr, Event)>,
    size: Vec2,
    event: &mut Option<Event>,
) {
    if !buttons.iter().any(|&(show, _, _)| show) {
        return;
    }

    for (show, label, action) in buttons {
        if show {
            if ui.button(label, size) {
                insert_if_empty(event, action);
            }
        } else {
            ui.dummy(size);
        }
        ui.same_line(0.0);
    }
    ui.new_line();
}

fn insert_if_empty<T>(a: &mut Option<T>, b: T) {
    if a.is_none() {
        *a = Some(b);
    }
}
<commit_msg>Improve copyright notice in About box<commit_after>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

mod board;
mod board_parts;
mod sys;
mod vec2;

use imgui::{ImGuiCond, ImStr, StyleVar, Ui};

use self::board::board;
pub use self::sys::run;
use self::vec2::Vec2;
use model::{Color, ColorMap, GameType, Model, Player};
use update::Event;

pub fn draw(ui: &Ui, size: (f32, f32), model: &Model) -> Option<Event> {
    let mut event = None;
    let mut window_states = model.window_states.borrow_mut();

    ui.main_menu_bar(|| {
        ui.menu(im_str!("Game")).build(|| {
            ui.menu_item(im_str!("New game")).enabled(false).build();

            ui.menu(im_str!("Laurentius")).build(|| {
                player_options(ui, &mut event, GameType::Laurentius);
            });
            ui.menu(im_str!("Ocius")).build(|| {
                player_options(ui, &mut event, GameType::Ocius);
            });

            ui.separator();

            if ui.menu_item(im_str!("Quit")).build() {
                insert_if_empty(&mut event, Event::Quit);
            }
        });

        ui.menu(im_str!("Computer")).build(|| {
            ui.slider_int(
                im_str!("Search depth"),
                &mut model.ai_search_depth.borrow_mut(),
                1,
                7,
            ).build();
            if ui.is_item_hovered() {
                ui.tooltip_text(
                    "How many moves ahead the computer will search.\nFewer moves is \
                     faster and easier, while more moves is slower and more difficult.",
                );
            }

            ui.menu_item(im_str!("Show debug info"))
                .selected(&mut window_states.ai_debug)
                .build();
        });

        ui.menu(im_str!("Help")).build(|| {
            ui.menu_item(im_str!("How to Play"))
                .selected(&mut window_states.how_to_play)
                .build();
            ui.menu_item(im_str!("About"))
                .selected(&mut window_states.about)
                .build();
        });
    });

    ui.with_style_var(StyleVar::WindowRounding(0.0), || {
        draw_window(ui, size, model, &mut event);
    });

    if window_states.ai_debug {
        ui.window(im_str!("AI Debug Info"))
            .opened(&mut window_states.ai_debug)
            .size((300.0, 600.0), ImGuiCond::FirstUseEver)
            .build(|| {
                if let Ok(debug_info) = model.ai.debug_info.read() {
                    ui.text(debug_info.clone());
                }
            });
    }

    if window_states.how_to_play {
        // TODO: Create an interactive, in-game tutorial to teach the rules of the game
        ui.window(im_str!("How to Play"))
            .opened(&mut window_states.how_to_play)
            .build(|| {
                ui.text(
                    "Unfortunately, there isn't an in-game tutorial. Sorry!\nSee coerceo.com for \
                     the rules of the game.",
                );
            });
    }

    if window_states.about {
        ui.window(im_str!("About"))
            .opened(&mut window_states.about)
            .build(|| {
                ui.text(
                    "Coerceo v1.0.0 (https://github.com/NPN/coerceo)

An unofficial clone of a strategic board game.

Copyright (C) 2017-2018 Ryan Huang
This program comes with absolutely no warranty.
See the GNU AGPL, version 3 or later, for details.

This program includes work from the following sources:

imgui-rs (https://github.com/Gekkio/imgui-rs)
Copyright (c) 2015-2017 The imgui-rs Developers
Licensed under the MIT License.

Fira Sans (https://github.com/mozilla/Fira)
Copyright (c) 2012-2015 The Mozilla Foundation and Telefonica S.A.
Licensed under the SIL Open Font License v1.1",
                );
            });
    }

    event
}

fn player_options(ui: &Ui, event: &mut Option<Event>, game_type: GameType) {
    use self::Player::*;
    if ui.menu_item(im_str!("Human vs. Human")).build() {
        insert_if_empty(
            event,
            Event::NewGame(game_type, ColorMap::new(Human, Human)),
        );
    }
    if ui.menu_item(im_str!("Human vs. Computer")).build() {
        insert_if_empty(
            event,
            Event::NewGame(game_type, ColorMap::new(Human, Computer)),
        );
    }
    if ui.menu_item(im_str!("Computer vs. Human")).build() {
        insert_if_empty(
            event,
            Event::NewGame(game_type, ColorMap::new(Computer, Human)),
        );
    }
    if ui.menu_item(im_str!("Computer vs. Computer")).build() {
        insert_if_empty(
            event,
            Event::NewGame(game_type, ColorMap::new(Computer, Computer)),
        );
    }
}

fn draw_window(ui: &Ui, size: (f32, f32), model: &Model, event: &mut Option<Event>) {
    ui.window(im_str!("Coerceo"))
        .size(size, ImGuiCond::Always)
        .position((0.0, 27.0), ImGuiCond::Always)
        .title_bar(false)
        .resizable(false)
        .movable(false)
        .no_bring_to_front_on_focus(true)
        .build(|| {
            ui.text("Welcome to Coerceo!");
            ui.text(format!(
                "{:?} vs. {:?}",
                model.players.white, model.players.black
            ));

            if let Some(click) = board(ui, model, Vec2::new(size.0 - 16.0, size.1 - 232.0)) {
                insert_if_empty(event, click);
            }

            let format_piece_count = |count| match count {
                1 => String::from("1 piece"),
                _ => format!("{} pieces", count),
            };

            let format_hex_count = |count| match count {
                1 => String::from("1 captured tile"),
                _ => format!("{} captured tiles", count),
            };

            let display_vitals = || {
                ui.text(format!(
                    "{:?} has {} and {}.",
                    Color::White,
                    format_piece_count(model.board.pieces(Color::White)),
                    format_hex_count(model.board.hexes(Color::White)),
                ));
                ui.text(format!(
                    "{:?} has {} and {}.",
                    Color::Black,
                    format_piece_count(model.board.pieces(Color::Black)),
                    format_hex_count(model.board.hexes(Color::Black)),
                ));
            };

            let button_size = Vec2::new(155.0, 29.0);
            use model::Outcome::*;
            match model.outcome {
                Win(color) => {
                    ui.text(format!("{:?} wins!", color));
                    display_vitals();
                    if model.can_undo() && ui.button(im_str!("Undo"), button_size) {
                        insert_if_empty(event, Event::Undo);
                    }
                }
                InProgress => {
                    if model.players.white == model.players.black {
                        ui.text(format!("It's {:?}'s turn.", model.board.turn,));
                    } else {
                        ui.text(match model.current_player() {
                            Player::Computer => "Waiting for the computer...",
                            Player::Human => "It's your turn.",
                        });
                    }

                    display_vitals();

                    horz_button_layout(
                        ui,
                        vec![
                            (model.can_undo(), im_str!("Undo"), Event::Undo),
                            (model.can_redo(), im_str!("Redo"), Event::Redo),
                        ],
                        button_size,
                        event,
                    );
                    let is_human_player = model.current_player() == Player::Human;
                    horz_button_layout(
                        ui,
                        vec![
                            (is_human_player, im_str!("Resign"), Event::Resign),
                            (
                                model.board.can_exchange() && is_human_player,
                                if model.exchanging {
                                    im_str!("Stop Exchanging")
                                } else {
                                    im_str!("Exchange")
                                },
                                Event::Exchange,
                            ),
                        ],
                        button_size,
                        event,
                    );
                }
                // Draw cases
                _ => {
                    let message = match model.outcome {
                        DrawStalemate => "It's a draw by stalemate!",
                        DrawThreefoldRepetition => "It's a draw by threefold repetition!",
                        DrawInsufficientMaterial => "It's a draw by insufficient material!",
                        _ => unreachable!(),
                    };
                    ui.text(message);
                    display_vitals();
                    if model.can_undo() && ui.button(im_str!("Undo"), button_size) {
                        insert_if_empty(event, Event::Undo);
                    }
                }
            }
        });
}

fn horz_button_layout(
    ui: &Ui,
    buttons: Vec<(bool, &ImStr, Event)>,
    size: Vec2,
    event: &mut Option<Event>,
) {
    if !buttons.iter().any(|&(show, _, _)| show) {
        return;
    }

    for (show, label, action) in buttons {
        if show {
            if ui.button(label, size) {
                insert_if_empty(event, action);
            }
        } else {
            ui.dummy(size);
        }
        ui.same_line(0.0);
    }
    ui.new_line();
}

fn insert_if_empty<T>(a: &mut Option<T>, b: T) {
    if a.is_none() {
        *a = Some(b);
    }
}
<|endoftext|>


<commit_before>use std::cmp;

#[derive(Clone)]
/// Create an iterator running multiple iterators in lockstep.
///
/// The iterator **Zip\<(I, J, ..., M)\>** is formed from a tuple of iterators and yields elements
/// until any of the subiterators yields **None**.
///
/// Iterator element type is like **(A, B, ..., E)** where **A** to **E** are the respective
/// subiterator types.
///
/// ## Example
///
/// ```
/// use itertools::Zip;
///
/// // Iterate over three sequences side-by-side
/// let mut xs = [0, 0, 0];
/// let ys = [69, 107, 101];
///
/// for (i, a, b) in Zip::new((0i32..100, xs.iter_mut(), ys.iter())) {
///    *a = i ^ *b;
/// }
///
/// assert_eq!(xs, [69, 106, 103]);
/// ```
pub struct Zip<T> {
    t: T
}

impl<T> Zip<T> where Zip<T>: Iterator
{
    /// Create a new **Zip** from a tuple of iterators.
    pub fn new(t: T) -> Zip<T>
    {
        Zip{t: t}
    }
}

macro_rules! impl_zip_iter {
    ($($B:ident),*) => (
        #[allow(non_snake_case)]
        impl<$($B),*> Iterator for Zip<($($B,)*)>
            where
            $(
                $B: Iterator,
            )*
        {
            type Item = ($($B::Item,)*);

            fn next(&mut self) -> Option<
                    ($($B::Item,)*)
                >
            {
                let &mut Zip { t : ($(ref mut $B,)*)} = self;
                // WARNING: partial consume possible
                // Zip worked the same.
                $(
                    let $B = match $B.next() {
                        None => return None,
                        Some(elt) => elt
                    };
                )*
                Some(($($B,)*))
            }

            fn size_hint(&self) -> (usize, Option<usize>)
            {
                let low = ::std::usize::MAX;
                let high = None;
                let &Zip { t : ($(ref $B,)*) } = self;
                $(
                    // update estimate
                    let (l, h) = $B.size_hint();
                    let low = cmp::min(low, l);
                    let high = match (high, h) {
                        (Some(u1), Some(u2)) => Some(cmp::min(u1, u2)),
                        _ => high.or(h)
                    };
                )*
                (low, high)
            }
        }
    );
}

impl_zip_iter!(A);
impl_zip_iter!(A, B);
impl_zip_iter!(A, B, C);
impl_zip_iter!(A, B, C, D);
impl_zip_iter!(A, B, C, D, E);
impl_zip_iter!(A, B, C, D, E, F);
impl_zip_iter!(A, B, C, D, E, F, G);
impl_zip_iter!(A, B, C, D, E, F, G, H);
impl_zip_iter!(A, B, C, D, E, F, G, H, I);

<commit_msg>ziptuple: Improve code style<commit_after>use std::cmp;

#[derive(Clone)]
/// Create an iterator running multiple iterators in lockstep.
///
/// The iterator **Zip\<(I, J, ..., M)\>** is formed from a tuple of iterators and yields elements
/// until any of the subiterators yields **None**.
///
/// Iterator element type is like **(A, B, ..., E)** where **A** to **E** are the respective
/// subiterator types.
///
/// ## Example
///
/// ```
/// use itertools::Zip;
///
/// // Iterate over three sequences side-by-side
/// let mut xs = [0, 0, 0];
/// let ys = [69, 107, 101];
///
/// for (i, a, b) in Zip::new((0i32..100, xs.iter_mut(), ys.iter())) {
///    *a = i ^ *b;
/// }
///
/// assert_eq!(xs, [69, 106, 103]);
/// ```
pub struct Zip<T> {
    t: T
}

impl<T> Zip<T> where Zip<T>: Iterator
{
    /// Create a new **Zip** from a tuple of iterators.
    pub fn new(t: T) -> Zip<T>
    {
        Zip{t: t}
    }
}

macro_rules! impl_zip_iter {
    ($($B:ident),*) => (
        #[allow(non_snake_case)]
        impl<$($B),*> Iterator for Zip<($($B,)*)>
            where
            $(
                $B: Iterator,
            )*
        {
            type Item = ($($B::Item,)*);

            fn next(&mut self) -> Option<<Self as Iterator>::Item>
            {
                let ($(ref mut $B,)*) = self.t;

                // NOTE: Just like iter::Zip, we check the iterators
                // for None in order. We may finish unevenly (some
                // iterators gave n + 1 elements, some only n).
                $(
                    let $B = match $B.next() {
                        None => return None,
                        Some(elt) => elt
                    };
                )*
                Some(($($B,)*))
            }

            fn size_hint(&self) -> (usize, Option<usize>)
            {
                let low = ::std::usize::MAX;
                let high = None;
                let ($(ref $B,)*) = self.t;
                $(
                    // update estimate
                    let (l, h) = $B.size_hint();
                    let low = cmp::min(low, l);
                    let high = match (high, h) {
                        (Some(u1), Some(u2)) => Some(cmp::min(u1, u2)),
                        _ => high.or(h)
                    };
                )*
                (low, high)
            }
        }
    );
}

impl_zip_iter!(A);
impl_zip_iter!(A, B);
impl_zip_iter!(A, B, C);
impl_zip_iter!(A, B, C, D);
impl_zip_iter!(A, B, C, D, E);
impl_zip_iter!(A, B, C, D, E, F);
impl_zip_iter!(A, B, C, D, E, F, G);
impl_zip_iter!(A, B, C, D, E, F, G, H);
impl_zip_iter!(A, B, C, D, E, F, G, H, I);

<|endoftext|>


<commit_before>
use {
    AddColor,
    Borrowed,
    Color,
    Field,
    Matrix2d,
    TweenPolygonsColorContext,
    Value,
};
use internal::{
    CanTransform,
    CanViewTransform,
    HasTransform,
    HasViewTransform,
};

/// An animation inbetweening context with color.
pub struct TweenPolygonsContext<'a, 'b> {
    /// Base/origin transform.
    pub base: Field<'a, Matrix2d>,
    /// Current transform.
    pub transform: Field<'a, Matrix2d>,
    /// Animation inbetweening factor.
    pub tween_factor: Field<'a, f64>,
    /// The animated polygons.
    pub polygons: Field<'a, &'b [&'b [f64]]>,
}

impl<'a, 'b> Clone for TweenPolygonsContext<'a, 'b> {
    #[inline(always)]
    fn clone(&self) -> TweenPolygonsContext<'static, 'b> {
        TweenPolygonsContext {
            base: self.base.clone(),
            transform: self.transform.clone(),
            tween_factor: self.tween_factor.clone(),
            polygons: self.polygons.clone(),
        }
    }
}

impl<'a, 'b> AddColor<'a, TweenPolygonsColorContext<'a, 'b>> for TweenPolygonsContext<'a, 'b> {
    /// Creates a RectangleColorContext.
    #[inline(always)]
    fn rgba(&'a self, r: f32, g: f32, b: f32, a: f32) -> TweenPolygonsColorContext<'a, 'b> {
        TweenPolygonsColorContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            color: Value(Color([r, g, b, a])),
            tween_factor: Borrowed(self.tween_factor.get()),
            polygons: Borrowed(self.polygons.get()),
        }
    }
}

impl<'a, 'b> HasTransform<'a, Matrix2d> for TweenPolygonsContext<'a, 'b> {
    #[inline(alwyas)]
    fn get_transform(&'a self) -> &'a Matrix2d {
        self.transform.get()
    }
}

impl<'a, 'b> CanTransform<'a, TweenPolygonsContext<'a, 'b>, Matrix2d> for TweenPolygonsContext<'a, 'b> {
    #[inline(always)]
    fn transform(&'a self, value: Matrix2d) -> TweenPolygonsContext<'a, 'b> {
        TweenPolygonsContext {
            base: Borrowed(self.base.get()),
            transform: Value(value),
            tween_factor: Borrowed(self.tween_factor.get()),
            polygons: Borrowed(self.polygons.get()),
        }
    }
}

impl<'a, 'b> HasViewTransform<'a, Matrix2d> for TweenPolygonsContext<'a, 'b> {
    #[inline(always)]
    fn get_view_transform(&'a self) -> &'a Matrix2d {
        self.base.get()
    }
}

impl<'a, 'b> CanViewTransform<'a, TweenPolygonsContext<'a, 'b>, Matrix2d> for TweenPolygonsContext<'a, 'b> {
    #[inline(always)]
    fn view_transform(&'a self, value: Matrix2d) -> TweenPolygonsContext<'a, 'b> {
        TweenPolygonsContext {
            base: Value(value),
            transform: Borrowed(self.transform.get()),
            tween_factor: Borrowed(self.tween_factor.get()),
            polygons: Borrowed(self.polygons.get()),
        }
    }
}

<commit_msg>Using type alias for `Polygons` in `TweenPolygonsContext`<commit_after>
use {
    AddColor,
    Borrowed,
    Color,
    Field,
    Matrix2d,
    Polygons,
    TweenPolygonsColorContext,
    Value,
};
use internal::{
    CanTransform,
    CanViewTransform,
    HasTransform,
    HasViewTransform,
};

/// An animation inbetweening context with color.
pub struct TweenPolygonsContext<'a, 'b> {
    /// Base/origin transform.
    pub base: Field<'a, Matrix2d>,
    /// Current transform.
    pub transform: Field<'a, Matrix2d>,
    /// Animation inbetweening factor.
    pub tween_factor: Field<'a, f64>,
    /// The animated polygons.
    pub polygons: Field<'a, Polygons<'b>>,
}

impl<'a, 'b> Clone for TweenPolygonsContext<'a, 'b> {
    #[inline(always)]
    fn clone(&self) -> TweenPolygonsContext<'static, 'b> {
        TweenPolygonsContext {
            base: self.base.clone(),
            transform: self.transform.clone(),
            tween_factor: self.tween_factor.clone(),
            polygons: self.polygons.clone(),
        }
    }
}

impl<'a, 'b> AddColor<'a, TweenPolygonsColorContext<'a, 'b>> for TweenPolygonsContext<'a, 'b> {
    /// Creates a RectangleColorContext.
    #[inline(always)]
    fn rgba(&'a self, r: f32, g: f32, b: f32, a: f32) -> TweenPolygonsColorContext<'a, 'b> {
        TweenPolygonsColorContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            color: Value(Color([r, g, b, a])),
            tween_factor: Borrowed(self.tween_factor.get()),
            polygons: Borrowed(self.polygons.get()),
        }
    }
}

impl<'a, 'b> HasTransform<'a, Matrix2d> for TweenPolygonsContext<'a, 'b> {
    #[inline(alwyas)]
    fn get_transform(&'a self) -> &'a Matrix2d {
        self.transform.get()
    }
}

impl<'a, 'b> CanTransform<'a, TweenPolygonsContext<'a, 'b>, Matrix2d> for TweenPolygonsContext<'a, 'b> {
    #[inline(always)]
    fn transform(&'a self, value: Matrix2d) -> TweenPolygonsContext<'a, 'b> {
        TweenPolygonsContext {
            base: Borrowed(self.base.get()),
            transform: Value(value),
            tween_factor: Borrowed(self.tween_factor.get()),
            polygons: Borrowed(self.polygons.get()),
        }
    }
}

impl<'a, 'b> HasViewTransform<'a, Matrix2d> for TweenPolygonsContext<'a, 'b> {
    #[inline(always)]
    fn get_view_transform(&'a self) -> &'a Matrix2d {
        self.base.get()
    }
}

impl<'a, 'b> CanViewTransform<'a, TweenPolygonsContext<'a, 'b>, Matrix2d> for TweenPolygonsContext<'a, 'b> {
    #[inline(always)]
    fn view_transform(&'a self, value: Matrix2d) -> TweenPolygonsContext<'a, 'b> {
        TweenPolygonsContext {
            base: Value(value),
            transform: Borrowed(self.transform.get()),
            tween_factor: Borrowed(self.tween_factor.get()),
            polygons: Borrowed(self.polygons.get()),
        }
    }
}

<|endoftext|>


<commit_before>use half::f16;
use std::io;

use crate::byteorder_io::ByteOrder;
use crate::multifilereader::HasError;
use crate::peekreader::PeekRead;

/// Processes an input and provides access to the data read in various formats
///
/// Currently only useful if the input implements `PeekRead`.
pub struct InputDecoder<'a, I>
where
    I: 'a,
{
    /// The input from which data is read
    input: &'a mut I,

    /// A memory buffer, it's size is set in `new`.
    data: Vec<u8>,
    /// The number of bytes in the buffer reserved for the peek data from `PeekRead`.
    reserved_peek_length: usize,

    /// The number of (valid) bytes in the buffer.
    used_normal_length: usize,
    /// The number of peek bytes in the buffer.
    used_peek_length: usize,

    /// Byte order used to read data from the buffer.
    byte_order: ByteOrder,
}

impl<'a, I> InputDecoder<'a, I> {
    /// Creates a new `InputDecoder` with an allocated buffer of `normal_length` + `peek_length` bytes.
    /// `byte_order` determines how to read multibyte formats from the buffer.
    pub fn new(
        input: &mut I,
        normal_length: usize,
        peek_length: usize,
        byte_order: ByteOrder,
    ) -> InputDecoder<I> {
        let mut bytes: Vec<u8> = Vec::with_capacity(normal_length + peek_length);
        unsafe {
            bytes.set_len(normal_length + peek_length);
        } // fast but uninitialized

        InputDecoder {
            input,
            data: bytes,
            reserved_peek_length: peek_length,
            used_normal_length: 0,
            used_peek_length: 0,
            byte_order,
        }
    }
}

impl<'a, I> InputDecoder<'a, I>
where
    I: PeekRead,
{
    /// calls `peek_read` on the internal stream to (re)fill the buffer. Returns a
    /// MemoryDecoder providing access to the result or returns an i/o error.
    pub fn peek_read(&mut self) -> io::Result<MemoryDecoder> {
        match self
            .input
            .peek_read(self.data.as_mut_slice(), self.reserved_peek_length)
        {
            Ok((n, p)) => {
                self.used_normal_length = n;
                self.used_peek_length = p;
                Ok(MemoryDecoder {
                    data: &mut self.data,
                    used_normal_length: self.used_normal_length,
                    used_peek_length: self.used_peek_length,
                    byte_order: self.byte_order,
                })
            }
            Err(e) => Err(e),
        }
    }
}

impl<'a, I> HasError for InputDecoder<'a, I>
where
    I: HasError,
{
    /// calls has_error on the internal stream.
    fn has_error(&self) -> bool {
        self.input.has_error()
    }
}

/// Provides access to the internal data in various formats
pub struct MemoryDecoder<'a> {
    /// A reference to the parents' data
    data: &'a mut Vec<u8>,
    /// The number of (valid) bytes in the buffer.
    used_normal_length: usize,
    /// The number of peek bytes in the buffer.
    used_peek_length: usize,
    /// Byte order used to read data from the buffer.
    byte_order: ByteOrder,
}

impl<'a> MemoryDecoder<'a> {
    /// Set a part of the internal buffer to zero.
    /// access to the whole buffer is possible, not just to the valid data.
    pub fn zero_out_buffer(&mut self, start: usize, end: usize) {
        for i in start..end {
            self.data[i] = 0;
        }
    }

    /// Returns the current length of the buffer. (ie. how much valid data it contains.)
    pub fn length(&self) -> usize {
        self.used_normal_length
    }

    /// Creates a clone of the internal buffer. The clone only contain the valid data.
    pub fn clone_buffer(&self, other: &mut Vec<u8>) {
        other.clone_from(&self.data);
        other.resize(self.used_normal_length, 0);
    }

    /// Returns a slice to the internal buffer starting at `start`.
    pub fn get_buffer(&self, start: usize) -> &[u8] {
        &self.data[start..self.used_normal_length]
    }

    /// Returns a slice to the internal buffer including the peek data starting at `start`.
    pub fn get_full_buffer(&self, start: usize) -> &[u8] {
        &self.data[start..self.used_normal_length + self.used_peek_length]
    }

    /// Returns a u8/u16/u32/u64 from the internal buffer at position `start`.
    pub fn read_uint(&self, start: usize, byte_size: usize) -> u64 {
        match byte_size {
            1 => u64::from(self.data[start]),
            2 => u64::from(self.byte_order.read_u16(&self.data[start..start + 2])),
            4 => u64::from(self.byte_order.read_u32(&self.data[start..start + 4])),
            8 => self.byte_order.read_u64(&self.data[start..start + 8]),
            _ => panic!("Invalid byte_size: {}", byte_size),
        }
    }

    /// Returns a f32/f64 from the internal buffer at position `start`.
    pub fn read_float(&self, start: usize, byte_size: usize) -> f64 {
        match byte_size {
            2 => f64::from(f16::from_bits(
                self.byte_order.read_u16(&self.data[start..start + 2]),
            )),
            4 => f64::from(self.byte_order.read_f32(&self.data[start..start + 4])),
            8 => self.byte_order.read_f64(&self.data[start..start + 8]),
            _ => panic!("Invalid byte_size: {}", byte_size),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::byteorder_io::ByteOrder;
    use crate::peekreader::PeekReader;
    use std::io::Cursor;

    #[test]
    fn smoke_test() {
        let data = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xff, 0xff];
        let mut input = PeekReader::new(Cursor::new(&data));
        let mut sut = InputDecoder::new(&mut input, 8, 2, ByteOrder::Little);

        match sut.peek_read() {
            Ok(mut mem) => {
                assert_eq!(8, mem.length());

                assert_eq!(-2.0, mem.read_float(0, 8));
                assert_eq!(-2.0, mem.read_float(4, 4));
                assert_eq!(0xc000000000000000, mem.read_uint(0, 8));
                assert_eq!(0xc0000000, mem.read_uint(4, 4));
                assert_eq!(0xc000, mem.read_uint(6, 2));
                assert_eq!(0xc0, mem.read_uint(7, 1));
                assert_eq!(&[0, 0xc0], mem.get_buffer(6));
                assert_eq!(&[0, 0xc0, 0xff, 0xff], mem.get_full_buffer(6));

                let mut copy: Vec<u8> = Vec::new();
                mem.clone_buffer(&mut copy);
                assert_eq!(vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0], copy);

                mem.zero_out_buffer(7, 8);
                assert_eq!(&[0, 0, 0xff, 0xff], mem.get_full_buffer(6));
            }
            Err(e) => {
                assert!(false, e);
            }
        }

        match sut.peek_read() {
            Ok(mem) => {
                assert_eq!(2, mem.length());
                assert_eq!(0xffff, mem.read_uint(0, 2));
            }
            Err(e) => {
                assert!(false, e);
            }
        }
    }
}
<commit_msg>od: remove Vec::set_len() usage in InputDecoder (#1739)<commit_after>use half::f16;
use std::io;

use crate::byteorder_io::ByteOrder;
use crate::multifilereader::HasError;
use crate::peekreader::PeekRead;

/// Processes an input and provides access to the data read in various formats
///
/// Currently only useful if the input implements `PeekRead`.
pub struct InputDecoder<'a, I>
where
    I: 'a,
{
    /// The input from which data is read
    input: &'a mut I,

    /// A memory buffer, it's size is set in `new`.
    data: Vec<u8>,
    /// The number of bytes in the buffer reserved for the peek data from `PeekRead`.
    reserved_peek_length: usize,

    /// The number of (valid) bytes in the buffer.
    used_normal_length: usize,
    /// The number of peek bytes in the buffer.
    used_peek_length: usize,

    /// Byte order used to read data from the buffer.
    byte_order: ByteOrder,
}

impl<'a, I> InputDecoder<'a, I> {
    /// Creates a new `InputDecoder` with an allocated buffer of `normal_length` + `peek_length` bytes.
    /// `byte_order` determines how to read multibyte formats from the buffer.
    pub fn new(
        input: &mut I,
        normal_length: usize,
        peek_length: usize,
        byte_order: ByteOrder,
    ) -> InputDecoder<I> {
        let bytes = vec![0; normal_length + peek_length];

        InputDecoder {
            input,
            data: bytes,
            reserved_peek_length: peek_length,
            used_normal_length: 0,
            used_peek_length: 0,
            byte_order,
        }
    }
}

impl<'a, I> InputDecoder<'a, I>
where
    I: PeekRead,
{
    /// calls `peek_read` on the internal stream to (re)fill the buffer. Returns a
    /// MemoryDecoder providing access to the result or returns an i/o error.
    pub fn peek_read(&mut self) -> io::Result<MemoryDecoder> {
        match self
            .input
            .peek_read(self.data.as_mut_slice(), self.reserved_peek_length)
        {
            Ok((n, p)) => {
                self.used_normal_length = n;
                self.used_peek_length = p;
                Ok(MemoryDecoder {
                    data: &mut self.data,
                    used_normal_length: self.used_normal_length,
                    used_peek_length: self.used_peek_length,
                    byte_order: self.byte_order,
                })
            }
            Err(e) => Err(e),
        }
    }
}

impl<'a, I> HasError for InputDecoder<'a, I>
where
    I: HasError,
{
    /// calls has_error on the internal stream.
    fn has_error(&self) -> bool {
        self.input.has_error()
    }
}

/// Provides access to the internal data in various formats
pub struct MemoryDecoder<'a> {
    /// A reference to the parents' data
    data: &'a mut Vec<u8>,
    /// The number of (valid) bytes in the buffer.
    used_normal_length: usize,
    /// The number of peek bytes in the buffer.
    used_peek_length: usize,
    /// Byte order used to read data from the buffer.
    byte_order: ByteOrder,
}

impl<'a> MemoryDecoder<'a> {
    /// Set a part of the internal buffer to zero.
    /// access to the whole buffer is possible, not just to the valid data.
    pub fn zero_out_buffer(&mut self, start: usize, end: usize) {
        for i in start..end {
            self.data[i] = 0;
        }
    }

    /// Returns the current length of the buffer. (ie. how much valid data it contains.)
    pub fn length(&self) -> usize {
        self.used_normal_length
    }

    /// Creates a clone of the internal buffer. The clone only contain the valid data.
    pub fn clone_buffer(&self, other: &mut Vec<u8>) {
        other.clone_from(&self.data);
        other.resize(self.used_normal_length, 0);
    }

    /// Returns a slice to the internal buffer starting at `start`.
    pub fn get_buffer(&self, start: usize) -> &[u8] {
        &self.data[start..self.used_normal_length]
    }

    /// Returns a slice to the internal buffer including the peek data starting at `start`.
    pub fn get_full_buffer(&self, start: usize) -> &[u8] {
        &self.data[start..self.used_normal_length + self.used_peek_length]
    }

    /// Returns a u8/u16/u32/u64 from the internal buffer at position `start`.
    pub fn read_uint(&self, start: usize, byte_size: usize) -> u64 {
        match byte_size {
            1 => u64::from(self.data[start]),
            2 => u64::from(self.byte_order.read_u16(&self.data[start..start + 2])),
            4 => u64::from(self.byte_order.read_u32(&self.data[start..start + 4])),
            8 => self.byte_order.read_u64(&self.data[start..start + 8]),
            _ => panic!("Invalid byte_size: {}", byte_size),
        }
    }

    /// Returns a f32/f64 from the internal buffer at position `start`.
    pub fn read_float(&self, start: usize, byte_size: usize) -> f64 {
        match byte_size {
            2 => f64::from(f16::from_bits(
                self.byte_order.read_u16(&self.data[start..start + 2]),
            )),
            4 => f64::from(self.byte_order.read_f32(&self.data[start..start + 4])),
            8 => self.byte_order.read_f64(&self.data[start..start + 8]),
            _ => panic!("Invalid byte_size: {}", byte_size),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::byteorder_io::ByteOrder;
    use crate::peekreader::PeekReader;
    use std::io::Cursor;

    #[test]
    fn smoke_test() {
        let data = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xff, 0xff];
        let mut input = PeekReader::new(Cursor::new(&data));
        let mut sut = InputDecoder::new(&mut input, 8, 2, ByteOrder::Little);

        match sut.peek_read() {
            Ok(mut mem) => {
                assert_eq!(8, mem.length());

                assert_eq!(-2.0, mem.read_float(0, 8));
                assert_eq!(-2.0, mem.read_float(4, 4));
                assert_eq!(0xc000000000000000, mem.read_uint(0, 8));
                assert_eq!(0xc0000000, mem.read_uint(4, 4));
                assert_eq!(0xc000, mem.read_uint(6, 2));
                assert_eq!(0xc0, mem.read_uint(7, 1));
                assert_eq!(&[0, 0xc0], mem.get_buffer(6));
                assert_eq!(&[0, 0xc0, 0xff, 0xff], mem.get_full_buffer(6));

                let mut copy: Vec<u8> = Vec::new();
                mem.clone_buffer(&mut copy);
                assert_eq!(vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0], copy);

                mem.zero_out_buffer(7, 8);
                assert_eq!(&[0, 0, 0xff, 0xff], mem.get_full_buffer(6));
            }
            Err(e) => {
                assert!(false, e);
            }
        }

        match sut.peek_read() {
            Ok(mem) => {
                assert_eq!(2, mem.length());
                assert_eq!(0xffff, mem.read_uint(0, 2));
            }
            Err(e) => {
                assert!(false, e);
            }
        }
    }
}
<|endoftext|>


<commit_before>#![allow(clippy::single_match_else)]
use rustc_tools_util::VersionInfo;

#[test]
fn check_that_clippy_lints_has_the_same_version_as_clippy() {
    let clippy_meta = cargo_metadata::MetadataCommand::new()
        .no_deps()
        .exec()
        .expect("could not obtain cargo metadata");
    std::env::set_current_dir(std::env::current_dir().unwrap().join("clippy_lints")).unwrap();
    let clippy_lints_meta = cargo_metadata::MetadataCommand::new()
        .no_deps()
        .exec()
        .expect("could not obtain cargo metadata");
    assert_eq!(clippy_lints_meta.packages[0].version, clippy_meta.packages[0].version);
    for package in &clippy_meta.packages[0].dependencies {
        if package.name == "clippy_lints" {
            assert!(package.req.matches(&clippy_lints_meta.packages[0].version));
            return;
        }
    }
}

#[test]
fn check_that_clippy_has_the_same_major_version_as_rustc() {
    let clippy_version = rustc_tools_util::get_version_info!();
    let clippy_major = clippy_version.major;
    let clippy_minor = clippy_version.minor;
    let clippy_patch = clippy_version.patch;

    // get the rustc version from cargo
    // this way the rust-toolchain file version is honored
    let rustc_version = String::from_utf8(
        std::process::Command::new("cargo")
            .arg("--version")
            .output()
            .expect("failed to run 'cargo --version'")
            .stdout,
    )
    .unwrap();
    // extract "1 50 0" from "cargo 1.50.0-nightly (a3c2627fb 2020-12-14)"
    let vsplit: Vec<&str> = rustc_version
        .split(' ')
        .nth(1)
        .unwrap()
        .split('-')
        .next()
        .unwrap()
        .split('.')
        .collect();
    match vsplit.as_slice() {
        [rustc_major, rustc_minor, _rustc_patch] => {
            // clippy 0.1.50 should correspond to rustc 1.50.0
            dbg!(&rustc_version);
            dbg!(&clippy_version);
            assert_eq!(clippy_major, 0); // this will probably stay the same for a long time
            assert_eq!(
                clippy_minor.to_string(),
                *rustc_major,
                "clippy minor version does not equal rustc major version"
            );
            assert_eq!(
                clippy_patch.to_string(),
                *rustc_minor,
                "clippy patch version does not equal rustc minor version"
            );
            // do not check rustc_patch because when a stable-patch-release is made (like 1.50.2),
            // we don't want our tests failing suddenly
        },
        _ => {
            dbg!(vsplit);
            panic!("Failed to parse rustc version");
        },
    };
}
<commit_msg>Use rustc --version in versioncheck<commit_after>#![allow(clippy::single_match_else)]
use rustc_tools_util::VersionInfo;

#[test]
fn check_that_clippy_lints_has_the_same_version_as_clippy() {
    let clippy_meta = cargo_metadata::MetadataCommand::new()
        .no_deps()
        .exec()
        .expect("could not obtain cargo metadata");
    std::env::set_current_dir(std::env::current_dir().unwrap().join("clippy_lints")).unwrap();
    let clippy_lints_meta = cargo_metadata::MetadataCommand::new()
        .no_deps()
        .exec()
        .expect("could not obtain cargo metadata");
    assert_eq!(clippy_lints_meta.packages[0].version, clippy_meta.packages[0].version);
    for package in &clippy_meta.packages[0].dependencies {
        if package.name == "clippy_lints" {
            assert!(package.req.matches(&clippy_lints_meta.packages[0].version));
            return;
        }
    }
}

#[test]
fn check_that_clippy_has_the_same_major_version_as_rustc() {
    let clippy_version = rustc_tools_util::get_version_info!();
    let clippy_major = clippy_version.major;
    let clippy_minor = clippy_version.minor;
    let clippy_patch = clippy_version.patch;

    // get the rustc version
    // this way the rust-toolchain file version is honored
    let rustc_version = String::from_utf8(
        std::process::Command::new("rustc")
            .arg("--version")
            .output()
            .expect("failed to run `rustc --version`")
            .stdout,
    )
    .unwrap();
    // extract "1 XX 0" from "rustc 1.XX.0-nightly (<commit> <date>)"
    let vsplit: Vec<&str> = rustc_version
        .split(' ')
        .nth(1)
        .unwrap()
        .split('-')
        .next()
        .unwrap()
        .split('.')
        .collect();
    match vsplit.as_slice() {
        [rustc_major, rustc_minor, _rustc_patch] => {
            // clippy 0.1.XX should correspond to rustc 1.XX.0
            assert_eq!(clippy_major, 0); // this will probably stay the same for a long time
            assert_eq!(
                clippy_minor.to_string(),
                *rustc_major,
                "clippy minor version does not equal rustc major version"
            );
            assert_eq!(
                clippy_patch.to_string(),
                *rustc_minor,
                "clippy patch version does not equal rustc minor version"
            );
            // do not check rustc_patch because when a stable-patch-release is made (like 1.50.2),
            // we don't want our tests failing suddenly
        },
        _ => {
            panic!("Failed to parse rustc version: {:?}", vsplit);
        },
    };
}
<|endoftext|>


<commit_before>#![feature(globs,macro_rules,phase)]

#[phase(plugin,link)]
extern crate nom;

use nom::{IResult,Producer,FileProducer,ProducerState,FlatMapper,Mapper,Mapper2,line_ending,not_line_ending, space, alphanumeric, is_alphanumeric, multispace};
use nom::IResult::*;

use std::str;
use std::collections::HashMap;
use std::fmt::Show;

fn empty_result(i:&[u8]) -> IResult<&[u8], ()> { Done(i,()) }
tag!(semicolon ";".as_bytes());
o!(comment_body<&[u8],&[u8]> semicolon ~ not_line_ending ~ );
o!(comment<&[u8], ()> comment_body line_ending ~ empty_result ~);
opt!(opt_comment<&[u8],&[u8]> comment_body);

tag!(lsb "[".as_bytes());
tag!(rsb "]".as_bytes());
fn category_name(input:&[u8]) -> IResult<&[u8], &str> {
  for idx in range(0, input.len()) {
    if input[idx] == ']' as u8 {
      return Done(input.slice_from(idx), input.slice(0, idx)).map_res(str::from_utf8)
    }
  }
  Done("".as_bytes(), input).map_res(str::from_utf8)
}
o!(category<&[u8], &str> lsb ~ category_name ~ rsb line_ending);

tag!(equal "=".as_bytes());
fn not_equal(input:&[u8]) -> IResult<&[u8], &[u8]> {
  for idx in range(0, input.len()) {
    if input[idx] == '=' as u8 {
      return Done(input.slice_from(idx), input.slice(0, idx))
    }
  }
  Done("".as_bytes(), input)
}

fn value_parser(input:&[u8]) -> IResult<&[u8], &str> {
  for idx in range(0, input.len()) {
    if input[idx] == '\n' as u8 || input[idx] == ';' as u8 {
      return Done(input.slice_from(idx), input.slice(0, idx)).map_res(str::from_utf8)
    }
  }
  Done("".as_bytes(), input).map_res(str::from_utf8)
}

fn parameter_parser(input: &[u8]) -> IResult<&[u8], &str> {
  alphanumeric(input).map_res(str::from_utf8)
}

opt!(opt_multispace<&[u8],&[u8]> multispace);
o!(value<&[u8],&str> space equal space ~ value_parser ~ space opt_comment opt_multispace);
chain!(key_value<&[u8],(&str,&str)>, ||{(key, val)},  key: parameter_parser, val: value,);

fn keys_and_values<'a>(input: &'a[u8], mut z: HashMap<&'a str, &'a str>) -> IResult<&'a[u8], HashMap<&'a str, &'a str> > {
  fold0_impl!(<&[u8], HashMap<&str, &str> >, |mut h:HashMap<&'a str, &'a str>, (k, v)| {
    h.insert(k,v);
    h
  }, key_value, input, z);
}

fn keys_and_values_wrapper<'a>(input:&'a[u8]) -> IResult<&'a[u8], HashMap<&'a str, &'a str> > {
  let h: HashMap<&str, &str> = HashMap::new();
  let res = keys_and_values(input, h);
  //println!("{}", res);
  res
}

chain!(category_and_keys<&[u8],(&str,HashMap<&str,&str>)>,move |:|{(category, keys)},  category: category, keys: keys_and_values_wrapper,);

fn categories<'a>(input: &'a[u8]) -> IResult<&'a[u8], HashMap<&'a str, HashMap<&'a str, &'a str> > > {
  let mut z: HashMap<&str, HashMap<&str, &str>> = HashMap::new();
  fold0_impl!(<&[u8], HashMap<&str, HashMap<&str, &str> > >, |mut h:HashMap<&'a str, HashMap<&'a str, &'a str> >, (k, v)| {
    h.insert(k,v);
    h
  }, category_and_keys, input, z);
}

#[test]
fn parse_comment_test() {
  let ini_file = ";comment
[category]
parameter=value
key = value2

[other]
number = 1234
str = a b cc dd ; comment";

  let ini_without_comment = "[category]
parameter=value
key = value2

[other]
number = 1234
str = a b cc dd ; comment";

  let res = Done((), ini_file.as_bytes()).flat_map(comment);
  println!("{}", res);
  match res {
    IResult::Done(i, o) => println!("i: {} | o: {}", str::from_utf8(i), o),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_comment.as_bytes(), ()));
}

#[test]
fn parse_category_test() {
  let ini_file = "[category]
parameter=value
key = value2";

  let ini_without_category = "parameter=value
key = value2";

  let res = Done((), ini_file.as_bytes()).flat_map(category);
  println!("{}", res);
  match res {
    IResult::Done(i, o) => println!("i: {} | o: {}", str::from_utf8(i), o),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_category.as_bytes(), "category"));
}

#[test]
fn parse_key_value_test() {
  let ini_file = "parameter=value
key = value2";

  let ini_without_key_value = "key = value2";

  let res = Done((), ini_file.as_bytes()).flat_map(key_value);
  println!("{}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {} | o: ({},{})", str::from_utf8(i), o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_key_value.as_bytes(), ("parameter", "value")));
}

#[test]
fn parse_key_value_with_space_test() {
  let ini_file = "parameter = value
key = value2";

  let ini_without_key_value = "key = value2";

  let res = Done((), ini_file.as_bytes()).flat_map(key_value);
  println!("{}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {} | o: ({},{})", str::from_utf8(i), o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_key_value.as_bytes(), ("parameter", "value")));
}

#[test]
fn parse_key_value_with_comment_test() {
  let ini_file = "parameter=value;abc
key = value2";

  let ini_without_key_value = "key = value2";

  let res = Done((), ini_file.as_bytes()).flat_map(key_value);
  println!("{}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {} | o: ({},{})", str::from_utf8(i), o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_key_value.as_bytes(), ("parameter", "value")));
}

#[test]
fn parse_multiple_keys_and_values_test() {
  let ini_file = "parameter=value;abc

key = value2

[category]";

  let ini_without_key_value = "[category]";

  let mut h: HashMap<&str, &str> = HashMap::new();
  let res = keys_and_values(ini_file.as_bytes(), h);
  println!("{}", res);
  match res {
    IResult::Done(i, ref o) => println!("i: {} | o: {}", str::from_utf8(i), o),
    _ => println!("error")
  }

  let mut expected: HashMap<&str, &str> = HashMap::new();
  expected.insert("parameter", "value");
  expected.insert("key", "value2");
  assert_eq!(res, Done(ini_without_key_value.as_bytes(), expected));
}

#[test]
fn parse_category_then_multiple_keys_and_values_test() {
  //FIXME: there can be an empty line or a comment line after a category
  let ini_file = "[abcd]
parameter=value;abc

key = value2

[category]";

  let ini_after_parser = "[category]";

  let res = category_and_keys(ini_file.as_bytes());
  println!("{}", res);
  match res {
    IResult::Done(i, ref o) => println!("i: {} | o: {}", str::from_utf8(i), o),
    _ => println!("error")
  }

  let mut expected_h: HashMap<&str, &str> = HashMap::new();
  expected_h.insert("parameter", "value");
  expected_h.insert("key", "value2");
  assert_eq!(res, Done(ini_after_parser.as_bytes(), ("abcd", expected_h)));
}

#[test]
fn parse_multiple_categories_test() {
  let ini_file = "[abcd]
parameter=value;abc

key = value2
[category]
parameter3=value3
key4 = value4
";

  let ini_after_parser = "";

  let res = categories(ini_file.as_bytes());
  println!("{}", res);
  match res {
    IResult::Done(i, ref o) => println!("i: {} | o: {}", str::from_utf8(i), o),
    _ => println!("error")
  }

  let mut expected_1: HashMap<&str, &str> = HashMap::new();
  expected_1.insert("parameter", "value");
  expected_1.insert("key", "value2");
  let mut expected_2: HashMap<&str, &str> = HashMap::new();
  expected_2.insert("parameter3", "value3");
  expected_2.insert("key4", "value4");
  let mut expected_h: HashMap<&str, HashMap<&str, &str>> = HashMap::new();
  expected_h.insert("abcd",     expected_1);
  expected_h.insert("category", expected_2);
  assert_eq!(res, Done(ini_after_parser.as_bytes(), expected_h));
}
<commit_msg>accept empty lines after sections<commit_after>#![feature(globs,macro_rules,phase)]

#[phase(plugin,link)]
extern crate nom;

use nom::{IResult,Producer,FileProducer,ProducerState,FlatMapper,Mapper,Mapper2,line_ending,not_line_ending, space, alphanumeric, is_alphanumeric, multispace};
use nom::IResult::*;

use std::str;
use std::collections::HashMap;
use std::fmt::Show;

fn empty_result(i:&[u8]) -> IResult<&[u8], ()> { Done(i,()) }
tag!(semicolon ";".as_bytes());
o!(comment_body<&[u8],&[u8]> semicolon ~ not_line_ending ~ );
o!(comment<&[u8], ()> comment_body line_ending ~ empty_result ~);
opt!(opt_comment<&[u8],&[u8]> comment_body);

tag!(lsb "[".as_bytes());
tag!(rsb "]".as_bytes());
fn category_name(input:&[u8]) -> IResult<&[u8], &str> {
  for idx in range(0, input.len()) {
    if input[idx] == ']' as u8 {
      return Done(input.slice_from(idx), input.slice(0, idx)).map_res(str::from_utf8)
    }
  }
  Done("".as_bytes(), input).map_res(str::from_utf8)
}
o!(category<&[u8], &str> lsb ~ category_name ~ rsb opt_multispace);

tag!(equal "=".as_bytes());
fn not_equal(input:&[u8]) -> IResult<&[u8], &[u8]> {
  for idx in range(0, input.len()) {
    if input[idx] == '=' as u8 {
      return Done(input.slice_from(idx), input.slice(0, idx))
    }
  }
  Done("".as_bytes(), input)
}

fn value_parser(input:&[u8]) -> IResult<&[u8], &str> {
  for idx in range(0, input.len()) {
    if input[idx] == '\n' as u8 || input[idx] == ';' as u8 {
      return Done(input.slice_from(idx), input.slice(0, idx)).map_res(str::from_utf8)
    }
  }
  Done("".as_bytes(), input).map_res(str::from_utf8)
}

fn parameter_parser(input: &[u8]) -> IResult<&[u8], &str> {
  alphanumeric(input).map_res(str::from_utf8)
}

opt!(opt_multispace<&[u8],&[u8]> multispace);
o!(value<&[u8],&str> space equal space ~ value_parser ~ space opt_comment opt_multispace);
chain!(key_value<&[u8],(&str,&str)>, ||{(key, val)},  key: parameter_parser, val: value,);

fn keys_and_values<'a>(input: &'a[u8], mut z: HashMap<&'a str, &'a str>) -> IResult<&'a[u8], HashMap<&'a str, &'a str> > {
  fold0_impl!(<&[u8], HashMap<&str, &str> >, |mut h:HashMap<&'a str, &'a str>, (k, v)| {
    h.insert(k,v);
    h
  }, key_value, input, z);
}

fn keys_and_values_wrapper<'a>(input:&'a[u8]) -> IResult<&'a[u8], HashMap<&'a str, &'a str> > {
  let h: HashMap<&str, &str> = HashMap::new();
  let res = keys_and_values(input, h);
  //println!("{}", res);
  res
}

chain!(category_and_keys<&[u8],(&str,HashMap<&str,&str>)>,move |:|{(category, keys)},  category: category, keys: keys_and_values_wrapper,);

fn categories<'a>(input: &'a[u8]) -> IResult<&'a[u8], HashMap<&'a str, HashMap<&'a str, &'a str> > > {
  let mut z: HashMap<&str, HashMap<&str, &str>> = HashMap::new();
  fold0_impl!(<&[u8], HashMap<&str, HashMap<&str, &str> > >, |mut h:HashMap<&'a str, HashMap<&'a str, &'a str> >, (k, v)| {
    h.insert(k,v);
    h
  }, category_and_keys, input, z);
}

#[test]
fn parse_comment_test() {
  let ini_file = ";comment
[category]
parameter=value
key = value2

[other]
number = 1234
str = a b cc dd ; comment";

  let ini_without_comment = "[category]
parameter=value
key = value2

[other]
number = 1234
str = a b cc dd ; comment";

  let res = Done((), ini_file.as_bytes()).flat_map(comment);
  println!("{}", res);
  match res {
    IResult::Done(i, o) => println!("i: {} | o: {}", str::from_utf8(i), o),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_comment.as_bytes(), ()));
}

#[test]
fn parse_category_test() {
  let ini_file = "[category]

parameter=value
key = value2";

  let ini_without_category = "parameter=value
key = value2";

  let res = Done((), ini_file.as_bytes()).flat_map(category);
  println!("{}", res);
  match res {
    IResult::Done(i, o) => println!("i: {} | o: {}", str::from_utf8(i), o),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_category.as_bytes(), "category"));
}

#[test]
fn parse_key_value_test() {
  let ini_file = "parameter=value
key = value2";

  let ini_without_key_value = "key = value2";

  let res = Done((), ini_file.as_bytes()).flat_map(key_value);
  println!("{}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {} | o: ({},{})", str::from_utf8(i), o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_key_value.as_bytes(), ("parameter", "value")));
}

#[test]
fn parse_key_value_with_space_test() {
  let ini_file = "parameter = value
key = value2";

  let ini_without_key_value = "key = value2";

  let res = Done((), ini_file.as_bytes()).flat_map(key_value);
  println!("{}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {} | o: ({},{})", str::from_utf8(i), o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_key_value.as_bytes(), ("parameter", "value")));
}

#[test]
fn parse_key_value_with_comment_test() {
  let ini_file = "parameter=value;abc
key = value2";

  let ini_without_key_value = "key = value2";

  let res = Done((), ini_file.as_bytes()).flat_map(key_value);
  println!("{}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {} | o: ({},{})", str::from_utf8(i), o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_key_value.as_bytes(), ("parameter", "value")));
}

#[test]
fn parse_multiple_keys_and_values_test() {
  let ini_file = "parameter=value;abc

key = value2

[category]";

  let ini_without_key_value = "[category]";

  let mut h: HashMap<&str, &str> = HashMap::new();
  let res = keys_and_values(ini_file.as_bytes(), h);
  println!("{}", res);
  match res {
    IResult::Done(i, ref o) => println!("i: {} | o: {}", str::from_utf8(i), o),
    _ => println!("error")
  }

  let mut expected: HashMap<&str, &str> = HashMap::new();
  expected.insert("parameter", "value");
  expected.insert("key", "value2");
  assert_eq!(res, Done(ini_without_key_value.as_bytes(), expected));
}

#[test]
fn parse_category_then_multiple_keys_and_values_test() {
  //FIXME: there can be an empty line or a comment line after a category
  let ini_file = "[abcd]
parameter=value;abc

key = value2

[category]";

  let ini_after_parser = "[category]";

  let res = category_and_keys(ini_file.as_bytes());
  println!("{}", res);
  match res {
    IResult::Done(i, ref o) => println!("i: {} | o: {}", str::from_utf8(i), o),
    _ => println!("error")
  }

  let mut expected_h: HashMap<&str, &str> = HashMap::new();
  expected_h.insert("parameter", "value");
  expected_h.insert("key", "value2");
  assert_eq!(res, Done(ini_after_parser.as_bytes(), ("abcd", expected_h)));
}

#[test]
fn parse_multiple_categories_test() {
  let ini_file = "[abcd]

parameter=value;abc

key = value2

[category]
parameter3=value3
key4 = value4
";

  let ini_after_parser = "";

  let res = categories(ini_file.as_bytes());
  println!("{}", res);
  match res {
    IResult::Done(i, ref o) => println!("i: {} | o: {}", str::from_utf8(i), o),
    _ => println!("error")
  }

  let mut expected_1: HashMap<&str, &str> = HashMap::new();
  expected_1.insert("parameter", "value");
  expected_1.insert("key", "value2");
  let mut expected_2: HashMap<&str, &str> = HashMap::new();
  expected_2.insert("parameter3", "value3");
  expected_2.insert("key4", "value4");
  let mut expected_h: HashMap<&str, HashMap<&str, &str>> = HashMap::new();
  expected_h.insert("abcd",     expected_1);
  expected_h.insert("category", expected_2);
  assert_eq!(res, Done(ini_after_parser.as_bytes(), expected_h));
}
<|endoftext|>


<commit_before>extern crate multiaddr;

use multiaddr::*;

#[test]
fn protocols_to_u16() {
    assert_eq!(ProtocolTypes::TCP.to_code(), 6u16);
}

#[test]
fn protocols_from_u16() {
    assert_eq!(ProtocolTypes::from_code(6u16), Some(ProtocolTypes::TCP));
    assert_eq!(ProtocolTypes::from_code(455u16), None);
}

#[test]
fn protocols_to_size() {
    assert_eq!(ProtocolTypes::TCP.to_size(), 16);
}

#[test]
fn protocols_to_name() {
    assert_eq!(ProtocolTypes::TCP.to_name(), "tcp");
}

#[test]
fn multiaddr_eq() {
    assert!(
        Multiaddr::new("/ip4/127.0.0.1").unwrap() == Multiaddr::new("/ip4/127.0.0.1").unwrap()
    );
    assert!(
        Multiaddr::new("/ip4/127.0.0.1").unwrap() != Multiaddr::new("/ip4/128.0.0.1").unwrap()
    );
}

fn assert_bytes(source: &str, target: &[u8]) -> () {
    let address = Multiaddr::new(source).unwrap();
    println!("source {:?}, target {:?}", source, target);
    assert_eq!(address.to_bytes(), target);
}

#[test]
fn byte_formats() {
    assert_bytes("/ip4/1.2.3.4", &[0, 4, 1, 2, 3, 4]);
    assert_bytes("/ip4/0.0.0.0", &[0, 4, 0, 0, 0, 0]);
    assert_bytes("/ip6/::1", &[
        0, 41,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
    ]);
    assert_bytes("/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21", &[
        0, 41,
        38, 1, 0, 9, 79, 129, 151, 0, 128, 62, 202, 101, 102, 232, 12, 33
    ]);
    // assert_bytes("/onion/timaq4ygg2iegci7:1234", &[]);
    // assert_bytes("/onion/timaq4ygg2iegci7:80/http", &[]);
    assert_bytes("/udp/0", &[0, 17, 0, 0]);
    assert_bytes("/tcp/0", &[0, 6, 0, 0]);
    assert_bytes("/sctp/0", &[0, 132, 0, 0]);
    assert_bytes("/udp/1234", &[0, 17, 4, 210]);
    assert_bytes("/tcp/1234", &[0, 6, 4, 210]);
    assert_bytes("/sctp/1234", &[0, 132, 4, 210]);
    assert_bytes("/udp/65535", &[0, 17, 255, 255]);
    assert_bytes("/tcp/65535", &[0, 6, 255, 255]);
    // assert_bytes("/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", &[]);
    assert_bytes("/udp/1234/sctp/1234", &[
        0, 17, 4, 210,
        0, 132, 4, 210
    ]);
    assert_bytes("/udp/1234/udt", &[
        0, 17, 4, 210,
        1, 46
    ]);
    assert_bytes("/udp/1234/utp", &[
        0, 17, 4, 210,
        1, 45
    ]);
    assert_bytes("/tcp/1234/http", &[
        0, 6, 4, 210,
        1, 224
    ]);
    assert_bytes("/tcp/1234/https", &[
        0, 6, 4, 210,
        1, 187
    ]);
    // assert_bytes("/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234", &[]);
    assert_bytes("/ip4/127.0.0.1/udp/1234", &[
        0, 4, 127, 0, 0, 1,
        0, 17, 4, 210
    ]);
    assert_bytes("/ip4/127.0.0.1/udp/0", &[
        0, 4, 127, 0, 0, 1,
        0, 17, 0, 0
    ]);
    assert_bytes("/ip4/127.0.0.1/tcp/1234", &[
        0, 4, 127, 0, 0, 1,
        0, 6, 4, 210
    ]);
    assert_bytes("/ip4/127.0.0.1/tcp/1234/", &[
        0, 4, 127, 0, 0, 1,
        0, 6, 4, 210
    ]);
    // assert_bytes("/ip4/127.0.0.1/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", &&[]);
    // assert_bytes("/ip4/127.0.0.1/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234", &[]);
}
<commit_msg>More tests for byte to protocol<commit_after>extern crate multiaddr;

use multiaddr::*;

#[test]
fn protocols_to_u16() {
    assert_eq!(ProtocolTypes::TCP.to_code(), 6u16);
}

#[test]
fn protocols_from_u16() {
    assert_eq!(ProtocolTypes::from_code(6u16), Some(ProtocolTypes::TCP));
    assert_eq!(ProtocolTypes::from_code(455u16), None);
}

#[test]
fn protocols_to_size() {
    assert_eq!(ProtocolTypes::TCP.to_size(), 16);
}

#[test]
fn protocols_to_name() {
    assert_eq!(ProtocolTypes::TCP.to_name(), "tcp");
}

#[test]
fn multiaddr_eq() {
    assert!(
        Multiaddr::new("/ip4/127.0.0.1").unwrap() == Multiaddr::new("/ip4/127.0.0.1").unwrap()
    );
    assert!(
        Multiaddr::new("/ip4/127.0.0.1").unwrap() != Multiaddr::new("/ip4/128.0.0.1").unwrap()
    );
}

fn assert_bytes(source: &str, target: &[u8], protocols: Vec<ProtocolTypes>) -> () {
    let address = Multiaddr::new(source).unwrap();
    println!("source {:?}, target {:?}", source, target);
    assert_eq!(address.to_bytes(), target);
    assert_eq!(address.protocols(), protocols);
}

#[test]
fn byte_formats() {
    assert_bytes("/ip4/1.2.3.4", &[0, 4, 1, 2, 3, 4], vec![ProtocolTypes::IP4]);
    assert_bytes("/ip4/0.0.0.0", &[0, 4, 0, 0, 0, 0], vec![ProtocolTypes::IP4]);
    assert_bytes("/ip6/::1", &[
        0, 41,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
    ], vec![ProtocolTypes::IP6]);
    assert_bytes("/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21", &[
        0, 41,
        38, 1, 0, 9, 79, 129, 151, 0, 128, 62, 202, 101, 102, 232, 12, 33
    ], vec![ProtocolTypes::IP6]);
    // assert_bytes("/onion/timaq4ygg2iegci7:1234", &[], vec![ProtocolTypes::Onion]);
    // assert_bytes("/onion/timaq4ygg2iegci7:80/http", &[], vec![ProtocolTypes::Onion]);
    assert_bytes("/udp/0", &[0, 17, 0, 0], vec![ProtocolTypes::UDP]);
    assert_bytes("/tcp/0", &[0, 6, 0, 0], vec![ProtocolTypes::TCP]);
    assert_bytes("/sctp/0", &[0, 132, 0, 0], vec![ProtocolTypes::SCTP]);
    assert_bytes("/udp/1234", &[0, 17, 4, 210], vec![ProtocolTypes::UDP]);
    assert_bytes("/tcp/1234", &[0, 6, 4, 210], vec![ProtocolTypes::TCP]);
    assert_bytes("/sctp/1234", &[0, 132, 4, 210], vec![ProtocolTypes::SCTP]);
    assert_bytes("/udp/65535", &[0, 17, 255, 255], vec![ProtocolTypes::UDP]);
    assert_bytes("/tcp/65535", &[0, 6, 255, 255], vec![ProtocolTypes::TCP]);
    // assert_bytes("/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", &[], vec![ProtocolTypes::IPFS]);
    assert_bytes("/udp/1234/sctp/1234", &[
        0, 17, 4, 210,
        0, 132, 4, 210
    ], vec![ProtocolTypes::UDP, ProtocolTypes::SCTP]);
    assert_bytes("/udp/1234/udt", &[
        0, 17, 4, 210,
        1, 46
    ], vec![ProtocolTypes::UDP, ProtocolTypes::UDT]);
    assert_bytes("/udp/1234/utp", &[
        0, 17, 4, 210,
        1, 45
    ], vec![ProtocolTypes::UDP, ProtocolTypes::UTP]);
    assert_bytes("/tcp/1234/http", &[
        0, 6, 4, 210,
        1, 224
    ], vec![ProtocolTypes::TCP, ProtocolTypes::HTTP]);
    assert_bytes("/tcp/1234/https", &[
        0, 6, 4, 210,
        1, 187
    ], vec![ProtocolTypes::TCP, ProtocolTypes::HTTPS]);
    // assert_bytes("/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234", &[
    // ], vec![ProtocolTypes::IPFS, ProtocolTypes::TCP]);
    assert_bytes("/ip4/127.0.0.1/udp/1234", &[
        0, 4, 127, 0, 0, 1,
        0, 17, 4, 210
    ], vec![ProtocolTypes::IP4, ProtocolTypes::UDP]);
    assert_bytes("/ip4/127.0.0.1/udp/0", &[
        0, 4, 127, 0, 0, 1,
        0, 17, 0, 0
    ], vec![ProtocolTypes::IP4, ProtocolTypes::UDP]);
    assert_bytes("/ip4/127.0.0.1/tcp/1234", &[
        0, 4, 127, 0, 0, 1,
        0, 6, 4, 210
    ], vec![ProtocolTypes::IP4, ProtocolTypes::TCP]);
    assert_bytes("/ip4/127.0.0.1/tcp/1234/", &[
        0, 4, 127, 0, 0, 1,
        0, 6, 4, 210
    ], vec![ProtocolTypes::IP4, ProtocolTypes::TCP]);
    // assert_bytes("/ip4/127.0.0.1/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", &[], vec![]);
    // assert_bytes("/ip4/127.0.0.1/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234", &[], vec![]);
}
<|endoftext|>


<commit_before>extern crate jpeg_decoder as jpeg;
extern crate png;
extern crate walkdir;

mod common;
mod crashtest;
mod reftest;
<commit_msg>Add test for read_info<commit_after>extern crate jpeg_decoder as jpeg;
extern crate png;
extern crate walkdir;

use std::path::Path;
use std::fs::File;

mod common;
mod crashtest;
mod reftest;

#[test]
fn read_info() {
    let path = Path::new("tests").join("reftest").join("images").join("mozilla").join("jpg-progressive.jpg");

    let mut decoder = jpeg::Decoder::new(File::open(&path).unwrap());
    let ref_data = decoder.decode().unwrap();
    let ref_info = decoder.info().unwrap();

    decoder = jpeg::Decoder::new(File::open(&path).unwrap());
    decoder.read_info().unwrap();
    let info = decoder.info().unwrap();
    let data = decoder.decode().unwrap();

    assert_eq!(info, decoder.info().unwrap());
    assert_eq!(info, ref_info);
    assert_eq!(data, ref_data);
}
<|endoftext|>