<commit_before>use std::collections::HashMap;

use ast::Expression;
use error::Error;
use to_javascript::ToJavaScript;

type BuiltinMap =
    HashMap<&'static str, fn(&String, &Vec<Box<Expression>>) -> Result<String, Error>>;

lazy_static! {
    pub static ref BUILTINS: BuiltinMap = {
        // The type definition is required to prevent compile errors
        let mut map: BuiltinMap = HashMap::new();

        map.insert("if", builtin_if);

        map.insert("return", builtin_return);

        // Plus and minus are both binary and unary
        // But I  have deemed binary to have a higher precedence, so binary goes first
        map.insert("+", builtin_binop);

        map.insert("-", builtin_binop);

        map.insert("*", builtin_binop);

        map.insert("/", builtin_binop);

        map.insert("%", builtin_binop);

        map.insert("!", builtin_unary);

        map.insert("++", builtin_unary);

        map.insert("--", builtin_unary);

        map.insert("~", builtin_unary);

        map.insert("typeof", builtin_unary);

        map.insert("delete", builtin_unary);

        // Don't delete this! The project will refuse to compile without it
        map
    };
}

pub fn builtin_binop(op: &String, args: &Vec<Box<Expression>>) -> Result<String, Error> {
    match args.len() {
        0 => Err(Error::too_few_arguments("binary operation")),
        1 => builtin_unary(op, args),
        2 => {
            // Debox and take from index
            // This is messy _but_ it should make the match easier to understand
            let &box ref left = &args[0];

            let &box ref right = &args[1];

            match (left, right) {
                (&Expression::Number(l), &Expression::Number(r)) => precalculate_numbers(op, l, r),

                (_, _) => Ok(format!("{}{}{}", left.eval()?, op, right.eval()?)),
            }
        }
        _ => {
            let joined = args
                .into_iter()

                // TODO: Remove unwrap
                .map(|expr| expr.eval().or_else(|e| Err(e)).unwrap())
                .collect::<Vec<String>>()
                .join(op);

            Ok(joined)
        }
    }
}

pub fn builtin_unary(op: &String, args: &Vec<Box<Expression>>) -> Result<String, Error> {
    match op.as_ref() {
        "+" | "-" | "!" | "++" | "--" | "~" => Ok(format!("{}{}", op, args[0].eval()?)),

        // Unary operators which are words next an extra space.
        "typeof" | "delete" => Ok(format!("{} {}", op, args[0].eval()?)),
        _ => Err(Error::too_few_arguments("unary operator")),
    }
}

pub fn builtin_if(_name: &String, args: &Vec<Box<Expression>>) -> Result<String, Error> {
    match args.len() {
        0 => Err(Error::too_few_arguments("if statement")),
        1 => Err(Error::too_few_arguments("if statement condition")),
        2 => Ok(format!(
            "if ({}) {{ {} }}",
            args[0].eval()?,
            args[1].eval()?
        )),
        3 => Ok(format!(
            "if ({}) {{ {} }} else {{ {} }}",
            args[0].eval()?,
            args[1].eval()?,
            args[2].eval()?
        )),
        // TODO: Add error message here
        _ => panic!("Unknown number of arguments supplied to if-statement"),
    }
}

pub fn builtin_return(_name: &String, args: &Vec<Box<Expression>>) -> Result<String, Error> {
    match args.len() {
        0 => Err(Error::too_few_arguments("return")),
        1 => Ok(format!("return {}", args[0].eval()?)),
        _ => Err(Error::too_many_arguments("return")),
    }
}

fn precalculate_numbers(op: &String, left: f64, right: f64) -> Result<String, Error> {
    match op.as_ref() {
        "+" => Ok(format!("{}", left + right)),
        "-" => Ok(format!("{}", left - right)),
        "*" => Ok(format!("{}", left * right)),
        "/" if right != 0.0 => Ok(format!("{}", left / right)),
        "%" => Ok(format!("{}", left % right)),

        "/" => Err(Error::invalid_expression(
            "Divide by zero encountered on numeric literal binary operation",
        )),

        // Assume divide by 0 here
        _ => Err(Error::invalid_expression(
            "Divide by zero encountered on numeric literal binary operation",
        )),
    }
}
<commit_msg>Add var, constand let, reorganise file<commit_after>use std::collections::HashMap;

use ast::Expression;
use error::Error;
use to_javascript::ToJavaScript;

type BuiltinMap =
    HashMap<&'static str, fn(&String, &Vec<Box<Expression>>) -> Result<String, Error>>;

lazy_static! {
    pub static ref BUILTINS: BuiltinMap = {
        // The type definition is required to prevent compile errors
        let mut map: BuiltinMap = HashMap::new();

        map.insert("if", builtin_if);

        map.insert("return", builtin_return);

        map.insert("const", builtin_binding);

        map.insert("var", builtin_binding);

        map.insert("left", builtin_binding);

        // Plus and minus are both binary and unary
        // But I  have deemed binary to have a higher precedence, so binary goes first
        map.insert("+", builtin_binop);

        map.insert("-", builtin_binop);

        map.insert("*", builtin_binop);

        map.insert("/", builtin_binop);

        map.insert("%", builtin_binop);

        map.insert("!", builtin_unary);

        map.insert("++", builtin_unary);

        map.insert("--", builtin_unary);

        map.insert("~", builtin_unary);

        map.insert("typeof", builtin_unary);

        map.insert("delete", builtin_unary);

        // Don't delete this! The project will refuse to compile without it
        map
    };
}

pub fn builtin_if(_name: &String, args: &Vec<Box<Expression>>) -> Result<String, Error> {
    match args.len() {
        0 => Err(Error::too_few_arguments("if statement")),
        1 => Err(Error::too_few_arguments("if statement condition")),
        2 => Ok(format!(
            "if ({}) {{ {} }}",
            args[0].eval()?,
            args[1].eval()?
        )),
        3 => Ok(format!(
            "if ({}) {{ {} }} else {{ {} }}",
            args[0].eval()?,
            args[1].eval()?,
            args[2].eval()?
        )),
        // TODO: Add error message here
        _ => panic!("Unknown number of arguments supplied to if-statement"),
    }
}

pub fn builtin_return(_name: &String, args: &Vec<Box<Expression>>) -> Result<String, Error> {
    match args.len() {
        0 => Err(Error::too_few_arguments("return")),
        1 => Ok(format!("return {}", args[0].eval()?)),
        _ => Err(Error::too_many_arguments("return")),
    }
}

pub fn builtin_binding(name: &String, args: &Vec<Box<Expression>>) -> Result<String, Error> {
    // TODO: Add name to the error messages
    match args.len() {
        0 => Err(Error::too_few_arguments("binding")),
        1 => Ok(format!("{} {}", name, args[0].eval()?)),
        _ => Err(Error::too_many_arguments("binding")),
    }
}

pub fn builtin_binop(op: &String, args: &Vec<Box<Expression>>) -> Result<String, Error> {
    match args.len() {
        0 => Err(Error::too_few_arguments("binary operation")),
        1 => builtin_unary(op, args),
        2 => {
            // Debox and take from index
            // This is messy _but_ it should make the match easier to understand
            let &box ref left = &args[0];

            let &box ref right = &args[1];

            match (left, right) {
                (&Expression::Number(l), &Expression::Number(r)) => precalculate_numbers(op, l, r),

                (_, _) => Ok(format!("{}{}{}", left.eval()?, op, right.eval()?)),
            }
        }
        _ => {
            let joined = args
                .into_iter()

                // TODO: Remove unwrap
                .map(|expr| expr.eval().or_else(|e| Err(e)).unwrap())
                .collect::<Vec<String>>()
                .join(op);

            Ok(joined)
        }
    }
}

pub fn builtin_unary(op: &String, args: &Vec<Box<Expression>>) -> Result<String, Error> {
    match op.as_ref() {
        "+" | "-" | "!" | "++" | "--" | "~" => Ok(format!("{}{}", op, args[0].eval()?)),

        // Unary operators which are words next an extra space.
        "typeof" | "delete" => Ok(format!("{} {}", op, args[0].eval()?)),
        _ => Err(Error::too_few_arguments("unary operator")),
    }
}

fn precalculate_numbers(op: &String, left: f64, right: f64) -> Result<String, Error> {
    match op.as_ref() {
        "+" => Ok(format!("{}", left + right)),
        "-" => Ok(format!("{}", left - right)),
        "*" => Ok(format!("{}", left * right)),
        "/" if right != 0.0 => Ok(format!("{}", left / right)),
        "%" => Ok(format!("{}", left % right)),

        "/" => Err(Error::invalid_expression(
            "Divide by zero encountered on numeric literal binary operation",
        )),

        // Assume divide by 0 here
        _ => Err(Error::invalid_expression(
            "Divide by zero encountered on numeric literal binary operation",
        )),
    }
}
<|endoftext|>


<commit_before>// Take a look at the license at the top of the repository in the LICENSE file.

// Once https://github.com/rust-lang/rfcs/blob/master/text/1422-pub-restricted.md
// feature gets stabilized, we can move common parts in here.

#[cfg(test)]
mod tests {
    use crate::{utils, ProcessExt, System, SystemExt};

    #[test]
    fn test_refresh_system() {
        let mut sys = System::new();
        sys.refresh_system();
        // We don't want to test on unsupported systems.
        if System::IS_SUPPORTED {
            assert!(sys.total_memory() != 0);
            assert!(sys.free_memory() != 0);
        }
        assert!(sys.total_memory() >= sys.free_memory());
        assert!(sys.total_swap() >= sys.free_swap());
    }

    #[test]
    fn test_refresh_process() {
        let mut sys = System::new();
        assert!(sys.processes().is_empty(), "no process should be listed!");
        sys.refresh_processes();
        // We don't want to test on unsupported systems.

        #[cfg(not(feature = "apple-sandbox"))]
        if System::IS_SUPPORTED {
            assert!(
                sys.refresh_process(utils::get_current_pid().expect("failed to get current pid")),
                "process not listed",
            );
        }
    }

    #[test]
    fn test_get_process() {
        let mut sys = System::new();
        sys.refresh_processes();
        if let Some(p) = sys.process(utils::get_current_pid().expect("failed to get current pid")) {
            assert!(p.memory() > 0);
        } else {
            #[cfg(not(feature = "apple-sandbox"))]
            assert!(!System::IS_SUPPORTED);
        }
    }

    #[test]
    fn check_if_send_and_sync() {
        trait Foo {
            fn foo(&self) {}
        }
        impl<T> Foo for T where T: Send {}

        trait Bar {
            fn bar(&self) {}
        }

        impl<T> Bar for T where T: Sync {}

        let mut sys = System::new();
        sys.refresh_processes();
        if let Some(p) = sys.process(utils::get_current_pid().expect("failed to get current pid")) {
            p.foo(); // If this doesn't compile, it'll simply mean that the Process type
                     // doesn't implement the Send trait.
            p.bar(); // If this doesn't compile, it'll simply mean that the Process type
                     // doesn't implement the Sync trait.
        } else {
            #[cfg(not(feature = "apple-sandbox"))]
            assert!(!System::IS_SUPPORTED);
        }
    }

    #[test]
    fn check_hostname_has_no_nuls() {
        let sys = System::new();

        if let Some(hostname) = sys.host_name() {
            assert!(!hostname.contains('\u{0}'))
        }
    }

    #[test]
    fn check_uptime() {
        let sys = System::new();
        let uptime = sys.uptime();
        if System::IS_SUPPORTED {
            std::thread::sleep(std::time::Duration::from_millis(1000));
            let new_uptime = sys.uptime();
            assert!(uptime < new_uptime);
        }
    }
}
<commit_msg>Improve refresh_process test<commit_after>// Take a look at the license at the top of the repository in the LICENSE file.

// Once https://github.com/rust-lang/rfcs/blob/master/text/1422-pub-restricted.md
// feature gets stabilized, we can move common parts in here.

#[cfg(test)]
mod tests {
    use crate::{utils, ProcessExt, System, SystemExt};

    #[test]
    fn test_refresh_system() {
        let mut sys = System::new();
        sys.refresh_system();
        // We don't want to test on unsupported systems.
        if System::IS_SUPPORTED {
            assert!(sys.total_memory() != 0);
            assert!(sys.free_memory() != 0);
        }
        assert!(sys.total_memory() >= sys.free_memory());
        assert!(sys.total_swap() >= sys.free_swap());
    }

    #[test]
    fn test_refresh_process() {
        let mut sys = System::new();
        assert!(sys.processes().is_empty(), "no process should be listed!");
        // We don't want to test on unsupported systems.

        #[cfg(not(feature = "apple-sandbox"))]
        if System::IS_SUPPORTED {
            assert!(
                sys.refresh_process(utils::get_current_pid().expect("failed to get current pid")),
                "process not listed",
            );
            // Ensure that the process was really added to the list!
            assert!(sys
                .process(utils::get_current_pid().expect("failed to get current pid"))
                .is_some());
        }
    }

    #[test]
    fn test_get_process() {
        let mut sys = System::new();
        sys.refresh_processes();
        if let Some(p) = sys.process(utils::get_current_pid().expect("failed to get current pid")) {
            assert!(p.memory() > 0);
        } else {
            #[cfg(not(feature = "apple-sandbox"))]
            assert!(!System::IS_SUPPORTED);
        }
    }

    #[test]
    fn check_if_send_and_sync() {
        trait Foo {
            fn foo(&self) {}
        }
        impl<T> Foo for T where T: Send {}

        trait Bar {
            fn bar(&self) {}
        }

        impl<T> Bar for T where T: Sync {}

        let mut sys = System::new();
        sys.refresh_processes();
        if let Some(p) = sys.process(utils::get_current_pid().expect("failed to get current pid")) {
            p.foo(); // If this doesn't compile, it'll simply mean that the Process type
                     // doesn't implement the Send trait.
            p.bar(); // If this doesn't compile, it'll simply mean that the Process type
                     // doesn't implement the Sync trait.
        } else {
            #[cfg(not(feature = "apple-sandbox"))]
            assert!(!System::IS_SUPPORTED);
        }
    }

    #[test]
    fn check_hostname_has_no_nuls() {
        let sys = System::new();

        if let Some(hostname) = sys.host_name() {
            assert!(!hostname.contains('\u{0}'))
        }
    }

    #[test]
    fn check_uptime() {
        let sys = System::new();
        let uptime = sys.uptime();
        if System::IS_SUPPORTED {
            std::thread::sleep(std::time::Duration::from_millis(1000));
            let new_uptime = sys.uptime();
            assert!(uptime < new_uptime);
        }
    }
}
<|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/>.
 */

use model::{ColorMap, FieldCoord, Model, Move, Player};

use self::Event::*;

#[derive(PartialEq)]
pub enum Event {
    Click(FieldCoord),
    Exchange,
    NewGame(ColorMap<Player>),
    Resign,
    Undo,
    Redo,
    Quit,
}

pub fn update(model: &mut Model, event: Option<Event>) -> bool {
    if event == Some(Quit) {
        return false;
    }

    match model.current_player() {
        Player::Human => {
            if let Some(event) = event {
                handle_event(model, event);
            }
        }
        Player::Computer => {
            if model.ai.is_idle() {
                model.ai.think(model.board, 6);
            }

            if let Some(event) = event {
                match event {
                    Click(_) | Exchange => {}
                    _ => {
                        model.ai.stop();
                        handle_event(model, event);
                        return true;
                    }
                }
            }

            if let Some(mv) = model.ai.try_recv() {
                model.board.apply_move(&mv);
                model.last_move = Some(mv);
            }
        }
    }
    true
}

fn handle_event(model: &mut Model, event: Event) {
    match event {
        Click(clicked) => if !model.is_game_over() {
            handle_click(model, clicked);
        },
        Exchange => if model.board.can_exchange() && !model.is_game_over() {
            model.exchanging = !model.exchanging;
            model.clear_selection();
        },
        NewGame(players) => *model = Model::new(players),
        Resign => {
            model.commit_state();
            model.board.resign();
        }
        Undo => model.undo_move(),
        Redo => model.redo_move(),
        Quit => unreachable!(),
    }
}

fn handle_click(model: &mut Model, clicked: FieldCoord) {
    match model.selected_piece {
        Some(selected) => {
            if clicked.color() != model.board.turn() || selected == clicked {
                model.clear_selection();
            } else if model.board.is_piece_on_field(&clicked) {
                model.selected_piece = Some(clicked);
            } else {
                try_move(model, Move::move_from_field(&selected, &clicked));
                model.clear_selection();
            }
        }
        None => {
            if model.exchanging && try_move(model, Move::exchange_from_field(&clicked)) {
                model.exchanging = false;
            } else if !model.exchanging && clicked.color() == model.board.turn()
                && model.board.is_piece_on_field(&clicked)
            {
                model.selected_piece = Some(clicked);
            }
        }
    }
}

fn try_move(model: &mut Model, mv: Move) -> bool {
    if model.board.can_apply_move(&mv) {
        model.commit_state();
        model.board.apply_move(&mv);
        model.last_move = Some(mv);
        true
    } else {
        false
    }
}
<commit_msg>Don't ask AI to move if game is over<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/>.
 */

use model::{ColorMap, FieldCoord, Model, Move, Player};

use self::Event::*;

#[derive(PartialEq)]
pub enum Event {
    Click(FieldCoord),
    Exchange,
    NewGame(ColorMap<Player>),
    Resign,
    Undo,
    Redo,
    Quit,
}

pub fn update(model: &mut Model, event: Option<Event>) -> bool {
    if event == Some(Quit) {
        return false;
    }

    match model.current_player() {
        Player::Human => {
            if let Some(event) = event {
                handle_event(model, event);
            }
        }
        Player::Computer => {
            if let Some(event) = event {
                match event {
                    Click(_) | Exchange => {}
                    _ => {
                        model.ai.stop();
                        handle_event(model, event);
                        return true;
                    }
                }
            }

            if !model.is_game_over() {
                if model.ai.is_idle() {
                    model.ai.think(model.board, 6);
                }
                if let Some(mv) = model.ai.try_recv() {
                    model.board.apply_move(&mv);
                    model.last_move = Some(mv);
                }
            }
        }
    }
    true
}

fn handle_event(model: &mut Model, event: Event) {
    match event {
        Click(clicked) => if !model.is_game_over() {
            handle_click(model, clicked);
        },
        Exchange => if model.board.can_exchange() && !model.is_game_over() {
            model.exchanging = !model.exchanging;
            model.clear_selection();
        },
        NewGame(players) => *model = Model::new(players),
        Resign => {
            model.commit_state();
            model.board.resign();
        }
        Undo => model.undo_move(),
        Redo => model.redo_move(),
        Quit => unreachable!(),
    }
}

fn handle_click(model: &mut Model, clicked: FieldCoord) {
    match model.selected_piece {
        Some(selected) => {
            if clicked.color() != model.board.turn() || selected == clicked {
                model.clear_selection();
            } else if model.board.is_piece_on_field(&clicked) {
                model.selected_piece = Some(clicked);
            } else {
                try_move(model, Move::move_from_field(&selected, &clicked));
                model.clear_selection();
            }
        }
        None => {
            if model.exchanging && try_move(model, Move::exchange_from_field(&clicked)) {
                model.exchanging = false;
            } else if !model.exchanging && clicked.color() == model.board.turn()
                && model.board.is_piece_on_field(&clicked)
            {
                model.selected_piece = Some(clicked);
            }
        }
    }
}

fn try_move(model: &mut Model, mv: Move) -> bool {
    if model.board.can_apply_move(&mv) {
        model.commit_state();
        model.board.apply_move(&mv);
        model.last_move = Some(mv);
        true
    } else {
        false
    }
}
<|endoftext|>


<commit_before>// Copyright 2015-2016, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use std::mem::transmute;

use glib::object::{Downcast, IsA};
use glib::signal::connect;
use glib::translate::*;
use glib_ffi::gboolean;
use gdk::{Event, RGBA};
use gdk_ffi;
use pango;
use ffi;

use {
    Inhibit,
    Object,
    Rectangle,
    StateFlags,
    Widget,
};

pub trait WidgetExtManual {
    fn intersect(&self, area: &Rectangle, intersection: Option<&mut Rectangle>) -> bool;

    fn override_background_color(&self, state: StateFlags, color: &RGBA);

    fn override_color(&self, state: StateFlags, color: &RGBA);

    fn override_cursor(&self, cursor: &RGBA, secondary_cursor: &RGBA);

    fn override_font(&self, font: &pango::FontDescription);

    fn override_symbolic_color(&self, name: &str, color: &RGBA);

    fn connect_map_event<F: Fn(&Self, &Event) -> Inhibit + 'static>(&self, f: F) -> u64;

    fn connect_unmap_event<F: Fn(&Self, &Event) -> Inhibit + 'static>(&self, f: F) -> u64;
}

impl<O: IsA<Widget> + IsA<Object>> WidgetExtManual for O {
    fn intersect(&self, area: &Rectangle, mut intersection: Option<&mut Rectangle>) -> bool {
        unsafe {
            from_glib(ffi::gtk_widget_intersect(self.to_glib_none().0, area.to_glib_none().0, intersection.to_glib_none_mut().0))
        }
    }

    fn override_background_color(&self, state: StateFlags, color: &RGBA) {
        unsafe {
            ffi::gtk_widget_override_background_color(self.to_glib_none().0,
                                                      state.to_glib(),
                                                      color.to_glib_none().0);
        }
    }

    fn override_color(&self, state: StateFlags, color: &RGBA) {
        unsafe {
            ffi::gtk_widget_override_color(self.to_glib_none().0,
                                           state.to_glib(),
                                           color.to_glib_none().0);
        }
    }

    fn override_symbolic_color(&self, name: &str, color: &RGBA) {
        unsafe {
            ffi::gtk_widget_override_symbolic_color(self.to_glib_none().0,
                                                    name.to_glib_none().0,
                                                    color.to_glib_none().0);
        }
    }

    fn override_cursor(&self, cursor: &RGBA, secondary_cursor: &RGBA) {
        unsafe {
            ffi::gtk_widget_override_cursor(self.to_glib_none().0,
                                            cursor.to_glib_none().0,
                                            secondary_cursor.to_glib_none().0);
        }
    }

    fn override_font(&self, font: &pango::FontDescription) {
        unsafe {
            ffi::gtk_widget_override_font(self.to_glib_none().0, font.to_glib_none().0)
        }
    }

   fn connect_map_event<F: Fn(&Self, &Event) -> Inhibit + 'static>(&self, f: F) -> u64 {
        unsafe {
            let f: Box<Box<Fn(&Self, &Event) -> Inhibit + 'static>> = Box::new(Box::new(f));
            connect(self.to_glib_none().0, "map-event",
                transmute(event_any_trampoline::<Self> as usize), Box::into_raw(f) as *mut _)
        }
    }

    fn connect_unmap_event<F: Fn(&Self, &Event) -> Inhibit + 'static>(&self, f: F) -> u64 {
        unsafe {
            let f: Box<Box<Fn(&Self, &Event) -> Inhibit + 'static>> = Box::new(Box::new(f));
            connect(self.to_glib_none().0, "unmap-event",
                transmute(event_any_trampoline::<Self> as usize), Box::into_raw(f) as *mut _)
        }
    }
}

unsafe extern "C" fn event_any_trampoline<T>(this: *mut ffi::GtkWidget, event: *mut gdk_ffi::GdkEventAny,
        f: &Box<Fn(&T, &Event) -> Inhibit + 'static>) -> gboolean
where T: IsA<Widget> {
    callback_guard!();
    f(&Widget::from_glib_none(this).downcast_unchecked(), &from_glib_none(event)).to_glib()
}
<commit_msg>Remove manual duplicate<commit_after>// Copyright 2015-2016, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use std::mem::transmute;

use glib::object::{Downcast, IsA};
use glib::signal::connect;
use glib::translate::*;
use glib_ffi::gboolean;
use gdk::Event;
use gdk_ffi;
use pango;
use ffi;

use {
    Inhibit,
    Object,
    Rectangle,
    Widget,
};

pub trait WidgetExtManual {
    fn intersect(&self, area: &Rectangle, intersection: Option<&mut Rectangle>) -> bool;

    fn override_font(&self, font: &pango::FontDescription);

    fn connect_map_event<F: Fn(&Self, &Event) -> Inhibit + 'static>(&self, f: F) -> u64;

    fn connect_unmap_event<F: Fn(&Self, &Event) -> Inhibit + 'static>(&self, f: F) -> u64;
}

impl<O: IsA<Widget> + IsA<Object>> WidgetExtManual for O {
    fn intersect(&self, area: &Rectangle, mut intersection: Option<&mut Rectangle>) -> bool {
        unsafe {
            from_glib(ffi::gtk_widget_intersect(self.to_glib_none().0, area.to_glib_none().0, intersection.to_glib_none_mut().0))
        }
    }

    fn override_font(&self, font: &pango::FontDescription) {
        unsafe {
            ffi::gtk_widget_override_font(self.to_glib_none().0, font.to_glib_none().0)
        }
    }

   fn connect_map_event<F: Fn(&Self, &Event) -> Inhibit + 'static>(&self, f: F) -> u64 {
        unsafe {
            let f: Box<Box<Fn(&Self, &Event) -> Inhibit + 'static>> = Box::new(Box::new(f));
            connect(self.to_glib_none().0, "map-event",
                transmute(event_any_trampoline::<Self> as usize), Box::into_raw(f) as *mut _)
        }
    }

    fn connect_unmap_event<F: Fn(&Self, &Event) -> Inhibit + 'static>(&self, f: F) -> u64 {
        unsafe {
            let f: Box<Box<Fn(&Self, &Event) -> Inhibit + 'static>> = Box::new(Box::new(f));
            connect(self.to_glib_none().0, "unmap-event",
                transmute(event_any_trampoline::<Self> as usize), Box::into_raw(f) as *mut _)
        }
    }
}

unsafe extern "C" fn event_any_trampoline<T>(this: *mut ffi::GtkWidget, event: *mut gdk_ffi::GdkEventAny,
        f: &Box<Fn(&T, &Event) -> Inhibit + 'static>) -> gboolean
where T: IsA<Widget> {
    callback_guard!();
    f(&Widget::from_glib_none(this).downcast_unchecked(), &from_glib_none(event)).to_glib()
}
<|endoftext|>


<commit_before>// Copyright 2015-2018 Deyan Ginev. See the LICENSE
// file at the top-level directory of this distribution.
//
// Licensed under the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>.
// This file may not be copied, modified, or distributed
// except according to those terms.

//! Worker for performing corpus imports, when served as "init" tasks by the `CorTeX` dispatcher
use rand::seq::SliceRandom;
use rand::thread_rng;
use std::borrow::Cow;
use std::error::Error;
use std::fs::File;
use std::path::Path;
use std::thread;
use std::time::Duration;
use zmq::{Context, Message, SNDMORE};

use crate::backend;
use crate::backend::DEFAULT_DB_ADDRESS;
use crate::importer::Importer;
use crate::models::{Corpus, NewCorpus, Task};
use pericortex::worker::Worker;

/// `Worker` for initializing/importing a new corpus into `CorTeX`
#[derive(Debug, Clone)]
pub struct InitWorker {
  /// name of the service ("init")
  pub service: String,
  /// version, as usual
  pub version: f32,
  /// message size, as usual
  pub message_size: usize,
  /// full URL (including port) to task source/dispatcher
  pub source: String,
  /// full URL (including port) to task sink/receiver
  pub sink: String,
  /// address to the Task store backend
  /// (special case, only for the init service, third-party workers can't access the Task store
  /// directly)
  pub backend_address: String,
  /// thread-local unique identifier
  pub identity: String,
}
impl Default for InitWorker {
  fn default() -> InitWorker {
    InitWorker {
      service: "init".to_string(),
      version: 0.1,
      message_size: 100_000,
      source: "tcp://localhost:51695".to_string(),
      sink: "tcp://localhost:51696".to_string(),
      backend_address: DEFAULT_DB_ADDRESS.to_string(),
      identity: String::new(),
    }
  }
}
impl Worker for InitWorker {
  fn get_service(&self) -> &str { &self.service }
  fn get_source_address(&self) -> Cow<str> { Cow::Borrowed(&self.source) }
  fn get_sink_address(&self) -> Cow<str> { Cow::Borrowed(&self.sink) }
  fn get_identity(&self) -> &str { &self.identity }
  fn set_identity(&mut self, identity: String) { self.identity = identity; }
  fn message_size(&self) -> usize { self.message_size }

  fn convert(&self, path_opt: &Path) -> Result<File, Box<dyn Error>> {
    let path = path_opt.to_str().unwrap().to_string();
    let name = path
      .rsplit_once('/')
      .map(|x| x.1)
      .unwrap_or(&path)
      .to_lowercase(); // TODO: this is Unix path only
    let backend = backend::from_address(&self.backend_address);
    let corpus = NewCorpus {
      name,
      path: path.clone(),
      complex: true,
      description: String::new(),
    };
    // Add the new corpus.
    backend.add(&corpus).expect("Failed to create new corpus.");
    let registered_corpus =
      Corpus::find_by_name(&path, &backend.connection).expect("Failed to create new corpus.");

    // Create an importer for the corpus, and then process all entries to populate CorTeX tasks
    let importer = Importer {
      corpus: registered_corpus,
      backend: backend::from_address(&self.backend_address),
      cwd: Importer::cwd(),
    };

    importer.process()?;
    // TODO: Stopgap, we should do the error-reporting well
    Err(From::from("init worker does not return a file handle."))
  }

  fn start(&mut self, limit: Option<usize>) -> Result<(), Box<dyn Error>> {
    let mut work_counter = 0;
    let mut rng = thread_rng();
    // Connect to a task ventilator
    let context_source = Context::new();
    let source = context_source.socket(zmq::DEALER).unwrap();
    let letters: Vec<_> = "abcdefghijklmonpqrstuvwxyz".chars().collect();
    let mut identity = String::new();
    for _step in 1..20 {
      identity.push(*letters.choose(&mut rng).unwrap());
    }
    source.set_identity(identity.as_bytes()).unwrap();

    assert!(source.connect(&self.get_source_address()).is_ok());
    // Connect to a task sink
    let context_sink = Context::new();
    let sink = context_sink.socket(zmq::PUSH).unwrap();
    assert!(sink.connect(&self.get_sink_address()).is_ok());
    let backend = backend::from_address(&self.backend_address);
    // Work in perpetuity
    loop {
      let mut taskid_msg = Message::new();
      let mut recv_msg = Message::new();

      source.send(self.get_service(), 0).unwrap();
      source.recv(&mut taskid_msg, 0).unwrap();
      let taskid = taskid_msg.as_str().unwrap();

      // Terminating with an empty message in place of a payload (INIT is special)
      source.recv(&mut recv_msg, 0).unwrap();

      let task_result = Task::find(taskid.parse::<i64>().unwrap(), &backend.connection);
      let task = match task_result {
        Ok(t) => t,
        _ => {
          // If there was nothing to do, retry a minute later
          thread::sleep(Duration::new(60, 0));
          continue;
        },
      };

      self.convert(Path::new(&task.entry))?;
      sink.send(&identity, SNDMORE).unwrap();
      sink.send(&self.get_service(), SNDMORE).unwrap();
      sink.send(taskid, SNDMORE).unwrap();
      sink.send(&Vec::new(), 0).unwrap();

      work_counter += 1;
      if let Some(upper_bound) = limit {
        if work_counter >= upper_bound {
          // Give enough time to complete the Final job.
          thread::sleep(Duration::from_millis(500));
          break;
        }
      };
    }
    Ok(())
  }
}
<commit_msg>fix oversight in refactoring importer<commit_after>// Copyright 2015-2018 Deyan Ginev. See the LICENSE
// file at the top-level directory of this distribution.
//
// Licensed under the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>.
// This file may not be copied, modified, or distributed
// except according to those terms.

//! Worker for performing corpus imports, when served as "init" tasks by the `CorTeX` dispatcher
use rand::seq::SliceRandom;
use rand::thread_rng;
use std::borrow::Cow;
use std::error::Error;
use std::fs::File;
use std::path::Path;
use std::thread;
use std::time::Duration;
use zmq::{Context, Message, SNDMORE};

use crate::backend;
use crate::backend::DEFAULT_DB_ADDRESS;
use crate::importer::Importer;
use crate::models::{Corpus, NewCorpus, Task};
use pericortex::worker::Worker;

/// `Worker` for initializing/importing a new corpus into `CorTeX`
#[derive(Debug, Clone)]
pub struct InitWorker {
  /// name of the service ("init")
  pub service: String,
  /// version, as usual
  pub version: f32,
  /// message size, as usual
  pub message_size: usize,
  /// full URL (including port) to task source/dispatcher
  pub source: String,
  /// full URL (including port) to task sink/receiver
  pub sink: String,
  /// address to the Task store backend
  /// (special case, only for the init service, third-party workers can't access the Task store
  /// directly)
  pub backend_address: String,
  /// thread-local unique identifier
  pub identity: String,
}
impl Default for InitWorker {
  fn default() -> InitWorker {
    InitWorker {
      service: "init".to_string(),
      version: 0.1,
      message_size: 100_000,
      source: "tcp://localhost:51695".to_string(),
      sink: "tcp://localhost:51696".to_string(),
      backend_address: DEFAULT_DB_ADDRESS.to_string(),
      identity: String::new(),
    }
  }
}
impl Worker for InitWorker {
  fn get_service(&self) -> &str { &self.service }
  fn get_source_address(&self) -> Cow<str> { Cow::Borrowed(&self.source) }
  fn get_sink_address(&self) -> Cow<str> { Cow::Borrowed(&self.sink) }
  fn get_identity(&self) -> &str { &self.identity }
  fn set_identity(&mut self, identity: String) { self.identity = identity; }
  fn message_size(&self) -> usize { self.message_size }

  fn convert(&self, path_opt: &Path) -> Result<File, Box<dyn Error>> {
    let path = path_opt.to_str().unwrap().to_string();
    let name = path
      .rsplit_once('/')
      .map(|x| x.1)
      .unwrap_or(&path)
      .to_lowercase(); // TODO: this is Unix path only
    let backend = backend::from_address(&self.backend_address);
    let corpus = NewCorpus {
      name,
      path: path.clone(),
      complex: true,
      description: String::new(),
    };
    // Add the new corpus.
    backend.add(&corpus).expect("Failed to create new corpus.");
    let registered_corpus =
      Corpus::find_by_name(&corpus.name, &backend.connection).expect("Failed to create new corpus.");

    // Create an importer for the corpus, and then process all entries to populate CorTeX tasks
    let importer = Importer {
      corpus: registered_corpus,
      backend: backend::from_address(&self.backend_address),
      cwd: Importer::cwd(),
    };

    importer.process()?;
    // TODO: Stopgap, we should do the error-reporting well
    Err(From::from("init worker does not return a file handle."))
  }

  fn start(&mut self, limit: Option<usize>) -> Result<(), Box<dyn Error>> {
    let mut work_counter = 0;
    let mut rng = thread_rng();
    // Connect to a task ventilator
    let context_source = Context::new();
    let source = context_source.socket(zmq::DEALER).unwrap();
    let letters: Vec<_> = "abcdefghijklmonpqrstuvwxyz".chars().collect();
    let mut identity = String::new();
    for _step in 1..20 {
      identity.push(*letters.choose(&mut rng).unwrap());
    }
    source.set_identity(identity.as_bytes()).unwrap();

    assert!(source.connect(&self.get_source_address()).is_ok());
    // Connect to a task sink
    let context_sink = Context::new();
    let sink = context_sink.socket(zmq::PUSH).unwrap();
    assert!(sink.connect(&self.get_sink_address()).is_ok());
    let backend = backend::from_address(&self.backend_address);
    // Work in perpetuity
    loop {
      let mut taskid_msg = Message::new();
      let mut recv_msg = Message::new();

      source.send(self.get_service(), 0).unwrap();
      source.recv(&mut taskid_msg, 0).unwrap();
      let taskid = taskid_msg.as_str().unwrap();

      // Terminating with an empty message in place of a payload (INIT is special)
      source.recv(&mut recv_msg, 0).unwrap();

      let task_result = Task::find(taskid.parse::<i64>().unwrap(), &backend.connection);
      let task = match task_result {
        Ok(t) => t,
        _ => {
          // If there was nothing to do, retry a minute later
          thread::sleep(Duration::new(60, 0));
          continue;
        },
      };

      self.convert(Path::new(&task.entry))?;
      sink.send(&identity, SNDMORE).unwrap();
      sink.send(&self.get_service(), SNDMORE).unwrap();
      sink.send(taskid, SNDMORE).unwrap();
      sink.send(&Vec::new(), 0).unwrap();

      work_counter += 1;
      if let Some(upper_bound) = limit {
        if work_counter >= upper_bound {
          // Give enough time to complete the Final job.
          thread::sleep(Duration::from_millis(500));
          break;
        }
      };
    }
    Ok(())
  }
}
<|endoftext|>


<commit_before>//! Module: czmq-zhashx

use {czmq_sys, Colander, Error, ErrorKind, Result};
use std::{error, fmt, mem, ptr};
use std::any::Any;
use std::ffi::CString;
use std::os::raw::c_void;

pub struct ZHashX {
    zhashx: *mut czmq_sys::zhashx_t,
    owned: bool
}

unsafe impl Send for ZHashX {}
unsafe impl Sync for ZHashX {}

impl Drop for ZHashX {
    fn drop(&mut self) {
        if self.owned {
            unsafe { czmq_sys::zhashx_destroy(&mut self.zhashx) };
        }
    }
}

impl ZHashX {
    pub fn new() -> ZHashX {
        ZHashX {
            zhashx: unsafe { czmq_sys::zhashx_new() },
            owned: true,
        }
    }

    pub fn from_raw(zhashx: *mut czmq_sys::zhashx_t, owned: bool) -> ZHashX {
        ZHashX {
            zhashx: zhashx,
            owned: owned,
        }
    }

    // pub fn zhashx_unpack(frame: *mut zframe_t) -> *mut zhashx_t;
    // pub fn zhashx_destroy(self_p: *mut *mut zhashx_t);

    pub fn insert(&self, key: &str, item: Box<Any>) -> Result<()> {
        let key_c = try!(CString::new(key));
        let rc = unsafe { czmq_sys::zhashx_insert(self.zhashx, key_c.as_ptr() as *const c_void, Box::into_raw(item) as *mut c_void) };

        // Deliberately leak this memory, which will be managed by C
        mem::forget(key_c);

        if rc == -1 {
            Err(Error::new(ErrorKind::NonZero, ZHashXError::CmdFailed))
        } else {
            Ok(())
        }
    }

    pub fn update(&self, key: &str, item: Box<Any>) {
        let key_c = CString::new(key).unwrap_or(CString::new("").unwrap());
        unsafe { czmq_sys::zhashx_update(self.zhashx, key_c.as_ptr() as *const c_void, Box::into_raw(item) as *mut c_void) };

        // Deliberately leak this memory, which will be managed by C
        mem::forget(key_c);
    }

    pub fn delete(&self, key: &str) {
        let key_c = CString::new(key).unwrap_or(CString::new("").unwrap());
        unsafe { czmq_sys::zhashx_delete(self.zhashx, key_c.as_ptr() as *const c_void) };
    }

    // pub fn zhashx_purge(_self: *mut zhashx_t);

    pub fn lookup<T>(&self, key: &str) -> Option<Colander<T>> {
        let key_c = CString::new(key).unwrap_or(CString::new("").unwrap());
        unsafe {
            let ptr = czmq_sys::zhashx_lookup(self.zhashx, key_c.as_ptr() as *const c_void);
            if ptr != ptr::null_mut() {
                Some(mem::transmute(Colander::from_raw(ptr)))
            } else {
                None
            }
        }
    }

    // pub fn zhashx_rename(_self: *mut zhashx_t,
    //                      old_key: *const ::std::os::raw::c_void,
    //                      new_key: *const ::std::os::raw::c_void)
    //  -> ::std::os::raw::c_int;
    // pub fn zhashx_freefn(_self: *mut zhashx_t,
    //                      key: *const ::std::os::raw::c_void,
    //                      free_fn: zhashx_free_fn)
    //  -> *mut ::std::os::raw::c_void;
    // pub fn zhashx_size(_self: *mut zhashx_t) -> size_t;
    // pub fn zhashx_keys(_self: *mut zhashx_t) -> *mut zhashxx_t;
    // pub fn zhashx_values(_self: *mut zhashx_t) -> *mut zhashxx_t;
    // pub fn zhashx_first(_self: *mut zhashx_t) -> *mut ::std::os::raw::c_void;
    // pub fn zhashx_next(_self: *mut zhashx_t) -> *mut ::std::os::raw::c_void;
    // pub fn zhashx_cursor(_self: *mut zhashx_t)
    //  -> *const ::std::os::raw::c_void;
    // pub fn zhashx_comment(_self: *mut zhashx_t,
    //                       format: *const ::std::os::raw::c_char, ...);
    // pub fn zhashx_save(_self: *mut zhashx_t,
    //                    filename: *const ::std::os::raw::c_char)
    //  -> ::std::os::raw::c_int;
    // pub fn zhashx_load(_self: *mut zhashx_t,
    //                    filename: *const ::std::os::raw::c_char)
    //  -> ::std::os::raw::c_int;
    // pub fn zhashx_refresh(_self: *mut zhashx_t) -> ::std::os::raw::c_int;
    // pub fn zhashx_pack(_self: *mut zhashx_t) -> *mut zframe_t;
    // pub fn zhashx_dup(_self: *mut zhashx_t) -> *mut zhashx_t;
    // pub fn zhashx_set_destructor(_self: *mut zhashx_t,
    //                              destructor: zhashx_destructor_fn);
    // pub fn zhashx_set_duplicator(_self: *mut zhashx_t,
    //                              duplicator: zhashx_duplicator_fn);
    // pub fn zhashx_set_key_destructor(_self: *mut zhashx_t,
    //                                  destructor: zhashx_destructor_fn);
    // pub fn zhashx_set_key_duplicator(_self: *mut zhashx_t,
    //                                  duplicator: zhashx_duplicator_fn);
    // pub fn zhashx_set_key_comparator(_self: *mut zhashx_t,
    //                                  comparator: zhashx_comparator_fn);
    // pub fn zhashx_set_key_hasher(_self: *mut zhashx_t,
    //                              hasher: zhashx_hash_fn);
    // pub fn zhashx_dup_v2(_self: *mut zhashx_t) -> *mut zhashx_t;
    // pub fn zhashx_autofree(_self: *mut zhashx_t);
    // pub fn zhashx_foreach(_self: *mut zhashx_t, callback: zhashx_foreach_fn,
    //                       argument: *mut ::std::os::raw::c_void)
    //  -> ::std::os::raw::c_int;
}

#[derive(Debug)]
pub enum ZHashXError {
    CmdFailed,
}

impl fmt::Display for ZHashXError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ZHashXError::CmdFailed => write!(f, "ZHashX command failed"),
        }
    }
}

impl error::Error for ZHashXError {
    fn description(&self) -> &str {
        match *self {
            ZHashXError::CmdFailed => "ZHashX command failed",
        }
    }
}

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

    #[test]
    fn test_crud() {
        let hash = ZHashX::new();

        let test_value = ZCert::new().unwrap();
        let pubkey = test_value.public_txt().to_string();
        assert!(hash.insert("mykey", Box::new(test_value)).is_ok());

        let cert = hash.lookup::<ZCert>("mykey").unwrap();
        assert_eq!(cert.public_txt(), pubkey);

        let test_value = ZCert::new().unwrap();
        let pubkey = test_value.public_txt().to_string();
        hash.update("mykey", Box::new(test_value));

        let cert = hash.lookup::<ZCert>("mykey").unwrap();
        assert_eq!(cert.public_txt(), pubkey);

        hash.delete("mykey");
        assert!(hash.lookup::<ZCert>("mykey").is_none());
    }
}
<commit_msg>Added ability to insert raw ptrs<commit_after>//! Module: czmq-zhashx

use {czmq_sys, Colander, Error, ErrorKind, Result};
use std::{error, fmt, mem, ptr};
use std::any::Any;
use std::ffi::CString;
use std::os::raw::c_void;

pub struct ZHashX {
    zhashx: *mut czmq_sys::zhashx_t,
    owned: bool
}

unsafe impl Send for ZHashX {}
unsafe impl Sync for ZHashX {}

impl Drop for ZHashX {
    fn drop(&mut self) {
        if self.owned {
            unsafe { czmq_sys::zhashx_destroy(&mut self.zhashx) };
        }
    }
}

impl ZHashX {
    pub fn new() -> ZHashX {
        ZHashX {
            zhashx: unsafe { czmq_sys::zhashx_new() },
            owned: true,
        }
    }

    pub fn from_raw(zhashx: *mut czmq_sys::zhashx_t, owned: bool) -> ZHashX {
        ZHashX {
            zhashx: zhashx,
            owned: owned,
        }
    }

    // pub fn zhashx_unpack(frame: *mut zframe_t) -> *mut zhashx_t;
    // pub fn zhashx_destroy(self_p: *mut *mut zhashx_t);

    pub fn insert(&self, key: &str, item: Box<Any>) -> Result<()> {
        self.insert_raw(key, Box::into_raw(item) as *mut c_void)
    }

    pub fn insert_raw(&self, key: &str, item: *mut c_void) -> Result<()> {
        let key_c = try!(CString::new(key));
        let rc = unsafe { czmq_sys::zhashx_insert(self.zhashx, key_c.as_ptr() as *const c_void, item) };

        // Deliberately leak this memory, which will be managed by C
        mem::forget(key_c);

        if rc == -1 {
            Err(Error::new(ErrorKind::NonZero, ZHashXError::CmdFailed))
        } else {
            Ok(())
        }
    }

    pub fn update(&self, key: &str, item: Box<Any>) {
        let key_c = CString::new(key).unwrap_or(CString::new("").unwrap());
        unsafe { czmq_sys::zhashx_update(self.zhashx, key_c.as_ptr() as *const c_void, Box::into_raw(item) as *mut c_void) };

        // Deliberately leak this memory, which will be managed by C
        mem::forget(key_c);
    }

    pub fn delete(&self, key: &str) {
        let key_c = CString::new(key).unwrap_or(CString::new("").unwrap());
        unsafe { czmq_sys::zhashx_delete(self.zhashx, key_c.as_ptr() as *const c_void) };
    }

    // pub fn zhashx_purge(_self: *mut zhashx_t);

    pub fn lookup<T>(&self, key: &str) -> Option<Colander<T>> {
        let key_c = CString::new(key).unwrap_or(CString::new("").unwrap());
        unsafe {
            let ptr = czmq_sys::zhashx_lookup(self.zhashx, key_c.as_ptr() as *const c_void);
            if ptr != ptr::null_mut() {
                Some(mem::transmute(Colander::from_raw(ptr)))
            } else {
                None
            }
        }
    }

    // pub fn zhashx_rename(_self: *mut zhashx_t,
    //                      old_key: *const ::std::os::raw::c_void,
    //                      new_key: *const ::std::os::raw::c_void)
    //  -> ::std::os::raw::c_int;
    // pub fn zhashx_freefn(_self: *mut zhashx_t,
    //                      key: *const ::std::os::raw::c_void,
    //                      free_fn: zhashx_free_fn)
    //  -> *mut ::std::os::raw::c_void;
    // pub fn zhashx_size(_self: *mut zhashx_t) -> size_t;
    // pub fn zhashx_keys(_self: *mut zhashx_t) -> *mut zhashxx_t;
    // pub fn zhashx_values(_self: *mut zhashx_t) -> *mut zhashxx_t;
    // pub fn zhashx_first(_self: *mut zhashx_t) -> *mut ::std::os::raw::c_void;
    // pub fn zhashx_next(_self: *mut zhashx_t) -> *mut ::std::os::raw::c_void;
    // pub fn zhashx_cursor(_self: *mut zhashx_t)
    //  -> *const ::std::os::raw::c_void;
    // pub fn zhashx_comment(_self: *mut zhashx_t,
    //                       format: *const ::std::os::raw::c_char, ...);
    // pub fn zhashx_save(_self: *mut zhashx_t,
    //                    filename: *const ::std::os::raw::c_char)
    //  -> ::std::os::raw::c_int;
    // pub fn zhashx_load(_self: *mut zhashx_t,
    //                    filename: *const ::std::os::raw::c_char)
    //  -> ::std::os::raw::c_int;
    // pub fn zhashx_refresh(_self: *mut zhashx_t) -> ::std::os::raw::c_int;
    // pub fn zhashx_pack(_self: *mut zhashx_t) -> *mut zframe_t;
    // pub fn zhashx_dup(_self: *mut zhashx_t) -> *mut zhashx_t;
    // pub fn zhashx_set_destructor(_self: *mut zhashx_t,
    //                              destructor: zhashx_destructor_fn);
    // pub fn zhashx_set_duplicator(_self: *mut zhashx_t,
    //                              duplicator: zhashx_duplicator_fn);
    // pub fn zhashx_set_key_destructor(_self: *mut zhashx_t,
    //                                  destructor: zhashx_destructor_fn);
    // pub fn zhashx_set_key_duplicator(_self: *mut zhashx_t,
    //                                  duplicator: zhashx_duplicator_fn);
    // pub fn zhashx_set_key_comparator(_self: *mut zhashx_t,
    //                                  comparator: zhashx_comparator_fn);
    // pub fn zhashx_set_key_hasher(_self: *mut zhashx_t,
    //                              hasher: zhashx_hash_fn);
    // pub fn zhashx_dup_v2(_self: *mut zhashx_t) -> *mut zhashx_t;
    // pub fn zhashx_autofree(_self: *mut zhashx_t);
    // pub fn zhashx_foreach(_self: *mut zhashx_t, callback: zhashx_foreach_fn,
    //                       argument: *mut ::std::os::raw::c_void)
    //  -> ::std::os::raw::c_int;
}

#[derive(Debug)]
pub enum ZHashXError {
    CmdFailed,
}

impl fmt::Display for ZHashXError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ZHashXError::CmdFailed => write!(f, "ZHashX command failed"),
        }
    }
}

impl error::Error for ZHashXError {
    fn description(&self) -> &str {
        match *self {
            ZHashXError::CmdFailed => "ZHashX command failed",
        }
    }
}

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

    #[test]
    fn test_crud() {
        let hash = ZHashX::new();

        let test_value = ZCert::new().unwrap();
        let pubkey = test_value.public_txt().to_string();
        assert!(hash.insert("mykey", Box::new(test_value)).is_ok());

        let cert = hash.lookup::<ZCert>("mykey").unwrap();
        assert_eq!(cert.public_txt(), pubkey);

        let test_value = ZCert::new().unwrap();
        let pubkey = test_value.public_txt().to_string();
        hash.update("mykey", Box::new(test_value));

        let cert = hash.lookup::<ZCert>("mykey").unwrap();
        assert_eq!(cert.public_txt(), pubkey);

        hash.delete("mykey");
        assert!(hash.lookup::<ZCert>("mykey").is_none());
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate cc;
extern crate glsl_to_cxx;
extern crate webrender_build;

use std::collections::HashSet;
use std::fmt::Write;
use webrender_build::shader::{ShaderFeatureFlags, get_shader_features};

// Shader key is in "name feature,feature" format.
// File name needs to be formatted as "name_feature_feature".
fn shader_file(shader_key: &str) -> String {
    shader_key.replace(' ', "_").replace(',', "_")
}

fn write_load_shader(shader_keys: &[String]) {
    let mut load_shader = String::new();
    for s in shader_keys {
        let _ = write!(load_shader, "#include \"{}.h\"\n", shader_file(s));
    }
    load_shader.push_str("ProgramLoader load_shader(const char* name) {\n");
    for s in shader_keys {
        let _ = write!(load_shader, "  if (!strcmp(name, \"{}\")) {{ return {}_program::loader; }}\n",
                       s, shader_file(s));
    }
    load_shader.push_str("  return nullptr;\n}\n");
    std::fs::write(std::env::var("OUT_DIR").unwrap() + "/load_shader.h", load_shader).unwrap();
}

fn process_imports(shader_dir: &str, shader: &str, included: &mut HashSet<String>, output: &mut String) {
    if !included.insert(shader.into()) {
        return;
    }
    println!("cargo:rerun-if-changed={}/{}.glsl", shader_dir, shader);
    let source = std::fs::read_to_string(format!("{}/{}.glsl", shader_dir, shader)).unwrap();
    for line in source.lines() {
        if line.starts_with("#include ") {
            let imports = line["#include ".len() ..].split(',');
            for import in imports {
                process_imports(shader_dir, import, included, output);
            }
        } else if line.starts_with("#version ") || line.starts_with("#extension ") {
            // ignore
        } else {
            output.push_str(line);
            output.push('\n');
        }
    }
}

fn translate_shader(shader_key: &str, shader_dir: &str) {
    let mut imported = String::from("#define SWGL 1\n#define __VERSION__ 150\n");
    let _ = write!(imported, "#define WR_MAX_VERTEX_TEXTURE_WIDTH {}U\n",
                   webrender_build::MAX_VERTEX_TEXTURE_WIDTH);

    let (basename, features) =
        shader_key.split_at(shader_key.find(' ').unwrap_or(shader_key.len()));
    if !features.is_empty() {
        for feature in features.trim().split(',') {
            let _ = write!(imported, "#define WR_FEATURE_{}\n", feature);
        }
    }

    process_imports(shader_dir, basename, &mut HashSet::new(), &mut imported);

    let shader = shader_file(shader_key);

    let out_dir = std::env::var("OUT_DIR").unwrap();
    let imp_name = format!("{}/{}.c", out_dir, shader);
    std::fs::write(&imp_name, imported).unwrap();

    let mut build = cc::Build::new();
    build.no_default_flags(true);
    if let Ok(tool) = build.try_get_compiler() {
        if tool.is_like_msvc() {
            build.flag("/EP");
            if tool.path().to_str().map_or(false, |p| p.contains("clang")) {
                build.flag("/clang:-undef");
            } else {
                build.flag("/u");
            }
        } else {
            build.flag("-xc").flag("-P").flag("-undef");
        }
    }
    // Use SWGLPP target to avoid pulling CFLAGS/CXXFLAGS.
    build.target("SWGLPP");
    build.file(&imp_name);
    let vs = build.clone()
        .define("WR_VERTEX_SHADER", Some("1"))
        .expand();
    let fs = build.clone()
        .define("WR_FRAGMENT_SHADER", Some("1"))
        .expand();
    let vs_name = format!("{}/{}.vert", out_dir, shader);
    let fs_name = format!("{}/{}.frag", out_dir, shader);
    std::fs::write(&vs_name, vs).unwrap();
    std::fs::write(&fs_name, fs).unwrap();

    let args = vec![
        "glsl_to_cxx".to_string(),
        vs_name,
        fs_name,
    ];
    let result = glsl_to_cxx::translate(&mut args.into_iter());
    std::fs::write(format!("{}/{}.h", out_dir, shader), result).unwrap();
}

fn main() {
    let shader_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap() + "/../webrender/res";

    let shader_flags =
        ShaderFeatureFlags::GL |
        ShaderFeatureFlags::DUAL_SOURCE_BLENDING |
        ShaderFeatureFlags::ADVANCED_BLEND_EQUATION |
        ShaderFeatureFlags::DEBUG;
    let mut shaders: Vec<String> = Vec::new();
    for (name, features) in get_shader_features(shader_flags) {
        shaders.extend(features.iter().map(|f| {
            if f.is_empty() { name.to_owned() } else { format!("{} {}", name, f) }
        }));
    }

    shaders.sort();

    // We need to ensure that the C preprocessor does not pull compiler flags from
    // the host or target environment. Set up a SWGLPP target with empty flags to
    // work around this.
    if let Ok(target) = std::env::var("TARGET") {
        if let Ok(cc) = std::env::var(format!("CC_{}", target))
                        .or(std::env::var(format!("CC_{}", target.replace("-", "_")))) {
            std::env::set_var("CC_SWGLPP", cc);
        }
    }
    std::env::set_var("CFLAGS_SWGLPP", "");

    for shader in &shaders {
        translate_shader(shader, &shader_dir);
    }

    write_load_shader(&shaders);

    println!("cargo:rerun-if-changed=src/blend.h");
    println!("cargo:rerun-if-changed=src/composite.h");
    println!("cargo:rerun-if-changed=src/gl_defs.h");
    println!("cargo:rerun-if-changed=src/glsl.h");
    println!("cargo:rerun-if-changed=src/program.h");
    println!("cargo:rerun-if-changed=src/rasterize.h");
    println!("cargo:rerun-if-changed=src/swgl_ext.h");
    println!("cargo:rerun-if-changed=src/texture.h");
    println!("cargo:rerun-if-changed=src/vector_type.h");
    println!("cargo:rerun-if-changed=src/gl.cc");
    let mut build = cc::Build::new();
    build.cpp(true);

    if let Ok(tool) = build.try_get_compiler() {
        if tool.is_like_msvc() {
            build.flag("/std:c++17")
                 .flag("/EHs-")
                 .flag("/GR-")
                 .flag("/UMOZILLA_CONFIG_H");
        } else {
            build.flag("-std=c++17")
                 .flag("-fno-exceptions")
                 .flag("-fno-rtti")
                 .flag("-fno-math-errno")
                 .flag("-UMOZILLA_CONFIG_H");
        }
        // SWGL relies heavily on inlining for performance so override -Oz with -O2
        if tool.args().contains(&"-Oz".into()) {
            build.flag("-O2");
        }

        // Most GLSL compilers assume something like fast-math so we turn it on.
        // However, reciprocal division makes it so 1/1 = 0.999994 which can produce a lot of fuzz
        // in reftests and the use of reciprocal instructions usually involves a refinement step
        // which bloats our already bloated code. Further, our shader code is sufficiently parallel
        // that we're more likely to be throughput bound vs latency bound. Having fewer
        // instructions makes things easier on the processor and in places where it matters we can
        // probably explicitly use reciprocal instructions and avoid the refinement step.
        if tool.is_like_msvc() {
            build.flag("/fp:fast")
                 .flag("-Xclang")
                 .flag("-mrecip=none");
        } else if tool.is_like_clang() {
            // gcc only supports -mrecip=none on some targets so to keep
            // things simple we don't use -ffast-math with gcc at all
            build.flag("-ffast-math")
                 .flag("-mrecip=none");
        }
    }

    build.file("src/gl.cc")
        .define("_GLIBCXX_USE_CXX11_ABI", Some("0"))
        .include(shader_dir)
        .include("src")
        .include(std::env::var("OUT_DIR").unwrap())
        .compile("gl_cc");
}
<commit_msg>Backed out 10 changesets (bug 1751331) for causing bustages. CLOSED TREE<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate cc;
extern crate glsl_to_cxx;
extern crate webrender_build;

use std::collections::HashSet;
use std::fmt::Write;
use webrender_build::shader::{ShaderFeatureFlags, get_shader_features};

// Shader key is in "name feature,feature" format.
// File name needs to be formatted as "name_feature_feature".
fn shader_file(shader_key: &str) -> String {
    shader_key.replace(' ', "_").replace(',', "_")
}

fn write_load_shader(shader_keys: &[String]) {
    let mut load_shader = String::new();
    for s in shader_keys {
        let _ = write!(load_shader, "#include \"{}.h\"\n", shader_file(s));
    }
    load_shader.push_str("ProgramLoader load_shader(const char* name) {\n");
    for s in shader_keys {
        let _ = write!(load_shader, "  if (!strcmp(name, \"{}\")) {{ return {}_program::loader; }}\n",
                       s, shader_file(s));
    }
    load_shader.push_str("  return nullptr;\n}\n");
    std::fs::write(std::env::var("OUT_DIR").unwrap() + "/load_shader.h", load_shader).unwrap();
}

fn process_imports(shader_dir: &str, shader: &str, included: &mut HashSet<String>, output: &mut String) {
    if !included.insert(shader.into()) {
        return;
    }
    println!("cargo:rerun-if-changed={}/{}.glsl", shader_dir, shader);
    let source = std::fs::read_to_string(format!("{}/{}.glsl", shader_dir, shader)).unwrap();
    for line in source.lines() {
        if line.starts_with("#include ") {
            let imports = line["#include ".len() ..].split(',');
            for import in imports {
                process_imports(shader_dir, import, included, output);
            }
        } else if line.starts_with("#version ") || line.starts_with("#extension ") {
            // ignore
        } else {
            output.push_str(line);
            output.push('\n');
        }
    }
}

fn translate_shader(shader_key: &str, shader_dir: &str) {
    let mut imported = String::from("#define SWGL 1\n#define __VERSION__ 150\n");
    let _ = write!(imported, "#define WR_MAX_VERTEX_TEXTURE_WIDTH {}U\n",
                   webrender_build::MAX_VERTEX_TEXTURE_WIDTH);

    let (basename, features) =
        shader_key.split_at(shader_key.find(' ').unwrap_or(shader_key.len()));
    if !features.is_empty() {
        for feature in features.trim().split(',') {
            let _ = write!(imported, "#define WR_FEATURE_{}\n", feature);
        }
    }

    process_imports(shader_dir, basename, &mut HashSet::new(), &mut imported);

    let shader = shader_file(shader_key);

    let out_dir = std::env::var("OUT_DIR").unwrap();
    let imp_name = format!("{}/{}.c", out_dir, shader);
    std::fs::write(&imp_name, imported).unwrap();

    let mut build = cc::Build::new();
    build.no_default_flags(true);
    if let Ok(tool) = build.try_get_compiler() {
        if tool.is_like_msvc() {
            build.flag("/EP");
            if tool.path().to_str().map_or(false, |p| p.contains("clang")) {
                build.flag("/clang:-undef");
            } else {
                build.flag("/u");
            }
        } else {
            build.flag("-xc").flag("-P").flag("-undef");
        }
    }
    // Use SWGLPP target to avoid pulling CFLAGS/CXXFLAGS.
    build.target("SWGLPP");
    build.file(&imp_name);
    let vs = build.clone()
        .define("WR_VERTEX_SHADER", Some("1"))
        .expand();
    let fs = build.clone()
        .define("WR_FRAGMENT_SHADER", Some("1"))
        .expand();
    let vs_name = format!("{}/{}.vert", out_dir, shader);
    let fs_name = format!("{}/{}.frag", out_dir, shader);
    std::fs::write(&vs_name, vs).unwrap();
    std::fs::write(&fs_name, fs).unwrap();

    let args = vec![
        "glsl_to_cxx".to_string(),
        vs_name,
        fs_name,
    ];
    let result = glsl_to_cxx::translate(&mut args.into_iter());
    std::fs::write(format!("{}/{}.h", out_dir, shader), result).unwrap();
}

fn main() {
    let shader_dir = match std::env::var("MOZ_SRC") {
        Ok(dir) => dir + "/gfx/wr/webrender/res",
        Err(_) => std::env::var("CARGO_MANIFEST_DIR").unwrap() + "/../webrender/res",
    };

    let shader_flags =
        ShaderFeatureFlags::GL |
        ShaderFeatureFlags::DUAL_SOURCE_BLENDING |
        ShaderFeatureFlags::ADVANCED_BLEND_EQUATION |
        ShaderFeatureFlags::DEBUG;
    let mut shaders: Vec<String> = Vec::new();
    for (name, features) in get_shader_features(shader_flags) {
        shaders.extend(features.iter().map(|f| {
            if f.is_empty() { name.to_owned() } else { format!("{} {}", name, f) }
        }));
    }

    shaders.sort();

    // We need to ensure that the C preprocessor does not pull compiler flags from
    // the host or target environment. Set up a SWGLPP target with empty flags to
    // work around this.
    if let Ok(target) = std::env::var("TARGET") {
        if let Ok(cc) = std::env::var(format!("CC_{}", target))
                        .or(std::env::var(format!("CC_{}", target.replace("-", "_")))) {
            std::env::set_var("CC_SWGLPP", cc);
        }
    }
    std::env::set_var("CFLAGS_SWGLPP", "");

    for shader in &shaders {
        translate_shader(shader, &shader_dir);
    }

    write_load_shader(&shaders);

    println!("cargo:rerun-if-changed=src/blend.h");
    println!("cargo:rerun-if-changed=src/composite.h");
    println!("cargo:rerun-if-changed=src/gl_defs.h");
    println!("cargo:rerun-if-changed=src/glsl.h");
    println!("cargo:rerun-if-changed=src/program.h");
    println!("cargo:rerun-if-changed=src/rasterize.h");
    println!("cargo:rerun-if-changed=src/swgl_ext.h");
    println!("cargo:rerun-if-changed=src/texture.h");
    println!("cargo:rerun-if-changed=src/vector_type.h");
    println!("cargo:rerun-if-changed=src/gl.cc");
    let mut build = cc::Build::new();
    build.cpp(true);

    if let Ok(tool) = build.try_get_compiler() {
        if tool.is_like_msvc() {
            build.flag("/std:c++17")
                 .flag("/EHs-")
                 .flag("/GR-")
                 .flag("/UMOZILLA_CONFIG_H");
        } else {
            build.flag("-std=c++17")
                 .flag("-fno-exceptions")
                 .flag("-fno-rtti")
                 .flag("-fno-math-errno")
                 .flag("-UMOZILLA_CONFIG_H");
        }
        // SWGL relies heavily on inlining for performance so override -Oz with -O2
        if tool.args().contains(&"-Oz".into()) {
            build.flag("-O2");
        }

        // Most GLSL compilers assume something like fast-math so we turn it on.
        // However, reciprocal division makes it so 1/1 = 0.999994 which can produce a lot of fuzz
        // in reftests and the use of reciprocal instructions usually involves a refinement step
        // which bloats our already bloated code. Further, our shader code is sufficiently parallel
        // that we're more likely to be throughput bound vs latency bound. Having fewer
        // instructions makes things easier on the processor and in places where it matters we can
        // probably explicitly use reciprocal instructions and avoid the refinement step.
        if tool.is_like_msvc() {
            build.flag("/fp:fast")
                 .flag("-Xclang")
                 .flag("-mrecip=none");
        } else if tool.is_like_clang() {
            // gcc only supports -mrecip=none on some targets so to keep
            // things simple we don't use -ffast-math with gcc at all
            build.flag("-ffast-math")
                 .flag("-mrecip=none");
        }
    }

    build.file("src/gl.cc")
        .define("_GLIBCXX_USE_CXX11_ABI", Some("0"))
        .include(shader_dir)
        .include("src")
        .include(std::env::var("OUT_DIR").unwrap())
        .compile("gl_cc");
}
<|endoftext|>


<commit_before>use shell_escape::escape;
use std::ffi::OsStr;
use std::io;
use std::path::{Path, PathBuf};
use std::process::{self, Command};
use walkdir::WalkDir;

#[derive(Debug)]
pub enum CliError {
    CommandFailed(String),
    IoError(io::Error),
    ProjectRootNotFound,
    WalkDirError(walkdir::Error),
}

impl From<io::Error> for CliError {
    fn from(error: io::Error) -> Self {
        CliError::IoError(error)
    }
}

impl From<walkdir::Error> for CliError {
    fn from(error: walkdir::Error) -> Self {
        CliError::WalkDirError(error)
    }
}

struct FmtContext {
    check: bool,
    verbose: bool,
}

pub fn run(check: bool, verbose: bool) {
    fn try_run(context: &FmtContext) -> Result<bool, CliError> {
        let mut success = true;

        let project_root = project_root()?;

        success &= cargo_fmt(context, project_root.as_path())?;
        success &= cargo_fmt(context, &project_root.join("clippy_dev"))?;
        success &= cargo_fmt(context, &project_root.join("rustc_tools_util"))?;

        for entry in WalkDir::new(project_root.join("tests")) {
            let entry = entry?;
            let path = entry.path();

            if path.extension() != Some("rs".as_ref()) || entry.file_name() == "ice-3891.rs" {
                continue;
            }

            success &= rustfmt(context, &path)?;
        }

        Ok(success)
    }

    fn output_err(err: CliError) {
        match err {
            CliError::CommandFailed(command) => {
                eprintln!("error: A command failed! `{}`", command);
            },
            CliError::IoError(err) => {
                eprintln!("error: {}", err);
            },
            CliError::ProjectRootNotFound => {
                eprintln!("error: Can't determine root of project. Please run inside a Clippy working dir.");
            },
            CliError::WalkDirError(err) => {
                eprintln!("error: {}", err);
            },
        }
    }

    let context = FmtContext { check, verbose };
    let result = try_run(&context);
    let code = match result {
        Ok(true) => 0,
        Ok(false) => {
            eprintln!();
            eprintln!("Formatting check failed.");
            eprintln!("Run `./util/dev fmt` to update formatting.");
            1
        },
        Err(err) => {
            output_err(err);
            1
        },
    };
    process::exit(code);
}

fn format_command(program: impl AsRef<OsStr>, dir: impl AsRef<Path>, args: &[impl AsRef<OsStr>]) -> String {
    let arg_display: Vec<_> = args
        .iter()
        .map(|a| escape(a.as_ref().to_string_lossy()).to_owned())
        .collect();

    format!(
        "cd {} && {} {}",
        escape(dir.as_ref().to_string_lossy()),
        escape(program.as_ref().to_string_lossy()),
        arg_display.join(" ")
    )
}

fn exec(
    context: &FmtContext,
    program: impl AsRef<OsStr>,
    dir: impl AsRef<Path>,
    args: &[impl AsRef<OsStr>],
) -> Result<bool, CliError> {
    if context.verbose {
        println!("{}", format_command(&program, &dir, args));
    }

    let mut child = Command::new(&program).current_dir(&dir).args(args.iter()).spawn()?;
    let code = child.wait()?;
    let success = code.success();

    if !context.check && !success {
        return Err(CliError::CommandFailed(format_command(&program, &dir, args)));
    }

    Ok(success)
}

fn cargo_fmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> {
    let mut args = vec!["+nightly", "fmt", "--all"];
    if context.check {
        args.push("--");
        args.push("--check");
    }
    let success = exec(context, "cargo", path, &args)?;

    Ok(success)
}

fn rustfmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> {
    let mut args = vec!["+nightly".as_ref(), path.as_os_str()];
    if context.check {
        args.push("--check".as_ref());
    }
    let success = exec(context, "rustfmt", std::env::current_dir()?, &args)?;
    if !success {
        eprintln!("rustfmt failed on {}", path.display());
    }
    Ok(success)
}

fn project_root() -> Result<PathBuf, CliError> {
    let current_dir = std::env::current_dir()?;
    for path in current_dir.ancestors() {
        let result = std::fs::read_to_string(path.join("Cargo.toml"));
        if let Err(err) = &result {
            if err.kind() == io::ErrorKind::NotFound {
                continue;
            }
        }

        let content = result?;
        if content.contains("[package]\nname = \"clippy\"") {
            return Ok(path.to_path_buf());
        }
    }

    Err(CliError::ProjectRootNotFound)
}
<commit_msg>Avoid rustfmt bug on Windows<commit_after>use shell_escape::escape;
use std::ffi::OsStr;
use std::io;
use std::path::{Path, PathBuf};
use std::process::{self, Command};
use walkdir::WalkDir;

#[derive(Debug)]
pub enum CliError {
    CommandFailed(String),
    IoError(io::Error),
    ProjectRootNotFound,
    WalkDirError(walkdir::Error),
}

impl From<io::Error> for CliError {
    fn from(error: io::Error) -> Self {
        CliError::IoError(error)
    }
}

impl From<walkdir::Error> for CliError {
    fn from(error: walkdir::Error) -> Self {
        CliError::WalkDirError(error)
    }
}

struct FmtContext {
    check: bool,
    verbose: bool,
}

pub fn run(check: bool, verbose: bool) {
    fn try_run(context: &FmtContext) -> Result<bool, CliError> {
        let mut success = true;

        let project_root = project_root()?;

        success &= cargo_fmt(context, project_root.as_path())?;
        success &= cargo_fmt(context, &project_root.join("clippy_dev"))?;
        success &= cargo_fmt(context, &project_root.join("rustc_tools_util"))?;

        for entry in WalkDir::new(project_root.join("tests")) {
            let entry = entry?;
            let path = entry.path();

            if path.extension() != Some("rs".as_ref())
                || entry.file_name() == "ice-3891.rs"
                // Avoid rustfmt bug rust-lang/rustfmt#1873
                || cfg!(windows) && entry.file_name() == "implicit_hasher.rs"
            {
                continue;
            }

            success &= rustfmt(context, &path)?;
        }

        Ok(success)
    }

    fn output_err(err: CliError) {
        match err {
            CliError::CommandFailed(command) => {
                eprintln!("error: A command failed! `{}`", command);
            },
            CliError::IoError(err) => {
                eprintln!("error: {}", err);
            },
            CliError::ProjectRootNotFound => {
                eprintln!("error: Can't determine root of project. Please run inside a Clippy working dir.");
            },
            CliError::WalkDirError(err) => {
                eprintln!("error: {}", err);
            },
        }
    }

    let context = FmtContext { check, verbose };
    let result = try_run(&context);
    let code = match result {
        Ok(true) => 0,
        Ok(false) => {
            eprintln!();
            eprintln!("Formatting check failed.");
            eprintln!("Run `./util/dev fmt` to update formatting.");
            1
        },
        Err(err) => {
            output_err(err);
            1
        },
    };
    process::exit(code);
}

fn format_command(program: impl AsRef<OsStr>, dir: impl AsRef<Path>, args: &[impl AsRef<OsStr>]) -> String {
    let arg_display: Vec<_> = args
        .iter()
        .map(|a| escape(a.as_ref().to_string_lossy()).to_owned())
        .collect();

    format!(
        "cd {} && {} {}",
        escape(dir.as_ref().to_string_lossy()),
        escape(program.as_ref().to_string_lossy()),
        arg_display.join(" ")
    )
}

fn exec(
    context: &FmtContext,
    program: impl AsRef<OsStr>,
    dir: impl AsRef<Path>,
    args: &[impl AsRef<OsStr>],
) -> Result<bool, CliError> {
    if context.verbose {
        println!("{}", format_command(&program, &dir, args));
    }

    let mut child = Command::new(&program).current_dir(&dir).args(args.iter()).spawn()?;
    let code = child.wait()?;
    let success = code.success();

    if !context.check && !success {
        return Err(CliError::CommandFailed(format_command(&program, &dir, args)));
    }

    Ok(success)
}

fn cargo_fmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> {
    let mut args = vec!["+nightly", "fmt", "--all"];
    if context.check {
        args.push("--");
        args.push("--check");
    }
    let success = exec(context, "cargo", path, &args)?;

    Ok(success)
}

fn rustfmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> {
    let mut args = vec!["+nightly".as_ref(), path.as_os_str()];
    if context.check {
        args.push("--check".as_ref());
    }
    let success = exec(context, "rustfmt", std::env::current_dir()?, &args)?;
    if !success {
        eprintln!("rustfmt failed on {}", path.display());
    }
    Ok(success)
}

fn project_root() -> Result<PathBuf, CliError> {
    let current_dir = std::env::current_dir()?;
    for path in current_dir.ancestors() {
        let result = std::fs::read_to_string(path.join("Cargo.toml"));
        if let Err(err) = &result {
            if err.kind() == io::ErrorKind::NotFound {
                continue;
            }
        }

        let content = result?;
        if content.contains("[package]\nname = \"clippy\"") {
            return Ok(path.to_path_buf());
        }
    }

    Err(CliError::ProjectRootNotFound)
}
<|endoftext|>


<commit_before>use crate::ladders::ladders;
use oppai_field::construct_field::construct_field;
use oppai_field::field::NonZeroPos;
use oppai_field::player::Player;
use rand::SeedableRng;
use rand_xoshiro::Xoshiro256PlusPlus;
use std::sync::atomic::AtomicBool;

const SEED: u64 = 7;

#[test]
fn ladders_escape() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    .........
    .........
    .........
    .........
    ..aA.....
    .aAAa....
    ..aa.....
    .........
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);

  assert_eq!(pos, None);
  assert_eq!(score, 0);
  assert_eq!(depth, 0);
}

#[test]
fn ladders_capture_1() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    .........
    ......a..
    .........
    .........
    ..aA.....
    .aAAa....
    ..aa.....
    .........
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);

  assert_eq!(pos, NonZeroPos::new(field.to_pos(3, 3)));
  assert_eq!(score, 3);
  assert_eq!(depth, 5);
}

#[test]
fn ladders_capture_2() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    .........
    ......a..
    .........
    .........
    .........
    .aAAa....
    ..aa.....
    .........
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);

  assert_eq!(pos, NonZeroPos::new(field.to_pos(2, 4)));
  assert_eq!(score, 2);
  assert_eq!(depth, 6);
}

#[test]
fn ladders_capture_3() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    .........
    .......a.
    .........
    .........
    .........
    .aAAa....
    ..aa.....
    .........
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);

  assert_eq!(pos, NonZeroPos::new(field.to_pos(2, 4)));
  assert_eq!(score, 2);
  assert_eq!(depth, 7);
}

#[test]
fn ladders_capture_4() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    .........
    .........
    ........a
    ........a
    .........
    .aAAa....
    ..aa.....
    .........
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);

  assert_eq!(pos, NonZeroPos::new(field.to_pos(2, 4)));
  assert_eq!(score, 2);
  assert_eq!(depth, 8);
}

#[test]
fn ladders_side_capture_1() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    ..............
    ...........aa.
    .............a
    .............a
    ...........Aa.
    .......a..AAa.
    ........aaaa..
    .aAAa.........
    ..aa..........
    ..............
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);

  assert_eq!(pos, NonZeroPos::new(field.to_pos(2, 6)));
  assert_eq!(score, 2);
  assert_eq!(depth, 7);
}

#[test]
fn ladders_side_capture_2() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    ..............
    ..............
    ............a.
    ..............
    ..............
    ..............
    .....aa.......
    .aa.aAAA......
    .AA..AAa......
    .aa.aAa.......
    .....a........
    ..............
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);

  assert_eq!(pos, NonZeroPos::new(field.to_pos(3, 8)));
  assert_eq!(score, 2);
  assert_eq!(depth, 10);
}

#[test]
fn ladders_fork() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    ......
    .aa...
    .AA...
    .aaAa.
    ......
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);

  assert_eq!(pos, NonZeroPos::new(field.to_pos(3, 2)));
  assert_eq!(score, 1);
  assert_eq!(depth, 1);
}

#[test]
fn ladders_fork_deep() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    ..a...
    ..A...
    .a....
    .aAAa.
    ..aa..
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);

  assert_eq!(pos, NonZeroPos::new(field.to_pos(2, 2)));
  assert_eq!(score, 1);
  assert_eq!(depth, 2);
}

#[test]
fn ladders_fork_stupid() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    .......
    .aa.aa.
    .AA.AA.
    .aaAaa.
    .......
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);

  assert_eq!(pos, None);
  assert_eq!(score, 0);
  assert_eq!(depth, 0);
}

#[test]
fn ladders_stupid() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    ..........
    ........a.
    ..........
    .Aaa......
    ..AAAa....
    .Aaaa.....
    ..........
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);

  assert_eq!(pos, None);
  assert_eq!(score, 0);
  assert_eq!(depth, 0);
}

#[test]
fn ladders_not_viable_1() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    ..........
    ........a.
    ..........
    .AaaA.....
    .aAAAa....
    .Aaaa.....
    ..........
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);
  assert_eq!(pos, None);
  assert_eq!(score, 0);
  assert_eq!(depth, 0);
}

#[test]
fn ladders_viable() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    .........
    ......a..
    .........
    .........
    ..aa.....
    .aAAA....
    ..aAAa...
    .aAAa....
    ..aa.....
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);
  assert_eq!(pos, NonZeroPos::new(field.to_pos(5, 5)));
  assert_eq!(score, 7);
  assert_eq!(depth, 5);
}

#[test]
fn ladders_not_viable_2() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    ..........
    .......a..
    ..........
    ..........
    ...aa.....
    .AaAAA....
    ...aAAa...
    .AaAAa....
    ...aa.....
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);
  assert_eq!(pos, None);
  assert_eq!(score, 0);
  assert_eq!(depth, 0);
}

#[test]
fn ladders_viable_multi() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    .................
    .......aaaa......
    ......a....aAA...
    .....aA.....aAA..
    .....AA......aAA.
    .....AA.....aAA..
    .....aa....aAA...
    .a.....aaaa......
    .................
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);

  assert_eq!(pos, NonZeroPos::new(field.to_pos(4, 4)));
  assert_eq!(score, 5);
  assert_eq!(depth, 5);
}

#[test]
fn ladders_viable_complex() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    .........
    ...AA.a..
    ..A.a....
    .Aaa.....
    .aA..Aa..
    .aA..aA..
    .aA..A...
    .aAAAaa..
    ..aaa....
    .........
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (_, score, _) = ladders(&mut field, Player::Red, &should_stop);
  // It's possible to capture 8 points here but current method is
  // limited - it doesn't consider ladders after captures.
  assert_eq!(score, 6);
}

#[test]
fn ladders_depth_choice() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    .............
    .............
    .............
    .............
    .............
    .a.aaa.......
    ...AAA.......
    ..aaaaa......
    .............
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);
  assert_eq!(pos, NonZeroPos::new(field.to_pos(6, 6)));
  assert_eq!(score, 3);
  assert_eq!(depth, 2);
}
<commit_msg>Add few ladders test.<commit_after>use crate::ladders::ladders;
use oppai_field::construct_field::construct_field;
use oppai_field::field::NonZeroPos;
use oppai_field::player::Player;
use rand::SeedableRng;
use rand_xoshiro::Xoshiro256PlusPlus;
use std::sync::atomic::AtomicBool;

const SEED: u64 = 7;

#[test]
fn ladders_escape() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    .........
    .........
    .........
    .........
    ..aA.....
    .aAAa....
    ..aa.....
    .........
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);

  assert_eq!(pos, None);
  assert_eq!(score, 0);
  assert_eq!(depth, 0);
}

#[test]
fn ladders_capture_1() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    .........
    ......a..
    .........
    .........
    ..aA.....
    .aAAa....
    ..aa.....
    .........
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);

  assert_eq!(pos, NonZeroPos::new(field.to_pos(3, 3)));
  assert_eq!(score, 3);
  assert_eq!(depth, 5);
}

#[test]
fn ladders_capture_2() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    .........
    ......a..
    .........
    .........
    .........
    .aAAa....
    ..aa.....
    .........
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);

  assert_eq!(pos, NonZeroPos::new(field.to_pos(2, 4)));
  assert_eq!(score, 2);
  assert_eq!(depth, 6);
}

#[test]
fn ladders_capture_3() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    .........
    .......a.
    .........
    .........
    .........
    .aAAa....
    ..aa.....
    .........
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);

  assert_eq!(pos, NonZeroPos::new(field.to_pos(2, 4)));
  assert_eq!(score, 2);
  assert_eq!(depth, 7);
}

#[test]
fn ladders_capture_4() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    .........
    .........
    ........a
    ........a
    .........
    .aAAa....
    ..aa.....
    .........
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);

  assert_eq!(pos, NonZeroPos::new(field.to_pos(2, 4)));
  assert_eq!(score, 2);
  assert_eq!(depth, 8);
}

#[test]
fn ladders_side_capture_1() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    ..............
    ...........aa.
    .............a
    .............a
    ...........Aa.
    .......a..AAa.
    ........aaaa..
    .aAAa.........
    ..aa..........
    ..............
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);

  assert_eq!(pos, NonZeroPos::new(field.to_pos(2, 6)));
  assert_eq!(score, 2);
  assert_eq!(depth, 7);
}

#[test]
fn ladders_side_capture_2() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    ..............
    ..............
    ............a.
    ..............
    ..............
    ..............
    .....aa.......
    .aa.aAAA......
    .AA..AAa......
    .aa.aAa.......
    .....a........
    ..............
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);

  assert_eq!(pos, NonZeroPos::new(field.to_pos(3, 8)));
  assert_eq!(score, 2);
  assert_eq!(depth, 10);
}

#[test]
fn ladders_shift() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    ...........
    .........a.
    ...........
    .......a...
    ...........
    .aAAa......
    ..aa.......
    ...........
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);

  assert_eq!(pos, NonZeroPos::new(field.to_pos(2, 4)));
  assert_eq!(score, 2);
  assert_eq!(depth, 9);
}

#[test]
fn ladders_rotate() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    ............
    .........a..
    .......a....
    ............
    .aAAa.......
    ..aa......a.
    ............
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);

  assert_eq!(pos, NonZeroPos::new(field.to_pos(2, 3)));
  assert_eq!(score, 2);
  assert_eq!(depth, 11);
}

#[test]
fn ladders_fork() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    ......
    .aa...
    .AA...
    .aaAa.
    ......
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);

  assert_eq!(pos, NonZeroPos::new(field.to_pos(3, 2)));
  assert_eq!(score, 1);
  assert_eq!(depth, 1);
}

#[test]
fn ladders_fork_deep() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    ..a...
    ..A...
    .a....
    .aAAa.
    ..aa..
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);

  assert_eq!(pos, NonZeroPos::new(field.to_pos(2, 2)));
  assert_eq!(score, 1);
  assert_eq!(depth, 2);
}

#[test]
fn ladders_fork_stupid() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    .......
    .aa.aa.
    .AA.AA.
    .aaAaa.
    .......
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);

  assert_eq!(pos, None);
  assert_eq!(score, 0);
  assert_eq!(depth, 0);
}

#[test]
fn ladders_stupid() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    ..........
    ........a.
    ..........
    .Aaa......
    ..AAAa....
    .Aaaa.....
    ..........
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);

  assert_eq!(pos, None);
  assert_eq!(score, 0);
  assert_eq!(depth, 0);
}

#[test]
fn ladders_not_viable_1() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    ..........
    ........a.
    ..........
    .AaaA.....
    .aAAAa....
    .Aaaa.....
    ..........
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);
  assert_eq!(pos, None);
  assert_eq!(score, 0);
  assert_eq!(depth, 0);
}

#[test]
fn ladders_viable() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    .........
    ......a..
    .........
    .........
    ..aa.....
    .aAAA....
    ..aAAa...
    .aAAa....
    ..aa.....
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);
  assert_eq!(pos, NonZeroPos::new(field.to_pos(5, 5)));
  assert_eq!(score, 7);
  assert_eq!(depth, 5);
}

#[test]
fn ladders_not_viable_2() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    ..........
    .......a..
    ..........
    ..........
    ...aa.....
    .AaAAA....
    ...aAAa...
    .AaAAa....
    ...aa.....
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);
  assert_eq!(pos, None);
  assert_eq!(score, 0);
  assert_eq!(depth, 0);
}

#[test]
fn ladders_viable_multi() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    .................
    .......aaaa......
    ......a....aAA...
    .....aA.....aAA..
    .....AA......aAA.
    .....AA.....aAA..
    .....aa....aAA...
    .a.....aaaa......
    .................
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);

  assert_eq!(pos, NonZeroPos::new(field.to_pos(4, 4)));
  assert_eq!(score, 5);
  assert_eq!(depth, 5);
}

#[test]
fn ladders_viable_complex() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    .........
    ...AA.a..
    ..A.a....
    .Aaa.....
    .aA..Aa..
    .aA..aA..
    .aA..A...
    .aAAAaa..
    ..aaa....
    .........
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (_, score, _) = ladders(&mut field, Player::Red, &should_stop);
  // It's possible to capture 8 points here but current method is
  // limited - it doesn't consider ladders after captures.
  assert_eq!(score, 6);
}

#[test]
fn ladders_depth_choice() {
  let mut rng = Xoshiro256PlusPlus::seed_from_u64(SEED);
  let mut field = construct_field(
    &mut rng,
    "
    .............
    .............
    .............
    .............
    .............
    .a.aaa.......
    ...AAA.......
    ..aaaaa......
    .............
    ",
  );

  let should_stop = AtomicBool::new(false);

  let (pos, score, depth) = ladders(&mut field, Player::Red, &should_stop);
  assert_eq!(pos, NonZeroPos::new(field.to_pos(6, 6)));
  assert_eq!(score, 3);
  assert_eq!(depth, 2);
}
<|endoftext|>