<commit_before>//! Facilities for implementing chess engines.

pub mod time_manager;

use std::collections::VecDeque;
use std::ops::Deref;
use std::sync::Arc;
use std::time::{SystemTime, Duration};
use std::cmp::{min, max};
use std::io;
use uci::*;
use search::*;
use self::time_manager::*;

/// Serves UCI commands until a "quit" command is received.
///
/// The current thread will block until the UCI session is closed.
///
/// Returns `Err` if the handshake was unsuccessful, or if an IO error
/// occurred.
pub fn run_server<S: SearchExecutor<ReportData = Vec<Variation>>>() -> io::Result<()> {
    run_engine::<Engine<S>>()
}

/// The chess starting position in Forsyth–Edwards notation (FEN).
const START_POSITION_FEN: &'static str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 1";

/// The version of the program.
const VERSION: &'static str = "0.1";

/// The name of the program.
const NAME: &'static str = "Alcibiades";

/// The author of the program.
const AUTHOR: &'static str = "Evgeni Pandurski";

/// Represents a condition for terminating the search.
enum PlayWhen {
    TimeManagement(TimeManager), // Stop when the time manager says.
    MoveTime(u64), // Stop after the given number of milliseconds.
    Nodes(u64), // Stop when the given number of nodes has been searched.
    Depth(Depth), // Stop when the given search depth has been completed.
    Never, // An infinite search.
}

/// Implements `UciEngine` trait.
struct Engine<S: SearchExecutor<ReportData = Vec<Variation>>> {
    tt: Arc<S::HashTable>,
    position: S::SearchNode,

    // From `SearchThread`.
    status: SearchStatus,
    searcher: S,

    // Tells the engine when it must stop thinking and play the best
    // move it has found.
    play_when: PlayWhen,

    // Tells the engine if it will be allowed to ponder. This option
    // is needed because the engine might change its time management
    // algorithm when pondering is allowed.
    pondering_is_allowed: bool,

    // `true` if the engine is thinking in pondering mode at the
    // moment.
    is_pondering: bool,

    // This helps the engine decide when to send periodic progress
    // reports.
    silent_since: SystemTime,

    // A queue for the messages send by the engine to the GUI.
    queue: VecDeque<EngineReply>,
}

impl<S: SearchExecutor<ReportData = Vec<Variation>>> UciEngine for Engine<S> {
    fn name() -> String {
        format!("{} {}", NAME, VERSION)
    }

    fn author() -> String {
        AUTHOR.to_string()
    }

    fn options() -> Vec<(String, OptionDescription)> {
        // Add up all suported options.
        let mut options = vec![
            ("Hash".to_string(), OptionDescription::Spin { min: 1, max: 64 * 1024, default: 16 }),
            ("Clear Hash".to_string(), OptionDescription::Button),
            ("Ponder".to_string(), OptionDescription::Check { default: false }),
        ];
        options.extend(S::options());

        // Remove duplicated options.
        options.sort_by(|a, b| a.0.cmp(&b.0));
        let mut options_dedup = vec![];
        let mut prev_name = String::from("");
        for o in options.drain(..) {
            if o.0 == prev_name {
                continue;
            }
            prev_name = o.0.clone();
            options_dedup.push(o);
        }
        options_dedup
    }

    fn new(tt_size_mb: Option<usize>) -> Engine<S> {
        let tt = Arc::new(S::HashTable::new(tt_size_mb));
        Engine {
            tt: tt.clone(),
            position: S::SearchNode::from_history(START_POSITION_FEN, &mut vec![].into_iter())
                          .ok()
                          .unwrap(),
            status: SearchStatus {
                done: true,
                depth: 0,
                searched_nodes: 0,
                started_at: SystemTime::now(),
                duration_millis: 0,
                nps: 0,
            },
            searcher: S::new(tt),
            play_when: PlayWhen::Never,
            pondering_is_allowed: false,
            is_pondering: false,
            silent_since: SystemTime::now(),
            queue: VecDeque::new(),
        }
    }

    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            "Ponder" => {
                // TODO: Can we remove this?
                self.pondering_is_allowed = value == "true";
            }
            "Hash" => {
                // We do not support re-sizing of the transposition
                // table once the engine has been started.
                return;
            }
            "Clear Hash" => {
                self.tt.clear();
            }
            _ => (),
        }
        S::set_option(name, value);
    }

    fn new_game(&mut self) {
        self.tt.clear();
    }

    fn position(&mut self, fen: &str, moves: &mut Iterator<Item = &str>) {
        if let Ok(p) = S::SearchNode::from_history(fen, moves) {
            self.position = p;
        }
    }

    fn go(&mut self, params: GoParams) {
        // Note: We ignore the "mate" field.
        //
        // TODO: What should we do with "mate"?
        let GoParams { searchmoves,
                       ponder,
                       wtime,
                       btime,
                       winc,
                       binc,
                       movestogo,
                       depth,
                       nodes,
                       movetime,
                       infinite,
                       .. } = params;

        self.terminate();
        self.tt.new_search();
        self.is_pondering = ponder;
        self.play_when = if infinite {
            PlayWhen::Never
        } else if movetime.is_some() {
            PlayWhen::MoveTime(movetime.unwrap())
        } else if nodes.is_some() {
            PlayWhen::Nodes(nodes.unwrap())
        } else if depth.is_some() {
            PlayWhen::Depth(min(depth.unwrap(), DEPTH_MAX as u64) as Depth)
        } else {
            PlayWhen::TimeManagement(TimeManager::new(self.position.board().to_move,
                                                      self.pondering_is_allowed,
                                                      wtime,
                                                      btime,
                                                      winc,
                                                      binc,
                                                      movestogo))
        };
        self.silent_since = SystemTime::now();
        self.start(searchmoves);
    }

    fn ponder_hit(&mut self) {
        if self.status.done {
            self.queue_best_move();
        } else {
            self.is_pondering = false;
        }
    }

    fn stop(&mut self) {
        self.terminate();
        self.queue_best_move();
    }

    fn wait_for_reply(&mut self, duration: Duration) -> Option<EngineReply> {
        if self.queue.is_empty() {
            let done = self.status.done;

            // Wait for `search_thread` to do some work, and hopefully
            // update its status. (We must do this even when the
            // search is done -- in that case the next line will just
            // yield the CPU to another process.)
            self.wait_status_update(duration);

            if !done {
                let &SearchStatus { done, depth, searched_nodes, duration_millis, .. } =
                    &self.status;

                // Check if we must play now.
                if !self.is_pondering &&
                   match self.play_when {
                    PlayWhen::TimeManagement(ref tm) => done || tm.must_play(),
                    PlayWhen::MoveTime(t) => done || duration_millis >= t,
                    PlayWhen::Nodes(n) => done || searched_nodes >= n,
                    PlayWhen::Depth(d) => done || depth >= d,
                    PlayWhen::Never => false,
                } {
                    self.stop();
                }
            }
        }

        self.queue.pop_front()
    }

    fn exit(&mut self) {
        self.terminate();
    }
}

impl<S: SearchExecutor<ReportData = Vec<Variation>>> Engine<S> {
    /// Adds a progress report message to the queue.
    fn queue_progress_report(&mut self) {
        let &SearchStatus { depth, searched_nodes, nps, .. } = &self.status;
        self.queue.push_back(EngineReply::Info(vec![
            InfoItem { info_type: "depth".to_string(), data: format!("{}", depth) },
            InfoItem { info_type: "nodes".to_string(), data: format!("{}", searched_nodes) },
            InfoItem { info_type: "nps".to_string(), data: format!("{}", nps) },
        ]));
        self.silent_since = SystemTime::now();
    }

    /// Adds a message containing the current (multi)PV to the queue.
    fn queue_variations(&mut self, variations: &Vec<Variation>) {
        for (i, &Variation { ref moves, value, bound }) in variations.iter().enumerate() {
            let bound_suffix = match bound {
                BOUND_EXACT => "",
                BOUND_UPPER => " upperbound",
                BOUND_LOWER => " lowerbound",
                _ => panic!("unexpected bound type"),
            };
            let mut moves_string = String::new();
            for m in moves {
                moves_string.push_str(&m.notation());
                moves_string.push(' ');
            }
            self.queue.push_back(EngineReply::Info(vec![
                InfoItem { info_type: "depth".to_string(), data: format!("{}", self.status.depth) },
                InfoItem { info_type: "multipv".to_string(), data: format!("{}", i + 1) },
                InfoItem { info_type: "score".to_string(), data: format!("cp {}{}", value, bound_suffix) },
                InfoItem { info_type: "time".to_string(), data: format!("{}", self.status.duration_millis) },
                InfoItem { info_type: "nodes".to_string(), data: format!("{}", self.status.searched_nodes) },
                InfoItem { info_type: "nps".to_string(), data: format!("{}", self.status.nps) },
                InfoItem { info_type: "pv".to_string(), data: moves_string },
            ]));
        }
        self.silent_since = SystemTime::now();
    }

    /// Adds a message containing the current best move to the queue.
    fn queue_best_move(&mut self) {
        let pv = extract_pv(self.tt.deref(), &self.position, 2);
        let best_move = if let Some(m) = pv.moves.get(0) {
            m.notation()
        } else {
            // If the PV is empty, pick the first legal move.
            self.position.legal_moves().get(0).map_or("0000".to_string(), |m| m.notation())
        };
        self.queue.push_back(EngineReply::BestMove {
            best_move: best_move,
            ponder_move: pv.moves.get(1).map(|m| m.notation()),
        });
        self.silent_since = SystemTime::now();
    }

    /// Terminates the currently running search (if any), starts a new
    /// search, updates the status.
    fn start(&mut self, mut searchmoves: Vec<String>) {
        // Validate `searchmoves`.
        let mut moves = vec![];
        let legal_moves = self.position.legal_moves();
        if !searchmoves.is_empty() {
            searchmoves.sort();
            for m in legal_moves.iter() {
                if searchmoves.binary_search(&m.notation()).is_ok() {
                    moves.push(*m);
                }
            }
        };
        let searchmoves = if moves.is_empty() {
            legal_moves
        } else {
            moves
        };

        // Terminate the currently running search.
        self.terminate();

        // Update the status.
        self.status = SearchStatus {
            done: false,
            depth: 0,
            searched_nodes: 0,
            started_at: SystemTime::now(),
            duration_millis: 0,
            ..self.status
        };

        // Start a new search.
        self.searcher.start_search(SearchParams {
            search_id: 0,
            position: self.position.clone(),
            depth: DEPTH_MAX,
            lower_bound: VALUE_MIN,
            upper_bound: VALUE_MAX,
            searchmoves: searchmoves,
        });
    }

    /// Terminates the currently running search (if any), updates the
    /// status.
    fn terminate(&mut self) {
        self.searcher.terminate_search();
        while !self.status.done {
            self.wait_status_update(Duration::from_millis(1000));
        }
    }

    /// Waits for a search status update, timing out after a specified
    /// duration or earlier.
    fn wait_status_update(&mut self, duration: Duration) {
        self.searcher.wait_report(duration);
        while let Ok(r) = self.searcher.try_recv_report() {
            self.process_report(r)
        }
    }

    /// A helper method. It updates the current status according to
    /// the received report message.
    fn process_report(&mut self, report: SearchReport<Vec<Variation>>) {
        // Register the search status with the time manager.
        if let PlayWhen::TimeManagement(ref mut tm) = self.play_when {
            tm.update_status(&report);
        }

        let duration = self.status.started_at.elapsed().unwrap();
        self.status.duration_millis = 1000 * duration.as_secs() +
                                      (duration.subsec_nanos() / 1000000) as u64;
        self.status.searched_nodes = report.searched_nodes;
        self.status.nps = 1000 * (self.status.nps + self.status.searched_nodes) /
                          (1000 + self.status.duration_millis);
        self.status.depth = report.depth;
        self.status.done = report.done;

        // Send the (multi)PV if available.
        self.queue_variations(&report.data);

        // Send a progress report periodically.
        if self.silent_since.elapsed().unwrap().as_secs() > 10 {
            self.queue_progress_report();
        }
    }
}

/// Contains information about the current progress of a search.
struct SearchStatus {
    /// `true` if the search is done, `false` otherwise.
    pub done: bool,

    /// The search depth completed so far.
    pub depth: Depth,

    /// The starting time for the search.
    pub started_at: SystemTime,

    /// The duration of the search in milliseconds.
    pub duration_millis: u64,

    /// The number of analyzed nodes.
    pub searched_nodes: u64,

    /// Average number of analyzed nodes per second.
    pub nps: u64,
}
<commit_msg>move the started_at field to Engine<commit_after>//! Facilities for implementing chess engines.

pub mod time_manager;

use std::collections::VecDeque;
use std::ops::Deref;
use std::sync::Arc;
use std::time::{SystemTime, Duration};
use std::cmp::min;
use std::io;
use uci::*;
use search::*;
use self::time_manager::*;

/// Serves UCI commands until a "quit" command is received.
///
/// The current thread will block until the UCI session is closed.
///
/// Returns `Err` if the handshake was unsuccessful, or if an IO error
/// occurred.
pub fn run_server<S: SearchExecutor<ReportData = Vec<Variation>>>() -> io::Result<()> {
    run_engine::<Engine<S>>()
}

/// The chess starting position in Forsyth–Edwards notation (FEN).
const START_FEN: &'static str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 1";

/// The version of the program.
const VERSION: &'static str = "0.1";

/// The name of the program.
const NAME: &'static str = "Alcibiades";

/// The author of the program.
const AUTHOR: &'static str = "Evgeni Pandurski";

/// Represents a condition for terminating the search.
enum PlayWhen {
    TimeManagement(TimeManager), // Stop when the time manager says.
    MoveTime(u64), // Stop after the given number of milliseconds.
    Nodes(u64), // Stop when the given number of nodes has been searched.
    Depth(Depth), // Stop when the given search depth has been completed.
    Never, // An infinite search.
}

/// Implements `UciEngine` trait.
struct Engine<S: SearchExecutor<ReportData = Vec<Variation>>> {
    tt: Arc<S::HashTable>,
    position: S::SearchNode,
    searcher: S,

    // The starting time of the current/last search.
    started_at: SystemTime,

    // The status of the current/last search.
    status: SearchStatus,

    // Tells the engine when it must stop thinking and play the best
    // move it has found.
    play_when: PlayWhen,

    // Tells the engine if it will be allowed to ponder. This option
    // is needed because the engine might change its time management
    // algorithm when pondering is allowed.
    pondering_is_allowed: bool,

    // `true` if the engine is thinking in pondering mode at the moment.
    is_pondering: bool,

    // A queue for the messages send by the engine to the GUI.
    queue: VecDeque<EngineReply>,

    // Helps the engine decide when to send periodic progress reports.
    silent_since: SystemTime,
}

impl<S: SearchExecutor<ReportData = Vec<Variation>>> UciEngine for Engine<S> {
    fn name() -> String {
        format!("{} {}", NAME, VERSION)
    }

    fn author() -> String {
        AUTHOR.to_string()
    }

    fn options() -> Vec<(String, OptionDescription)> {
        // Add up all suported options.
        let mut options = vec![
            ("Hash".to_string(), OptionDescription::Spin { min: 1, max: 64 * 1024, default: 16 }),
            ("Clear Hash".to_string(), OptionDescription::Button),
            ("Ponder".to_string(), OptionDescription::Check { default: false }),
        ];
        options.extend(S::options());

        // Remove duplicated options.
        options.sort_by(|a, b| a.0.cmp(&b.0));
        let mut options_dedup = vec![];
        let mut prev_name = String::from("");
        for o in options.drain(..) {
            if o.0 == prev_name {
                continue;
            }
            prev_name = o.0.clone();
            options_dedup.push(o);
        }
        options_dedup
    }

    fn new(tt_size_mb: Option<usize>) -> Engine<S> {
        let tt = Arc::new(S::HashTable::new(tt_size_mb));
        Engine {
            tt: tt.clone(),
            position: S::SearchNode::from_history(START_FEN, &mut vec![].into_iter()).ok().unwrap(),
            searcher: S::new(tt),
            started_at: SystemTime::now(),
            status: SearchStatus {
                done: true,
                depth: 0,
                searched_nodes: 0,
                duration_millis: 0,
                nps: 0,
            },
            play_when: PlayWhen::Never,
            pondering_is_allowed: false,
            is_pondering: false,
            queue: VecDeque::new(),
            silent_since: SystemTime::now(),
        }
    }

    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            "Ponder" => {
                // TODO: Can we remove this?
                self.pondering_is_allowed = value == "true";
            }
            "Hash" => {
                // We do not support re-sizing of the transposition
                // table once the engine has been started.
                return;
            }
            "Clear Hash" => {
                self.tt.clear();
            }
            _ => (),
        }
        S::set_option(name, value);
    }

    fn new_game(&mut self) {
        self.tt.clear();
    }

    fn position(&mut self, fen: &str, moves: &mut Iterator<Item = &str>) {
        if let Ok(p) = S::SearchNode::from_history(fen, moves) {
            self.position = p;
        }
    }

    fn go(&mut self, params: GoParams) {
        // Note: We ignore the "mate" field.
        //
        // TODO: What should we do with "mate"?
        let GoParams { searchmoves,
                       ponder,
                       wtime,
                       btime,
                       winc,
                       binc,
                       movestogo,
                       depth,
                       nodes,
                       movetime,
                       infinite,
                       .. } = params;

        self.terminate();
        self.tt.new_search();
        self.is_pondering = ponder;
        self.play_when = if infinite {
            PlayWhen::Never
        } else if movetime.is_some() {
            PlayWhen::MoveTime(movetime.unwrap())
        } else if nodes.is_some() {
            PlayWhen::Nodes(nodes.unwrap())
        } else if depth.is_some() {
            PlayWhen::Depth(min(depth.unwrap(), DEPTH_MAX as u64) as Depth)
        } else {
            PlayWhen::TimeManagement(TimeManager::new(self.position.board().to_move,
                                                      self.pondering_is_allowed,
                                                      wtime,
                                                      btime,
                                                      winc,
                                                      binc,
                                                      movestogo))
        };
        self.silent_since = SystemTime::now();
        self.start(searchmoves);
    }

    fn ponder_hit(&mut self) {
        if self.status.done {
            self.queue_best_move();
        } else {
            self.is_pondering = false;
        }
    }

    fn stop(&mut self) {
        self.terminate();
        self.queue_best_move();
    }

    fn wait_for_reply(&mut self, duration: Duration) -> Option<EngineReply> {
        if self.queue.is_empty() {
            let done = self.status.done;

            // Wait for `search_thread` to do some work, and hopefully
            // update its status. (We must do this even when the
            // search is done -- in that case the next line will just
            // yield the CPU to another process.)
            self.wait_status_update(duration);

            if !done {
                let &SearchStatus { done, depth, searched_nodes, duration_millis, .. } =
                    &self.status;

                // Check if we must play now.
                if !self.is_pondering &&
                   match self.play_when {
                    PlayWhen::TimeManagement(ref tm) => done || tm.must_play(),
                    PlayWhen::MoveTime(t) => done || duration_millis >= t,
                    PlayWhen::Nodes(n) => done || searched_nodes >= n,
                    PlayWhen::Depth(d) => done || depth >= d,
                    PlayWhen::Never => false,
                } {
                    self.stop();
                }
            }
        }

        self.queue.pop_front()
    }

    fn exit(&mut self) {
        self.terminate();
    }
}

impl<S: SearchExecutor<ReportData = Vec<Variation>>> Engine<S> {
    /// Adds a progress report message to the queue.
    fn queue_progress_report(&mut self) {
        let SearchStatus { ref depth, ref searched_nodes, ref nps, .. } = self.status;
        self.queue.push_back(EngineReply::Info(vec![
            InfoItem { info_type: "depth".to_string(), data: format!("{}", depth) },
            InfoItem { info_type: "nodes".to_string(), data: format!("{}", searched_nodes) },
            InfoItem { info_type: "nps".to_string(), data: format!("{}", nps) },
        ]));
        self.silent_since = SystemTime::now();
    }

    /// Adds a message containing the current (multi)PV to the queue.
    fn queue_variations(&mut self, variations: &Vec<Variation>) {
        let SearchStatus { ref depth, ref searched_nodes, ref duration_millis, ref nps, .. } =
            self.status;
        for (i, &Variation { ref moves, value, bound }) in variations.iter().enumerate() {
            let bound_suffix = match bound {
                BOUND_EXACT => "",
                BOUND_UPPER => " upperbound",
                BOUND_LOWER => " lowerbound",
                _ => panic!("unexpected bound type"),
            };
            let mut moves_string = String::new();
            for m in moves {
                moves_string.push_str(&m.notation());
                moves_string.push(' ');
            }
            self.queue.push_back(EngineReply::Info(vec![
                InfoItem { info_type: "depth".to_string(), data: format!("{}", depth) },
                InfoItem { info_type: "multipv".to_string(), data: format!("{}", i + 1) },
                InfoItem { info_type: "score".to_string(), data: format!("cp {}{}", value, bound_suffix) },
                InfoItem { info_type: "time".to_string(), data: format!("{}", duration_millis) },
                InfoItem { info_type: "nodes".to_string(), data: format!("{}", searched_nodes) },
                InfoItem { info_type: "nps".to_string(), data: format!("{}", nps) },
                InfoItem { info_type: "pv".to_string(), data: moves_string },
            ]));
        }
        self.silent_since = SystemTime::now();
    }

    /// Adds a message containing the current best move to the queue.
    fn queue_best_move(&mut self) {
        let pv = extract_pv(self.tt.deref(), &self.position, 2);
        let best_move = if let Some(m) = pv.moves.get(0) {
            m.notation()
        } else {
            // If the PV is empty, pick the first legal move.
            self.position.legal_moves().get(0).map_or("0000".to_string(), |m| m.notation())
        };
        self.queue.push_back(EngineReply::BestMove {
            best_move: best_move,
            ponder_move: pv.moves.get(1).map(|m| m.notation()),
        });
        self.silent_since = SystemTime::now();
    }

    /// Terminates the currently running search (if any), starts a new
    /// search, updates the status.
    fn start(&mut self, mut searchmoves: Vec<String>) {
        // Validate `searchmoves`.
        let mut moves = vec![];
        let legal_moves = self.position.legal_moves();
        if !searchmoves.is_empty() {
            searchmoves.sort();
            for m in legal_moves.iter() {
                if searchmoves.binary_search(&m.notation()).is_ok() {
                    moves.push(*m);
                }
            }
        };
        let searchmoves = if moves.is_empty() {
            legal_moves
        } else {
            moves
        };

        // Terminate the currently running search.
        self.terminate();

        // Update the status.
        self.status = SearchStatus {
            done: false,
            depth: 0,
            searched_nodes: 0,
            duration_millis: 0,
            ..self.status
        };

        // Start a new search.
        self.searcher.start_search(SearchParams {
            search_id: 0,
            position: self.position.clone(),
            depth: DEPTH_MAX,
            lower_bound: VALUE_MIN,
            upper_bound: VALUE_MAX,
            searchmoves: searchmoves,
        });
    }

    /// Terminates the currently running search (if any), updates the
    /// status.
    fn terminate(&mut self) {
        self.searcher.terminate_search();
        while !self.status.done {
            self.wait_status_update(Duration::from_millis(1000));
        }
    }

    /// Waits for a search status update, timing out after a specified
    /// duration or earlier.
    fn wait_status_update(&mut self, duration: Duration) {
        self.searcher.wait_report(duration);
        while let Ok(r) = self.searcher.try_recv_report() {
            self.process_report(r)
        }
    }

    /// A helper method. It updates the current status according to
    /// the received report message.
    fn process_report(&mut self, report: SearchReport<Vec<Variation>>) {
        // Register the search status with the time manager.
        if let PlayWhen::TimeManagement(ref mut tm) = self.play_when {
            tm.update_status(&report);
        }

        let duration = self.started_at.elapsed().unwrap();
        self.status.duration_millis = 1000 * duration.as_secs() +
                                      (duration.subsec_nanos() / 1000000) as u64;
        self.status.searched_nodes = report.searched_nodes;
        self.status.nps = 1000 * (self.status.nps + self.status.searched_nodes) /
                          (1000 + self.status.duration_millis);
        self.status.depth = report.depth;
        self.status.done = report.done;

        // Send the (multi)PV if available.
        self.queue_variations(&report.data);

        // Send a progress report periodically.
        if self.silent_since.elapsed().unwrap().as_secs() > 10 {
            self.queue_progress_report();
        }
    }
}

/// Contains information about the current progress of a search.
struct SearchStatus {
    pub done: bool,
    pub depth: Depth,
    pub searched_nodes: u64,

    /// The duration of the search in milliseconds.
    pub duration_millis: u64,

    /// Average number of analyzed nodes per second.
    pub nps: u64,
}
<|endoftext|>


<commit_before>use error::msg::Msg;
use error::msg::MsgWithPos;

use parser::lexer::position::Position;

pub struct Diagnostic {
    errors: Vec<MsgWithPos>,
}

impl Diagnostic {
    pub fn new() -> Diagnostic {
        Diagnostic {
            errors: Vec::new()
        }
    }

    pub fn report(&mut self, pos: Position, msg: Msg) {
        self.errors.push(MsgWithPos::new(pos, msg));
    }

    pub fn has_errors(&self) -> bool {
        !self.errors.is_empty()
    }
}
<commit_msg>diag: now uses RefCell too<commit_after>use std::cell::RefCell;

use error::msg::Msg;
use error::msg::MsgWithPos;

use parser::lexer::position::Position;

pub struct Diagnostic {
    errors: RefCell<Vec<MsgWithPos>>,
}

impl Diagnostic {
    pub fn new() -> Diagnostic {
        Diagnostic {
            errors: RefCell::new(Vec::new())
        }
    }

    pub fn report(&self, pos: Position, msg: Msg) {
        self.errors.borrow_mut().push(MsgWithPos::new(pos, msg));
    }

    pub fn has_errors(&self) -> bool {
        !self.errors.borrow().is_empty()
    }
}
<|endoftext|>


<commit_before>use std::sync::{Arc, RwLock};
use std::time::{Duration, Instant};

use super::*;
use input;
use res;
use sched;
use video;

type Result<T> = ::std::result::Result<T, ::failure::Error>;

#[derive(Default, Copy, Clone)]
struct ContextData {
    shutdown: bool,
}

/// The context of sub-systems that could be accessed from multi-thread environments safely.
#[derive(Clone)]
pub struct Context {
    pub res: Arc<res::ResourceSystemShared>,
    pub input: Arc<input::InputSystemShared>,
    pub time: Arc<time::TimeSystemShared>,
    pub video: Arc<video::VideoSystemShared>,
    pub window: Arc<window::WindowShared>,
    pub sched: Arc<sched::ScheduleSystemShared>,

    data: Arc<RwLock<ContextData>>,
}

impl Context {
    /// Shutdown the whole application at the end of this frame.
    pub fn shutdown(&self) {
        self.data.write().unwrap().shutdown = true;
    }

    /// Returns true if we are going to shutdown the application at the end of this frame.
    pub fn is_shutdown(&self) -> bool {
        self.data.read().unwrap().shutdown
    }
}

/// `Engine` is the root object of the game application. It binds various sub-systems in
/// a central place and takes take of trivial tasks like the execution order or life-time
/// management.
pub struct Engine {
    pub window: window::Window,
    pub input: input::InputSystem,
    pub video: video::VideoSystem,
    pub res: res::ResourceSystem,
    pub time: time::TimeSystem,
    pub sched: sched::ScheduleSystem,

    context: Context,
    headless: bool,
}

impl Engine {
    /// Constructs a new, empty engine.
    pub fn new() -> Result<Self> {
        Engine::new_with(&Settings::default())
    }

    /// Setup engine with specified settings.
    pub fn new_with(settings: &Settings) -> Result<Self> {
        let sched = sched::ScheduleSystem::new(6, None, None);
        let sched_shared = sched.shared();

        let input = input::InputSystem::new(settings.input);
        let input_shared = input.shared();

        let window = if settings.headless {
            window::Window::headless()
        } else {
            window::Window::new(settings.window.clone())?
        };

        let res = res::ResourceSystem::new(sched_shared.clone())?;
        let res_shared = res.shared();

        let video = if settings.headless {
            video::VideoSystem::headless(res_shared.clone())
        } else {
            video::VideoSystem::new(&window, res_shared.clone())?
        };

        let video_shared = video.shared();

        let time = time::TimeSystem::new(settings.engine);
        let time_shared = time.shared();

        let context = Context {
            res: res_shared,
            input: input_shared,
            time: time_shared,
            video: video_shared,
            window: window.shared(),
            sched: sched_shared,
            data: Arc::new(RwLock::new(ContextData::default())),
        };

        Ok(Engine {
            input: input,
            window: window,
            video: video,
            res: res,
            time: time,
            sched: sched,

            context: context,
            headless: settings.headless,
        })
    }

    pub fn context(&self) -> &Context {
        &self.context
    }

    /// Run the main loop of `Engine`, this will block the working
    /// thread until we finished.
    pub fn run<T>(mut self, application: T) -> Result<Self>
    where
        T: Application + Send + Sync + 'static,
    {
        let application = Arc::new(RwLock::new(application));

        let dir = ::std::env::current_dir()?;
        info!("CWD: {:?}.", dir);

        let latch = Arc::new(sched::latch::LockLatch::new());
        Self::execute_frame(&self.context, latch.clone(), application.clone());

        let mut alive = true;
        while alive {
            self.input.advance(self.window.hidpi());

            // Poll any possible events first.
            for v in self.window.advance() {
                match *v {
                    events::Event::Application(value) => {
                        {
                            let mut application = application.write().unwrap();
                            application.on_receive_event(&self.context, value)?;
                        }

                        if let events::ApplicationEvent::Closed = value {
                            alive = false;
                        }
                    }

                    events::Event::InputDevice(value) => self.input.update_with(value),
                }
            }

            alive = alive && !self.context.is_shutdown();
            if !alive {
                break;
            }

            self.time.advance();
            self.video.swap_frames();

            let (video_info, duration) = {
                let duration = latch.wait_and_take()?;

                // Perform update and render submitting for frame [x], and drawing
                // frame [x-1] at the same time.
                Self::execute_frame(&self.context, latch.clone(), application.clone());
                // This will block the main-thread until all the video commands is finished by GPU.
                let video_info = self.video.advance(&self.window)?;
                (video_info, duration)
            };

            self.window.swap_buffers()?;

            {
                let info = FrameInfo {
                    video: video_info,
                    duration: duration,
                    fps: self.time.shared().get_fps(),
                };

                let mut application = application.write().unwrap();
                application.on_post_update(&self.context, &info)?;
            }

            alive = alive && !self.context.is_shutdown() && !self.headless;
        }

        {
            let mut application = application.write().unwrap();
            application.on_exit(&self.context)?;
        }

        self.sched.terminate();
        self.sched.wait_until_terminated();
        Ok(self)
    }

    fn execute_frame<T>(
        ctx: &Context,
        latch: Arc<sched::latch::LockLatch<Result<Duration>>>,
        app: Arc<RwLock<T>>,
    ) where
        T: Application + Send + Sync + 'static,
    {
        let run = |ctx, app: Arc<RwLock<T>>| {
            let ts = Instant::now();

            let mut application = app.write().unwrap();
            application.on_update(&ctx)?;
            application.on_render(&ctx)?;

            Ok(Instant::now() - ts)
        };

        let ctx_clone = ctx.clone();
        ctx.sched.spawn(move || latch.set(run(ctx_clone, app)));
    }
}
<commit_msg>Removed `Sync` requirement of `Application`.<commit_after>use std::sync::{Arc, RwLock};
use std::time::{Duration, Instant};

use super::*;
use input;
use res;
use sched;
use video;

type Result<T> = ::std::result::Result<T, ::failure::Error>;

#[derive(Default, Copy, Clone)]
struct ContextData {
    shutdown: bool,
}

/// The context of sub-systems that could be accessed from multi-thread environments safely.
#[derive(Clone)]
pub struct Context {
    pub res: Arc<res::ResourceSystemShared>,
    pub input: Arc<input::InputSystemShared>,
    pub time: Arc<time::TimeSystemShared>,
    pub video: Arc<video::VideoSystemShared>,
    pub window: Arc<window::WindowShared>,
    pub sched: Arc<sched::ScheduleSystemShared>,

    data: Arc<RwLock<ContextData>>,
}

impl Context {
    /// Shutdown the whole application at the end of this frame.
    pub fn shutdown(&self) {
        self.data.write().unwrap().shutdown = true;
    }

    /// Returns true if we are going to shutdown the application at the end of this frame.
    pub fn is_shutdown(&self) -> bool {
        self.data.read().unwrap().shutdown
    }
}

/// `Engine` is the root object of the game application. It binds various sub-systems in
/// a central place and takes take of trivial tasks like the execution order or life-time
/// management.
pub struct Engine {
    pub window: window::Window,
    pub input: input::InputSystem,
    pub video: video::VideoSystem,
    pub res: res::ResourceSystem,
    pub time: time::TimeSystem,
    pub sched: sched::ScheduleSystem,

    context: Context,
    headless: bool,
}

impl Engine {
    /// Constructs a new, empty engine.
    pub fn new() -> Result<Self> {
        Engine::new_with(&Settings::default())
    }

    /// Setup engine with specified settings.
    pub fn new_with(settings: &Settings) -> Result<Self> {
        let sched = sched::ScheduleSystem::new(6, None, None);
        let sched_shared = sched.shared();

        let input = input::InputSystem::new(settings.input);
        let input_shared = input.shared();

        let window = if settings.headless {
            window::Window::headless()
        } else {
            window::Window::new(settings.window.clone())?
        };

        let res = res::ResourceSystem::new(sched_shared.clone())?;
        let res_shared = res.shared();

        let video = if settings.headless {
            video::VideoSystem::headless(res_shared.clone())
        } else {
            video::VideoSystem::new(&window, res_shared.clone())?
        };

        let video_shared = video.shared();

        let time = time::TimeSystem::new(settings.engine);
        let time_shared = time.shared();

        let context = Context {
            res: res_shared,
            input: input_shared,
            time: time_shared,
            video: video_shared,
            window: window.shared(),
            sched: sched_shared,
            data: Arc::new(RwLock::new(ContextData::default())),
        };

        Ok(Engine {
            input: input,
            window: window,
            video: video,
            res: res,
            time: time,
            sched: sched,

            context: context,
            headless: settings.headless,
        })
    }

    pub fn context(&self) -> &Context {
        &self.context
    }

    /// Run the main loop of `Engine`, this will block the working
    /// thread until we finished.
    pub fn run<T>(mut self, application: T) -> Result<Self>
    where
        T: Application + Send + 'static,
    {
        let dir = ::std::env::current_dir()?;
        info!("CWD: {:?}.", dir);

        let latch = Arc::new(sched::latch::LockLatch::new());
        Self::execute_frame(latch.clone(), self.context.clone(), application);

        let mut alive = true;
        let mut frame_info = None;
        while alive {
            self.time.advance();

            let (mut application, duration) = latch.wait_and_take()?;
            if let Some(frame_info) = frame_info {
                application.on_post_update(&self.context, &frame_info)?;
            }

            self.input.advance(self.window.hidpi());
            // Poll any possible events first.
            for v in self.window.advance() {
                match *v {
                    events::Event::Application(value) => {
                        application.on_receive_event(&self.context, value)?;

                        if let events::ApplicationEvent::Closed = value {
                            alive = false;
                        }
                    }

                    events::Event::InputDevice(value) => self.input.update_with(value),
                }
            }

            alive = alive && !self.context.is_shutdown() && !self.headless;
            if !alive {
                application.on_exit(&self.context)?;
                break;
            }

            // Perform update and render submitting for frame [x], and drawing
            // frame [x-1] at the same time.
            self.video.swap_frames();
            Self::execute_frame(latch.clone(), self.context.clone(), application);

            // This will block the main-thread until all the video commands is finished by GPU.
            let video_info = self.video.advance(&self.window)?;
            self.window.swap_buffers()?;

            //
            frame_info = Some(FrameInfo {
                video: video_info,
                duration: duration,
                fps: self.time.shared().get_fps(),
            });
        }

        self.sched.terminate();
        self.sched.wait_until_terminated();
        Ok(self)
    }

    fn execute_frame<T>(
        latch: Arc<sched::latch::LockLatch<Result<(T, Duration)>>>,
        ctx: Context,
        mut application: T,
    ) where
        T: Application + Send + 'static,
    {
        let ctx_clone = ctx.clone();
        let run = move || {
            let ts = Instant::now();
            application.on_update(&ctx_clone)?;
            application.on_render(&ctx_clone)?;

            Ok((application, Instant::now() - ts))
        };

        ctx.sched.spawn(move || latch.set(run()));
    }
}
<|endoftext|>


<commit_before>use std::io;

use tag;
use err;
use serial;
use byte::write_byte;

impl serial::traits::Asn1Info for u64 {
  fn asn1_type(&self) -> tag::Type {
    tag::Type::from("INTEGER")
  }

  fn asn1_class(&self) -> tag::Class {
    tag::Class::Universal
  }

  fn asn1_tagnum(&self) -> tag::TagNum {
    tag::TagNum::from(2u8)
  }

  fn asn1_constructed(&self) -> bool {
    false
  }
}

impl serial::traits::Asn1Serialize for u64 {
  fn serialize_imp<W: io::Write>(&self, writer: &mut W) -> Result<(), err::EncodeError> {
    let mut started = false;
    // Loop through bytes in int backwards, start writing when first non-zero byte is encounted.
    for offset in (0..8).rev() {
      let shifted: u64 = self >> (offset * 8);
      let byte: u8 = (shifted & 0xff) as u8;
      if !started {
        if byte == 0 {
          continue;
        }
        started = true;
      }
      try!(write_byte(writer, byte));
    }

    // If we haven't written anything, write a zero byte.
    if !started {
      try!(write_byte(writer, 0 as u8));
    }
    Ok(())
  }
}

impl serial::traits::Asn1Deserialize for u64 {
  fn deserialize_imp<I: Iterator<Item=io::Result<u8>>>(reader: &mut I, len: tag::Len) -> Result<Self, err::DecodeError> {
    unimplemented!();
  }
}
<commit_msg>Add deserialize for int<commit_after>use std::io;

use tag;
use err;
use serial;
use byte::{read_byte, write_byte};

impl serial::traits::Asn1Info for u64 {
  fn asn1_type(&self) -> tag::Type {
    tag::Type::from("INTEGER")
  }

  fn asn1_class(&self) -> tag::Class {
    tag::Class::Universal
  }

  fn asn1_tagnum(&self) -> tag::TagNum {
    tag::TagNum::from(2u8)
  }

  fn asn1_constructed(&self) -> bool {
    false
  }
}

impl serial::traits::Asn1Serialize for u64 {
  fn serialize_imp<W: io::Write>(&self, writer: &mut W) -> Result<(), err::EncodeError> {
    let mut started = false;
    // Loop through bytes in int backwards, start writing when first non-zero byte is encounted.
    for offset in (0..8).rev() {
      let shifted: u64 = self >> (offset * 8);
      let byte: u8 = (shifted & 0xff) as u8;
      if !started {
        if byte == 0 {
          continue;
        }
        started = true;
      }
      try!(write_byte(writer, byte));
    }

    // If we haven't written anything, write a zero byte.
    if !started {
      try!(write_byte(writer, 0 as u8));
    }
    Ok(())
  }
}

impl serial::traits::Asn1Deserialize for u64 {
  fn deserialize_imp<I: Iterator<Item=io::Result<u8>>>(reader: &mut I, len: tag::Len) -> Result<Self, err::DecodeError> {
    let len_num = try!(match len {
      tag::Len::Def(l) => Ok(l),
      _ => Err(err::DecodeError::PrimIndef),
    });

    let mut int: u64 = 0;

    for _ in 0..len_num {
      let byte = try!(read_byte(reader));
      int = (int << 8) + int as u64;
    }

    Ok(int)
  }
}
<|endoftext|>


<commit_before>extern crate diffie_hellman;

use diffie_hellman::*;

#[test]
fn test_private_key_in_range_key() {
    let primes: Vec<u64> = vec![5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 773, 967,
                                3461, 6131];
    let private_keys: Vec<u64> = primes.iter().map(|x| private_key(*x)).collect();

    for i in 0..primes.len() {
        assert!(1 < private_keys[i] && private_keys[i] < primes[i]);
    }
}

#[test]
#[ignore]
fn test_public_key_correct() {
    let p: u64 = 23;
    let g: u64 = 5;

    let private_key: u64 = 6;
    let expected: u64 = 8;

    assert_eq!(public_key(p, g, private_key), expected);
}

#[test]
#[ignore]
fn test_secret_key_correct() {
    let p: u64 = 11;
    let g: u64 = 7;

    let private_key_a = 7;
    let public_key_b = 8;
    let secret = secret(p, public_key_b, private_key_a);
    let expected = 2;

    assert_eq!(secret, expected);
}

#[test]
#[ignore]
fn test_changed_secret_key() {
    let p: u64 = 13;
    let g: u64 = 11;

    let private_key_a = private_key(p);
    let private_key_b = private_key(p);

    let public_key_a = public_key(p, g, private_key_a);
    let public_key_b = public_key(p, g, private_key_b);

    // Key exchange
    let secret_a = secret(p, public_key_b, private_key_a);
    let secret_b = secret(p, public_key_a, private_key_b);

    assert_eq!(secret_a, secret_b);
}
<commit_msg>diffie-hellmann: remove unused variable from test (#405)<commit_after>extern crate diffie_hellman;

use diffie_hellman::*;

#[test]
fn test_private_key_in_range_key() {
    let primes: Vec<u64> = vec![5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 773, 967,
                                3461, 6131];
    let private_keys: Vec<u64> = primes.iter().map(|x| private_key(*x)).collect();

    for i in 0..primes.len() {
        assert!(1 < private_keys[i] && private_keys[i] < primes[i]);
    }
}

#[test]
#[ignore]
fn test_public_key_correct() {
    let p: u64 = 23;
    let g: u64 = 5;

    let private_key: u64 = 6;
    let expected: u64 = 8;

    assert_eq!(public_key(p, g, private_key), expected);
}

#[test]
#[ignore]
fn test_secret_key_correct() {
    let p: u64 = 11;

    let private_key_a = 7;
    let public_key_b = 8;
    let secret = secret(p, public_key_b, private_key_a);
    let expected = 2;

    assert_eq!(secret, expected);
}

#[test]
#[ignore]
fn test_changed_secret_key() {
    let p: u64 = 13;
    let g: u64 = 11;

    let private_key_a = private_key(p);
    let private_key_b = private_key(p);

    let public_key_a = public_key(p, g, private_key_a);
    let public_key_b = public_key(p, g, private_key_b);

    // Key exchange
    let secret_a = secret(p, public_key_b, private_key_a);
    let secret_b = secret(p, public_key_a, private_key_b);

    assert_eq!(secret_a, secret_b);
}
<|endoftext|>


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

use crate::ast::Position;

#[derive(Debug)]
pub struct Error {
    message: String,
    pos: Option<Position>,
    call_stack: Vec<Position>,
}

impl Error {
    pub fn new(msg: String, pos: Position) -> Self {
        Self {
            message: msg,
            pos: Some(pos),
            call_stack: Vec::new(),
        }
    }

    pub fn with_pos(mut self, pos: Position) -> Self {
        self.pos = Some(pos);
        self
    }

    pub fn push_call_stack(&mut self, pos: Position) {
        self.call_stack.push(pos);
    }
}

macro_rules! decorate_error {
    ($pos:expr => $result:expr) => {
        match $result {
            Ok(v) => Ok(v),
            Err(e) => Err(e.with_pos($pos.clone())),
        }
    };
}

macro_rules! decorate_call {
    ($pos:expr => $result:expr) => {
        match $result {
            Ok(v) => Ok(v),
            Err(mut e) => {
                e.push_call_stack($pos.clone());
                Err(e)
            }
        }
    };
}

impl From<regex::Error> for Error {
    fn from(e: regex::Error) -> Self {
        Error {
            message: format!("{}", e),
            pos: None,
            call_stack: Vec::new(),
        }
    }
}

impl From<std::io::Error> for Error {
    fn from(e: std::io::Error) -> Self {
        let msg = match e.kind() {
            io::ErrorKind::NotFound => format!("OSError: Path not found: {}", e),
            _ => format!("{}", e),
        };
        Error {
            message: msg,
            pos: None,
            call_stack: Vec::new(),
        }
    }
}

impl Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if let Some(ref pos) = self.pos {
            write!(f, "{} at {}", self.message, pos)?;
        } else {
            write!(f, "{}", self.message)?;
        }
        if !self.call_stack.is_empty() {
            for p in self.call_stack.iter() {
                write!(f, "\nVIA: {}", p)?;
            }
        }
        Ok(())
    }
}

impl std::error::Error for Error {}
<commit_msg>DEV: Handle Windows File errors<commit_after>// Copyright 2019 Jeremy Wall
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::convert::From;
use std::fmt;
use std::fmt::Display;
use std::io;

use crate::ast::Position;

#[derive(Debug)]
pub struct Error {
    message: String,
    pos: Option<Position>,
    call_stack: Vec<Position>,
}

impl Error {
    pub fn new(msg: String, pos: Position) -> Self {
        Self {
            message: msg,
            pos: Some(pos),
            call_stack: Vec::new(),
        }
    }

    pub fn with_pos(mut self, pos: Position) -> Self {
        self.pos = Some(pos);
        self
    }

    pub fn push_call_stack(&mut self, pos: Position) {
        self.call_stack.push(pos);
    }
}

macro_rules! decorate_error {
    ($pos:expr => $result:expr) => {
        match $result {
            Ok(v) => Ok(v),
            Err(e) => Err(e.with_pos($pos.clone())),
        }
    };
}

macro_rules! decorate_call {
    ($pos:expr => $result:expr) => {
        match $result {
            Ok(v) => Ok(v),
            Err(mut e) => {
                e.push_call_stack($pos.clone());
                Err(e)
            }
        }
    };
}

impl From<regex::Error> for Error {
    fn from(e: regex::Error) -> Self {
        Error {
            message: format!("{}", e),
            pos: None,
            call_stack: Vec::new(),
        }
    }
}

impl From<std::io::Error> for Error {
    fn from(e: std::io::Error) -> Self {
        let msg = match dbg!(e.kind()) {
            io::ErrorKind::NotFound | io::ErrorKind::Other => format!("OSError: Path not found: {}", e),
            _ => format!("{}", e),
        };
        Error {
            message: msg,
            pos: None,
            call_stack: Vec::new(),
        }
    }
}

impl Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if let Some(ref pos) = self.pos {
            write!(f, "{} at {}", self.message, pos)?;
        } else {
            write!(f, "{}", self.message)?;
        }
        if !self.call_stack.is_empty() {
            for p in self.call_stack.iter() {
                write!(f, "\nVIA: {}", p)?;
            }
        }
        Ok(())
    }
}

impl std::error::Error for Error {}
<|endoftext|>


<commit_before>use curl;
use git2;

use anyhow::Error;

use crate::util::errors::{CargoResult, HttpNot200};
use crate::util::Config;

pub struct Retry<'a> {
    config: &'a Config,
    remaining: u32,
}

impl<'a> Retry<'a> {
    pub fn new(config: &'a Config) -> CargoResult<Retry<'a>> {
        Ok(Retry {
            config,
            remaining: config.net_config()?.retry.unwrap_or(2),
        })
    }

    pub fn r#try<T>(&mut self, f: impl FnOnce() -> CargoResult<T>) -> CargoResult<Option<T>> {
        match f() {
            Err(ref e) if maybe_spurious(e) && self.remaining > 0 => {
                let msg = format!(
                    "spurious network error ({} tries \
                     remaining): {}",
                    self.remaining, e
                );
                self.config.shell().warn(msg)?;
                self.remaining -= 1;
                Ok(None)
            }
            other => other.map(Some),
        }
    }
}

fn maybe_spurious(err: &Error) -> bool {
    for e in err.chain() {
        if let Some(git_err) = e.downcast_ref::<git2::Error>() {
            match git_err.class() {
                git2::ErrorClass::Net | git2::ErrorClass::Os => return true,
                _ => (),
            }
        }
        if let Some(curl_err) = e.downcast_ref::<curl::Error>() {
            if curl_err.is_couldnt_connect()
                || curl_err.is_couldnt_resolve_proxy()
                || curl_err.is_couldnt_resolve_host()
                || curl_err.is_operation_timedout()
                || curl_err.is_recv_error()
                || curl_err.is_http2_stream_error()
                || curl_err.is_ssl_connect_error()
            {
                return true;
            }
        }
        if let Some(not_200) = e.downcast_ref::<HttpNot200>() {
            if 500 <= not_200.code && not_200.code < 600 {
                return true;
            }
        }
    }
    false
}

/// Wrapper method for network call retry logic.
///
/// Retry counts provided by Config object `net.retry`. Config shell outputs
/// a warning on per retry.
///
/// Closure must return a `CargoResult`.
///
/// # Examples
///
/// ```
/// # use crate::cargo::util::{CargoResult, Config};
/// # let download_something = || return Ok(());
/// # let config = Config::default().unwrap();
/// use cargo::util::network;
/// let cargo_result = network::with_retry(&config, || download_something());
/// ```
pub fn with_retry<T, F>(config: &Config, mut callback: F) -> CargoResult<T>
where
    F: FnMut() -> CargoResult<T>,
{
    let mut retry = Retry::new(config)?;
    loop {
        if let Some(ret) = retry.r#try(&mut callback)? {
            return Ok(ret);
        }
    }
}

#[test]
fn with_retry_repeats_the_call_then_works() {
    use crate::core::Shell;

    //Error HTTP codes (5xx) are considered maybe_spurious and will prompt retry
    let error1 = HttpNot200 {
        code: 501,
        url: "Uri".to_string(),
    }
    .into();
    let error2 = HttpNot200 {
        code: 502,
        url: "Uri".to_string(),
    }
    .into();
    let mut results: Vec<CargoResult<()>> = vec![Ok(()), Err(error1), Err(error2)];
    let config = Config::default().unwrap();
    *config.shell() = Shell::from_write(Box::new(Vec::new()));
    let result = with_retry(&config, || results.pop().unwrap());
    assert!(result.is_ok())
}

#[test]
fn with_retry_finds_nested_spurious_errors() {
    use crate::core::Shell;

    //Error HTTP codes (5xx) are considered maybe_spurious and will prompt retry
    //String error messages are not considered spurious
    let error1 = anyhow::Error::from(HttpNot200 {
        code: 501,
        url: "Uri".to_string(),
    });
    let error1 = anyhow::Error::from(error1.context("A non-spurious wrapping err"));
    let error2 = anyhow::Error::from(HttpNot200 {
        code: 502,
        url: "Uri".to_string(),
    });
    let error2 = anyhow::Error::from(error2.context("A second chained error"));
    let mut results: Vec<CargoResult<()>> = vec![Ok(()), Err(error1), Err(error2)];
    let config = Config::default().unwrap();
    *config.shell() = Shell::from_write(Box::new(Vec::new()));
    let result = with_retry(&config, || results.pop().unwrap());
    assert!(result.is_ok())
}

#[test]
fn curle_http2_stream_is_spurious() {
    let code = curl_sys::CURLE_HTTP2_STREAM;
    let err = curl::Error::new(code);
    assert!(maybe_spurious(&err.into()));
}
<commit_msg>Auto merge of #7788 - alexcrichton:retry-another, r=ehuss<commit_after>use curl;
use git2;

use anyhow::Error;

use crate::util::errors::{CargoResult, HttpNot200};
use crate::util::Config;

pub struct Retry<'a> {
    config: &'a Config,
    remaining: u32,
}

impl<'a> Retry<'a> {
    pub fn new(config: &'a Config) -> CargoResult<Retry<'a>> {
        Ok(Retry {
            config,
            remaining: config.net_config()?.retry.unwrap_or(2),
        })
    }

    pub fn r#try<T>(&mut self, f: impl FnOnce() -> CargoResult<T>) -> CargoResult<Option<T>> {
        match f() {
            Err(ref e) if maybe_spurious(e) && self.remaining > 0 => {
                let msg = format!(
                    "spurious network error ({} tries \
                     remaining): {}",
                    self.remaining, e
                );
                self.config.shell().warn(msg)?;
                self.remaining -= 1;
                Ok(None)
            }
            other => other.map(Some),
        }
    }
}

fn maybe_spurious(err: &Error) -> bool {
    for e in err.chain() {
        if let Some(git_err) = e.downcast_ref::<git2::Error>() {
            match git_err.class() {
                git2::ErrorClass::Net | git2::ErrorClass::Os => return true,
                _ => (),
            }
        }
        if let Some(curl_err) = e.downcast_ref::<curl::Error>() {
            if curl_err.is_couldnt_connect()
                || curl_err.is_couldnt_resolve_proxy()
                || curl_err.is_couldnt_resolve_host()
                || curl_err.is_operation_timedout()
                || curl_err.is_recv_error()
                || curl_err.is_http2_stream_error()
                || curl_err.is_ssl_connect_error()
                || curl_err.is_partial_file()
            {
                return true;
            }
        }
        if let Some(not_200) = e.downcast_ref::<HttpNot200>() {
            if 500 <= not_200.code && not_200.code < 600 {
                return true;
            }
        }
    }
    false
}

/// Wrapper method for network call retry logic.
///
/// Retry counts provided by Config object `net.retry`. Config shell outputs
/// a warning on per retry.
///
/// Closure must return a `CargoResult`.
///
/// # Examples
///
/// ```
/// # use crate::cargo::util::{CargoResult, Config};
/// # let download_something = || return Ok(());
/// # let config = Config::default().unwrap();
/// use cargo::util::network;
/// let cargo_result = network::with_retry(&config, || download_something());
/// ```
pub fn with_retry<T, F>(config: &Config, mut callback: F) -> CargoResult<T>
where
    F: FnMut() -> CargoResult<T>,
{
    let mut retry = Retry::new(config)?;
    loop {
        if let Some(ret) = retry.r#try(&mut callback)? {
            return Ok(ret);
        }
    }
}

#[test]
fn with_retry_repeats_the_call_then_works() {
    use crate::core::Shell;

    //Error HTTP codes (5xx) are considered maybe_spurious and will prompt retry
    let error1 = HttpNot200 {
        code: 501,
        url: "Uri".to_string(),
    }
    .into();
    let error2 = HttpNot200 {
        code: 502,
        url: "Uri".to_string(),
    }
    .into();
    let mut results: Vec<CargoResult<()>> = vec![Ok(()), Err(error1), Err(error2)];
    let config = Config::default().unwrap();
    *config.shell() = Shell::from_write(Box::new(Vec::new()));
    let result = with_retry(&config, || results.pop().unwrap());
    assert!(result.is_ok())
}

#[test]
fn with_retry_finds_nested_spurious_errors() {
    use crate::core::Shell;

    //Error HTTP codes (5xx) are considered maybe_spurious and will prompt retry
    //String error messages are not considered spurious
    let error1 = anyhow::Error::from(HttpNot200 {
        code: 501,
        url: "Uri".to_string(),
    });
    let error1 = anyhow::Error::from(error1.context("A non-spurious wrapping err"));
    let error2 = anyhow::Error::from(HttpNot200 {
        code: 502,
        url: "Uri".to_string(),
    });
    let error2 = anyhow::Error::from(error2.context("A second chained error"));
    let mut results: Vec<CargoResult<()>> = vec![Ok(()), Err(error1), Err(error2)];
    let config = Config::default().unwrap();
    *config.shell() = Shell::from_write(Box::new(Vec::new()));
    let result = with_retry(&config, || results.pop().unwrap());
    assert!(result.is_ok())
}

#[test]
fn curle_http2_stream_is_spurious() {
    let code = curl_sys::CURLE_HTTP2_STREAM;
    let err = curl::Error::new(code);
    assert!(maybe_spurious(&err.into()));
}
<|endoftext|>


<commit_before>use libc;

use ClientStatus;

/// An error that can occur in JACK.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Error {
    CallbackDeregistrationError,
    CallbackRegistrationError,
    ClientActivationError,
    ClientDeactivationError,
    ClientError(ClientStatus),
    FreewheelError,
    InvalidDeactivation,
    NotEnoughSpace,
    PortAliasError,
    PortAlreadyConnected(String, String),
    PortConnectionError(String, String),
    PortDisconnectionError,
    PortMonitorError,
    PortNamingError,
    PortRegistrationError(String),
    SetBufferSizeError,
    TimeError,
    WeakFunctionNotFound,
    ClientIsNoLongerAlive,
    UnknownError,
}

/// Used by `NotificationHandler::latency()`.
#[derive(Clone, Copy, Debug)]
pub enum LatencyType {
    Capture,
    Playback,
}

/// Specify an option, either to continue processing, or to stop.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Control {
    /// Continue processing.
    Continue,

    /// Stop processing.
    Quit,
}

impl Control {
    pub fn to_ffi(self) -> libc::c_int {
        match self {
            Control::Continue => 0,
            Control::Quit => -1,
        }
    }
}

impl Default for Control {
    fn default() -> Self {
        Control::Continue
    }
}
<commit_msg>Implement basic std::Error formatting (#101)<commit_after>use libc;

use ClientStatus;

/// An error that can occur in JACK.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Error {
    CallbackDeregistrationError,
    CallbackRegistrationError,
    ClientActivationError,
    ClientDeactivationError,
    ClientError(ClientStatus),
    FreewheelError,
    InvalidDeactivation,
    NotEnoughSpace,
    PortAliasError,
    PortAlreadyConnected(String, String),
    PortConnectionError(String, String),
    PortDisconnectionError,
    PortMonitorError,
    PortNamingError,
    PortRegistrationError(String),
    SetBufferSizeError,
    TimeError,
    WeakFunctionNotFound,
    ClientIsNoLongerAlive,
    UnknownError,
}

impl std::fmt::Display for Error {
	fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
		write!(f, "JackError: {:?}", &self) // FIXME
	}
}

impl std::error::Error for Error {
}

/// Used by `NotificationHandler::latency()`.
#[derive(Clone, Copy, Debug)]
pub enum LatencyType {
    Capture,
    Playback,
}

/// Specify an option, either to continue processing, or to stop.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Control {
    /// Continue processing.
    Continue,

    /// Stop processing.
    Quit,
}

impl Control {
    pub fn to_ffi(self) -> libc::c_int {
        match self {
            Control::Continue => 0,
            Control::Quit => -1,
        }
    }
}

impl Default for Control {
    fn default() -> Self {
        Control::Continue
    }
}
<|endoftext|>


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

type FreeVariable = u64;
type VariableName<'a> = &'a str;

/// AST node expression
#[derive(Copy, Clone, Debug, PartialEq)]
enum Expression<'a> {
    Number(f64),
    Boolean(bool),

    /// simple variable
    Variable(VariableName<'a>),

    /// fn (x, y, z) -> expr
    Lambda(&'a [VariableName<'a>], &'a Expression<'a>),

    /// let x = expr in expr
    Let(VariableName<'a>, &'a Expression<'a>, &'a Expression<'a>),

    /// some_fn (x, y, z)
    FnCall(&'a Expression<'a>, &'a [ &'a Expression<'a>])
}

#[derive(Clone, Debug, PartialEq)]
enum Type {
    Number,
    Boolean,
    Unbound(FreeVariable),
    // FIXME: can I avoid having this be boxed and vec'd?
    Lambda(Vec<Type>, Box<Type>),
}

type TypeEnv<'a> = HashMap<VariableName<'a>, Type>;

/// Bind previously unbound type variables to a specified type.
fn bind_type<'a>(env: &mut TypeEnv<'a>, var: FreeVariable, ty: Type) -> Type {
    println!("substitute unbound({}) for {:?}", var, ty);

    for val in env.values_mut() {
        if *val == Type::Unbound(var) {
            *val = ty.clone();
        }
    }

    ty
}

#[derive(Clone, Copy)]
struct VariableGen { current: FreeVariable }
impl VariableGen {
    fn next(&mut self) -> FreeVariable { self.current += 1; self.current }
}

type UnificationError = &'static str;

/// modifies the environment to unify the given types (or returns false if not possible)
fn unify<'a>(a: Type, b: Type, env: &mut TypeEnv<'a>) -> Result<Type, UnificationError> {
    match (a, b) {
        // Easy. Literals match with themselves
        (Type::Number, Type::Number) => Ok(Type::Number),
        (Type::Boolean, Type::Boolean) => Ok(Type::Boolean),

        (Type::Unbound(n), ty) | (ty, Type::Unbound(n)) => Ok(bind_type(env, n, ty)),

        (Type::Lambda(args1, body1), Type::Lambda(args2, body2)) => {
            if args1.len() != args2.len() {
                return Err("lambdas take differing numbers of arguments")
            }

            let mut unified_args = Vec::with_capacity(args1.len());

            for iter in args1.iter().zip(args2.iter()) {
                let (a1, a2) = iter;

                // TODO: should try to avoid the clone here
                match unify(a1.clone(), a2.clone(), env) {
                    Ok(ty) => unified_args.push(ty),
                    err => return err
                }
            }

            match unify(*body1, *body2, env) {
                Ok(body_ty) => Ok(Type::Lambda(unified_args, Box::new(body_ty))),
                err => err
            }
        }

        _ => Err("could not unify types")
    }
}

/// Run algorithm W on expr with given type env, yielding the resolved
/// type of the expression, or the type error encountered.
fn w<'a>(expr: &Expression<'a>, env: &mut TypeEnv<'a>, inst: &mut VariableGen) -> Type {
    match *expr {
        // simplest case: literals have immediately defined types.
        Expression::Number(_) => Type::Number,
        Expression::Boolean(_) => Type::Boolean,

        // variables we need to look up inside our type environment
        Expression::Variable(name) => match env.get(name) {
            Some(typ) => typ.clone(),
            None => panic!("undefined variable, {}", name)
        },

        Expression::Lambda(args, body) => {
            let body_ty = w(body, env, inst);
            let args_ty = args
                .into_iter()
                .map(|arg| {
                    let ty = inst.next();
                    env.insert(arg, Type::Unbound(ty));
                    Type::Unbound(ty)
                })
                .collect::<Vec<Type>>();

            Type::Lambda(args_ty, Box::new(body_ty))
        },

        // TODO: need an occurs check here.
        Expression::Let(name, expr, body) => {
            let expr_ty = w(expr, env, inst);
            env.insert(name, expr_ty);

            w(body, env, inst)
        },

        // 1. ensure that the callable in this function call is truly callable.
        // 2. ensure that the number and type of args match
        // 3. return type of body of lambda
        Expression::FnCall(callable, args) => {
            // FIXME: this seems broken.
            let mut env_ = env.clone();
            let mut inst_ = inst.clone();

            let type_args = args
                .iter()
                .map(|arg| {
                    w(arg, &mut env_, &mut inst_)
                })
                .collect::<Vec<Type>>();

            // Try to unify the lambda we are calling with the way we are calling it.
            unify(w(callable, env, inst),
                  Type::Lambda(type_args, Box::new(Type::Unbound(inst.next()))),
                  env)
                .map(move |ty|{
                    match ty {
                        Type::Lambda(_, return_type) => *return_type,
                        _ => panic!("BUG: expected unify to return lambda, not {:?}", ty)
                    }
                })
                .or(Err("could not unify lambda types"))
                // FIXME: this should eventually return a result anyway.
                .unwrap()
        }
    }
}

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

    fn infer(exp: Expression) -> Type {
        let mut inst = VariableGen { current: 0 };
        let mut env = HashMap::new();

        w(&exp, &mut env, &mut inst)
    }

    #[test]
    #[should_panic]
    fn undefined_variable() {
        let var = Expression::Variable("x");

        // FIXME: until we return a result type this will panic.
        infer(var);
    }

    #[test]
    fn literal_inference() {
        let num = Expression::Number(123.0);
        let boolean = Expression::Boolean(true);

        assert_eq!(infer(num), Type::Number);
        assert_eq!(infer(boolean), Type::Boolean);
    }

    #[test]
    fn lambda_expression_inference() {
        let args = ["x"];
        let num = Expression::Number(123.0);

        // \x -> 123.0
        let lambda = Expression::Lambda(&args, &num);

        assert_eq!(infer(lambda), Type::Lambda(vec![Type::Unbound(1)], Box::new(Type::Number)));

        // \x -> \y -> 123.0
        let lambdalambda = Expression::Lambda(&args, &lambda);
        assert_eq!(infer(lambdalambda),
                   Type::Lambda(vec![Type::Unbound(2)], Box::new(
                       Type::Lambda(vec![Type::Unbound(1)], Box::new(Type::Number)))));
    }

    #[test]
    fn let_inference() {
        let boolean = Expression::Boolean(false);
        let var_x = Expression::Variable("x");
        let var_y = Expression::Variable("y");

        // let x = false in x
        let let_ = Expression::Let("x", &boolean, &var_x);

        assert_eq!(infer(let_), Type::Boolean);

        // let y = (let x = false in x) in y
        let nested_let = Expression::Let("y", &let_, &var_y);
        assert_eq!(infer(nested_let), Type::Boolean);
    }

    #[test]
    fn fn_call_inference() {
        let args = ["x"];
        let num = Expression::Number(123.0);
        let boolean = Expression::Boolean(false);

        // \x -> 123.0
        let lambda = Expression::Lambda(&args, &num);

        let app_args = [&boolean];
        let application = Expression::FnCall(&lambda, &app_args);

        assert_eq!(infer(application), Type::Number);

        // let
        //    x = (\a -> a)
        // in
        //    let y = (\a -> a)
        //    in
        //       x (y)
        // ===> \a -> a
        let var_a = Expression::Variable("a");
        let var_x = Expression::Variable("x");
        let var_y = Expression::Variable("y");
        let args_a = ["a"];
        let args_y = [&var_y];
        let lambda_x = Expression::Lambda(&args_a, &var_a);
        let lambda_y = Expression::Lambda(&args_a, &var_a);

        let fncall = Expression::FnCall(&var_x, &args_y);

        let let_y = Expression::Let("y", &lambda_y, &fncall);
        let let_x = Expression::Let("x", &lambda_x, &let_y);

        assert_eq!(infer(let_x), Type::Lambda(vec![Type::Unbound(3)], Box::new(Type::Unbound(3))))
    }

    #[test]
    fn test_unify() {
        let mut env = HashMap::new();

        assert_eq!(unify(Type::Number, Type::Number, &mut env), Ok(Type::Number));
        assert!(unify(Type::Number, Type::Boolean, &mut env).is_err());
        assert_eq!(unify(Type::Unbound(1), Type::Number, &mut env), Ok(Type::Number));

        assert_eq!(unify(Type::Lambda(vec![Type::Unbound(1), Type::Unbound(2)], Box::new(Type::Number)),
                         Type::Lambda(vec![Type::Unbound(3), Type::Unbound(4)], Box::new(Type::Number)),
                         &mut env),
                   Ok(Type::Lambda(vec![Type::Unbound(3), Type::Unbound(4)], Box::new(Type::Number))));
    }
}

fn main() {
    println!("patience.");
}
<commit_msg>works, need to fix fn returning fn<commit_after>use std::collections::HashMap;

type FreeVariable = u64;
type VariableName<'a> = &'a str;

/// AST node expression
#[derive(Copy, Clone, Debug, PartialEq)]
enum Expression<'a> {
    Number(f64),
    Boolean(bool),

    /// simple variable
    Variable(VariableName<'a>),

    /// fn (x, y, z) -> expr
    Lambda(&'a [VariableName<'a>], &'a Expression<'a>),

    /// let x = expr in expr
    Let(VariableName<'a>, &'a Expression<'a>, &'a Expression<'a>),

    /// some_fn (x, y, z)
    FnCall(&'a Expression<'a>, &'a [ &'a Expression<'a>])
}

#[derive(Clone, Debug, PartialEq)]
enum Type {
    Number,
    Boolean,
    Unbound(FreeVariable),
    // FIXME: can I avoid having this be boxed and vec'd?
    Lambda(Vec<Type>, Box<Type>),
}

type TypeEnv<'a> = HashMap<VariableName<'a>, Type>;

/// Bind previously unbound type variables to a specified type.
fn bind_type<'a>(env: &mut TypeEnv<'a>, var: FreeVariable, ty: Type) -> Type {
    println!("substitute unbound({}) for {:?}", var, ty);

    for val in env.values_mut() {
        if *val == Type::Unbound(var) {
            *val = ty.clone();
        }
    }

    ty
}

#[derive(Clone, Copy)]
struct VariableGen { current: FreeVariable }
impl VariableGen {
    fn next(&mut self) -> FreeVariable { self.current += 1; self.current }
}

type UnificationError = &'static str;
type TypeError = &'static str;

/// modifies the environment to unify the given types (or returns false if not possible)
fn unify<'a>(a: Type, b: Type, env: &mut TypeEnv<'a>) -> Result<Type, UnificationError> {
    match (a, b) {
        // Easy. Literals match with themselves
        (Type::Number, Type::Number) => Ok(Type::Number),
        (Type::Boolean, Type::Boolean) => Ok(Type::Boolean),

        (Type::Unbound(n), ty) | (ty, Type::Unbound(n)) => Ok(bind_type(env, n, ty)),

        (Type::Lambda(args1, body1), Type::Lambda(args2, body2)) => {
            if args1.len() != args2.len() {
                return Err("lambdas take differing numbers of arguments")
            }

            let mut unified_args = Vec::with_capacity(args1.len());

            for iter in args1.into_iter().zip(args2.into_iter()) {
                let (a1, a2) = iter;

                // TODO: should try to avoid the clone here
                match unify(a1, a2, env) {
                    Ok(ty) => unified_args.push(ty),
                    err => return err
                }
            }

            match unify(*body1, *body2, env) {
                Ok(body_ty) => Ok(Type::Lambda(unified_args, Box::new(body_ty))),
                err => err
            }
        }

        _ => Err("could not unify types")
    }
}

/// Run algorithm W on expr with given type env, yielding the resolved
/// type of the expression, or the type error encountered.
fn w<'a>(expr: &Expression<'a>, env: &mut TypeEnv<'a>, inst: &mut VariableGen) -> Type {
    match *expr {
        // simplest case: literals have immediately defined types.
        Expression::Number(_) => Type::Number,
        Expression::Boolean(_) => Type::Boolean,

        // variables we need to look up inside our type environment
        Expression::Variable(name) => match env.get(name) {
            Some(typ) => typ.clone(),
            None => panic!("undefined variable, {}", name)
        },

        Expression::Lambda(args, body) => {
            let args_ty = args
                .into_iter()
                .map(|arg| {
                    let ty = inst.next();
                    env.insert(arg, Type::Unbound(ty));
                    Type::Unbound(ty)
                })
                .collect::<Vec<Type>>();

            let body_ty = w(body, env, inst);

            Type::Lambda(args_ty, Box::new(body_ty))
        },

        // TODO: need an occurs check here.
        Expression::Let(name, expr, body) => {
            let expr_ty = w(expr, env, inst);
            env.insert(name, expr_ty);

            w(body, env, inst)
        },

        // 1. ensure that the callable in this function call is truly callable.
        // 2. ensure that the number and type of args match
        // 3. return type of body of lambda
        Expression::FnCall(callable, args) => {
            // FIXME: this seems broken.
            let mut env_ = env.clone();
            let mut inst_ = inst.clone();

            let type_args = args
                .iter()
                .map(|arg| {
                    w(arg, &mut env_, &mut inst_)
                })
                .collect::<Vec<Type>>();

            // Try to unify the lambda we are calling with the way we are calling it.
            unify(w(callable, &mut env_, inst),
                  Type::Lambda(type_args, Box::new(Type::Unbound(inst.next()))),
                  &mut env_)
                .map(move |ty|{
                    match ty {
                        Type::Lambda(_, return_type) => *return_type,
                        _ => panic!("BUG: expected unify to return lambda, not {:?}", ty)
                    }
                })
                .or(Err("could not unify lambda types"))
                // FIXME: this should eventually return a result anyway.
                .unwrap()
        }
    }
}

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

    fn infer(exp: Expression) -> Type {
        let mut inst = VariableGen { current: 0 };
        let mut env = HashMap::new();

        w(&exp, &mut env, &mut inst)
    }

    #[test]
    #[should_panic]
    fn undefined_variable() {
        let var = Expression::Variable("x");

        // FIXME: until we return a result type this will panic.
        infer(var);
    }

    #[test]
    fn literal_inference() {
        let num = Expression::Number(123.0);
        let boolean = Expression::Boolean(true);

        assert_eq!(infer(num), Type::Number);
        assert_eq!(infer(boolean), Type::Boolean);
    }

    #[test]
    fn lambda_expression_inference() {
        let args = ["x"];
        let num = Expression::Number(123.0);

        // \x -> 123.0
        let lambda = Expression::Lambda(&args, &num);

        assert_eq!(infer(lambda), Type::Lambda(vec![Type::Unbound(1)], Box::new(Type::Number)));

        // \x -> \y -> 123.0
        let lambdalambda = Expression::Lambda(&args, &lambda);
        assert_eq!(infer(lambdalambda),
                   Type::Lambda(vec![Type::Unbound(1)], Box::new(
                       Type::Lambda(vec![Type::Unbound(2)], Box::new(Type::Number)))));
    }

    #[test]
    fn let_inference() {
        let boolean = Expression::Boolean(false);
        let var_x = Expression::Variable("x");
        let var_y = Expression::Variable("y");

        // let x = false in x
        let let_ = Expression::Let("x", &boolean, &var_x);

        assert_eq!(infer(let_), Type::Boolean);

        // let y = (let x = false in x) in y
        let nested_let = Expression::Let("y", &let_, &var_y);
        assert_eq!(infer(nested_let), Type::Boolean);
    }

    #[test]
    fn fn_call_inference() {
        let args = ["x"];
        let num = Expression::Number(123.0);
        let boolean = Expression::Boolean(false);

        // \x -> 123.0
        let lambda = Expression::Lambda(&args, &num);

        let app_args = [&boolean];
        let application = Expression::FnCall(&lambda, &app_args);

        assert_eq!(infer(application), Type::Number);

        // let
        //    x = (\a -> a)
        // in
        //    let y = (\a -> a)
        //    in
        //       x (y)
        // ===> \a -> a
        let var_a = Expression::Variable("a");
        let var_x = Expression::Variable("x");
        let var_y = Expression::Variable("y");
        let args_a = ["a"];
        let args_y = [&var_y];
        let lambda_x = Expression::Lambda(&args_a, &var_a);
        let lambda_y = Expression::Lambda(&args_a, &var_a);

        let fncall = Expression::FnCall(&var_x, &args_y);

        let let_y = Expression::Let("y", &lambda_y, &fncall);
        let let_x = Expression::Let("x", &lambda_x, &let_y);

        assert_eq!(infer(let_x), Type::Lambda(vec![Type::Unbound(3)], Box::new(Type::Unbound(3))))
    }

    #[test]
    fn test_unify() {
        let mut env = HashMap::new();

        assert_eq!(unify(Type::Number, Type::Number, &mut env), Ok(Type::Number));
        assert!(unify(Type::Number, Type::Boolean, &mut env).is_err());
        assert_eq!(unify(Type::Unbound(1), Type::Number, &mut env), Ok(Type::Number));

        assert_eq!(unify(Type::Lambda(vec![Type::Unbound(1), Type::Unbound(2)], Box::new(Type::Number)),
                         Type::Lambda(vec![Type::Unbound(3), Type::Unbound(4)], Box::new(Type::Number)),
                         &mut env),
                   Ok(Type::Lambda(vec![Type::Unbound(3), Type::Unbound(4)], Box::new(Type::Number))));
    }
}

fn main() {
    println!("patience.");
}
<|endoftext|>