<commit_before>pub mod search;

use std::thread;
use std::cmp::min;
use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver};
use std::time::SystemTime;
use std::num::Wrapping;
use basetypes::*;
use uci::{UciEngine, UciEngineFactory, EngineReply, OptionName, OptionDescription};
use position::Position;
use chess_move::*;
use tt::*;

// use rand;
// use rand::distributions::{Sample, Range};
// let mut rng = rand::thread_rng();
// let mut between = Range::new(0, 10);
// let x = between.sample(&mut rng);

const VERSION: &'static str = "0.1";
const MAX_DEPTH: u8 = 126;

/// Implements `UciEngine` trait.
pub struct Engine {
    position: Position,
    pondering_is_allowed: bool,
    is_thinking: bool,
    is_pondering: bool,
    replies: Vec<EngineReply>,
    tt: Arc<TranspositionTable>,
    commands: Sender<search::Command>,
    reports: Receiver<search::Report>,

    // Search status info
    search_id: usize,
    thinking_since: SystemTime,
    no_reports_since: SystemTime,
    current_depth: u8,
    searched_nodes: NodeCount,
    searched_time: u64, // milliseconds
    nps: u64,  // nodes per second
    stop_when: TimeManagement,
    mangled_pv: bool,
}

impl Engine {
    /// Creates a new instance.
    ///
    /// `tt_size_mb` is the preferred size of the transposition
    /// table in Mbytes.
    pub fn new(tt_size_mb: usize) -> Engine {
        let mut tt = TranspositionTable::new();
        tt.resize(tt_size_mb);
        let tt = Arc::new(tt);
        let (commands_tx, commands_rx) = channel();
        let (reports_tx, reports_rx) = channel();
        let tt2 = tt.clone();
        thread::spawn(move || {
            search::run_deepening(tt2, commands_rx, reports_tx);
        });

        Engine {
            position: Position::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 \
                                          1")
                          .ok()
                          .unwrap(),
            pondering_is_allowed: false,
            is_thinking: false,
            is_pondering: false,
            replies: vec![],
            tt: tt,
            commands: commands_tx,
            reports: reports_rx,
            search_id: 0,
            thinking_since: SystemTime::now(),
            no_reports_since: SystemTime::now(),
            current_depth: 0,
            searched_nodes: 0,
            searched_time: 0,
            nps: 0,
            stop_when: TimeManagement::Infinite,
            mangled_pv: false,
        }
    }
}

impl UciEngine for Engine {
    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            // Tells the engine that it will be allowed to ponder.
            // This option is needed because the engine might change
            // its time management algorithm when pondering is
            // allowed.
            "Ponder" => {
                self.pondering_is_allowed = value == "true";
            }

            // An invalid option. Notice that we do not support
            // re-sizing of the transposition table once the engine
            // had started.
            _ => (),
        }
    }

    fn new_game(&mut self) {
        // Clearing the transposition table would not change anything,
        // so we do nothing.
    }

    fn position(&mut self, fen: &str, moves: &mut Iterator<Item = &str>) {
        if !self.is_thinking {
            if let Ok(p) = Position::from_history(fen, moves) {
                self.position = p;
                self.tt.new_search();
            }
        }
    }

    #[allow(unused_variables)]
    fn go(&mut self,
          searchmoves: Option<Vec<String>>,
          ponder: bool,
          wtime: Option<u64>,
          btime: Option<u64>,
          winc: Option<u64>,
          binc: Option<u64>,
          movestogo: Option<u64>,
          depth: Option<u64>,
          nodes: Option<u64>,
          mate: Option<u64>,
          movetime: Option<u64>,
          infinite: bool) {
        if !self.is_thinking {
            // Note: We ignore the "searchmoves" parameter.

            self.is_thinking = true;
            self.is_pondering = ponder;
            self.search_id = (Wrapping(self.search_id) + Wrapping(1)).0;
            self.thinking_since = SystemTime::now();
            self.no_reports_since = SystemTime::now();
            self.searched_nodes = 0;
            self.searched_time = 0;
            self.current_depth = 0;
            self.mangled_pv = false;

            // Figure out when we should stop thinking.
            let (time, inc) = if self.position.board().to_move() == WHITE {
                (wtime, winc.unwrap_or(0))
            } else {
                (btime, binc.unwrap_or(0))
            };
            self.stop_when = if infinite {
                TimeManagement::Infinite
            } else if movetime.is_some() {
                TimeManagement::MoveTime(movetime.unwrap())
            } else if nodes.is_some() {
                TimeManagement::Nodes(nodes.unwrap())
            } else if depth.is_some() {
                TimeManagement::Depth(min(depth.unwrap(), MAX_DEPTH as u64) as u8)
            } else {
                // TODO: Robert Hyatt proposes this:
                //
                // nMoves =  min( numberOfMovesOutOfBook, 10 )
                // factor = 2 - nMoves / 10
                // target = timeLeft / numberOfMovesUntilNextTimeControl
                // time   = factor * target
                let time = time.unwrap_or(0);
                let movestogo = movestogo.unwrap_or(40);
                let movetime = (time + inc * movestogo) / movestogo;
                TimeManagement::MoveTimeHint(min(movetime, time / 2))
            };

            // Start a search with the maximum possible depth.
            self.commands
                .send(search::Command::Search {
                    search_id: self.search_id,
                    position: self.position.clone(),
                    depth: MAX_DEPTH,
                    lower_bound: -20000,
                    upper_bound: 20000,
                })
                .unwrap();
        }
    }

    fn ponder_hit(&mut self) {
        if self.is_thinking && self.is_pondering {
            self.is_pondering = false;
        }
    }

    fn stop(&mut self) {
        if self.is_thinking {
            self.commands.send(search::Command::Stop).unwrap();
            let best_move = self.get_best_move();
            self.replies.push(EngineReply::BestMove {
                best_move: best_move,
                ponder_move: None,
            });
            self.is_thinking = false;
        }
    }

    fn is_thinking(&self) -> bool {
        self.is_thinking
    }

    fn get_reply(&mut self) -> Option<EngineReply> {
        // TODO: implement panic mode for MoveTimeHint management.
        if self.is_thinking && !self.is_pondering {
            if match self.stop_when {
                TimeManagement::MoveTime(t) => self.searched_time >= t,
                TimeManagement::MoveTimeHint(t) => self.searched_time >= t,
                TimeManagement::Nodes(n) => self.searched_nodes >= n,
                TimeManagement::Depth(d) => self.current_depth > d,
                TimeManagement::Infinite => false,
            } {
                self.stop();
            }
        }

        // Empty the reports queue.
        while let Ok(report) = self.reports.try_recv() {
            if self.is_thinking {
                match report {
                    // Search progress.
                    search::Report::Progress { search_id, searched_nodes, depth }
                        if search_id == self.search_id => {

                        self.register_progress(depth, searched_nodes);
                        if self.no_reports_since.elapsed().unwrap().as_secs() > 20 {
                            if self.mangled_pv {
                                self.report_pv(depth - 1);
                            } else {
                                self.report_progress();
                            }
                        }
                    }

                    // Terminate the search (unless pondering of infinite).
                    search::Report::Done { search_id, .. } if search_id == self.search_id => {
                        if !self.is_pondering {
                            self.stop_when = if let TimeManagement::Infinite = self.stop_when {
                                TimeManagement::Infinite
                            } else {
                                TimeManagement::MoveTime(0)
                            };
                        }
                    }

                    // Stale reports from stopped searches.
                    _ => (),
                }
            }
        }

        if self.replies.len() > 0 {
            Some(self.replies.remove(0))
        } else {
            None
        }
    }
}

impl Engine {
    // A helper method.
    fn register_progress(&mut self, depth: u8, searched_nodes: NodeCount) {
        let thinking_duration = self.thinking_since.elapsed().unwrap();
        self.searched_nodes = searched_nodes;
        self.searched_time = 1000 * thinking_duration.as_secs() +
                             (thinking_duration.subsec_nanos() / 1000000) as u64;
        self.nps = 1000 * (self.nps + self.searched_nodes) / (1000 + self.searched_time);
        if self.current_depth < depth {
            self.current_depth = depth;
            self.report_pv(depth - 1);
            if !self.mangled_pv {
                self.report_progress();
            }
        }
    }

    // A helper method. It extracts the primary variation (PV) from
    // the transposition table (TT) and sends it to the GUI.
    fn report_pv(&mut self, depth: u8) {
        let mut prev_move = None;
        let mut p = self.position.clone();

        // Extract the PV, the value, and the bound from the TT.
        let mut pv = Vec::new();
        let mut value = -20000;
        let mut bound = BOUND_LOWER;
        while let Some(entry) = self.tt.probe(p.hash()) {
            if pv.len() < depth as usize && entry.bound() != BOUND_NONE {
                if let Some(m) = prev_move {
                    pv.push(m);
                }
                if pv.len() & 1 == 0 {
                    value = entry.value();
                    bound = entry.bound();
                } else {
                    // In this case the value stored in `entry` is
                    // from other side's perspective.
                    value = -entry.value();
                    bound = match entry.bound() {
                        BOUND_UPPER => BOUND_LOWER,
                        BOUND_LOWER => BOUND_UPPER,
                        x => x,
                    };
                }
                if bound == BOUND_EXACT {
                    if let Some(m) = p.try_move_digest(entry.move16()) {
                        if p.do_move(m) && !p.is_repeated() {
                            // Try to extend the PV.
                            prev_move = Some(m);
                            continue;
                        }
                    }
                }
            }
            break;
        }
        self.mangled_pv = bound != BOUND_EXACT || pv.len() < depth as usize;

        // Send the extracted info to the GUI.
        let value_suffix = match bound {
            BOUND_EXACT => "",
            BOUND_UPPER => " upperbound",
            BOUND_LOWER => " lowerbound",
            _ => panic!("unexpected bound type"),
        };
        let mut pv_string = String::new();
        for m in pv {
            pv_string.push(' ');
            pv_string.push_str(&m.notation());
        }
        self.replies.push(EngineReply::Info(vec![
            ("depth".to_string(), format!("{}", depth)),
            ("score".to_string(), format!("cp {}{}", value, value_suffix)),
            ("time".to_string(), format!("{}", self.searched_time)),
            ("nodes".to_string(), format!("{}", self.searched_nodes)),
            ("nps".to_string(), format!("{}", self.nps)),
            ("pv".to_string(), pv_string),
        ]));
        self.no_reports_since = SystemTime::now();
    }

    // A helper method. It reports the depth, the node count, and
    // nodes per second to the GUI.
    fn report_progress(&mut self) {
        self.replies.push(EngineReply::Info(vec![
            ("depth".to_string(), format!("{}", self.current_depth)),
            ("nodes".to_string(), format!("{}", self.searched_nodes)),
            ("nps".to_string(), format!("{}", self.nps)),
        ]));
        self.no_reports_since = SystemTime::now();
    }

    fn get_best_move(&mut self) -> String {
        let mut m = match self.tt.probe(self.position.hash()) {
            Some(entry) => entry.move16(),
            None => 0,
        };
        if m == 0 {
            // Pick the first legal move.
            let mut first_legal_move = Move::invalid();
            let mut v = MoveStack::new();
            self.position.generate_moves(&mut v);
            while let Some(x) = v.pop() {
                if self.position.do_move(x) {
                    self.position.undo_move();
                    first_legal_move = x;
                    break;
                }
            }
            m = first_legal_move.digest();
        }
        if m != 0 {
            let move_type = move_type(m);
            let orig_square = orig_square(m);
            let dest_square = dest_square(m);
            let promoted_piece = match aux_data(m) {
                0 => "q",
                1 => "r",
                2 => "b",
                3 => "n",
                _ => panic!("invalid promoted piece code"),
            };
            format!("{}{}{}",
                    notation(orig_square),
                    notation(dest_square),
                    if move_type == MOVE_PROMOTION {
                        promoted_piece
                    } else {
                        ""
                    })

        } else {
            "0000".to_string()
        }
    }
}

/// Implements `UciEngineFactory` trait.
pub struct EngineFactory;

impl UciEngineFactory<Engine> for EngineFactory {
    fn name(&self) -> String {
        format!("Alcibiades {}", VERSION)
    }

    fn author(&self) -> String {
        "Evgeni Pandurski".to_string()
    }

    fn options(&self) -> Vec<(OptionName, OptionDescription)> {
        vec![
            // TODO: Calculate a sane limit for the hash size.
            ("Hash".to_string(), OptionDescription::Spin { min: 1, max: 2048, default: 16 }),
            ("Ponder".to_string(), OptionDescription::Check { default: false }),
        ]
    }

    fn create(&self, hash_size_mb: Option<usize>) -> Engine {
        Engine::new(hash_size_mb.unwrap_or(16))
    }
}

enum TimeManagement {
    MoveTime(u64),
    MoveTimeHint(u64),
    Nodes(NodeCount),
    Depth(u8),
    Infinite,
}
<commit_msg>beautifications<commit_after>pub mod search;

use std::thread;
use std::cmp::min;
use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver};
use std::time::SystemTime;
use std::num::Wrapping;
use basetypes::*;
use uci::{UciEngine, UciEngineFactory, EngineReply, OptionName, OptionDescription};
use position::Position;
use chess_move::*;
use tt::*;

// use rand;
// use rand::distributions::{Sample, Range};
// let mut rng = rand::thread_rng();
// let mut between = Range::new(0, 10);
// let x = between.sample(&mut rng);

const VERSION: &'static str = "0.1";
const MAX_DEPTH: u8 = 126;

/// Implements `UciEngine` trait.
pub struct Engine {
    position: Position,
    tt: Arc<TranspositionTable>,
    commands: Sender<search::Command>,
    reports: Receiver<search::Report>,
    replies: Vec<EngineReply>,

    // 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,

    is_thinking: bool,
    is_pondering: bool,

    // Search status info
    search_id: usize,
    thinking_since: SystemTime,
    no_reports_since: SystemTime,
    current_depth: u8,
    searched_nodes: NodeCount,
    searched_time: u64, // milliseconds
    nps: u64,  // nodes per second
    mangled_pv: bool, // `true` if the primary variation is imperfect
    stop_when: TimeManagement,
}

impl Engine {
    /// Creates a new instance.
    ///
    /// `tt_size_mb` is the preferred size of the transposition
    /// table in Mbytes.
    pub fn new(tt_size_mb: usize) -> Engine {
        let mut tt = TranspositionTable::new();
        tt.resize(tt_size_mb);
        let tt = Arc::new(tt);
        let (commands_tx, commands_rx) = channel();
        let (reports_tx, reports_rx) = channel();
        let tt2 = tt.clone();
        thread::spawn(move || {
            search::run_deepening(tt2, commands_rx, reports_tx);
        });

        Engine {
            position: Position::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 \
                                          1")
                          .ok()
                          .unwrap(),
            tt: tt,
            commands: commands_tx,
            reports: reports_rx,
            replies: vec![],
            pondering_is_allowed: false,
            is_thinking: false,
            is_pondering: false,
            search_id: 0,
            thinking_since: SystemTime::now(),
            no_reports_since: SystemTime::now(),
            current_depth: 0,
            searched_nodes: 0,
            searched_time: 0,
            nps: 0,
            mangled_pv: false,
            stop_when: TimeManagement::Infinite,
        }
    }
}

impl UciEngine for Engine {
    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            "Ponder" => {
                self.pondering_is_allowed = value == "true";
            }

            // An invalid option. Notice that we do not support
            // re-sizing of the transposition table once the engine
            // had started.
            _ => (),
        }
    }

    fn new_game(&mut self) {
        // Clearing the transposition table would not change anything,
        // so we do nothing.
    }

    fn position(&mut self, fen: &str, moves: &mut Iterator<Item = &str>) {
        if !self.is_thinking {
            if let Ok(p) = Position::from_history(fen, moves) {
                self.position = p;
                self.tt.new_search();
            }
        }
    }

    #[allow(unused_variables)]
    fn go(&mut self,
          searchmoves: Option<Vec<String>>,
          ponder: bool,
          wtime: Option<u64>,
          btime: Option<u64>,
          winc: Option<u64>,
          binc: Option<u64>,
          movestogo: Option<u64>,
          depth: Option<u64>,
          nodes: Option<u64>,
          mate: Option<u64>,
          movetime: Option<u64>,
          infinite: bool) {
        if !self.is_thinking {
            // Note: We ignore "searchmoves" and "mate" parameters.

            self.is_thinking = true;
            self.is_pondering = ponder;
            self.search_id = (Wrapping(self.search_id) + Wrapping(1)).0;
            self.thinking_since = SystemTime::now();
            self.no_reports_since = SystemTime::now();
            self.current_depth = 0;
            self.searched_nodes = 0;
            self.searched_time = 0;
            self.mangled_pv = false;

            // Figure out when we should stop thinking.
            let (time, inc) = if self.position.board().to_move() == WHITE {
                (wtime, winc.unwrap_or(0))
            } else {
                (btime, binc.unwrap_or(0))
            };
            self.stop_when = if infinite {
                TimeManagement::Infinite
            } else if movetime.is_some() {
                TimeManagement::MoveTime(movetime.unwrap())
            } else if nodes.is_some() {
                TimeManagement::Nodes(nodes.unwrap())
            } else if depth.is_some() {
                TimeManagement::Depth(depth.unwrap() as u8)
            } else {
                let time = time.unwrap_or(0);
                let movestogo = movestogo.unwrap_or(40);
                let movetime = (time + inc * movestogo) / movestogo;
                TimeManagement::MoveTimeHint(min(movetime, time / 2))
            };

            // Start deepening search to the maximum possible depth.
            self.commands
                .send(search::Command::Search {
                    search_id: self.search_id,
                    position: self.position.clone(),
                    depth: MAX_DEPTH,
                    lower_bound: -20000,
                    upper_bound: 20000,
                })
                .unwrap();
        }
    }

    fn ponder_hit(&mut self) {
        if self.is_thinking && self.is_pondering {
            self.is_pondering = false;
        }
    }

    fn stop(&mut self) {
        if self.is_thinking {
            self.commands.send(search::Command::Stop).unwrap();
            let best_move = self.get_best_move();
            self.replies.push(EngineReply::BestMove {
                best_move: best_move,
                ponder_move: None,
            });
            self.is_thinking = false;
        }
    }

    fn is_thinking(&self) -> bool {
        self.is_thinking
    }

    fn get_reply(&mut self) -> Option<EngineReply> {
        // Check if we should stop thinking.
        if self.is_thinking && !self.is_pondering {
            if match self.stop_when {
                TimeManagement::MoveTime(t) => self.searched_time >= t,
                TimeManagement::MoveTimeHint(t) => self.searched_time >= t,
                TimeManagement::Nodes(n) => self.searched_nodes >= n,
                TimeManagement::Depth(d) => self.current_depth > d,
                TimeManagement::Infinite => false,
            } {
                self.stop();
            }
        }

        // Check the reports queue.
        while let Ok(report) = self.reports.try_recv() {
            if self.is_thinking {
                match report {
                    // Search progress.
                    search::Report::Progress { search_id, searched_nodes, depth }
                        if search_id == self.search_id => {

                        self.register_progress(depth, searched_nodes);
                        if self.no_reports_since.elapsed().unwrap().as_secs() > 20 {
                            if self.mangled_pv {
                                self.report_pv(depth - 1);
                            } else {
                                self.report_progress();
                            }
                        }
                    }

                    // Terminate the search (unless pondering of infinite).
                    search::Report::Done { search_id, .. } if search_id == self.search_id => {
                        if !self.is_pondering {
                            self.stop_when = if let TimeManagement::Infinite = self.stop_when {
                                TimeManagement::Infinite
                            } else {
                                TimeManagement::MoveTime(0)
                            };
                        }
                    }

                    // Stale reports from stopped searches.
                    _ => (),
                }
            }
        }

        if self.replies.len() > 0 {
            Some(self.replies.remove(0))
        } else {
            None
        }
    }
}

impl Engine {
    // A helper method.
    fn register_progress(&mut self, depth: u8, searched_nodes: NodeCount) {
        let thinking_duration = self.thinking_since.elapsed().unwrap();
        self.searched_nodes = searched_nodes;
        self.searched_time = 1000 * thinking_duration.as_secs() +
                             (thinking_duration.subsec_nanos() / 1000000) as u64;
        self.nps = 1000 * (self.nps + self.searched_nodes) / (1000 + self.searched_time);
        if self.current_depth < depth {
            self.current_depth = depth;
            self.report_pv(depth - 1);
            if !self.mangled_pv {
                self.report_progress();
            }
        }
    }

    // A helper method. It extracts the primary variation (PV) from
    // the transposition table (TT) and sends it to the GUI.
    fn report_pv(&mut self, depth: u8) {
        let mut prev_move = None;
        let mut p = self.position.clone();

        // Extract the PV, the value, and the bound from the TT.
        let mut pv = Vec::new();
        let mut value = -20000;
        let mut bound = BOUND_LOWER;
        while let Some(entry) = self.tt.probe(p.hash()) {
            if pv.len() < depth as usize && entry.bound() != BOUND_NONE {
                if let Some(m) = prev_move {
                    pv.push(m);
                }
                if pv.len() & 1 == 0 {
                    value = entry.value();
                    bound = entry.bound();
                } else {
                    // In this case the value stored in `entry` is
                    // from other side's perspective.
                    value = -entry.value();
                    bound = match entry.bound() {
                        BOUND_UPPER => BOUND_LOWER,
                        BOUND_LOWER => BOUND_UPPER,
                        x => x,
                    };
                }
                if bound == BOUND_EXACT {
                    if let Some(m) = p.try_move_digest(entry.move16()) {
                        if p.do_move(m) && !p.is_repeated() {
                            // Try to extend the PV.
                            prev_move = Some(m);
                            continue;
                        }
                    }
                }
            }
            break;
        }
        self.mangled_pv = bound != BOUND_EXACT || pv.len() < depth as usize;

        // Send the extracted info to the GUI.
        let value_suffix = match bound {
            BOUND_EXACT => "",
            BOUND_UPPER => " upperbound",
            BOUND_LOWER => " lowerbound",
            _ => panic!("unexpected bound type"),
        };
        let mut pv_string = String::new();
        for m in pv {
            pv_string.push(' ');
            pv_string.push_str(&m.notation());
        }
        self.replies.push(EngineReply::Info(vec![
            ("depth".to_string(), format!("{}", depth)),
            ("score".to_string(), format!("cp {}{}", value, value_suffix)),
            ("time".to_string(), format!("{}", self.searched_time)),
            ("nodes".to_string(), format!("{}", self.searched_nodes)),
            ("nps".to_string(), format!("{}", self.nps)),
            ("pv".to_string(), pv_string),
        ]));
        self.no_reports_since = SystemTime::now();
    }

    // A helper method. It reports the depth, the node count, and
    // nodes per second to the GUI.
    fn report_progress(&mut self) {
        self.replies.push(EngineReply::Info(vec![
            ("depth".to_string(), format!("{}", self.current_depth)),
            ("nodes".to_string(), format!("{}", self.searched_nodes)),
            ("nps".to_string(), format!("{}", self.nps)),
        ]));
        self.no_reports_since = SystemTime::now();
    }

    fn get_best_move(&mut self) -> String {
        let mut m = match self.tt.probe(self.position.hash()) {
            Some(entry) => entry.move16(),
            None => 0,
        };
        if m == 0 {
            // Pick the first legal move.
            let mut first_legal_move = Move::invalid();
            let mut v = MoveStack::new();
            self.position.generate_moves(&mut v);
            while let Some(x) = v.pop() {
                if self.position.do_move(x) {
                    self.position.undo_move();
                    first_legal_move = x;
                    break;
                }
            }
            m = first_legal_move.digest();
        }
        if m != 0 {
            let move_type = move_type(m);
            let orig_square = orig_square(m);
            let dest_square = dest_square(m);
            let promoted_piece = match aux_data(m) {
                0 => "q",
                1 => "r",
                2 => "b",
                3 => "n",
                _ => panic!("invalid promoted piece code"),
            };
            format!("{}{}{}",
                    notation(orig_square),
                    notation(dest_square),
                    if move_type == MOVE_PROMOTION {
                        promoted_piece
                    } else {
                        ""
                    })

        } else {
            "0000".to_string()
        }
    }
}

/// Implements `UciEngineFactory` trait.
pub struct EngineFactory;

impl UciEngineFactory<Engine> for EngineFactory {
    fn name(&self) -> String {
        format!("Alcibiades {}", VERSION)
    }

    fn author(&self) -> String {
        "Evgeni Pandurski".to_string()
    }

    fn options(&self) -> Vec<(OptionName, OptionDescription)> {
        vec![
            // TODO: Calculate a sane limit for the hash size.
            ("Hash".to_string(), OptionDescription::Spin { min: 1, max: 2048, default: 16 }),
            ("Ponder".to_string(), OptionDescription::Check { default: false }),
        ]
    }

    fn create(&self, hash_size_mb: Option<usize>) -> Engine {
        Engine::new(hash_size_mb.unwrap_or(16))
    }
}

enum TimeManagement {
    MoveTime(u64),
    MoveTimeHint(u64),
    Nodes(NodeCount),
    Depth(u8),
    Infinite,
}
<|endoftext|>


<commit_before>use circle::Circle;
use fx32::Fx32;
use ray2::Ray2;
use vec2::Vec2;

#[derive (Clone, Copy)]
pub enum Ray2TraceResult {
	Hit (Fx32, Vec2, Vec2),
	Miss,
}

pub fn fold_closer_result (a: Ray2TraceResult, b: Ray2TraceResult) -> Ray2TraceResult {
	match a {
		Ray2TraceResult::Miss => {
			return b;
		},
		Ray2TraceResult::Hit (a_t, ..) => {
			match b {
				Ray2TraceResult::Miss => {
					return a;
				},
				Ray2TraceResult::Hit (b_t, ..) => {
					if a_t < b_t {
						return a;
					}
					else {
						return b;
					}
				}
			}
		},
	}
}

pub fn ray_trace_circle (ray: &Ray2, circle: &Circle) -> Ray2TraceResult {
	let toward_circle = circle.center - ray.start;

	if toward_circle * ray.dir < Fx32::from_int (0) {
		// Ray is heading out of the circle's space or is already out
		return Ray2TraceResult::Miss;
	}

	// TODO: Handle ray starting inside circle

	// The following is derived from the quadratic formula and my Lua code
	let a = ray.dir.length_sq ();

	if a == Fx32::from_int (0) {
		return Ray2TraceResult::Miss;
	}

	let b =
		Fx32::from_int (2) * (ray.start * ray.dir) -
		Fx32::from_int (2) * (circle.center * ray.dir);

	let c = circle.center.length_sq () + ray.start.length_sq ()
		- Fx32::from_int (2) * (circle.center * ray.start)
		- circle.radius.square_64 ();

	let determinant = b * b - Fx32::from_int (4) * a * c;
	if determinant < Fx32::from_int (0) {
		return Ray2TraceResult::Miss;
	}

	let t = (-b - determinant.sqrt_64 ()) / (Fx32::from_int (2) * a);

	if t >= Fx32::from_int (0) && t <= Fx32::from_int (1) {
		let ccd_pos = ray.at (t);

		let normal = (ccd_pos - circle.center).normalized ();

		return Ray2TraceResult::Hit (t, ccd_pos, normal);
	}

	Ray2TraceResult::Miss
}
<commit_msg>Refactoring the raytrace fn to make intermediate numbers smaller<commit_after>use circle::Circle;
use fx32::Fx32;
use ray2::Ray2;
use vec2::Vec2;

#[derive (Clone, Copy)]
pub enum Ray2TraceResult {
	Hit (Fx32, Vec2, Vec2),
	Miss,
}

pub fn fold_closer_result (a: Ray2TraceResult, b: Ray2TraceResult) -> Ray2TraceResult {
	match a {
		Ray2TraceResult::Miss => {
			return b;
		},
		Ray2TraceResult::Hit (a_t, ..) => {
			match b {
				Ray2TraceResult::Miss => {
					return a;
				},
				Ray2TraceResult::Hit (b_t, ..) => {
					if a_t < b_t {
						return a;
					}
					else {
						return b;
					}
				}
			}
		},
	}
}

pub fn ray_trace_circle (ray: &Ray2, circle: &Circle) -> Ray2TraceResult {
	let toward_circle = circle.center - ray.start;

	if toward_circle * ray.dir < Fx32::from_int (0) {
		// Ray is heading out of the circle's space or is already out
		return Ray2TraceResult::Miss;
	}

	// TODO: Handle ray starting inside circle

	// The following is derived from the quadratic formula and my Lua code
	let a = ray.dir.length_sq ();

	if a == Fx32::from_int (0) {
		return Ray2TraceResult::Miss;
	}

	let b = (ray.dir * toward_circle) * Fx32::from_int (-2);

	let c = circle.center.length_sq () + ray.start.length_sq ()
		- Fx32::from_int (2) * (circle.center * ray.start)
		- circle.radius.square_64 ();

	let determinant = b * b - Fx32::from_int (4) * a * c;
	if determinant < Fx32::from_int (0) {
		return Ray2TraceResult::Miss;
	}

	let t = (-b - determinant.sqrt_64 ()) / (Fx32::from_int (2) * a);

	if t >= Fx32::from_int (0) && t <= Fx32::from_int (1) {
		let ccd_pos = ray.at (t);

		let normal = (ccd_pos - circle.center).normalized ();

		return Ray2TraceResult::Hit (t, ccd_pos, normal);
	}

	Ray2TraceResult::Miss
}
<|endoftext|>


<commit_before>/// The event system will have a trigger function that takes an
/// argument that decides if the event will be triggered this tick
/// or the next tick. The default should be next tick to prevent
/// inadvertent infinite loop bugs.
///
/// The event system can then schedule all events handlers that
/// do not mutate components to be run in parallel.
/// All handlers that do mutate the component list can be run
/// sequentially after that. Perhaps we can even schedule
/// those with non-overlapping mutable component lists to
/// run in parallel.
///
/// The event loop should trigger every n ms and execute any
/// events that are queued up. Every event has a function
/// that is called by the trigger! macro that puts the event
/// on the queue.
///
use std::collections::{ HashSet, HashMap };
use std::any::{ Any, TypeId };
use shared_mutex::{ SharedMutex, MappedSharedMutexReadGuard, MappedSharedMutexWriteGuard };

use components;

pub trait Handler {
	fn run(&self, Vec<MappedSharedMutexReadGuard<Any>>, Vec<MappedSharedMutexWriteGuard<Any>>);
	fn component_types(&self) -> Vec<TypeId>;
	fn mut_component_types(&self) -> Vec<TypeId>;
}

#[derive(Clone)]
pub struct Event {
	pub name: String,
	pub get_handler_instances: fn () -> Vec<Box<Handler>>
}

// The new events sets contain the events that have data in their queues so
// are ready to be ran.
lazy_static! {
	pub static ref THIS_TICK_NEW_EVENTS: SharedMutex<HashSet<String>> = SharedMutex::new(HashSet::new());
	pub static ref NEXT_TICK_NEW_EVENTS: SharedMutex<HashSet<String>> = SharedMutex::new(HashSet::new());
	pub static ref REGISTERED_EVENTS: SharedMutex<HashMap<String, Event>> = SharedMutex::new(HashMap::new());
}

pub fn trigger_this_tick(event_name: &String) {
	let mut new_events_set = THIS_TICK_NEW_EVENTS.write().expect("THIS_TICK_NEW_EVENTS mutex was corrupted.");
	new_events_set.insert(event_name.clone());
}

pub fn trigger_next_tick(event_name: &String) {
	let mut new_events_set = NEXT_TICK_NEW_EVENTS.write().expect("NEXT_TICK_NEW_EVENTS mutex was corrupted.");
	new_events_set.insert(event_name.clone());
}

pub fn register_event(event : Event) {
	let mut events = REGISTERED_EVENTS.write().expect("REGISTERED_EVENTS mutex was corrupted.");
	events.insert(event.name.clone(), event);
}

pub fn has_events() -> bool {
	THIS_TICK_NEW_EVENTS.read().expect("THIS_TICK_NEW_EVENTS mutex was corrupted").len() > 0
}

// Runs a single iteration of the event system. Can be run in a loop to process events
// continuously, but should be interleaved with `next_tick` to progress properly.
pub fn run_events() {
	let events : Vec<Event>;
	{
		let event_names : Vec<String>;
		{
			let mut event_names_lock = THIS_TICK_NEW_EVENTS.write().expect("THIS_TICK_NEW_EVENTS mutex was corrupted");
			event_names = event_names_lock.drain().collect();
		}

		let events_lock = REGISTERED_EVENTS.read().expect("REGISTERED_EVENTS mutex was corrupted");
		events = event_names.iter().map(|n|
			events_lock.get(&*n).cloned().expect("Unknown event triggered")
		).collect();
	}

	let handlers = events.iter().flat_map(|e| (e.get_handler_instances)() );

	// TODO sort and parellize handler invocations
	for handler in handlers {
		let component_types = handler.component_types();
		let mut_component_types = handler.mut_component_types();

		let mut locks : Vec<MappedSharedMutexReadGuard<Any>>= vec![];
		let mut mut_locks : Vec<MappedSharedMutexWriteGuard<Any>>= vec![];

		// we obtain the correct component locks
		for typ in component_types {
			locks.push(components::get_components_read_lock(typ));
		}
		for typ in mut_component_types {
			mut_locks.push(components::get_components_write_lock(typ));
		}

		// we run the handlers
		handler.run(locks, mut_locks)
	}
}

// Progresses the system to the next tick.
// Interleave `next_tick` between `run_events` invocations to make sure events that are
// scheduled to run only once per tick are triggered.
// A good scheme could be to run `next_tick` every 16ms, while `run_events` is ran continuously.
pub fn next_tick() {
	let mut this_tick_events = THIS_TICK_NEW_EVENTS.write().expect("THIS_TICK_NEW_EVENTS mutex was corrupted");
	let mut next_events_lock = NEXT_TICK_NEW_EVENTS.write().expect("THIS_TICK_NEW_EVENTS mutex was corrupted");

	for next_event in next_events_lock.drain() {
		this_tick_events.insert(next_event);
	}
}

#[macro_export]
macro_rules! event {
	( $name:ident, $( $field_name:ident : $field_typ:ty ),* ) => (
		pub mod $name {
			use shared_mutex::{ SharedMutex, MappedSharedMutexWriteGuard, MappedSharedMutexReadGuard };
			use std::any::{ Any, TypeId };
			use $crate::events;
			use uuid::Uuid;

			/// Example component
			#[derive(PartialEq,Eq,Clone)]
			pub struct Data {
				$(pub $field_name : $field_typ),*
			}

			pub type HandlerFn = fn(&Vec<Data>, Vec<MappedSharedMutexReadGuard<Any>>, Vec<MappedSharedMutexWriteGuard<Any>>);

			pub struct Handler {
				handler_fn: HandlerFn,
				component_types: Vec<TypeId>,
				mut_component_types: Vec<TypeId>
			}

			pub struct HandlerInstance {
				handler_fn: HandlerFn,
				component_types: Vec<TypeId>,
				mut_component_types: Vec<TypeId>,
				data: Vec<Data>
			}

			impl HandlerInstance {
				pub fn new(h: &Handler, d: Vec<Data>) -> HandlerInstance {
					HandlerInstance {
						handler_fn: h.handler_fn,
						// TODO are these clones really necessary? would be cool if they could be static refs
						component_types: h.component_types.clone(),
						mut_component_types: h.mut_component_types.clone(),
						data: d
					}
				}
			}

			impl events::Handler for HandlerInstance {
				fn run(&self, components: Vec<MappedSharedMutexReadGuard<Any>>, mut_components: Vec<MappedSharedMutexWriteGuard<Any>>) {
					let handler_fn = self.handler_fn;
					let data = &self.data;
					handler_fn(data, components, mut_components)
				}

				fn component_types(&self) -> Vec<TypeId> { self.component_types.clone() }
				fn mut_component_types(&self) -> Vec<TypeId> { self.mut_component_types.clone() }
			}

			lazy_static! {
				/// EVENT_UUID is used internally to index events, is randomly
				/// generated at first access.
				pub static ref EVENT_UUID: String = Uuid::new_v4().simple().to_string();
				pub static ref HANDLERS: SharedMutex<Vec<Handler>> = SharedMutex::new(vec![]);
				pub static ref THIS_TICK_DATA: SharedMutex<Vec<Data>> = SharedMutex::new(vec![]);
			}

			/// Listeners are a list of functions that should be called by trigger
			pub fn trigger($($field_name : $field_typ),*) {
				let argument = Data {
					$($field_name : $field_name),*
				};

				let mut data = THIS_TICK_DATA.write().expect("THIS_TICK_DATA mutex corrupted");
				data.push(argument);

				events::trigger_this_tick(&*EVENT_UUID);
			}

			pub fn register_handler(handler_fn: HandlerFn, component_types: Vec<TypeId>, mut_component_types: Vec<TypeId>) {
				let mut handlers = HANDLERS.write().expect("Events HANDLERS mutex corrupted");
				let handler = Handler {
					handler_fn : handler_fn,
					// TODO are these clones really necessary? would be cool if they could be static refs
					component_types : component_types.clone(),
					mut_component_types : mut_component_types.clone()
				};
				handlers.push(handler);

				let event = events::Event {
					name: (&*EVENT_UUID).clone(),
					get_handler_instances: get_handler_instances
				};

				events::register_event(event)
			}

			pub fn get_handler_instances() -> Vec<Box<events::Handler>> {
				let mut data_old = THIS_TICK_DATA.write().expect("TICK DATA mutex corrupted");
				let data : Vec<Data> = data_old.drain(..).collect();

				let handlers_lock = HANDLERS.read().expect("HANDLERS mutex corrupted");
				handlers_lock.iter().map(|h|
					//TODO eliminate this data.clone
					Box::new(HandlerInstance::new(h, data.clone())) as Box<events::Handler>
				).collect()
			}

			pub fn clear_handlers() {
				let mut handlers = HANDLERS.write().expect("Events HANDLERS mutex corrupted");
				handlers.clear();
			}
		}
	)
}

#[macro_export]
macro_rules! sync_event {
	( $name:ident, $field_name:ident : $field_typ:ty ) => (
		pub mod $name {
			use shared_mutex::SharedMutex;

			pub type Argument<'a,'b> = $field_typ;
			pub type HandlerFn = fn(Argument);

			lazy_static! {
				pub static ref HANDLERS: SharedMutex<Vec<HandlerFn>> = SharedMutex::new(vec![]);
			}

			/// Listeners are a list of functions that should be called by trigger
			pub fn trigger<'a, 'b>($field_name : $field_typ) {
				let handlers = HANDLERS.read().expect("HANDLERS mutex corrupted");
				// TODO let handlers define priorities so they can be ordered
				for handler_fn in handlers.iter() {
					handler_fn($field_name);
				}
			}

			pub fn register_handler(handler_fn: HandlerFn) {
				let mut handlers = HANDLERS.write().expect("Events HANDLERS mutex corrupted");
				handlers.push(handler_fn);
			}

			pub fn clear_handlers() {
				let mut handlers = HANDLERS.write().expect("Events HANDLERS mutex corrupted");
				handlers.clear();
			}
		}
	)
}
<commit_msg>log what kind of event was triggered<commit_after>/// The event system will have a trigger function that takes an
/// argument that decides if the event will be triggered this tick
/// or the next tick. The default should be next tick to prevent
/// inadvertent infinite loop bugs.
///
/// The event system can then schedule all events handlers that
/// do not mutate components to be run in parallel.
/// All handlers that do mutate the component list can be run
/// sequentially after that. Perhaps we can even schedule
/// those with non-overlapping mutable component lists to
/// run in parallel.
///
/// The event loop should trigger every n ms and execute any
/// events that are queued up. Every event has a function
/// that is called by the trigger! macro that puts the event
/// on the queue.
///
use std::collections::{ HashSet, HashMap };
use std::any::{ Any, TypeId };
use shared_mutex::{ SharedMutex, MappedSharedMutexReadGuard, MappedSharedMutexWriteGuard };

use components;

pub trait Handler {
	fn run(&self, Vec<MappedSharedMutexReadGuard<Any>>, Vec<MappedSharedMutexWriteGuard<Any>>);
	fn component_types(&self) -> Vec<TypeId>;
	fn mut_component_types(&self) -> Vec<TypeId>;
}

#[derive(Clone)]
pub struct Event {
	pub name: String,
	pub get_handler_instances: fn () -> Vec<Box<Handler>>
}

// The new events sets contain the events that have data in their queues so
// are ready to be ran.
lazy_static! {
	pub static ref THIS_TICK_NEW_EVENTS: SharedMutex<HashSet<String>> = SharedMutex::new(HashSet::new());
	pub static ref NEXT_TICK_NEW_EVENTS: SharedMutex<HashSet<String>> = SharedMutex::new(HashSet::new());
	pub static ref REGISTERED_EVENTS: SharedMutex<HashMap<String, Event>> = SharedMutex::new(HashMap::new());
}

pub fn trigger_this_tick(event_name: &String) {
	let mut new_events_set = THIS_TICK_NEW_EVENTS.write().expect("THIS_TICK_NEW_EVENTS mutex was corrupted.");
	new_events_set.insert(event_name.clone());
}

pub fn trigger_next_tick(event_name: &String) {
	let mut new_events_set = NEXT_TICK_NEW_EVENTS.write().expect("NEXT_TICK_NEW_EVENTS mutex was corrupted.");
	new_events_set.insert(event_name.clone());
}

pub fn register_event(event : Event) {
	let mut events = REGISTERED_EVENTS.write().expect("REGISTERED_EVENTS mutex was corrupted.");
	events.insert(event.name.clone(), event);
}

pub fn has_events() -> bool {
	THIS_TICK_NEW_EVENTS.read().expect("THIS_TICK_NEW_EVENTS mutex was corrupted").len() > 0
}

// Runs a single iteration of the event system. Can be run in a loop to process events
// continuously, but should be interleaved with `next_tick` to progress properly.
pub fn run_events() {
	let events : Vec<Event>;
	{
		let event_names : Vec<String>;
		{
			let mut event_names_lock = THIS_TICK_NEW_EVENTS.write().expect("THIS_TICK_NEW_EVENTS mutex was corrupted");
			event_names = event_names_lock.drain().collect();
		}

		let events_lock = REGISTERED_EVENTS.read().expect("REGISTERED_EVENTS mutex was corrupted");
		events = event_names.iter().map(|n|
			events_lock.get(&*n).cloned().expect(&format!("Unknown event triggered: {}", n))
		).collect();
	}

	let handlers = events.iter().flat_map(|e| (e.get_handler_instances)() );

	// TODO sort and parellize handler invocations
	for handler in handlers {
		let component_types = handler.component_types();
		let mut_component_types = handler.mut_component_types();

		let mut locks : Vec<MappedSharedMutexReadGuard<Any>>= vec![];
		let mut mut_locks : Vec<MappedSharedMutexWriteGuard<Any>>= vec![];

		// we obtain the correct component locks
		for typ in component_types {
			locks.push(components::get_components_read_lock(typ));
		}
		for typ in mut_component_types {
			mut_locks.push(components::get_components_write_lock(typ));
		}

		// we run the handlers
		handler.run(locks, mut_locks)
	}
}

// Progresses the system to the next tick.
// Interleave `next_tick` between `run_events` invocations to make sure events that are
// scheduled to run only once per tick are triggered.
// A good scheme could be to run `next_tick` every 16ms, while `run_events` is ran continuously.
pub fn next_tick() {
	let mut this_tick_events = THIS_TICK_NEW_EVENTS.write().expect("THIS_TICK_NEW_EVENTS mutex was corrupted");
	let mut next_events_lock = NEXT_TICK_NEW_EVENTS.write().expect("THIS_TICK_NEW_EVENTS mutex was corrupted");

	for next_event in next_events_lock.drain() {
		this_tick_events.insert(next_event);
	}
}

#[macro_export]
macro_rules! event {
	( $name:ident, $( $field_name:ident : $field_typ:ty ),* ) => (
		pub mod $name {
			use shared_mutex::{ SharedMutex, MappedSharedMutexWriteGuard, MappedSharedMutexReadGuard };
			use std::any::{ Any, TypeId };
			use $crate::events;
			use uuid::Uuid;

			/// Example component
			#[derive(PartialEq,Eq,Clone)]
			pub struct Data {
				$(pub $field_name : $field_typ),*
			}

			pub type HandlerFn = fn(&Vec<Data>, Vec<MappedSharedMutexReadGuard<Any>>, Vec<MappedSharedMutexWriteGuard<Any>>);

			pub struct Handler {
				handler_fn: HandlerFn,
				component_types: Vec<TypeId>,
				mut_component_types: Vec<TypeId>
			}

			pub struct HandlerInstance {
				handler_fn: HandlerFn,
				component_types: Vec<TypeId>,
				mut_component_types: Vec<TypeId>,
				data: Vec<Data>
			}

			impl HandlerInstance {
				pub fn new(h: &Handler, d: Vec<Data>) -> HandlerInstance {
					HandlerInstance {
						handler_fn: h.handler_fn,
						// TODO are these clones really necessary? would be cool if they could be static refs
						component_types: h.component_types.clone(),
						mut_component_types: h.mut_component_types.clone(),
						data: d
					}
				}
			}

			impl events::Handler for HandlerInstance {
				fn run(&self, components: Vec<MappedSharedMutexReadGuard<Any>>, mut_components: Vec<MappedSharedMutexWriteGuard<Any>>) {
					let handler_fn = self.handler_fn;
					let data = &self.data;
					handler_fn(data, components, mut_components)
				}

				fn component_types(&self) -> Vec<TypeId> { self.component_types.clone() }
				fn mut_component_types(&self) -> Vec<TypeId> { self.mut_component_types.clone() }
			}

			lazy_static! {
				/// EVENT_UUID is used internally to index events, is randomly
				/// generated at first access.
				pub static ref EVENT_UUID: String = Uuid::new_v4().simple().to_string();
				pub static ref HANDLERS: SharedMutex<Vec<Handler>> = SharedMutex::new(vec![]);
				pub static ref THIS_TICK_DATA: SharedMutex<Vec<Data>> = SharedMutex::new(vec![]);
			}

			/// Listeners are a list of functions that should be called by trigger
			pub fn trigger($($field_name : $field_typ),*) {
				let argument = Data {
					$($field_name : $field_name),*
				};

				let mut data = THIS_TICK_DATA.write().expect("THIS_TICK_DATA mutex corrupted");
				data.push(argument);

				events::trigger_this_tick(&*EVENT_UUID);
			}

			pub fn register_handler(handler_fn: HandlerFn, component_types: Vec<TypeId>, mut_component_types: Vec<TypeId>) {
				let mut handlers = HANDLERS.write().expect("Events HANDLERS mutex corrupted");
				let handler = Handler {
					handler_fn : handler_fn,
					// TODO are these clones really necessary? would be cool if they could be static refs
					component_types : component_types.clone(),
					mut_component_types : mut_component_types.clone()
				};
				handlers.push(handler);

				let event = events::Event {
					name: (&*EVENT_UUID).clone(),
					get_handler_instances: get_handler_instances
				};

				events::register_event(event)
			}

			pub fn get_handler_instances() -> Vec<Box<events::Handler>> {
				let mut data_old = THIS_TICK_DATA.write().expect("TICK DATA mutex corrupted");
				let data : Vec<Data> = data_old.drain(..).collect();

				let handlers_lock = HANDLERS.read().expect("HANDLERS mutex corrupted");
				handlers_lock.iter().map(|h|
					//TODO eliminate this data.clone
					Box::new(HandlerInstance::new(h, data.clone())) as Box<events::Handler>
				).collect()
			}

			pub fn clear_handlers() {
				let mut handlers = HANDLERS.write().expect("Events HANDLERS mutex corrupted");
				handlers.clear();
			}
		}
	)
}

#[macro_export]
macro_rules! sync_event {
	( $name:ident, $field_name:ident : $field_typ:ty ) => (
		pub mod $name {
			use shared_mutex::SharedMutex;

			pub type Argument<'a,'b> = $field_typ;
			pub type HandlerFn = fn(Argument);

			lazy_static! {
				pub static ref HANDLERS: SharedMutex<Vec<HandlerFn>> = SharedMutex::new(vec![]);
			}

			/// Listeners are a list of functions that should be called by trigger
			pub fn trigger<'a, 'b>($field_name : $field_typ) {
				let handlers = HANDLERS.read().expect("HANDLERS mutex corrupted");
				// TODO let handlers define priorities so they can be ordered
				for handler_fn in handlers.iter() {
					handler_fn($field_name);
				}
			}

			pub fn register_handler(handler_fn: HandlerFn) {
				let mut handlers = HANDLERS.write().expect("Events HANDLERS mutex corrupted");
				handlers.push(handler_fn);
			}

			pub fn clear_handlers() {
				let mut handlers = HANDLERS.write().expect("Events HANDLERS mutex corrupted");
				handlers.clear();
			}
		}
	)
}
<|endoftext|>


<commit_before>use std::error;
use std::fmt;

#[derive(PartialEq, Eq, Clone, Copy)]
pub struct SendError<T>(pub T);

#[derive(PartialEq, Eq, Clone, Copy)]
pub enum TrySendError<T> {
    Full(T),
    Disconnected(T),
}

#[derive(PartialEq, Eq, Clone, Copy)]
pub enum SendTimeoutError<T> {
    Timeout(T),
    Disconnected(T),
}

#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct RecvError;

#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum TryRecvError {
    Empty,
    Disconnected,
}

#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum RecvTimeoutError {
    Timeout,
    Disconnected,
}

impl<T> fmt::Debug for SendError<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        "SendError(..)".fmt(f)
    }
}

impl<T> fmt::Display for SendError<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        "sending on a closed channel".fmt(f)
    }
}

impl<T: Send> error::Error for SendError<T> {
    fn description(&self) -> &str {
        "sending on a closed channel"
    }

    fn cause(&self) -> Option<&error::Error> {
        None
    }
}

impl<T> fmt::Debug for TrySendError<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            TrySendError::Full(..) => "Full(..)".fmt(f),
            TrySendError::Disconnected(..) => "Disconnected(..)".fmt(f),
        }
    }
}

impl<T> fmt::Display for TrySendError<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            TrySendError::Full(..) => "sending on a full channel".fmt(f),
            TrySendError::Disconnected(..) => "sending on a closed channel".fmt(f),
        }
    }
}

impl<T: Send> error::Error for TrySendError<T> {
    fn description(&self) -> &str {
        match *self {
            TrySendError::Full(..) => "sending on a full channel",
            TrySendError::Disconnected(..) => "sending on a closed channel",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        None
    }
}

impl<T> fmt::Debug for SendTimeoutError<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        "SendTimeoutError(..)".fmt(f)
    }
}

impl<T> fmt::Display for SendTimeoutError<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            SendTimeoutError::Timeout(..) => "timed out waiting on channel".fmt(f),
            SendTimeoutError::Disconnected(..) => "sending on a closed channel".fmt(f),
        }
    }
}

impl<T: Send> error::Error for SendTimeoutError<T> {
    fn description(&self) -> &str {
        "sending on a closed channel"
    }

    fn cause(&self) -> Option<&error::Error> {
        None
    }
}

impl fmt::Display for RecvError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        "receiving on a closed channel".fmt(f)
    }
}

impl error::Error for RecvError {
    fn description(&self) -> &str {
        "receiving on a closed channel"
    }

    fn cause(&self) -> Option<&error::Error> {
        None
    }
}

impl fmt::Display for TryRecvError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            TryRecvError::Empty => "receiving on an empty channel".fmt(f),
            TryRecvError::Disconnected => "receiving on a closed channel".fmt(f),
        }
    }
}

impl error::Error for TryRecvError {
    fn description(&self) -> &str {
        match *self {
            TryRecvError::Empty => "receiving on an empty channel",
            TryRecvError::Disconnected => "receiving on a closed channel",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        None
    }
}

impl fmt::Display for RecvTimeoutError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            RecvTimeoutError::Timeout => "timed out waiting on channel".fmt(f),
            RecvTimeoutError::Disconnected => "channel is empty and sending half is closed".fmt(f),
        }
    }
}

impl error::Error for RecvTimeoutError {
    fn description(&self) -> &str {
        match *self {
            RecvTimeoutError::Timeout => "timed out waiting on channel",
            RecvTimeoutError::Disconnected => "channel is empty and sending half is closed",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        None
    }
}
<commit_msg>More docs<commit_after>use std::error;
use std::fmt;

/// An error returned from the [`Sender::send`] method on channels.
///
/// A send operation can only fail if the receiving end of a channel is disconnected, implying that
/// the data could never be received. The error contains the data being sent as a payload so it can
/// be recovered.
///
/// [`Sender::send`]: struct.Sender.html#method.send
#[derive(PartialEq, Eq, Clone, Copy)]
pub struct SendError<T>(pub T);

/// This enumeration is the list of the possible error outcomes for the [`try_send`] method.
///
/// [`try_send`]: struct.Sender.html#method.try_send
#[derive(PartialEq, Eq, Clone, Copy)]
pub enum TrySendError<T> {
    /// The data could not be sent on the channel because it would require that the callee block to
    /// send the data.
    ///
    /// If this is a bounded positive-capacity channel, then it is full at this time. If this is a
    /// zero-capacity channel, then there is no [`Receiver`] available to acquire the data.
    ///
    /// [`Receiver`]: struct.Receiver.html
    Full(T),

    /// This channel's receiving half has disconnected, so the data could not be sent. The data is
    /// returned back to the callee in this case.
    Disconnected(T),
}

/// This enumeration is the list of possible errors that made [`send_timeout`] unable to return
/// data when called. This can occur with bounded channels only.
///
/// [`send_timeout`]: struct.Sender.html#method.send_timeout
#[derive(PartialEq, Eq, Clone, Copy)]
pub enum SendTimeoutError<T> {
    /// This channel is currently full, but the receivers have not yet disconnected.
    Timeout(T),

    /// The channel's receiving half has become disconnected.
    Disconnected(T),
}

/// An error returned from the [`recv`] method on a [`Receiver`].
///
/// The [`recv`] operation can only fail if the sending half of a channel is disconnected, implying
/// that no further messages will ever be received.
///
/// [`recv`]: struct.Receiver.html#method.recv
/// [`Receiver`]: struct.Receiver.html
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct RecvError;

/// This enumeration is the list of the possible reasons that [`try_recv`] could not return data
/// when called. This can occur with both bounded and unbounded channels.
///
/// [`try_recv`]: struct.Receiver.html#method.recv
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum TryRecvError {
    /// This channel is currently empty, but the senders have not yet disconnected, so data may yet
    /// become available.
    Empty,

    /// The channel's sending half has become disconnected, and there will never be any more data
    /// received on it.
    Disconnected,
}

/// This enumeration is the list of possible errors that made [`recv_timeout`] unable to return
/// data when called. This can occur with both bounded and unbounded channels.
///
/// [`recv_timeout`]: struct.Receiver.html#method.recv_timeout
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum RecvTimeoutError {
    /// This channel is currently empty, but the senders have not yet disconnected, so data may yet
    /// become available.
    Timeout,

    /// The channel's sending half has become disconnected, and there will never be any more data
    /// received on it.
    Disconnected,
}

impl<T> fmt::Debug for SendError<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        "SendError(..)".fmt(f)
    }
}

impl<T> fmt::Display for SendError<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        "sending on a closed channel".fmt(f)
    }
}

impl<T: Send> error::Error for SendError<T> {
    fn description(&self) -> &str {
        "sending on a closed channel"
    }

    fn cause(&self) -> Option<&error::Error> {
        None
    }
}

impl<T> fmt::Debug for TrySendError<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            TrySendError::Full(..) => "Full(..)".fmt(f),
            TrySendError::Disconnected(..) => "Disconnected(..)".fmt(f),
        }
    }
}

impl<T> fmt::Display for TrySendError<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            TrySendError::Full(..) => "sending on a full channel".fmt(f),
            TrySendError::Disconnected(..) => "sending on a closed channel".fmt(f),
        }
    }
}

impl<T: Send> error::Error for TrySendError<T> {
    fn description(&self) -> &str {
        match *self {
            TrySendError::Full(..) => "sending on a full channel",
            TrySendError::Disconnected(..) => "sending on a closed channel",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        None
    }
}

impl<T> fmt::Debug for SendTimeoutError<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        "SendTimeoutError(..)".fmt(f)
    }
}

impl<T> fmt::Display for SendTimeoutError<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            SendTimeoutError::Timeout(..) => "timed out waiting on channel".fmt(f),
            SendTimeoutError::Disconnected(..) => "sending on a closed channel".fmt(f),
        }
    }
}

impl<T: Send> error::Error for SendTimeoutError<T> {
    fn description(&self) -> &str {
        "sending on a closed channel"
    }

    fn cause(&self) -> Option<&error::Error> {
        None
    }
}

impl fmt::Display for RecvError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        "receiving on a closed channel".fmt(f)
    }
}

impl error::Error for RecvError {
    fn description(&self) -> &str {
        "receiving on a closed channel"
    }

    fn cause(&self) -> Option<&error::Error> {
        None
    }
}

impl fmt::Display for TryRecvError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            TryRecvError::Empty => "receiving on an empty channel".fmt(f),
            TryRecvError::Disconnected => "receiving on a closed channel".fmt(f),
        }
    }
}

impl error::Error for TryRecvError {
    fn description(&self) -> &str {
        match *self {
            TryRecvError::Empty => "receiving on an empty channel",
            TryRecvError::Disconnected => "receiving on a closed channel",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        None
    }
}

impl fmt::Display for RecvTimeoutError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            RecvTimeoutError::Timeout => "timed out waiting on channel".fmt(f),
            RecvTimeoutError::Disconnected => "channel is empty and sending half is closed".fmt(f),
        }
    }
}

impl error::Error for RecvTimeoutError {
    fn description(&self) -> &str {
        match *self {
            RecvTimeoutError::Timeout => "timed out waiting on channel",
            RecvTimeoutError::Disconnected => "channel is empty and sending half is closed",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        None
    }
}
<|endoftext|>


<commit_before>use csv;
use std::{cmp, error, fmt};
use std::error::Error;
use term_painter::{Attr, Color, ToStyle};
use term_size;
use textwrap;

pub trait Helpful {
    fn help(&self) -> String;
}

#[derive(Clone, Debug)]
pub enum Location {
    FileLineColumn(String, usize, usize),
    FileRowField(String, usize, usize),
    FileLine(String, usize),
    File(String),
    Unspecified,
}

impl fmt::Display for Location {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Location::FileLineColumn(ref file, line, col) => write!(f, "{}:{}:{}", file, line, col),
            Location::FileRowField(ref file, row, field) => {
                write!(f, "{}:{}[{}]", file, row, field)
            }
            Location::FileLine(ref file, line) => write!(f, "{}:{}]", file, line),
            Location::File(ref file) => write!(f, "{}", file),
            Location::Unspecified => Ok(()),
        }
    }
}

#[derive(Debug)]
pub struct ErrorWithLocation<E> {
    location: Location,
    error: E,
}

pub trait ErrorLoc<E>: Sized {
    #[inline]
    fn location(self, location: Location) -> ErrorWithLocation<E>;
}

impl<E: error::Error, F: Into<E>> ErrorLoc<E> for F {
    #[inline]
    fn location(self, location: Location) -> ErrorWithLocation<E> {
        ErrorWithLocation::new(location, self.into())
    }
}

pub trait ResultLoc<V, E: ErrorLoc<E>> {
    #[inline]
    fn err_location(self, location: Location) -> Result<V, ErrorWithLocation<E>>;
}

impl<V, E: error::Error, F: Into<E>> ResultLoc<V, E> for Result<V, F> {
    #[inline]
    fn err_location(self, location: Location) -> Result<V, ErrorWithLocation<E>> {
        self.map_err(|f| f.location(location))
    }
}

impl<E> ErrorWithLocation<E> {
    pub fn new<F: Into<E>>(location: Location, error: F) -> ErrorWithLocation<E> {
        ErrorWithLocation {
            location: location,
            error: error.into(),
        }
    }

    pub fn from_error<F: Into<E>>(other: F) -> ErrorWithLocation<E> {
        ErrorWithLocation::new(Location::Unspecified, other.into())
    }

    pub fn convert<F: From<E>>(self) -> ErrorWithLocation<F> {
        ErrorWithLocation {
            location: self.location,
            error: self.error.into(),
        }
    }
}

impl<E: fmt::Display + Helpful> ErrorWithLocation<E> {
    pub fn print_help(&self) {
        println!("{}{} {}",
                 Attr::Bold.paint((Color::Red.paint("error"))),
                 Attr::Bold.paint(":"),
                 Attr::Bold.paint(self.error()));
        match *self.location() {
            Location::Unspecified => (),
            _ => println!("  --> {}", Color::Yellow.paint(self.location())),
        }

        let dims = term_size::dimensions().unwrap_or((80, 25));

        let term_width = cmp::max(dims.0, 4);
        let out = textwrap::wrap(self.error.help().as_str(), term_width - 3)
            .into_iter()
            .map(|line| textwrap::indent(line.as_str(), "   "))
            .fold(String::new(), |s1, s2| s1 + s2.as_str());
        println!("{}", out);
    }
}

impl<E> ErrorWithLocation<E> {
    pub fn error(&self) -> &E {
        &self.error
    }

    pub fn into_error(self) -> E {
        self.error
    }

    pub fn location(&self) -> &Location {
        &self.location
    }
}

impl<E: fmt::Display> fmt::Display for ErrorWithLocation<E> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self.location() {
            Location::Unspecified => write!(f, "{}", self.error()),
            _ => write!(f, "{}: {}", self.location(), self.error()),
        }
    }
}

impl<E: error::Error> error::Error for ErrorWithLocation<E> {
    fn description(&self) -> &str {
        self.error.description()
    }

    fn cause(&self) -> Option<&error::Error> {
        Some(self.error())
    }
}

impl<E> From<E> for ErrorWithLocation<E> {
    fn from(e: E) -> ErrorWithLocation<E> {
        ErrorWithLocation::from_error(e)
    }
}

#[derive(Debug)]
pub enum CheckError {
    NotASchema,
    SchemaNotAFile,
    InvalidCsvxFilename,
    SchemaLoadError(SchemaLoadError),
    SchemaPathUtf8Error,
}

impl From<SchemaLoadError> for CheckError {
    fn from(e: SchemaLoadError) -> CheckError {
        CheckError::SchemaLoadError(e)
    }
}

impl fmt::Display for CheckError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if let Some(cause) = self.cause() {
            write!(f, "{}", cause)
        } else {
            write!(f, "{}", self.description())
        }
    }
}

impl error::Error for CheckError {
    fn description(&self) -> &str {
        match *self {
            CheckError::NotASchema => "not a schema",
            CheckError::SchemaNotAFile => "schema is not a file",
            CheckError::InvalidCsvxFilename => "filename is not a valid CSVX filename",
            CheckError::SchemaLoadError(_) => "could not load schema",
            CheckError::SchemaPathUtf8Error => "filename UTF8 decoding error",
        }
    }

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

impl Helpful for CheckError {
    fn help(&self) -> String {
        match *self {
            CheckError::NotASchema => {
                "The file you provided is not a schema. The third field of \
                the filename must be of the form `csvx-schema-` followed by \
                the version number. As an example, when defining a schema \
                named `animals-2`, with a date of Dec 31st, 2015 and CSVX \
                version 5, the resulting filename should be: \
                `animals-2_20151231_csvx-schema-5.csv`. \n\
                Note that filenames are case sensitive!"
                        .to_owned()
            }
            CheckError::SchemaNotAFile => "The schema you supplied is not a valid file".to_owned(),
            CheckError::InvalidCsvxFilename => {
                "The filename provided is not in a valid CSVX form. CSVX \
                filenames have three components: The table name, date and \
                schema name. All components must be lowercase letters, \
                numbers or hyphens and start with a letter.\n\n\
                The table name component identifies the file or rather the \
                table it was exported from.\n\
                The date component is for the date the file was exported on.\n\
                The schema name component indicates which schema should be \
                used to validate its contents.\n\n\
                Example: With a table name of `nyc-zoo`, a date of Dec 31st, \
                2015 and using a schema named `animals-2`, the resulting \
                filename should be `nyc-zoo_20151231_animals-2.csv`."
                        .to_owned()
            }
            CheckError::SchemaPathUtf8Error => {
                "The filename you supplied contained UTF-8 errors. CSVX \
                filenames should only contain ASCII characters, please rename \
                the file and try again."
                        .to_owned()
            }
            CheckError::SchemaLoadError(ref e) => e.help(),
        }
    }
}

#[derive(Clone, Debug)]
pub enum ColumnConstraintsError {
    MalformedConstraint,
    UnknownConstraint(String),
}

#[derive(Clone, Debug)]
pub enum ColumnTypeError {
    UnknownType,
}

#[derive(Debug)]
pub enum SchemaLoadError {
    Csv(csv::Error),
    MissingHeader,
    BadHeader,
    BadIdentifier(usize, String),
    BadType(usize, ColumnTypeError),
    BadConstraints(usize, ColumnConstraintsError),
}

impl fmt::Display for SchemaLoadError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if let Some(cause) = self.cause() {
            write!(f, "{}", cause)
        } else {
            write!(f, "{}", self.description())
        }
    }
}

impl error::Error for SchemaLoadError {
    fn description(&self) -> &str {
        match *self {
            _ => "TBW",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            _ => None,
        }
    }
}

impl Helpful for SchemaLoadError {
    fn help(&self) -> String {
        "FIXME".to_owned()
    }
}

impl From<csv::Error> for SchemaLoadError {
    fn from(e: csv::Error) -> SchemaLoadError {
        SchemaLoadError::Csv(e)
    }
}

#[derive(Debug)]
pub enum ValidationError {
    Csv(csv::Error),
    MissingHeaders,
    HeaderMismatch(usize, String),
    RowLengthMismatch(usize),
    ValueError(usize, usize, ValueError),
}

#[derive(Debug)]
pub enum ValueError {
    NonNullable,
    InvalidBool,
    InvalidInt,
    InvalidEnum,
    InvalidDecimal,
    InvalidDate,
    InvalidDateTime,
    InvalidTime,
}
<commit_msg>Added err_loc to allow construction via closure.<commit_after>use csv;
use std::{cmp, error, fmt};
use std::error::Error;
use term_painter::{Attr, Color, ToStyle};
use term_size;
use textwrap;

pub trait Helpful {
    fn help(&self) -> String;
}

#[derive(Clone, Debug)]
pub enum Location {
    FileLineColumn(String, usize, usize),
    FileRowField(String, usize, usize),
    FileLine(String, usize),
    File(String),
    Unspecified,
}

impl fmt::Display for Location {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Location::FileLineColumn(ref file, line, col) => write!(f, "{}:{}:{}", file, line, col),
            Location::FileRowField(ref file, row, field) => {
                write!(f, "{}:{}[{}]", file, row, field)
            }
            Location::FileLine(ref file, line) => write!(f, "{}:{}]", file, line),
            Location::File(ref file) => write!(f, "{}", file),
            Location::Unspecified => Ok(()),
        }
    }
}

#[derive(Debug)]
pub struct ErrorWithLocation<E> {
    location: Location,
    error: E,
}

pub trait ErrorLoc<E>: Sized {
    #[inline]
    fn location(self, location: Location) -> ErrorWithLocation<E>;
}

impl<E: error::Error, F: Into<E>> ErrorLoc<E> for F {
    #[inline]
    fn location(self, location: Location) -> ErrorWithLocation<E> {
        ErrorWithLocation::new(location, self.into())
    }
}

pub trait ResultLoc<V, E: ErrorLoc<E>>: Sized {
    #[inline]
    fn err_location(self, location: Location) -> Result<V, ErrorWithLocation<E>>;

    #[inline]
    fn err_loc<F: FnOnce() -> Location>(self, floc: F) -> Result<V, ErrorWithLocation<E>> {
        self.err_location(floc())
    }
}

impl<V, E: error::Error, F: Into<E>> ResultLoc<V, E> for Result<V, F> {
    #[inline]
    fn err_location(self, location: Location) -> Result<V, ErrorWithLocation<E>> {
        self.map_err(|f| f.location(location))
    }
}

impl<E> ErrorWithLocation<E> {
    pub fn new<F: Into<E>>(location: Location, error: F) -> ErrorWithLocation<E> {
        ErrorWithLocation {
            location: location,
            error: error.into(),
        }
    }

    pub fn from_error<F: Into<E>>(other: F) -> ErrorWithLocation<E> {
        ErrorWithLocation::new(Location::Unspecified, other.into())
    }

    pub fn convert<F: From<E>>(self) -> ErrorWithLocation<F> {
        ErrorWithLocation {
            location: self.location,
            error: self.error.into(),
        }
    }
}

impl<E: fmt::Display + Helpful> ErrorWithLocation<E> {
    pub fn print_help(&self) {
        println!("{}{} {}",
                 Attr::Bold.paint((Color::Red.paint("error"))),
                 Attr::Bold.paint(":"),
                 Attr::Bold.paint(self.error()));
        match *self.location() {
            Location::Unspecified => (),
            _ => println!("  --> {}", Color::Yellow.paint(self.location())),
        }

        let dims = term_size::dimensions().unwrap_or((80, 25));

        let term_width = cmp::max(dims.0, 4);
        let out = textwrap::wrap(self.error.help().as_str(), term_width - 3)
            .into_iter()
            .map(|line| textwrap::indent(line.as_str(), "   "))
            .fold(String::new(), |s1, s2| s1 + s2.as_str());
        println!("{}", out);
    }
}

impl<E> ErrorWithLocation<E> {
    pub fn error(&self) -> &E {
        &self.error
    }

    pub fn into_error(self) -> E {
        self.error
    }

    pub fn location(&self) -> &Location {
        &self.location
    }
}

impl<E: fmt::Display> fmt::Display for ErrorWithLocation<E> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self.location() {
            Location::Unspecified => write!(f, "{}", self.error()),
            _ => write!(f, "{}: {}", self.location(), self.error()),
        }
    }
}

impl<E: error::Error> error::Error for ErrorWithLocation<E> {
    fn description(&self) -> &str {
        self.error.description()
    }

    fn cause(&self) -> Option<&error::Error> {
        Some(self.error())
    }
}

impl<E> From<E> for ErrorWithLocation<E> {
    fn from(e: E) -> ErrorWithLocation<E> {
        ErrorWithLocation::from_error(e)
    }
}

#[derive(Debug)]
pub enum CheckError {
    NotASchema,
    SchemaNotAFile,
    InvalidCsvxFilename,
    SchemaLoadError(SchemaLoadError),
    SchemaPathUtf8Error,
}

impl From<SchemaLoadError> for CheckError {
    fn from(e: SchemaLoadError) -> CheckError {
        CheckError::SchemaLoadError(e)
    }
}

impl fmt::Display for CheckError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if let Some(cause) = self.cause() {
            write!(f, "{}", cause)
        } else {
            write!(f, "{}", self.description())
        }
    }
}

impl error::Error for CheckError {
    fn description(&self) -> &str {
        match *self {
            CheckError::NotASchema => "not a schema",
            CheckError::SchemaNotAFile => "schema is not a file",
            CheckError::InvalidCsvxFilename => "filename is not a valid CSVX filename",
            CheckError::SchemaLoadError(_) => "could not load schema",
            CheckError::SchemaPathUtf8Error => "filename UTF8 decoding error",
        }
    }

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

impl Helpful for CheckError {
    fn help(&self) -> String {
        match *self {
            CheckError::NotASchema => {
                "The file you provided is not a schema. The third field of \
                the filename must be of the form `csvx-schema-` followed by \
                the version number. As an example, when defining a schema \
                named `animals-2`, with a date of Dec 31st, 2015 and CSVX \
                version 5, the resulting filename should be: \
                `animals-2_20151231_csvx-schema-5.csv`. \n\
                Note that filenames are case sensitive!"
                        .to_owned()
            }
            CheckError::SchemaNotAFile => "The schema you supplied is not a valid file".to_owned(),
            CheckError::InvalidCsvxFilename => {
                "The filename provided is not in a valid CSVX form. CSVX \
                filenames have three components: The table name, date and \
                schema name. All components must be lowercase letters, \
                numbers or hyphens and start with a letter.\n\n\
                The table name component identifies the file or rather the \
                table it was exported from.\n\
                The date component is for the date the file was exported on.\n\
                The schema name component indicates which schema should be \
                used to validate its contents.\n\n\
                Example: With a table name of `nyc-zoo`, a date of Dec 31st, \
                2015 and using a schema named `animals-2`, the resulting \
                filename should be `nyc-zoo_20151231_animals-2.csv`."
                        .to_owned()
            }
            CheckError::SchemaPathUtf8Error => {
                "The filename you supplied contained UTF-8 errors. CSVX \
                filenames should only contain ASCII characters, please rename \
                the file and try again."
                        .to_owned()
            }
            CheckError::SchemaLoadError(ref e) => e.help(),
        }
    }
}

#[derive(Clone, Debug)]
pub enum ColumnConstraintsError {
    MalformedConstraint,
    UnknownConstraint(String),
}

#[derive(Clone, Debug)]
pub enum ColumnTypeError {
    UnknownType,
}

#[derive(Debug)]
pub enum SchemaLoadError {
    Csv(csv::Error),
    MissingHeader,
    BadHeader,
    BadIdentifier(usize, String),
    BadType(usize, ColumnTypeError),
    BadConstraints(usize, ColumnConstraintsError),
}

impl fmt::Display for SchemaLoadError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if let Some(cause) = self.cause() {
            write!(f, "{}", cause)
        } else {
            write!(f, "{}", self.description())
        }
    }
}

impl error::Error for SchemaLoadError {
    fn description(&self) -> &str {
        match *self {
            _ => "TBW",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            _ => None,
        }
    }
}

impl Helpful for SchemaLoadError {
    fn help(&self) -> String {
        "FIXME".to_owned()
    }
}

impl From<csv::Error> for SchemaLoadError {
    fn from(e: csv::Error) -> SchemaLoadError {
        SchemaLoadError::Csv(e)
    }
}

#[derive(Debug)]
pub enum ValidationError {
    Csv(csv::Error),
    MissingHeaders,
    HeaderMismatch(usize, String),
    RowLengthMismatch(usize),
    ValueError(usize, usize, ValueError),
}

#[derive(Debug)]
pub enum ValueError {
    NonNullable,
    InvalidBool,
    InvalidInt,
    InvalidEnum,
    InvalidDecimal,
    InvalidDate,
    InvalidDateTime,
    InvalidTime,
}
<|endoftext|>


<commit_before>use std::fmt;
use stack::Stack;
use evaluate::Evaluate;
use try_from_iterator::TryFromIterator;
use try_from_ref::{TryFromRef, TryIntoRef};

/// Used to specify an `Operand` or an `Evaluator`.
#[derive(Debug, Copy, Clone)]
pub enum Arithm<T, O: Evaluate<T>> {
    Operand(T),
    Evaluator(O),
}

/// Interpret a [`Reverse Polish notated`] expression (cf. `3 4 +`).
///
/// `Evaluate` method returns the valid result or an [`Evaluate::Err`]
/// if an evaluation fails.
///
/// `Expressions` are constructed from [`str`] the most of the time.
/// Use the [`try_into()`] method to create an `Expression` type,
/// the result contain informations about the possible error at conversion time.
///
/// [`Reverse Polish notated`]: https://en.wikipedia.org/wiki/Reverse_Polish_notation
/// [`Evaluate::Err`]: ../evaluate/trait.Evaluate.html#associatedtype.Err
/// [`str`]: https://doc.rust-lang.org/std/str/index.html
/// [`try_into()`]: https://doc.rust-lang.org/std/convert/trait.TryInto.html#tymethod.try_into
#[derive(Debug)]
pub struct Expression<T, O: Evaluate<T>> {
    stack_max: usize,
    expr: Vec<Arithm<T, O>>,
}

impl<T: Copy, O: Evaluate<T> + Copy> Expression<T, O> {
    /// Evaluate the `RPN` expression. Returns the result
    /// or the [`evaluate Error`](../evaluate/trait.Evaluate.html#associatedtype.Err).
    pub fn evaluate(&self) -> Result<T, O::Err> {
        let mut stack = Stack::with_capacity(self.stack_max);
        for arithm in &self.expr {
            match *arithm {
                Arithm::Operand(operand) => stack.push(operand),
                Arithm::Evaluator(evaluator) => evaluator.evaluate(&mut stack)?,
            }
        }
        Ok(stack.pop().unwrap())
    }
}

impl TryFromIterator for Expression<T, O>
    where T: TryFromRef,
          O: Evaluate<T> + TryFromRef
{
    type Err = ExprResult<<T as TryFromRef>::Err, <O as TryFromRef>::Err>;
    fn try_from_iter(iter: I) -> Result<Self, Self::Err> where I: IntoIterator<Item=A> {
        use self::ExprResult::*;
        let mut final_expr = Vec::new();
        for token in iter {
            let arithm = match TryIntoRef::<T>::try_into_ref(&token) {
                Ok(operand) => Arithm::Operand(operand),
                Err(operand_err) => {
                    match TryIntoRef::<O>::try_into_ref(&token) {
                        Ok(evaluator) => Arithm::Evaluator(evaluator),
                        Err(evaluator_err) => return Err(InvalidToken(operand_err, evaluator_err)),
                    }
                }
            };
            final_expr.push(arithm);
        }
        match Expression::check_validity(&final_expr) {
            Ok(_) => Ok(Expression {
                stack_max: Expression::compute_stack_max(&final_expr),
                expr: final_expr
            }),
            Err(err) => Err(ExprResult::OperandErr(err)),
        }
    }
}

/// Used to specify the error during the convertion.
#[derive(Debug, PartialEq)]
pub enum ExprResult {
    OperandErr(OperandErr),
    InvalidToken(A, B),
}

#[derive(Debug, PartialEq)]
struct InvalidToken(A, B);

#[derive(Debug, PartialEq)]
pub enum OperandErr {
    TooManyOperands,
    NotEnoughOperand,
}

impl<T, O: Evaluate<T>> Expression<T, O> {
    fn check_validity(expr: &[Arithm<T, O>]) -> Result<(), OperandErr> {
        // TODO make this more Rusty
        use self::OperandErr::*;
        let mut num_operands: usize = 0;
        for arithm in expr {
            match *arithm {
                Arithm::Operand(_) => num_operands += 1,
                Arithm::Evaluator(ref evaluator) => {
                    let needed = evaluator.operands_needed();
                    num_operands = num_operands.checked_sub(needed).ok_or(NotEnoughOperand)?;
                    num_operands += evaluator.operands_generated();
                },
            }
        }
        match num_operands {
            0 => Err(NotEnoughOperand),
            1 => Ok(()),
            _ => Err(TooManyOperands),
        }
    }
}

impl<T, O: Evaluate<T>> Expression<T, O> {
    fn compute_stack_max(expr: &[Arithm<T, O>]) -> usize {
        expr.iter().map(|arithm| {
            match *arithm {
                Arithm::Operand(_) => 1,
                Arithm::Evaluator(ref op) => {
                    op.operands_generated() as isize - op.operands_needed() as isize
                }
            }
        }).fold((0, 0isize), |(max, acc_count), count| {
            let acc = (acc_count + count) as usize;
            if acc > max { (acc, acc as isize) } else { (max, acc as isize) }
        }).0
    }
}

impl<T, O> fmt::Display for Expression<T, O>
    where T: fmt::Display,
          O: Evaluate<T> + fmt::Display
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let len = self.expr.len();
        for (i, arithm) in self.expr.iter().enumerate() {
            match *arithm {
                Arithm::Operand(ref operand) => operand.fmt(f)?,
                Arithm::Evaluator(ref evaluator) => evaluator.fmt(f)?,
            }
            if i != len - 1 {
                f.write_str(" ")?
            }
        }
        Ok(())
    }
}
<commit_msg>expression: Make TryFromIterator impl more Rusty (WIP)<commit_after>use std::fmt;
use stack::Stack;
use evaluate::Evaluate;
use try_from_iterator::TryFromIterator;
use try_from_ref::{TryFromRef, TryIntoRef};

/// Used to specify an `Operand` or an `Evaluator`.
#[derive(Debug, Copy, Clone)]
pub enum Arithm<T, O: Evaluate<T>> {
    Operand(T),
    Evaluator(O),
}

/// Interpret a [`Reverse Polish notated`] expression (cf. `3 4 +`).
///
/// `Evaluate` method returns the valid result or an [`Evaluate::Err`]
/// if an evaluation fails.
///
/// `Expressions` are constructed from [`str`] the most of the time.
/// Use the [`try_into()`] method to create an `Expression` type,
/// the result contain informations about the possible error at conversion time.
///
/// [`Reverse Polish notated`]: https://en.wikipedia.org/wiki/Reverse_Polish_notation
/// [`Evaluate::Err`]: ../evaluate/trait.Evaluate.html#associatedtype.Err
/// [`str`]: https://doc.rust-lang.org/std/str/index.html
/// [`try_into()`]: https://doc.rust-lang.org/std/convert/trait.TryInto.html#tymethod.try_into
#[derive(Debug)]
pub struct Expression<T, O: Evaluate<T>> {
    stack_max: usize,
    expr: Vec<Arithm<T, O>>,
}

impl<T: Copy, O: Evaluate<T> + Copy> Expression<T, O> {
    /// Evaluate the `RPN` expression. Returns the result
    /// or the [`evaluate Error`](../evaluate/trait.Evaluate.html#associatedtype.Err).
    pub fn evaluate(&self) -> Result<T, O::Err> {
        let mut stack = Stack::with_capacity(self.stack_max);
        for arithm in &self.expr {
            match *arithm {
                Arithm::Operand(operand) => stack.push(operand),
                Arithm::Evaluator(evaluator) => evaluator.evaluate(&mut stack)?,
            }
        }
        Ok(stack.pop().unwrap())
    }
}

impl TryFromIterator for Expression<T, O>
    where T: TryFromRef,
          O: Evaluate<T> + TryFromRef
{
    type Err = ExprResult<<T as TryFromRef>::Err, <O as TryFromRef>::Err>;
    fn try_from_iter(iter: I) -> Result<Self, Self::Err> where I: IntoIterator<Item=A> {
        use self::ExprResult::*;
        let final_expr: Vec<_> = iter.into_iter().map(|token| {
            match TryIntoRef::<T>::try_into_ref(&token) {
                Ok(operand) => Arithm::Operand(operand),
                Err(operand_err) => {
                    match TryIntoRef::<O>::try_into_ref(&token) {
                        Ok(evaluator) => Arithm::Evaluator(evaluator),
                        Err(evaluator_err) => {
                            return Err(InvalidToken(operand_err, evaluator_err))
                        }
                    }
                }
            }
        }).collect();
        match Expression::check_validity(&final_expr) {
            Ok(_) => Ok(Expression {
                stack_max: Expression::compute_stack_max(&final_expr),
                expr: final_expr
            }),
            Err(err) => Err(ExprResult::OperandErr(err)),
        }
    }
}

/// Used to specify the error during the convertion.
#[derive(Debug, PartialEq)]
pub enum ExprResult {
    OperandErr(OperandErr),
    InvalidToken(A, B),
}

#[derive(Debug, PartialEq)]
struct InvalidToken(A, B);

#[derive(Debug, PartialEq)]
pub enum OperandErr {
    TooManyOperands,
    NotEnoughOperand,
}

impl<T, O: Evaluate<T>> Expression<T, O> {
    fn check_validity(expr: &[Arithm<T, O>]) -> Result<(), OperandErr> {
        // TODO make this more Rusty
        use self::OperandErr::*;
        let mut num_operands: usize = 0;
        for arithm in expr {
            match *arithm {
                Arithm::Operand(_) => num_operands += 1,
                Arithm::Evaluator(ref evaluator) => {
                    let needed = evaluator.operands_needed();
                    num_operands = num_operands.checked_sub(needed).ok_or(NotEnoughOperand)?;
                    num_operands += evaluator.operands_generated();
                },
            }
        }
        match num_operands {
            0 => Err(NotEnoughOperand),
            1 => Ok(()),
            _ => Err(TooManyOperands),
        }
    }
}

impl<T, O: Evaluate<T>> Expression<T, O> {
    fn compute_stack_max(expr: &[Arithm<T, O>]) -> usize {
        expr.iter().map(|arithm| {
            match *arithm {
                Arithm::Operand(_) => 1,
                Arithm::Evaluator(ref op) => {
                    op.operands_generated() as isize - op.operands_needed() as isize
                }
            }
        }).fold((0, 0isize), |(max, acc_count), count| {
            let acc = (acc_count + count) as usize;
            if acc > max { (acc, acc as isize) } else { (max, acc as isize) }
        }).0
    }
}

impl<T, O> fmt::Display for Expression<T, O>
    where T: fmt::Display,
          O: Evaluate<T> + fmt::Display
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let len = self.expr.len();
        for (i, arithm) in self.expr.iter().enumerate() {
            match *arithm {
                Arithm::Operand(ref operand) => operand.fmt(f)?,
                Arithm::Evaluator(ref evaluator) => evaluator.fmt(f)?,
            }
            if i != len - 1 {
                f.write_str(" ")?
            }
        }
        Ok(())
    }
}
<|endoftext|>