<commit_before>use uci_engine::UciEngine;
use uci;
use std::sync::Arc;
use std::sync::Mutex;
use uci_engine::UciOption;
use uci::TimeRestriction;
use uci::EngineComm;
use search_algorithms::board::Board;
use uci::UciInfo;
use search_algorithms::board::Color::*;
use search_algorithms::board::GameResult::*;
use search_algorithms::board::GameResult;
use std::ops::Add;
use time::Duration;

extern crate ordered_float;
use ordered_float::OrderedFloat;
use std::ops::Not;
use search_algorithms::alpha_beta;
use std::fmt::Debug;
use search_algorithms::alpha_beta::AlphaBeta;
use search_algorithms::board::ExtendedBoard;
use pgn::PgnBoard;
use std::hash::Hash;
use uci_engine::UciOptionType;
use uci::TimeInfo;
use std::time::Instant;

const GAME_TIME: Duration = Duration::from_millis(30000);
const INC_MS: Duration = Duration::from_millis(300);

const EVAL_DEPENDENCE : f32 = 10.0;

pub struct MonteCarlo<B: Board> {
    root: MonteCarloTree<B>,
    time_limit: uci::TimeRestriction,
    start_time: Instant,
    engine_comm : Arc<Mutex<EngineComm>>,
}

impl<B> UciEngine<B> for MonteCarlo<B>
where B: ExtendedBoard + PgnBoard + Debug + Hash + Eq + 'static {
    fn init() -> Self {
        MonteCarlo {
            root: MonteCarloTree::new_root(&mut B::start_board()),
            time_limit: TimeRestriction::Infinite,
            engine_comm: Arc::new(Mutex::new(EngineComm::new())),
            start_time: Instant::now(),
        }
    }

    fn uci_options(&mut self) -> Vec<UciOption> {
        vec![]
    }

    fn set_uci_option(&mut self, _: UciOption) {}

    fn search(mut self, mut board: B, time_limit: TimeRestriction, engine_comm: Arc<Mutex<EngineComm>>,
              _: Option<Vec<<B as Board>::Move>>) -> Box<Iterator<Item=UciInfo<B>>> {

        self.root = MonteCarloTree::new_root(&mut board);
        self.time_limit = time_limit;
        self.engine_comm = engine_comm;
        self.start_time = Instant::now();

        Box::new(DepthIterator { monte_carlo: self, root_board: board })
    }
}

struct DepthIterator<B: Board> {
    monte_carlo: MonteCarlo<B>,
    root_board: B,
}

impl<B> Iterator for DepthIterator<B>
where B: ExtendedBoard + PgnBoard + Debug + Hash + Eq + 'static {
    type Item = UciInfo<B>;

    fn next(&mut self) -> Option<<Self as Iterator>::Item> {
        for _ in 0..10 {
            let mut game = vec![];
            let result = self.monte_carlo.root.select(self.root_board.clone(), &mut game);

            let mut board = self.root_board.clone();
            for (i, mv) in game.into_iter().enumerate() {
                if i == 0 && board.side_to_move() == Black {
                    print!("{}... {} ", 1, board.move_to_san(&mv));
                }
                else if board.side_to_move() == White {
                    print!("{}. {} ", (i + 1) / 2 + 1, board.move_to_san(&mv));
                }
                else {
                    print!("{} ", board.move_to_san(&mv));
                }
                board.do_move(mv);
            }
            println!(" {:?}", result);
        }

        let root = &self.monte_carlo.root;
        println!("Root: {}, static winrate: {}, true winrate: {}, score: {:?}, exploration: {}",
                 root.static_eval, eval_to_win_pct(root.static_eval),
                 root.score.win_rate(),
                 root.score, root.expl_value(root.searches()));

        let (mv, _) = root.children.as_ref().unwrap().iter()
            .min_by_key(|&(_, ref child)| OrderedFloat(child.score.win_rate()))
            .unwrap();

        for (mv, child) in root.children.as_ref().unwrap() {
            println!("\tChild {:?}: {}, static winrate: {}, true winrate: {}, score: {:?}, exploration: {}",
                     mv,
                     child.static_eval, eval_to_win_pct(child.static_eval),
                     child.score.win_rate(),
                     child.score, child.expl_value(root.searches()));
        }
        println!();

        Some(UciInfo {
            color: self.root_board.side_to_move(),
            depth: 0,
            seldepth: 0,
            time: 0,
            nodes: root.searches(),
            hashfull: 0.0,
            pvs: vec![(alpha_beta::Score::Val(win_pct_to_eval(root.score.win_rate())), vec![mv.clone()])]
        })
    }
}

#[derive(Debug)]
struct MonteCarloTree<B: Board> {
    children: Option<Vec<(B::Move, MonteCarloTree<B>)>>,
    score: Score,
    static_eval: f32,
}

impl<B> MonteCarloTree<B>
where B: ExtendedBoard + PgnBoard + Debug + Hash + Eq + 'static {
    fn new_root(board: &mut B) -> MonteCarloTree<B> {
        MonteCarloTree {
            children: None,
            score: Score::new(),
            static_eval: board.static_eval(),
        }
    }

    fn searches(&self) -> u64 {
        self.score.wins + self.score.losses + self.score.draws
    }

    fn expl_value(self: &MonteCarloTree<B>, parent_searches: u64) -> f32 {

        let extra_losses = EVAL_DEPENDENCE - eval_to_win_pct(self.static_eval) * EVAL_DEPENDENCE;

        let exploitation = (self.score.draws as f32 * 0.5 + self.score.losses as f32 + extra_losses)
            / (self.searches() as f32 + EVAL_DEPENDENCE);

        let exploration = 1.42 * f32::sqrt(
            f32::ln(parent_searches as f32 + 1.0) / (self.searches() as f32 + EVAL_DEPENDENCE));
        exploitation + exploration
    }

    fn select(&mut self, mut board: B, game: &mut Vec<B::Move>) -> Score {
        let searches = self.searches();
        if let Some(result) = board.game_result() {
            let mut score = Score::new();
            match (result, board.side_to_move()) {
                (WhiteWin, White) | (BlackWin, Black) => score.wins += 1,
                (WhiteWin, Black) | (BlackWin, White) => score.losses += 1,
                (Draw, _) => score.draws += 1,
            }
            return score;
        }
        if let Some(children) = self.children.as_mut() {

            let child_index = (0..children.len())
                .max_by_key(|&i| OrderedFloat(children[i].1.expl_value(searches)))
                .unwrap().clone();

            let (ref mut mv, ref mut child) = children.get_mut(child_index).unwrap();
            board.do_move(mv.clone());
            game.push(mv.clone());

            let result = !child.select(board, game);

            self.score = self.score + result;
            return result;
        }

        return self.expand(board, game);
    }

    fn expand(&mut self, mut board: B, game: &mut Vec<B::Move>) -> Score {
        let mut moves = vec![];
        board.generate_moves(&mut moves);

        let mut engine: AlphaBeta<B> = AlphaBeta::init();

        engine.set_uci_option(UciOption {
            name: "multipv".to_string(),
            option_type: UciOptionType::Spin(moves.len() as i64, 1, 1)
        });

        engine.set_uci_option(UciOption {
            name: "hash".to_string(),
            option_type: UciOptionType::Spin(32, 1, 1)
        });

        let results = engine.best_moves_multipv(
            board.clone(),
            TimeRestriction::MoveTime(INC_MS * f32::sqrt(moves.len() as f32).ceil() as u32),
        None).unwrap();

        let children = results.into_iter()
            .map(|(score, mv)| {
                let child = MonteCarloTree {
                    children: None,
                    score: Score::new(),
                    static_eval: match score {
                        alpha_beta::Score::Val(val) => - val,
                        alpha_beta::Score::Draw(_) => 0.0,
                        alpha_beta::Score::Win(_) => -100.0,
                        alpha_beta::Score::Loss(_) => 100.0,
                    }
                };
                (mv, child)
            })
            .collect::<Vec<_>>();

        self.children = Some(children);

        let (mv, child) = self.children.as_mut().unwrap().get_mut(0).unwrap();
        board.do_move(mv.clone());
        game.push(mv.clone());
        let result = !simulate(board, game);
        self.score = self.score + result;
        child.score = child.score + !result;

        result
    }
}

fn simulate<B>(mut board: B, game: &mut Vec<B::Move>) -> Score
    where B: ExtendedBoard + PgnBoard + Debug + Hash + Eq + 'static {

    let start_color = board.side_to_move();

    let mut white_time = GAME_TIME;
    let mut black_time = GAME_TIME;

    loop {
        if let Some(result) = board.game_result() {
            let mut score = Score::new();
            match (result, board.side_to_move()) {
                (WhiteWin, White) | (BlackWin, Black) => score.wins += 1,
                (WhiteWin, Black) | (BlackWin, White) => score.losses += 1,
                (Draw, _) => score.draws += 1,
            }
            println!("{:?} ({} to move) on the board", result, board.side_to_move());
            return if board.side_to_move() == start_color { score } else { !score };
        }

        let turn_start = Instant::now();

        let mut engine: AlphaBeta<B> = AlphaBeta::init();

        engine.set_uci_option(UciOption {
            name: "hash".to_string(),
            option_type: UciOptionType::Spin(16, 1, 1)
        });

        let time_info = TimeInfo {
            white_time,
            black_time,
            white_inc: INC_MS,
            black_inc: INC_MS,
            moves_to_go: None
        };

        let (_, mv) = engine.best_move(
            board.clone(),
            TimeRestriction::GameTime(time_info),
            None)
            .unwrap(); // TODO: handle errors

        let time_elapsed = Instant::now() - turn_start;

        match board.side_to_move() {
            White if white_time < time_elapsed => {
                println!("White wins on time");
                return Score::from_game_result(&BlackWin)
            },
            Black if black_time < time_elapsed => {
                println!("Black wins on time");
                return Score::from_game_result(&WhiteWin)
            },
            White => {
                white_time -= time_elapsed;
                white_time += INC_MS;
            },
            Black => {
                black_time -= time_elapsed;
                black_time += INC_MS;
            },
        }

        game.push(mv.clone());
        board.do_move(mv);
    }
}

pub fn win_pct_to_eval(pct: f32) -> f32 {
    2.90680623072 * f32::tan(3.096181612 * (pct - 0.5))
}

pub fn eval_to_win_pct(cp: f32) -> f32 {
    (f32::atan(cp / 2.90680623072) / 3.096181612 + 0.5)
}

#[derive(PartialEq, Clone, Debug, Copy)]
pub struct Score {
    pub wins: u64,
    pub losses: u64,
    pub draws : u64,
}

impl Score {
    pub fn new() -> Self {
        Score { wins: 0, losses: 0, draws: 0 }
    }

    /// Creates a score object containing a single result
    pub fn from_game_result(result: &GameResult) -> Self {
        let mut score = Score::new();
        match *result {
            WhiteWin => score.wins += 1,
            BlackWin => score.losses += 1,
            Draw => score.draws += 1,
        }
        score
    }

    pub fn win_rate(&self) -> f32 {
        (self.wins as f32 + self.draws as f32 * 0.5) / (self.wins + self.draws + self.losses) as f32
    }
}

impl Add for Score {
    type Output = Score;

    fn add(self, rhs: Score) -> <Self as Add<Score>>::Output {
        Score {
            wins: self.wins + rhs.wins,
            losses: self.losses + rhs.losses,
            draws: self.draws + rhs.draws,
        }
    }
}

impl Not for Score {
    type Output = Self;

    fn not(self) -> <Self as Not>::Output {
        Score {
            wins: self.losses,
            losses: self.wins,
            draws: self.draws,
        }
    }
}<commit_msg>Parallelize MonteCarlo with rayon<commit_after>use uci_engine::UciEngine;
use uci;
use std::sync::Arc;
use std::sync::Mutex;
use uci_engine::UciOption;
use uci::TimeRestriction;
use uci::EngineComm;
use search_algorithms::board::Board;
use uci::UciInfo;
use search_algorithms::board::Color::*;
use search_algorithms::board::GameResult::*;
use search_algorithms::board::GameResult;
use std::ops::Add;
use time::Duration;

extern crate ordered_float;
use ordered_float::OrderedFloat;
use std::ops::Not;
use search_algorithms::alpha_beta;
use std::fmt::Debug;
use search_algorithms::alpha_beta::AlphaBeta;
use search_algorithms::board::ExtendedBoard;
use pgn::PgnBoard;
use std::hash::Hash;
use uci_engine::UciOptionType;
use uci::TimeInfo;
use std::time::Instant;
use std::sync::RwLock;
use rayon::iter::IntoParallelIterator;
use rayon::iter::ParallelIterator;
use std::io::BufWriter;
use std::fs::OpenOptions;
use std::io::Write;

const GAME_TIME: Duration = Duration::from_millis(30000);
const INC_MS: Duration = Duration::from_millis(300);

const EVAL_DEPENDENCE : f32 = 16.0;

pub struct MonteCarlo<B: Board> {
    root: MonteCarloTree<B>,
    time_limit: uci::TimeRestriction,
    start_time: Instant,
    engine_comm : Arc<Mutex<EngineComm>>,
}

impl<B> UciEngine<B> for MonteCarlo<B>
where B: ExtendedBoard + PgnBoard + Debug + Hash + Eq + 'static + Sync, B::Move: Send + Sync {
    fn init() -> Self {
        MonteCarlo {
            root: MonteCarloTree::new_root(&mut B::start_board()),
            time_limit: TimeRestriction::Infinite,
            engine_comm: Arc::new(Mutex::new(EngineComm::new())),
            start_time: Instant::now(),
        }
    }

    fn uci_options(&mut self) -> Vec<UciOption> {
        vec![]
    }

    fn set_uci_option(&mut self, _: UciOption) {}

    fn search(mut self, mut board: B, time_limit: TimeRestriction, engine_comm: Arc<Mutex<EngineComm>>,
              _: Option<Vec<<B as Board>::Move>>) -> Box<Iterator<Item=UciInfo<B>>> {

        self.root = MonteCarloTree::new_root(&mut board);
        self.time_limit = time_limit;
        self.engine_comm = engine_comm;
        self.start_time = Instant::now();

        Box::new(DepthIterator { monte_carlo: self, root_board: board })
    }
}

struct DepthIterator<B: Board> {
    monte_carlo: MonteCarlo<B>,
    root_board: B,
}

impl<B> Iterator for DepthIterator<B>
where B: ExtendedBoard + PgnBoard + Debug + Hash + Eq + 'static + Sync, B::Move: Send + Sync {
    type Item = UciInfo<B>;

    fn next(&mut self) -> Option<<Self as Iterator>::Item> {

        let mut open_options = OpenOptions::new();
        open_options.append(true).create(true);
        let pgn_file = open_options.open("book.pgn").unwrap();
        let pgn_writer = Mutex::new(BufWriter::new(pgn_file));

        (0..10).into_par_iter().for_each(|i: i32| {
            let mut game = vec![];
            let score = self.monte_carlo.root.select(self.root_board.clone(), &mut game);

            let mut board = self.root_board.clone();

            let result = match board.side_to_move() {
                _ if score.draws == 1 => Some(Draw),
                White if score.wins == 1 => Some(WhiteWin),
                White if score.losses == 1 => Some(BlackWin),
                Black if score.wins == 1 => Some(BlackWin),
                Black if score.losses == 1 => Some(WhiteWin),
                _ => None
            };
            {
                let mut writer = pgn_writer.lock().unwrap();
                board.clone().game_to_pgn(&game, "", "", "????.??.??", &i.to_string(),
                                  "rasher", "rasher", result, &[],
                                  &mut *writer).unwrap();
                (*writer).flush().unwrap();
            }

            for (i, mv) in game.into_iter().enumerate() {
                if i == 0 && board.side_to_move() == Black {
                    print!("{}... {} ", 1, board.move_to_san(&mv));
                }
                else if board.side_to_move() == White {
                    print!("{}. {} ", (i + 1) / 2 + 1, board.move_to_san(&mv));
                }
                else {
                    print!("{} ", board.move_to_san(&mv));
                }
                board.do_move(mv);
            }
            println!(" {:?}", score);
        });

        let root = &self.monte_carlo.root;
        let root_score = root.score.lock().unwrap();
        let root_children = root.children.read().unwrap();
        println!("Root: {}, static winrate: {}, true winrate: {}, score: {:?}",
                 root.static_eval, eval_to_win_pct(root.static_eval),
                 root_score.win_rate(),
                 *root_score);

        let (mv, _) = root_children.as_ref().unwrap().iter()
            .min_by_key(|&(_, child)| OrderedFloat(child.score.lock().unwrap().win_rate()))
            .unwrap();

        for (mv, child) in root_children.as_ref().unwrap() {
            let child_win_rate = { child.score.lock().unwrap().win_rate() };
            println!("\tChild {:?}: {}, static winrate: {}, true winrate: {}, score: {:?}, exploration: {}",
                     mv,
                     child.static_eval, eval_to_win_pct(child.static_eval),
                     child_win_rate,
                     child.score, child.expl_value(root_score.searches()));
        }
        println!();

        Some(UciInfo {
            color: self.root_board.side_to_move(),
            depth: 0,
            seldepth: 0,
            time: 0,
            nodes: root_score.searches(),
            hashfull: 0.0,
            pvs: vec![(alpha_beta::Score::Val(win_pct_to_eval(root_score.win_rate())), vec![mv.clone()])]
        })
    }
}

type MonteCarloChild<B> = (<B as Board>::Move, MonteCarloTree<B>);

#[derive(Debug)]
struct MonteCarloTree<B: Board> {
    children: RwLock<Option<Vec<MonteCarloChild<B>>>>,
    score: Mutex<Score>,
    static_eval: f32,
}

impl<B> MonteCarloTree<B>
where B: ExtendedBoard + PgnBoard + Debug + Hash + Eq + 'static {
    fn new_root(board: &mut B) -> MonteCarloTree<B> {
        MonteCarloTree {
            children: RwLock::new(None),
            score: Mutex::new(Score::new()),
            static_eval: board.static_eval(),
        }
    }

    fn expl_value(self: &MonteCarloTree<B>, parent_searches: u64) -> f32 {

        let score = self.score.lock().unwrap();

        let extra_losses = EVAL_DEPENDENCE - eval_to_win_pct(self.static_eval) * EVAL_DEPENDENCE;

        let exploitation = (score.draws as f32 * 0.5 + score.losses as f32 + extra_losses)
            / (score.searches() as f32 + EVAL_DEPENDENCE);

        let exploration = 1.42 * f32::sqrt(
            f32::ln(parent_searches as f32 + 1.0) / (score.searches() as f32 + EVAL_DEPENDENCE));
        exploitation + exploration
    }

    fn select(&self, mut board: B, game: &mut Vec<B::Move>) -> Score {

        if let Some(result) = board.game_result() {
            let mut score = Score::new();
            match (result, board.side_to_move()) {
                (WhiteWin, White) | (BlackWin, Black) => score.wins += 1,
                (WhiteWin, Black) | (BlackWin, White) => score.losses += 1,
                (Draw, _) => score.draws += 1,
            }
            return score;
        }

        { // Immediately give the position an extra win, so that it is less likely to be simultaneously selected by other threads
            self.score.lock().unwrap().wins += 1;
        }

        let searches = {
            self.score.lock().unwrap().searches().clone()
        };

        if let Some(children) = self.children.read().unwrap().as_ref() {

            let child_index = (0..children.len())
                .max_by_key(|&i| OrderedFloat(children[i].1.expl_value(searches)))
                .unwrap().clone();

            let (ref mv, ref child) = children.get(child_index).unwrap();
            board.do_move(mv.clone());
            game.push(mv.clone());

            let result = !child.select(board, game);

            let mut score = self.score.lock().unwrap();
            (*score).wins -= 1; // Remove the extra win added earlier
            *score = *score + result;
            return result;
        }

        let result =  self.expand(board, game);

        // Remove the extra win added earlier
        self.score.lock().unwrap().wins -= 1;

        result
    }

    fn expand(&self, mut board: B, game: &mut Vec<B::Move>) -> Score {
        let mut moves = vec![];
        board.generate_moves(&mut moves);
        {
            if let Ok(mut self_children) = self.children.try_write() {

                let mut engine: AlphaBeta<B> = AlphaBeta::init();

                engine.set_uci_option(UciOption {
                    name: "multipv".to_string(),
                    option_type: UciOptionType::Spin(moves.len() as i64, 1, 1)
                });

                engine.set_uci_option(UciOption {
                    name: "hash".to_string(),
                    option_type: UciOptionType::Spin(32, 1, 1)
                });

                let results = engine.best_moves_multipv(
                    board.clone(),
                    TimeRestriction::MoveTime(INC_MS * f32::sqrt(moves.len() as f32).ceil() as u32),
                    None).unwrap();

                let children = results.into_iter()
                    .map(|(score, mv)| {
                        let child = MonteCarloTree {
                            children: RwLock::new(None),
                            score: Mutex::new(Score::new()),
                            static_eval: match score {
                                alpha_beta::Score::Val(val) => -val,
                                alpha_beta::Score::Draw(_) => 0.0,
                                alpha_beta::Score::Win(_) => -100.0,
                                alpha_beta::Score::Loss(_) => 100.0,
                            }
                        };
                        (mv, child)
                    })
                    .collect::<Vec<_>>();

                *self_children = Some(children);

                let (ref mv, ref child) = self_children.as_ref().unwrap()[0];

                board.do_move(mv.clone());
                game.push(mv.clone());

                // Immediately give the position an extra win, so that it is less likely to be simultaneously selected by other threads
                (*child.score.lock().unwrap()).wins += 1;
            }
            else {
                info!("Abort expansion of {}", board.to_fen());
                return self.select(board, game);
            }
        }

        let result = !simulate(board, game);
        {
            self.score.lock().map(|mut score| *score = *score + result).unwrap();
        }
        let children = self.children.read();
        let child = &children.as_ref().unwrap().as_ref().unwrap().get(0).unwrap().1;
        child.score.lock().map(|mut score| {
            (*score).wins -= 1; // Remove the extra win added earlier
            *score = *score + !result
        }).unwrap();

        result
    }
}

fn simulate<B>(mut board: B, game: &mut Vec<B::Move>) -> Score
    where B: ExtendedBoard + PgnBoard + Debug + Hash + Eq + 'static {

    let start_color = board.side_to_move();

    let mut white_time = GAME_TIME;
    let mut black_time = GAME_TIME;

    loop {
        if let Some(result) = board.game_result() {
            let mut score = Score::new();
            match (result, board.side_to_move()) {
                (WhiteWin, White) | (BlackWin, Black) => score.wins += 1,
                (WhiteWin, Black) | (BlackWin, White) => score.losses += 1,
                (Draw, _) => score.draws += 1,
            }
            println!("{:?} ({} to move) on the board", result, board.side_to_move());
            return if board.side_to_move() == start_color { score } else { !score };
        }

        let turn_start = Instant::now();

        let mut engine: AlphaBeta<B> = AlphaBeta::init();

        engine.set_uci_option(UciOption {
            name: "hash".to_string(),
            option_type: UciOptionType::Spin(16, 1, 1)
        });

        let time_info = TimeInfo {
            white_time,
            black_time,
            white_inc: INC_MS,
            black_inc: INC_MS,
            moves_to_go: None
        };

        let (_, mv) = engine.best_move(
            board.clone(),
            TimeRestriction::GameTime(time_info),
            None)
            .unwrap(); // TODO: handle errors

        let time_elapsed = Instant::now() - turn_start;

        match board.side_to_move() {
            White if white_time < time_elapsed => {
                println!("White wins on time");
                return Score::from_game_result(&BlackWin)
            },
            Black if black_time < time_elapsed => {
                println!("Black wins on time");
                return Score::from_game_result(&WhiteWin)
            },
            White => {
                white_time -= time_elapsed;
                white_time += INC_MS;
            },
            Black => {
                black_time -= time_elapsed;
                black_time += INC_MS;
            },
        }

        game.push(mv.clone());
        board.do_move(mv);
    }
}

pub fn win_pct_to_eval(pct: f32) -> f32 {
    2.90680623072 * f32::tan(3.096181612 * (pct - 0.5))
}

pub fn eval_to_win_pct(cp: f32) -> f32 {
    (f32::atan(cp / 2.90680623072) / 3.096181612 + 0.5)
}

#[derive(PartialEq, Clone, Debug, Copy)]
pub struct Score {
    pub wins: u64,
    pub losses: u64,
    pub draws : u64,
}

impl Score {
    pub fn new() -> Self {
        Score { wins: 0, losses: 0, draws: 0 }
    }

    /// Creates a score object containing a single result
    pub fn from_game_result(result: &GameResult) -> Self {
        let mut score = Score::new();
        match *result {
            WhiteWin => score.wins += 1,
            BlackWin => score.losses += 1,
            Draw => score.draws += 1,
        }
        score
    }

    pub fn win_rate(&self) -> f32 {
        (self.wins as f32 + self.draws as f32 * 0.5) / (self.searches()) as f32
    }

    pub fn searches(&self) -> u64 {
        self.wins + self.draws + self.losses
    }
}

impl Add for Score {
    type Output = Score;

    fn add(self, rhs: Score) -> <Self as Add<Score>>::Output {
        Score {
            wins: self.wins + rhs.wins,
            losses: self.losses + rhs.losses,
            draws: self.draws + rhs.draws,
        }
    }
}

impl Not for Score {
    type Output = Self;

    fn not(self) -> <Self as Not>::Output {
        Score {
            wins: self.losses,
            losses: self.wins,
            draws: self.draws,
        }
    }
}<|endoftext|>


<commit_before>use std::io::{BufWriter, Write, Error, ErrorKind};
use std::io;

use mesh::*;

pub trait Writer {
    fn extentions(&self) -> Vec<&'static str>;

    fn is_supported(&self, name: &str) -> bool {
        self.extentions().iter().any(|ext| name.ends_with(ext))
    }

    fn write(&self, mesh: &Mesh, file: &mut Write) -> io::Result<()>;
}

pub struct YaveWriter {
}

impl YaveWriter {
    pub fn new() -> YaveWriter {
        YaveWriter {}
    }
}

impl Writer for YaveWriter {
    fn write(&self, mesh: &Mesh, file: &mut Write) -> io::Result<()> {
        fn buf_helper<T: Write>(mesh: &Mesh, file: &mut T) -> Result<(), EncodingError> {
            let magic = 0x79617665 as u32;
            let version: u64 = 1;

            try!(encode_into(&magic, file, SizeLimit::Infinite));
            try!(encode_into(&version, file, SizeLimit::Infinite));

            try!(encode_into(&mesh.vertices, file, SizeLimit::Infinite));
            try!(encode_into(&mesh.indices, file, SizeLimit::Infinite));

            Ok(())
        }
        match buf_helper(mesh, &mut BufWriter::new(file)) {
            Ok(m) => Ok(m),
            Err(err) => Err(Error::new(ErrorKind::InvalidData, err))
        }
    }

    fn extentions(&self) -> Vec<&'static str> {
        vec![".ym"]
    }
}

pub struct BadObjWriter {
}

impl BadObjWriter {
    pub fn new() -> BadObjWriter {
        BadObjWriter {}
    }
}

impl Writer for BadObjWriter {
    fn write(&self, mesh: &Mesh, file: &mut Write) -> io::Result<()> {
        let mut buf = BufWriter::new(file);
        for v in &mesh.vertices {
            let pos = format!("v {} {} {}\n", v.position.0, v.position.1, v.position.2);
            try!(buf.write(pos.into_bytes().as_slice()));

            let uv = format!("vt {} {}\n", v.uv.0, v.uv.1);
            try!(buf.write(uv.into_bytes().as_slice()));

            let norm = format!("vn {} {} {}\n", v.normal.0, v.normal.1, v.normal.2);
            try!(buf.write(norm.into_bytes().as_slice()));
        }

        try!(buf.write(b"s off\n"));

        for tri in &mesh.indices {
            let line = format!("f {}/{}/{} {}/{}/{} {}/{}/{}\n",
                            tri.0 + 1, tri.0 + 1, tri.0 + 1,
                            tri.1 + 1, tri.1 + 1, tri.1 + 1,
                            tri.2 + 1, tri.2 + 1, tri.2 + 1,
                        );
            try!(buf.write(line.into_bytes().as_slice()));
        }

        Ok(())
    }

    fn extentions(&self) -> Vec<&'static str> {
        vec![".obj"]
    }
}
<commit_msg>Fixed import.<commit_after>use std::io::{BufWriter, Write, Error, ErrorKind};
use std::io;

use mesh::*;

use bincode::*;
use bincode::rustc_serialize::*;
use rustc_serialize::*;

pub trait Writer {
    fn extentions(&self) -> Vec<&'static str>;

    fn is_supported(&self, name: &str) -> bool {
        self.extentions().iter().any(|ext| name.ends_with(ext))
    }

    fn write(&self, mesh: &Mesh, file: &mut Write) -> io::Result<()>;
}

pub struct YaveWriter {
}

impl YaveWriter {
    pub fn new() -> YaveWriter {
        YaveWriter {}
    }
}

impl Writer for YaveWriter {
    fn write(&self, mesh: &Mesh, file: &mut Write) -> io::Result<()> {
        fn buf_helper<T: Write>(mesh: &Mesh, file: &mut T) -> Result<(), EncodingError> {
            let magic = 0x79617665 as u32;
            let version: u64 = 1;

            try!(encode_into(&magic, file, SizeLimit::Infinite));
            try!(encode_into(&version, file, SizeLimit::Infinite));

            try!(encode_into(&mesh.vertices, file, SizeLimit::Infinite));
            try!(encode_into(&mesh.indices, file, SizeLimit::Infinite));

            Ok(())
        }
        match buf_helper(mesh, &mut BufWriter::new(file)) {
            Ok(m) => Ok(m),
            Err(err) => Err(Error::new(ErrorKind::InvalidData, err))
        }
    }

    fn extentions(&self) -> Vec<&'static str> {
        vec![".ym"]
    }
}

pub struct BadObjWriter {
}

impl BadObjWriter {
    pub fn new() -> BadObjWriter {
        BadObjWriter {}
    }
}

impl Writer for BadObjWriter {
    fn write(&self, mesh: &Mesh, file: &mut Write) -> io::Result<()> {
        let mut buf = BufWriter::new(file);
        for v in &mesh.vertices {
            let pos = format!("v {} {} {}\n", v.position.0, v.position.1, v.position.2);
            try!(buf.write(pos.into_bytes().as_slice()));

            let uv = format!("vt {} {}\n", v.uv.0, v.uv.1);
            try!(buf.write(uv.into_bytes().as_slice()));

            let norm = format!("vn {} {} {}\n", v.normal.0, v.normal.1, v.normal.2);
            try!(buf.write(norm.into_bytes().as_slice()));
        }

        try!(buf.write(b"s off\n"));

        for tri in &mesh.indices {
            let line = format!("f {}/{}/{} {}/{}/{} {}/{}/{}\n",
                            tri.0 + 1, tri.0 + 1, tri.0 + 1,
                            tri.1 + 1, tri.1 + 1, tri.1 + 1,
                            tri.2 + 1, tri.2 + 1, tri.2 + 1,
                        );
            try!(buf.write(line.into_bytes().as_slice()));
        }

        Ok(())
    }

    fn extentions(&self) -> Vec<&'static str> {
        vec![".obj"]
    }
}
<|endoftext|>


<commit_before>use std::ptr::null;
use std::str::raw;
use libc::{c_int, c_char};
use sync::mutex::{StaticMutex, MUTEX_INIT};

#[link(name="gdal")]
extern {
    fn OGRRegisterAll();
    fn OGROpen(pszName: *c_char, bUpdate: c_int, pahDriverList: *()) -> *();
    fn OGR_DS_GetLayerCount(hDS: *()) -> c_int;
    fn OGR_DS_Destroy(hDataSource: *());
    fn OGR_DS_GetLayer(hDS: *(), iLayer: c_int) -> *();
    fn OGR_L_GetNextFeature(hLayer: *()) -> *();
    fn OGR_F_GetFieldIndex(hFeat: *(), pszName: *c_char) -> c_int;
    fn OGR_F_GetFieldAsString(hFeat: *(), iField: c_int) -> *c_char;
    fn OGR_F_Destroy(hFeat: *());
}

static mut LOCK: StaticMutex = MUTEX_INIT;
static mut registered_drivers: bool = false;

fn register_drivers() {
    unsafe {
        let _g = LOCK.lock();
        if ! registered_drivers {
            OGRRegisterAll();
            registered_drivers = true;
        }
    }
}

pub struct VectorDataset {
    c_dataset: *(),
}

impl VectorDataset {
    pub fn get_layer_count(&self) -> int {
        return unsafe { OGR_DS_GetLayerCount(self.c_dataset) } as int;
    }

    pub fn get_layer<'a>(&'a self, idx: int) -> Option<Layer<'a>> {
        let c_layer = unsafe { OGR_DS_GetLayer(self.c_dataset, idx as c_int) };
        return match c_layer.is_null() {
            true  => None,
            false => Some(Layer{vector_dataset: self, c_layer: c_layer}),
        };
    }
}

impl Drop for VectorDataset {
    fn drop(&mut self) {
        unsafe { OGR_DS_Destroy(self.c_dataset); }
    }
}

pub struct Layer<'a> {
    vector_dataset: &'a VectorDataset,
    c_layer: *(),
}

impl<'a> Layer<'a> {
    pub fn features<'a>(&'a self) -> FeatureIterator<'a> {
        return FeatureIterator{layer: self};
    }
}

pub struct FeatureIterator<'a> {
    layer: &'a Layer<'a>,
}

impl<'a> Iterator<Feature> for FeatureIterator<'a> {
    #[inline]
    fn next(&mut self) -> Option<Feature> {
        let c_feature = unsafe { OGR_L_GetNextFeature(self.layer.c_layer) };
        return match c_feature.is_null() {
            true  => None,
            false => Some(Feature{c_feature: c_feature}),
        };
    }
}

pub struct Feature {
    c_feature: *(),
}

impl Feature {
    pub fn get_field(&self, name: String) -> String {
        return name.with_c_str(|c_name| {
            unsafe {
                let field_id = OGR_F_GetFieldIndex(self.c_feature, c_name);
                let c_value = OGR_F_GetFieldAsString(self.c_feature, field_id);
                return raw::from_c_str(c_value);
            }
        });
    }
}

impl Drop for Feature {
    fn drop(&mut self) {
        unsafe { OGR_F_Destroy(self.c_feature); }
    }
}

pub fn open(path: &Path) -> Option<VectorDataset> {
    register_drivers();
    let filename = path.as_str().unwrap();
    let c_dataset = filename.with_c_str(|c_filename| {
        return unsafe { OGROpen(c_filename, 0, null()) };
    });
    return match c_dataset.is_null() {
        true  => None,
        false => Some(VectorDataset{c_dataset: c_dataset}),
    };
}

#[cfg(test)]
mod test {
    use std::os::getenv;
    use std::path::Path;
    use super::{Feature, open};

    fn fixture_path(name: &str) -> Path {
        let envvar = "RUST_GDAL_TEST_FIXTURES";
        let fixtures = match getenv(envvar) {
            Some(p) => Path::new(p),
            None => fail!("Environment variable {} not set", envvar)
        };
        let rv = fixtures.join(name);
        return rv;
    }

    #[test]
    fn test_layer_count() {
        let ds = open(&fixture_path("roads.geojson")).unwrap();
        assert_eq!(ds.get_layer_count(), 1);
    }

    #[test]
    fn test_iterate_features() {
        let ds = open(&fixture_path("roads.geojson")).unwrap();
        let layer = ds.get_layer(0).unwrap();
        let features: Vec<Feature> = layer.features().collect();
        assert_eq!(features.len(), 21);
    }

    #[test]
    fn test_get_field() {
        let ds = open(&fixture_path("roads.geojson")).unwrap();
        let layer = ds.get_layer(0).unwrap();
        let feature = layer.features().next().unwrap();
        assert_eq!(feature.get_field("highway".to_string()),
                   "footway".to_string());
        assert_eq!(
            layer.features()
                 .count(|f|
                    f.get_field("highway".to_string()) ==
                    "residential".to_string()),
            2);
    }
}
<commit_msg>Feature keeps a reference to Layer<commit_after>use std::ptr::null;
use std::str::raw;
use libc::{c_int, c_char};
use sync::mutex::{StaticMutex, MUTEX_INIT};

#[link(name="gdal")]
extern {
    fn OGRRegisterAll();
    fn OGROpen(pszName: *c_char, bUpdate: c_int, pahDriverList: *()) -> *();
    fn OGR_DS_GetLayerCount(hDS: *()) -> c_int;
    fn OGR_DS_Destroy(hDataSource: *());
    fn OGR_DS_GetLayer(hDS: *(), iLayer: c_int) -> *();
    fn OGR_L_GetNextFeature(hLayer: *()) -> *();
    fn OGR_F_GetFieldIndex(hFeat: *(), pszName: *c_char) -> c_int;
    fn OGR_F_GetFieldAsString(hFeat: *(), iField: c_int) -> *c_char;
    fn OGR_F_Destroy(hFeat: *());
}

static mut LOCK: StaticMutex = MUTEX_INIT;
static mut registered_drivers: bool = false;

fn register_drivers() {
    unsafe {
        let _g = LOCK.lock();
        if ! registered_drivers {
            OGRRegisterAll();
            registered_drivers = true;
        }
    }
}

pub struct VectorDataset {
    c_dataset: *(),
}

impl VectorDataset {
    pub fn get_layer_count(&self) -> int {
        return unsafe { OGR_DS_GetLayerCount(self.c_dataset) } as int;
    }

    pub fn get_layer<'a>(&'a self, idx: int) -> Option<Layer<'a>> {
        let c_layer = unsafe { OGR_DS_GetLayer(self.c_dataset, idx as c_int) };
        return match c_layer.is_null() {
            true  => None,
            false => Some(Layer{vector_dataset: self, c_layer: c_layer}),
        };
    }
}

impl Drop for VectorDataset {
    fn drop(&mut self) {
        unsafe { OGR_DS_Destroy(self.c_dataset); }
    }
}

pub struct Layer<'a> {
    vector_dataset: &'a VectorDataset,
    c_layer: *(),
}

impl<'a> Layer<'a> {
    pub fn features<'a>(&'a self) -> FeatureIterator<'a> {
        return FeatureIterator{layer: self};
    }
}

pub struct FeatureIterator<'a> {
    layer: &'a Layer<'a>,
}

impl<'a> Iterator<Feature<'a>> for FeatureIterator<'a> {
    #[inline]
    fn next(&mut self) -> Option<Feature<'a>> {
        let c_feature = unsafe { OGR_L_GetNextFeature(self.layer.c_layer) };
        return match c_feature.is_null() {
            true  => None,
            false => Some(Feature{layer: self.layer, c_feature: c_feature}),
        };
    }
}

pub struct Feature<'a> {
    layer: &'a Layer<'a>,
    c_feature: *(),
}

impl<'a> Feature<'a> {
    pub fn get_field(&self, name: String) -> String {
        return name.with_c_str(|c_name| {
            unsafe {
                let field_id = OGR_F_GetFieldIndex(self.c_feature, c_name);
                let c_value = OGR_F_GetFieldAsString(self.c_feature, field_id);
                return raw::from_c_str(c_value);
            }
        });
    }
}

#[unsafe_destructor]
impl<'a> Drop for Feature<'a> {
    fn drop(&mut self) {
        unsafe { OGR_F_Destroy(self.c_feature); }
    }
}

pub fn open(path: &Path) -> Option<VectorDataset> {
    register_drivers();
    let filename = path.as_str().unwrap();
    let c_dataset = filename.with_c_str(|c_filename| {
        return unsafe { OGROpen(c_filename, 0, null()) };
    });
    return match c_dataset.is_null() {
        true  => None,
        false => Some(VectorDataset{c_dataset: c_dataset}),
    };
}

#[cfg(test)]
mod test {
    use std::os::getenv;
    use std::path::Path;
    use super::{Feature, open};

    fn fixture_path(name: &str) -> Path {
        let envvar = "RUST_GDAL_TEST_FIXTURES";
        let fixtures = match getenv(envvar) {
            Some(p) => Path::new(p),
            None => fail!("Environment variable {} not set", envvar)
        };
        let rv = fixtures.join(name);
        return rv;
    }

    #[test]
    fn test_layer_count() {
        let ds = open(&fixture_path("roads.geojson")).unwrap();
        assert_eq!(ds.get_layer_count(), 1);
    }

    #[test]
    fn test_iterate_features() {
        let ds = open(&fixture_path("roads.geojson")).unwrap();
        let layer = ds.get_layer(0).unwrap();
        let features: Vec<Feature> = layer.features().collect();
        assert_eq!(features.len(), 21);
    }

    #[test]
    fn test_get_field() {
        let ds = open(&fixture_path("roads.geojson")).unwrap();
        let layer = ds.get_layer(0).unwrap();
        let feature = layer.features().next().unwrap();
        assert_eq!(feature.get_field("highway".to_string()),
                   "footway".to_string());
        assert_eq!(
            layer.features()
                 .count(|f|
                    f.get_field("highway".to_string()) ==
                    "residential".to_string()),
            2);
    }
}
<|endoftext|>


<commit_before>#![feature(test)] // compiletest_rs requires this attribute
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
#![warn(rust_2018_idioms, unused_lifetimes)]

use compiletest_rs as compiletest;
use compiletest_rs::common::Mode as TestMode;

use std::collections::HashMap;
use std::env::{self, remove_var, set_var, var_os};
use std::ffi::{OsStr, OsString};
use std::fs;
use std::io;
use std::path::{Path, PathBuf};

mod cargo;

// whether to run internal tests or not
const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal-lints");

/// All crates used in UI tests are listed here
static TEST_DEPENDENCIES: &[&str] = &[
    "clippy_utils",
    "derive_new",
    "if_chain",
    "itertools",
    "quote",
    "regex",
    "serde",
    "serde_derive",
    "syn",
];

// Test dependencies may need an `extern crate` here to ensure that they show up
// in the depinfo file (otherwise cargo thinks they are unused)
#[allow(unused_extern_crates)]
extern crate clippy_utils;
#[allow(unused_extern_crates)]
extern crate derive_new;
#[allow(unused_extern_crates)]
extern crate if_chain;
#[allow(unused_extern_crates)]
extern crate itertools;
#[allow(unused_extern_crates)]
extern crate quote;
#[allow(unused_extern_crates)]
extern crate syn;

/// Produces a string with an `--extern` flag for all UI test crate
/// dependencies.
///
/// The dependency files are located by parsing the depinfo file for this test
/// module. This assumes the `-Z binary-dep-depinfo` flag is enabled. All test
/// dependencies must be added to Cargo.toml at the project root. Test
/// dependencies that are not *directly* used by this test module require an
/// `extern crate` declaration.
fn extern_flags() -> String {
    let current_exe_depinfo = {
        let mut path = env::current_exe().unwrap();
        path.set_extension("d");
        std::fs::read_to_string(path).unwrap()
    };
    let mut crates: HashMap<&str, &str> = HashMap::with_capacity(TEST_DEPENDENCIES.len());
    for line in current_exe_depinfo.lines() {
        // each dependency is expected to have a Makefile rule like `/path/to/crate-hash.rlib:`
        let parse_name_path = || {
            if line.starts_with(char::is_whitespace) {
                return None;
            }
            let path_str = line.strip_suffix(':')?;
            let path = Path::new(path_str);
            if !matches!(path.extension()?.to_str()?, "rlib" | "so" | "dylib" | "dll") {
                return None;
            }
            let (name, _hash) = path.file_stem()?.to_str()?.rsplit_once('-')?;
            // the "lib" prefix is not present for dll files
            let name = name.strip_prefix("lib").unwrap_or(name);
            Some((name, path_str))
        };
        if let Some((name, path)) = parse_name_path() {
            if TEST_DEPENDENCIES.contains(&name) {
                // A dependency may be listed twice if it is available in sysroot,
                // and the sysroot dependencies are listed first. As of the writing,
                // this only seems to apply to if_chain.
                crates.insert(name, path);
            }
        }
    }
    let not_found: Vec<&str> = TEST_DEPENDENCIES
        .iter()
        .copied()
        .filter(|n| !crates.contains_key(n))
        .collect();
    assert!(
        not_found.is_empty(),
        "dependencies not found in depinfo: {:?}",
        not_found
    );
    crates
        .into_iter()
        .map(|(name, path)| format!(" --extern {}={}", name, path))
        .collect()
}

fn default_config() -> compiletest::Config {
    let mut config = compiletest::Config::default();

    if let Ok(filters) = env::var("TESTNAME") {
        config.filters = filters.split(',').map(std::string::ToString::to_string).collect();
    }

    if let Some(path) = option_env!("RUSTC_LIB_PATH") {
        let path = PathBuf::from(path);
        config.run_lib_path = path.clone();
        config.compile_lib_path = path;
    }
    let current_exe_path = std::env::current_exe().unwrap();
    let deps_path = current_exe_path.parent().unwrap();
    let profile_path = deps_path.parent().unwrap();

    // Using `-L dependency={}` enforces that external dependencies are added with `--extern`.
    // This is valuable because a) it allows us to monitor what external dependencies are used
    // and b) it ensures that conflicting rlibs are resolved properly.
    let host_libs = option_env!("HOST_LIBS")
        .map(|p| format!(" -L dependency={}", Path::new(p).join("deps").display()))
        .unwrap_or_default();
    config.target_rustcflags = Some(format!(
        "--emit=metadata -Dwarnings -Zui-testing -L dependency={}{}{}",
        deps_path.display(),
        host_libs,
        extern_flags(),
    ));

    config.build_base = profile_path.join("test");
    config.rustc_path = profile_path.join(if cfg!(windows) {
        "clippy-driver.exe"
    } else {
        "clippy-driver"
    });
    config
}

fn run_ui(cfg: &mut compiletest::Config) {
    cfg.mode = TestMode::Ui;
    cfg.src_base = Path::new("tests").join("ui");
    // use tests/clippy.toml
    let _g = VarGuard::set("CARGO_MANIFEST_DIR", std::fs::canonicalize("tests").unwrap());
    compiletest::run_tests(cfg);
}

fn run_internal_tests(cfg: &mut compiletest::Config) {
    // only run internal tests with the internal-tests feature
    if !RUN_INTERNAL_TESTS {
        return;
    }
    cfg.mode = TestMode::Ui;
    cfg.src_base = Path::new("tests").join("ui-internal");
    compiletest::run_tests(cfg);
}

fn run_ui_toml(config: &mut compiletest::Config) {
    fn run_tests(config: &compiletest::Config, mut tests: Vec<tester::TestDescAndFn>) -> Result<bool, io::Error> {
        let mut result = true;
        let opts = compiletest::test_opts(config);
        for dir in fs::read_dir(&config.src_base)? {
            let dir = dir?;
            if !dir.file_type()?.is_dir() {
                continue;
            }
            let dir_path = dir.path();
            let _g = VarGuard::set("CARGO_MANIFEST_DIR", &dir_path);
            for file in fs::read_dir(&dir_path)? {
                let file = file?;
                let file_path = file.path();
                if file.file_type()?.is_dir() {
                    continue;
                }
                if file_path.extension() != Some(OsStr::new("rs")) {
                    continue;
                }
                let paths = compiletest::common::TestPaths {
                    file: file_path,
                    base: config.src_base.clone(),
                    relative_dir: dir_path.file_name().unwrap().into(),
                };
                let test_name = compiletest::make_test_name(config, &paths);
                let index = tests
                    .iter()
                    .position(|test| test.desc.name == test_name)
                    .expect("The test should be in there");
                result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
            }
        }
        Ok(result)
    }

    config.mode = TestMode::Ui;
    config.src_base = Path::new("tests").join("ui-toml").canonicalize().unwrap();

    let tests = compiletest::make_tests(config);

    let res = run_tests(config, tests);
    match res {
        Ok(true) => {},
        Ok(false) => panic!("Some tests failed"),
        Err(e) => {
            panic!("I/O failure during tests: {:?}", e);
        },
    }
}

fn run_ui_cargo(config: &mut compiletest::Config) {
    fn run_tests(
        config: &compiletest::Config,
        filters: &[String],
        mut tests: Vec<tester::TestDescAndFn>,
    ) -> Result<bool, io::Error> {
        let mut result = true;
        let opts = compiletest::test_opts(config);

        for dir in fs::read_dir(&config.src_base)? {
            let dir = dir?;
            if !dir.file_type()?.is_dir() {
                continue;
            }

            // Use the filter if provided
            let dir_path = dir.path();
            for filter in filters {
                if !dir_path.ends_with(filter) {
                    continue;
                }
            }

            for case in fs::read_dir(&dir_path)? {
                let case = case?;
                if !case.file_type()?.is_dir() {
                    continue;
                }

                let src_path = case.path().join("src");

                // When switching between branches, if the previous branch had a test
                // that the current branch does not have, the directory is not removed
                // because an ignored Cargo.lock file exists.
                if !src_path.exists() {
                    continue;
                }

                env::set_current_dir(&src_path)?;
                for file in fs::read_dir(&src_path)? {
                    let file = file?;
                    if file.file_type()?.is_dir() {
                        continue;
                    }

                    // Search for the main file to avoid running a test for each file in the project
                    let file_path = file.path();
                    match file_path.file_name().and_then(OsStr::to_str) {
                        Some("main.rs") => {},
                        _ => continue,
                    }
                    let _g = VarGuard::set("CLIPPY_CONF_DIR", case.path());
                    let paths = compiletest::common::TestPaths {
                        file: file_path,
                        base: config.src_base.clone(),
                        relative_dir: src_path.strip_prefix(&config.src_base).unwrap().into(),
                    };
                    let test_name = compiletest::make_test_name(config, &paths);
                    let index = tests
                        .iter()
                        .position(|test| test.desc.name == test_name)
                        .expect("The test should be in there");
                    result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
                }
            }
        }
        Ok(result)
    }

    if cargo::is_rustc_test_suite() {
        return;
    }

    config.mode = TestMode::Ui;
    config.src_base = Path::new("tests").join("ui-cargo").canonicalize().unwrap();

    let tests = compiletest::make_tests(config);

    let current_dir = env::current_dir().unwrap();
    let res = run_tests(config, &config.filters, tests);
    env::set_current_dir(current_dir).unwrap();

    match res {
        Ok(true) => {},
        Ok(false) => panic!("Some tests failed"),
        Err(e) => {
            panic!("I/O failure during tests: {:?}", e);
        },
    }
}

fn prepare_env() {
    set_var("CLIPPY_DISABLE_DOCS_LINKS", "true");
    set_var("__CLIPPY_INTERNAL_TESTS", "true");
    //set_var("RUST_BACKTRACE", "0");
}

#[test]
fn compile_test() {
    prepare_env();
    let mut config = default_config();
    run_ui(&mut config);
    run_ui_toml(&mut config);
    run_ui_cargo(&mut config);
    run_internal_tests(&mut config);
}

/// Restores an env var on drop
#[must_use]
struct VarGuard {
    key: &'static str,
    value: Option<OsString>,
}

impl VarGuard {
    fn set(key: &'static str, val: impl AsRef<OsStr>) -> Self {
        let value = var_os(key);
        set_var(key, val);
        Self { key, value }
    }
}

impl Drop for VarGuard {
    fn drop(&mut self) {
        match self.value.as_deref() {
            None => remove_var(self.key),
            Some(value) => set_var(self.key, value),
        }
    }
}
<commit_msg>Auto merge of #7711 - camsteffen:depinfo-error, r=flip1995<commit_after>#![feature(test)] // compiletest_rs requires this attribute
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
#![warn(rust_2018_idioms, unused_lifetimes)]

use compiletest_rs as compiletest;
use compiletest_rs::common::Mode as TestMode;

use std::collections::HashMap;
use std::env::{self, remove_var, set_var, var_os};
use std::ffi::{OsStr, OsString};
use std::fs;
use std::io;
use std::path::{Path, PathBuf};

mod cargo;

// whether to run internal tests or not
const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal-lints");

/// All crates used in UI tests are listed here
static TEST_DEPENDENCIES: &[&str] = &[
    "clippy_utils",
    "derive_new",
    "if_chain",
    "itertools",
    "quote",
    "regex",
    "serde",
    "serde_derive",
    "syn",
];

// Test dependencies may need an `extern crate` here to ensure that they show up
// in the depinfo file (otherwise cargo thinks they are unused)
#[allow(unused_extern_crates)]
extern crate clippy_utils;
#[allow(unused_extern_crates)]
extern crate derive_new;
#[allow(unused_extern_crates)]
extern crate if_chain;
#[allow(unused_extern_crates)]
extern crate itertools;
#[allow(unused_extern_crates)]
extern crate quote;
#[allow(unused_extern_crates)]
extern crate syn;

/// Produces a string with an `--extern` flag for all UI test crate
/// dependencies.
///
/// The dependency files are located by parsing the depinfo file for this test
/// module. This assumes the `-Z binary-dep-depinfo` flag is enabled. All test
/// dependencies must be added to Cargo.toml at the project root. Test
/// dependencies that are not *directly* used by this test module require an
/// `extern crate` declaration.
fn extern_flags() -> String {
    let current_exe_depinfo = {
        let mut path = env::current_exe().unwrap();
        path.set_extension("d");
        std::fs::read_to_string(path).unwrap()
    };
    let mut crates: HashMap<&str, &str> = HashMap::with_capacity(TEST_DEPENDENCIES.len());
    for line in current_exe_depinfo.lines() {
        // each dependency is expected to have a Makefile rule like `/path/to/crate-hash.rlib:`
        let parse_name_path = || {
            if line.starts_with(char::is_whitespace) {
                return None;
            }
            let path_str = line.strip_suffix(':')?;
            let path = Path::new(path_str);
            if !matches!(path.extension()?.to_str()?, "rlib" | "so" | "dylib" | "dll") {
                return None;
            }
            let (name, _hash) = path.file_stem()?.to_str()?.rsplit_once('-')?;
            // the "lib" prefix is not present for dll files
            let name = name.strip_prefix("lib").unwrap_or(name);
            Some((name, path_str))
        };
        if let Some((name, path)) = parse_name_path() {
            if TEST_DEPENDENCIES.contains(&name) {
                // A dependency may be listed twice if it is available in sysroot,
                // and the sysroot dependencies are listed first. As of the writing,
                // this only seems to apply to if_chain.
                crates.insert(name, path);
            }
        }
    }
    let not_found: Vec<&str> = TEST_DEPENDENCIES
        .iter()
        .copied()
        .filter(|n| !crates.contains_key(n))
        .collect();
    assert!(
        not_found.is_empty(),
        "dependencies not found in depinfo: {:?}\n\
        help: Make sure the `-Z binary-dep-depinfo` rust flag is enabled\n\
        help: Try adding to dev-dependencies in Cargo.toml",
        not_found
    );
    crates
        .into_iter()
        .map(|(name, path)| format!(" --extern {}={}", name, path))
        .collect()
}

fn default_config() -> compiletest::Config {
    let mut config = compiletest::Config::default();

    if let Ok(filters) = env::var("TESTNAME") {
        config.filters = filters.split(',').map(std::string::ToString::to_string).collect();
    }

    if let Some(path) = option_env!("RUSTC_LIB_PATH") {
        let path = PathBuf::from(path);
        config.run_lib_path = path.clone();
        config.compile_lib_path = path;
    }
    let current_exe_path = std::env::current_exe().unwrap();
    let deps_path = current_exe_path.parent().unwrap();
    let profile_path = deps_path.parent().unwrap();

    // Using `-L dependency={}` enforces that external dependencies are added with `--extern`.
    // This is valuable because a) it allows us to monitor what external dependencies are used
    // and b) it ensures that conflicting rlibs are resolved properly.
    let host_libs = option_env!("HOST_LIBS")
        .map(|p| format!(" -L dependency={}", Path::new(p).join("deps").display()))
        .unwrap_or_default();
    config.target_rustcflags = Some(format!(
        "--emit=metadata -Dwarnings -Zui-testing -L dependency={}{}{}",
        deps_path.display(),
        host_libs,
        extern_flags(),
    ));

    config.build_base = profile_path.join("test");
    config.rustc_path = profile_path.join(if cfg!(windows) {
        "clippy-driver.exe"
    } else {
        "clippy-driver"
    });
    config
}

fn run_ui(cfg: &mut compiletest::Config) {
    cfg.mode = TestMode::Ui;
    cfg.src_base = Path::new("tests").join("ui");
    // use tests/clippy.toml
    let _g = VarGuard::set("CARGO_MANIFEST_DIR", std::fs::canonicalize("tests").unwrap());
    compiletest::run_tests(cfg);
}

fn run_internal_tests(cfg: &mut compiletest::Config) {
    // only run internal tests with the internal-tests feature
    if !RUN_INTERNAL_TESTS {
        return;
    }
    cfg.mode = TestMode::Ui;
    cfg.src_base = Path::new("tests").join("ui-internal");
    compiletest::run_tests(cfg);
}

fn run_ui_toml(config: &mut compiletest::Config) {
    fn run_tests(config: &compiletest::Config, mut tests: Vec<tester::TestDescAndFn>) -> Result<bool, io::Error> {
        let mut result = true;
        let opts = compiletest::test_opts(config);
        for dir in fs::read_dir(&config.src_base)? {
            let dir = dir?;
            if !dir.file_type()?.is_dir() {
                continue;
            }
            let dir_path = dir.path();
            let _g = VarGuard::set("CARGO_MANIFEST_DIR", &dir_path);
            for file in fs::read_dir(&dir_path)? {
                let file = file?;
                let file_path = file.path();
                if file.file_type()?.is_dir() {
                    continue;
                }
                if file_path.extension() != Some(OsStr::new("rs")) {
                    continue;
                }
                let paths = compiletest::common::TestPaths {
                    file: file_path,
                    base: config.src_base.clone(),
                    relative_dir: dir_path.file_name().unwrap().into(),
                };
                let test_name = compiletest::make_test_name(config, &paths);
                let index = tests
                    .iter()
                    .position(|test| test.desc.name == test_name)
                    .expect("The test should be in there");
                result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
            }
        }
        Ok(result)
    }

    config.mode = TestMode::Ui;
    config.src_base = Path::new("tests").join("ui-toml").canonicalize().unwrap();

    let tests = compiletest::make_tests(config);

    let res = run_tests(config, tests);
    match res {
        Ok(true) => {},
        Ok(false) => panic!("Some tests failed"),
        Err(e) => {
            panic!("I/O failure during tests: {:?}", e);
        },
    }
}

fn run_ui_cargo(config: &mut compiletest::Config) {
    fn run_tests(
        config: &compiletest::Config,
        filters: &[String],
        mut tests: Vec<tester::TestDescAndFn>,
    ) -> Result<bool, io::Error> {
        let mut result = true;
        let opts = compiletest::test_opts(config);

        for dir in fs::read_dir(&config.src_base)? {
            let dir = dir?;
            if !dir.file_type()?.is_dir() {
                continue;
            }

            // Use the filter if provided
            let dir_path = dir.path();
            for filter in filters {
                if !dir_path.ends_with(filter) {
                    continue;
                }
            }

            for case in fs::read_dir(&dir_path)? {
                let case = case?;
                if !case.file_type()?.is_dir() {
                    continue;
                }

                let src_path = case.path().join("src");

                // When switching between branches, if the previous branch had a test
                // that the current branch does not have, the directory is not removed
                // because an ignored Cargo.lock file exists.
                if !src_path.exists() {
                    continue;
                }

                env::set_current_dir(&src_path)?;
                for file in fs::read_dir(&src_path)? {
                    let file = file?;
                    if file.file_type()?.is_dir() {
                        continue;
                    }

                    // Search for the main file to avoid running a test for each file in the project
                    let file_path = file.path();
                    match file_path.file_name().and_then(OsStr::to_str) {
                        Some("main.rs") => {},
                        _ => continue,
                    }
                    let _g = VarGuard::set("CLIPPY_CONF_DIR", case.path());
                    let paths = compiletest::common::TestPaths {
                        file: file_path,
                        base: config.src_base.clone(),
                        relative_dir: src_path.strip_prefix(&config.src_base).unwrap().into(),
                    };
                    let test_name = compiletest::make_test_name(config, &paths);
                    let index = tests
                        .iter()
                        .position(|test| test.desc.name == test_name)
                        .expect("The test should be in there");
                    result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
                }
            }
        }
        Ok(result)
    }

    if cargo::is_rustc_test_suite() {
        return;
    }

    config.mode = TestMode::Ui;
    config.src_base = Path::new("tests").join("ui-cargo").canonicalize().unwrap();

    let tests = compiletest::make_tests(config);

    let current_dir = env::current_dir().unwrap();
    let res = run_tests(config, &config.filters, tests);
    env::set_current_dir(current_dir).unwrap();

    match res {
        Ok(true) => {},
        Ok(false) => panic!("Some tests failed"),
        Err(e) => {
            panic!("I/O failure during tests: {:?}", e);
        },
    }
}

fn prepare_env() {
    set_var("CLIPPY_DISABLE_DOCS_LINKS", "true");
    set_var("__CLIPPY_INTERNAL_TESTS", "true");
    //set_var("RUST_BACKTRACE", "0");
}

#[test]
fn compile_test() {
    prepare_env();
    let mut config = default_config();
    run_ui(&mut config);
    run_ui_toml(&mut config);
    run_ui_cargo(&mut config);
    run_internal_tests(&mut config);
}

/// Restores an env var on drop
#[must_use]
struct VarGuard {
    key: &'static str,
    value: Option<OsString>,
}

impl VarGuard {
    fn set(key: &'static str, val: impl AsRef<OsStr>) -> Self {
        let value = var_os(key);
        set_var(key, val);
        Self { key, value }
    }
}

impl Drop for VarGuard {
    fn drop(&mut self) {
        match self.value.as_deref() {
            None => remove_var(self.key),
            Some(value) => set_var(self.key, value),
        }
    }
}
<|endoftext|>


<commit_before>use hyper::{HeaderMap, StatusCode};

use hyperx::header::ByteRangeSpec;
use hyperx::header::ByteRangeSpec::FromTo;
use hyperx::header::Range::Bytes;
use hyperx::header::ContentRangeSpec;
use hyperx::Headers;

use reqwest;
use reqwest::Client;

use buffer::Buffer;
use reader::Reader;

use std::cmp;
use std::io::{Error, ErrorKind, Read, Seek, SeekFrom};
use std::str::FromStr;
use std::time::Instant;

fn get_head(filename: &str) -> Result<reqwest::Response, reqwest::Error> {
    if filename.contains(".amazonaws.com") {
        let range = vec![FromTo(0, 0)];

        let mut headers = Headers::new();
        headers.set(Bytes(range));
        let client = reqwest::Client::builder()
            .default_headers(HeaderMap::from(headers))
            .build()
            .unwrap();

        client.get(filename).send()
    } else {
        let client = Client::new();
        client.head(filename).send()
    }
}

#[derive(Debug)]
struct ResponseData {
    body_data: Vec<u8>,
    file_size: u64,
}

fn get_data(filename: &str, range: Vec<ByteRangeSpec>) -> Result<ResponseData, String> {
    let mut headers = Headers::new();
    headers.set(Bytes(range));
    let client = reqwest::Client::builder()
        .default_headers(HeaderMap::from(headers))
        .build()
        .unwrap();

    let mut response = match client.get(filename).send() {
        Ok(content) => content,
        Err(_msg) => return Err("bad request".to_string()),
    };

    let status = response.status();

    if !(status == StatusCode::OK || status == StatusCode::PARTIAL_CONTENT) {
        error!("ERROR {:?}", response);
        return Err("bad response status".to_string());
    }

    let mut body: Vec<u8> = vec![];
    let _result = response.copy_to(&mut body);

    let file_size = match get_content_range(&response) {
        Ok(length) => length.unwrap_or(0),
        Err(_msg) => return Err("bad response header".to_string()),
    };

    Ok(ResponseData {
        body_data: body,
        file_size,
    })
}

fn get_content_range(response: &reqwest::Response) -> Result<Option<u64>, String> {
    match response.headers().get("Content-Range") {
        Some(content_range) => {
            match content_range.to_str() {
                Ok(content_range_str) => {
                    match ContentRangeSpec::from_str(content_range_str) {
                        Ok(ContentRangeSpec::Bytes {
                            range: _range,
                            instance_length: length,
                        }) => Ok(length),
                        Ok(ContentRangeSpec::Unregistered {
                            unit: _unit,
                            resp: _resp,
                        }) => Err("Unregistered, actually unsupported".to_string()),
                        _ => Err("Error parsing content range from str".to_string())
                    }
                },
                _ => Err("Error serializing header value to str".to_string())
            }
        },
        None => Err("Missing content_range".to_string()),
    }
}

#[derive(Debug)]
pub struct HttpReader {
    pub filename: String,
    pub file_size: Option<u64>,
    pub position: u64,
    pub buffer: Buffer,
}

pub fn exists(filename: &str) -> bool {
    match get_head(filename) {
        Ok(resp) => resp.status().is_success(),
        Err(_msg) => false,
    }
}

fn get_data_range(position: u64, size: usize, max_end_position: Option<u64>) -> Vec<ByteRangeSpec> {
    let start = position;
    let end = match (position, size) {
        (0, 0) => 0,
        (_, _) => match max_end_position {
            Some(max) => {
                let max_size = max - position;
                position + cmp::min((size - 1) as u64, max_size)
            }
            None => position + (size - 1) as u64,
        },
    };

    vec![FromTo(start, end)]
}

fn load_data(reader: &mut HttpReader, size: usize) -> Result<Option<Vec<u8>>, String> {
    let start = Instant::now();
    info!("make HTTP request with request {:?} bytes", size);

    let position = match reader.buffer.size {
        Some(_) => reader.buffer.position,
        None => reader.position,
    };

    if let Some(total_file_size) = reader.file_size {
        if position >= total_file_size {
            info!(
                "request range out of range: {} > {}",
                position, total_file_size
            );
            return Ok(None);
        }
    }

    let range = get_data_range(position, size, reader.buffer.max_end_position);
    let response = match get_data(&reader.filename, range) {
        Ok(data) => data,
        Err(msg) => return Err(msg),
    };

    let elapsed = start.elapsed();
    if elapsed.as_secs() > 0 {
        warn!("Request duration {} seconds", elapsed.as_secs());
    }

    let new_position = position + response.body_data.len() as u64;
    match reader.buffer.size {
        Some(_) => {
            reader.buffer.position = new_position;
        }
        None => {
            reader.position = new_position;
        }
    };
    Ok(Some(response.body_data))
}

impl Reader for HttpReader {
    fn new() -> HttpReader {
        HttpReader {
            filename: "".to_string(),
            file_size: None,
            position: 0,
            buffer: Buffer {
                size: None,
                position: 0,
                max_end_position: None,
                buffer: vec![],
            },
        }
    }

    fn open(&mut self, filename: &str) -> Result<(), String> {
        self.filename = filename.to_string();

        match get_head(filename) {
            Err(msg) => Err(msg.to_string()),
            Ok(response) => {
                let content_length = match get_content_range(&response) {
                    Ok(length) => length,
                    _ => response.content_length(),
                };

                self.file_size = content_length;
                Ok(())
            }
        }
    }

    fn get_position(&mut self) -> Result<u64, String> {
        Ok(self.position)
    }

    fn get_cache_size(&self) -> Option<usize> {
        self.buffer.size
    }

    fn set_cache_size(&mut self, cache_size: Option<usize>) {
        self.buffer.size = cache_size;
    }

    fn get_max_end_position(&self) -> Option<u64> {
        self.buffer.max_end_position
    }

    fn set_max_end_position(&mut self, max_end_position: Option<u64>) {
        self.buffer.max_end_position = max_end_position;
    }

    fn get_size(&mut self) -> Result<u64, String> {
        match self.file_size {
            Some(length) => Ok(length),
            None => Err("No length detected".to_string()),
        }
    }
}

impl Read for HttpReader {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
        if self.buffer.get_cached_size() >= buf.len() {
            self.position += buf.len() as u64;
            if self.buffer.get_data(buf) {
                Ok(buf.len())
            } else {
                Err(Error::new(
                    ErrorKind::Other,
                    "unable to read data from HTTP cache",
                ))
            }
        } else if let Some(buffer_size) = self.buffer.size {
            let some_data =
                load_data(self, buffer_size).map_err(|msg| Error::new(ErrorKind::Other, msg))?;

            if let Some(data) = some_data {
                self.buffer.append_data(&data.to_vec());
                self.position += buf.len() as u64;
                if self.buffer.get_data(buf) {
                    return Ok(buf.len());
                } else {
                    return Err(Error::new(
                        ErrorKind::Other,
                        "unable to read data from HTTP cache",
                    ));
                }
            } else {
                Ok(0)
            }
        } else {
            let some_data =
                load_data(self, buf.len()).map_err(|msg| Error::new(ErrorKind::Other, msg))?;

            if let Some(data) = some_data {
                if data.len() >= buf.len() {
                    buf.clone_from_slice(&data);
                    Ok(data.len())
                } else {
                    Ok(0)
                }
            } else {
                Ok(0)
            }
        }
    }
}

impl Seek for HttpReader {
    fn seek(&mut self, seek_from: SeekFrom) -> Result<u64, Error> {
        match seek_from {
            SeekFrom::Current(offset) => {
                self.position += offset as u64;
                if self.buffer.size.is_some() {
                    if offset > 0 && self.buffer.get_cached_size() > offset as usize {
                        let mut skipped_data = vec![];
                        skipped_data.resize(offset as usize, 0);
                        let _skiped_data = self.buffer.get_data(&mut skipped_data);
                    } else {
                        self.buffer.reset();
                    }
                }
            }
            SeekFrom::Start(offset) => {
                self.buffer.reset();
                self.position = offset;
                if self.buffer.size.is_some() {
                    self.buffer.position = self.position;
                }
            }
            SeekFrom::End(offset) => {
                self.buffer.reset();
                match self.file_size {
                    Some(size) => {
                        self.position = size - offset as u64;
                        if self.buffer.size.is_some() {
                            self.buffer.position = self.position;
                        }
                    }
                    None => return Err(Error::new(ErrorKind::Other, "Missing file size")),
                }
            }
        }
        Ok(self.position)
    }
}
<commit_msg>fix syntax<commit_after>use hyper::StatusCode;

use hyperx::{
    header::{ByteRangeSpec, ByteRangeSpec::FromTo, ContentRangeSpec, Range::Bytes},
    Headers,
};

use reqwest;
use reqwest::{header, Client};

use buffer::Buffer;
use reader::Reader;

use std::cmp;
use std::io::{Error, ErrorKind, Read, Seek, SeekFrom};
use std::str::FromStr;
use std::time::Instant;

fn get_head(filename: &str) -> Result<reqwest::Response, reqwest::Error> {
    if filename.contains(".amazonaws.com") {
        let range = vec![FromTo(0, 0)];

        let mut headers = Headers::new();
        headers.set(Bytes(range));
        let client = reqwest::Client::builder()
            .default_headers(headers.into())
            .build()
            .unwrap();

        client.get(filename).send()
    } else {
        let client = Client::new();
        client.head(filename).send()
    }
}

#[derive(Debug)]
struct ResponseData {
    body_data: Vec<u8>,
    file_size: u64,
}

fn get_data(filename: &str, range: Vec<ByteRangeSpec>) -> Result<ResponseData, String> {
    let mut headers = Headers::new();
    headers.set(Bytes(range));
    let client = reqwest::Client::builder()
        .default_headers(headers.into())
        .build()
        .unwrap();

    let mut response = match client.get(filename).send() {
        Ok(content) => content,
        Err(_msg) => return Err("bad request".to_string()),
    };

    let status = response.status();

    if !(status == StatusCode::OK || status == StatusCode::PARTIAL_CONTENT) {
        error!("ERROR {:?}", response);
        return Err("bad response status".to_string());
    }

    let mut body: Vec<u8> = vec![];
    let _result = response.copy_to(&mut body);

    let file_size = match get_content_range(&response) {
        Ok(length) => length.unwrap_or(0),
        Err(_msg) => return Err("bad response header".to_string()),
    };

    Ok(ResponseData {
        body_data: body,
        file_size,
    })
}

fn get_content_range(response: &reqwest::Response) -> Result<Option<u64>, String> {
    if let Some(content_range) = response.headers().get(header::CONTENT_RANGE) {
        let content_range_str = content_range
            .to_str()
            .map_err(|msg| format!("Error serializing header value to str: {}", msg))?;

        match ContentRangeSpec::from_str(content_range_str)
            .map_err(|msg| format!("Error parsing content range from str: {}", msg))?
        {
            ContentRangeSpec::Bytes {
                instance_length: length,
                ..
            } => Ok(length),
            ContentRangeSpec::Unregistered { .. } => {
                Err("Unregistered, actually unsupported".to_string())
            }
        }
    } else {
        Err("Missing content_range".to_string())
    }
}

#[derive(Debug)]
pub struct HttpReader {
    pub filename: String,
    pub file_size: Option<u64>,
    pub position: u64,
    pub buffer: Buffer,
}

pub fn exists(filename: &str) -> bool {
    match get_head(filename) {
        Ok(resp) => resp.status().is_success(),
        Err(_msg) => false,
    }
}

fn get_data_range(position: u64, size: usize, max_end_position: Option<u64>) -> Vec<ByteRangeSpec> {
    let start = position;
    let end = match (position, size) {
        (0, 0) => 0,
        (_, _) => match max_end_position {
            Some(max) => {
                let max_size = max - position;
                position + cmp::min((size - 1) as u64, max_size)
            }
            None => position + (size - 1) as u64,
        },
    };

    vec![FromTo(start, end)]
}

fn load_data(reader: &mut HttpReader, size: usize) -> Result<Option<Vec<u8>>, String> {
    let start = Instant::now();
    info!("make HTTP request with request {:?} bytes", size);

    let position = match reader.buffer.size {
        Some(_) => reader.buffer.position,
        None => reader.position,
    };

    if let Some(total_file_size) = reader.file_size {
        if position >= total_file_size {
            info!(
                "request range out of range: {} > {}",
                position, total_file_size
            );
            return Ok(None);
        }
    }

    let range = get_data_range(position, size, reader.buffer.max_end_position);
    let response = match get_data(&reader.filename, range) {
        Ok(data) => data,
        Err(msg) => return Err(msg),
    };

    let elapsed = start.elapsed();
    if elapsed.as_secs() > 0 {
        warn!("Request duration {} seconds", elapsed.as_secs());
    }

    let new_position = position + response.body_data.len() as u64;
    match reader.buffer.size {
        Some(_) => {
            reader.buffer.position = new_position;
        }
        None => {
            reader.position = new_position;
        }
    };
    Ok(Some(response.body_data))
}

impl Reader for HttpReader {
    fn new() -> HttpReader {
        HttpReader {
            filename: "".to_string(),
            file_size: None,
            position: 0,
            buffer: Buffer {
                size: None,
                position: 0,
                max_end_position: None,
                buffer: vec![],
            },
        }
    }

    fn open(&mut self, filename: &str) -> Result<(), String> {
        self.filename = filename.to_string();

        match get_head(filename) {
            Err(msg) => Err(msg.to_string()),
            Ok(response) => {
                let content_length = match get_content_range(&response) {
                    Ok(length) => length,
                    _ => response.content_length(),
                };

                self.file_size = content_length;
                Ok(())
            }
        }
    }

    fn get_position(&mut self) -> Result<u64, String> {
        Ok(self.position)
    }

    fn get_cache_size(&self) -> Option<usize> {
        self.buffer.size
    }

    fn set_cache_size(&mut self, cache_size: Option<usize>) {
        self.buffer.size = cache_size;
    }

    fn get_max_end_position(&self) -> Option<u64> {
        self.buffer.max_end_position
    }

    fn set_max_end_position(&mut self, max_end_position: Option<u64>) {
        self.buffer.max_end_position = max_end_position;
    }

    fn get_size(&mut self) -> Result<u64, String> {
        match self.file_size {
            Some(length) => Ok(length),
            None => Err("No length detected".to_string()),
        }
    }
}

impl Read for HttpReader {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
        if self.buffer.get_cached_size() >= buf.len() {
            self.position += buf.len() as u64;
            if self.buffer.get_data(buf) {
                Ok(buf.len())
            } else {
                Err(Error::new(
                    ErrorKind::Other,
                    "unable to read data from HTTP cache",
                ))
            }
        } else if let Some(buffer_size) = self.buffer.size {
            let some_data =
                load_data(self, buffer_size).map_err(|msg| Error::new(ErrorKind::Other, msg))?;

            if let Some(data) = some_data {
                self.buffer.append_data(&data.to_vec());
                self.position += buf.len() as u64;
                if self.buffer.get_data(buf) {
                    return Ok(buf.len());
                } else {
                    return Err(Error::new(
                        ErrorKind::Other,
                        "unable to read data from HTTP cache",
                    ));
                }
            } else {
                Ok(0)
            }
        } else {
            let some_data =
                load_data(self, buf.len()).map_err(|msg| Error::new(ErrorKind::Other, msg))?;

            if let Some(data) = some_data {
                if data.len() >= buf.len() {
                    buf.clone_from_slice(&data);
                    Ok(data.len())
                } else {
                    Ok(0)
                }
            } else {
                Ok(0)
            }
        }
    }
}

impl Seek for HttpReader {
    fn seek(&mut self, seek_from: SeekFrom) -> Result<u64, Error> {
        match seek_from {
            SeekFrom::Current(offset) => {
                self.position += offset as u64;
                if self.buffer.size.is_some() {
                    if offset > 0 && self.buffer.get_cached_size() > offset as usize {
                        let mut skipped_data = vec![];
                        skipped_data.resize(offset as usize, 0);
                        let _skiped_data = self.buffer.get_data(&mut skipped_data);
                    } else {
                        self.buffer.reset();
                    }
                }
            }
            SeekFrom::Start(offset) => {
                self.buffer.reset();
                self.position = offset;
                if self.buffer.size.is_some() {
                    self.buffer.position = self.position;
                }
            }
            SeekFrom::End(offset) => {
                self.buffer.reset();
                match self.file_size {
                    Some(size) => {
                        self.position = size - offset as u64;
                        if self.buffer.size.is_some() {
                            self.buffer.position = self.position;
                        }
                    }
                    None => return Err(Error::new(ErrorKind::Other, "Missing file size")),
                }
            }
        }
        Ok(self.position)
    }
}
<|endoftext|>


<commit_before>#![cfg_attr(feature = "nightly", feature(lang_items, core_intrinsics, panic_implementation))]
#![cfg_attr(feature = "nightly", no_std)]
#![cfg_attr(feature = "nightly", no_main)]

// Pull in the system libc library for what crt0.o likely requires.
extern crate libc;
extern crate raw_cpuid;

#[cfg(feature = "nightly")]
use core::panic::PanicInfo;

#[cfg(feature = "nightly")]
#[no_mangle]
pub extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 {
    let _c = raw_cpuid::CpuId::new();
    0
}

#[cfg(not(feature = "nightly"))]
fn main() {
    let _c = raw_cpuid::CpuId::new();
}

#[cfg_attr(feature = "nightly", lang = "eh_personality")]
#[no_mangle]
pub extern "C" fn rust_eh_personality() {}

#[cfg_attr(feature = "nightly", lang = "eh_unwind_resume")]
#[no_mangle]
pub extern "C" fn rust_eh_unwind_resume() {}

#[cfg(feature = "nightly")]
#[cfg_attr(feature = "nightly", panic_implementation)]
fn panic_impl(_info: &PanicInfo) -> ! {
    loop {}
}
<commit_msg>Update test a bit more to hopefully work on Linux/Win.<commit_after>#![cfg_attr(feature = "nightly", feature(lang_items, core_intrinsics, panic_implementation))]
#![cfg_attr(feature = "nightly", no_std)]
#![cfg_attr(feature = "nightly", no_main)]

// Pull in the system libc library for what crt0.o likely requires.
extern crate libc;
extern crate raw_cpuid;

#[cfg(feature = "nightly")]
use core::panic::PanicInfo;

#[cfg(feature = "nightly")]
#[no_mangle]
pub extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 {
    let _c = raw_cpuid::CpuId::new();
    0
}

#[cfg(not(feature = "nightly"))]
fn main() {
    let _c = raw_cpuid::CpuId::new();
}

#[cfg(feature = "nightly")]
#[cfg_attr(feature = "nightly", lang = "eh_personality")]
#[no_mangle]
pub extern "C" fn rust_eh_personality() {}

#[cfg(feature = "nightly")]
#[cfg_attr(feature = "nightly", lang = "eh_unwind_resume")]
#[no_mangle]
pub extern "C" fn rust_eh_unwind_resume() {}

#[cfg(feature = "nightly")]
#[cfg_attr(feature = "nightly", panic_implementation)]
fn panic_impl(_info: &PanicInfo) -> ! {
    loop {}
}
<|endoftext|>


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

type Stack<T> = Vec<T>;

#[derive(Default)]
pub struct Interpreter<'a, 'b, T: 'b> {
    data_stack: Stack<T>,
    vocabulary: HashMap<&'a str, &'b Word<T>>,
}

impl<'a, 'b, T: 'b> Interpreter<'a, 'b, T> where T: FromStr {

    fn push(&mut self, value: T) {
        self.data_stack.push(value)
    }

    fn pop(&mut self) -> Option<T> {
        self.data_stack.pop()
    }

    fn eval_token(&mut self, token: &str) {
        if let Ok(literal) = token.parse::<T>() {
            self.push(literal);
        } else {
            if let Some(&word) = self.vocabulary.get(token) {
                word.eval_within(self);
            }
        }
    }

    fn define_word(&mut self, name: &'a str, word: &'b Word<T>) {
        self.vocabulary.insert(name, word);
    }

    fn lookup(&self, token: &str) -> Option<&&Word<T>> {
        self.vocabulary.get(token)
    }
}

struct Word<T> {
    entry: Box<Fn(&mut Interpreter<T>) -> ()>,
}

impl<T> Word<T> {
    fn eval_within(&self, mut i: &mut Interpreter<T>) {
      (self.entry)(&mut i)
    }
}

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

    fn fixture_plus() -> Word<i32> {
        Word {
            entry: Box::new(|ref mut i| {
                if let (Some(b), Some(a)) = (i.pop(), i.pop()) {
                    i.push(a + b)
                } else { panic!("Stack underflow") }
            })
        }
    }

    #[test]
    fn register_word() {
        let plus = fixture_plus();
        let mut interpreter = Interpreter::<i32>::default();
        interpreter.define_word("+", &plus);
        interpreter.push(51);
        interpreter.push(42);
        interpreter.eval_token("+");
        assert_eq!(Some(93), interpreter.pop())
    }

    #[test]
    fn plus_word() {
        let mut interpreter = Interpreter::<i32>::default();
        let plus = fixture_plus();
        interpreter.push(42);
        interpreter.push(51);
        plus.eval_within(&mut interpreter);
        assert_eq!(Some(93), interpreter.pop())
    }

    #[test]
    fn push_literal() {
        let mut interpreter = Interpreter::<i32>::default();
        interpreter.eval_token("42");
        assert_eq!(Some(42), interpreter.pop());
    }

    #[test]
    fn pops_nothing() {
        let mut interpreter = Interpreter::<i32>::default();
        assert_eq!(None, interpreter.pop())
    }

    #[test]
    fn pops_as_pushed() {
        let mut interpreter = Interpreter::<i32>::default();
        interpreter.push(42);
        assert_eq!(Some(42), interpreter.pop());
        assert_eq!(None, interpreter.pop())
    }

    #[test]
    fn pops_in_reverse_order() {
        let mut interpreter = Interpreter::<i32>::default();
        interpreter.push(42);
        interpreter.push(51);
        assert_eq!(Some(51), interpreter.pop());
        assert_eq!(Some(42), interpreter.pop());
        assert_eq!(None, interpreter.pop())
    }
}
<commit_msg>De-inline call to lookup<commit_after>use std::str::FromStr;
use std::collections::HashMap;

type Stack<T> = Vec<T>;

#[derive(Default)]
pub struct Interpreter<'a, 'b, T: 'b> {
    data_stack: Stack<T>,
    vocabulary: HashMap<&'a str, &'b Word<T>>,
}

impl<'a, 'b, T: 'b> Interpreter<'a, 'b, T> where T: FromStr {

    fn push(&mut self, value: T) {
        self.data_stack.push(value)
    }

    fn pop(&mut self) -> Option<T> {
        self.data_stack.pop()
    }

    fn eval_token(&mut self, token: &str) {
        if let Ok(literal) = token.parse::<T>() {
            self.push(literal);
        } else {
            if let Some(&word) = self.lookup(token) {
                word.eval_within(self);
            }
        }
    }

    fn define_word(&mut self, name: &'a str, word: &'b Word<T>) {
        self.vocabulary.insert(name, word);
    }

    fn lookup(&self, token: &str) -> Option<&&Word<T>> {
        self.vocabulary.get(token)
    }
}

struct Word<T> {
    entry: Box<Fn(&mut Interpreter<T>) -> ()>,
}

impl<T> Word<T> {
    fn eval_within(&self, mut i: &mut Interpreter<T>) {
      (self.entry)(&mut i)
    }
}

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

    fn fixture_plus() -> Word<i32> {
        Word {
            entry: Box::new(|ref mut i| {
                if let (Some(b), Some(a)) = (i.pop(), i.pop()) {
                    i.push(a + b)
                } else { panic!("Stack underflow") }
            })
        }
    }

    #[test]
    fn register_word() {
        let plus = fixture_plus();
        let mut interpreter = Interpreter::<i32>::default();
        interpreter.define_word("+", &plus);
        interpreter.push(51);
        interpreter.push(42);
        interpreter.eval_token("+");
        assert_eq!(Some(93), interpreter.pop())
    }

    #[test]
    fn plus_word() {
        let mut interpreter = Interpreter::<i32>::default();
        let plus = fixture_plus();
        interpreter.push(42);
        interpreter.push(51);
        plus.eval_within(&mut interpreter);
        assert_eq!(Some(93), interpreter.pop())
    }

    #[test]
    fn push_literal() {
        let mut interpreter = Interpreter::<i32>::default();
        interpreter.eval_token("42");
        assert_eq!(Some(42), interpreter.pop());
    }

    #[test]
    fn pops_nothing() {
        let mut interpreter = Interpreter::<i32>::default();
        assert_eq!(None, interpreter.pop())
    }

    #[test]
    fn pops_as_pushed() {
        let mut interpreter = Interpreter::<i32>::default();
        interpreter.push(42);
        assert_eq!(Some(42), interpreter.pop());
        assert_eq!(None, interpreter.pop())
    }

    #[test]
    fn pops_in_reverse_order() {
        let mut interpreter = Interpreter::<i32>::default();
        interpreter.push(42);
        interpreter.push(51);
        assert_eq!(Some(51), interpreter.pop());
        assert_eq!(Some(42), interpreter.pop());
        assert_eq!(None, interpreter.pop())
    }
}
<|endoftext|>