<commit_before>use board::std_board;
use board::std_board::{ChessBoard, Piece, PieceType, Square};
use board::sjadam_move::{SjadamMove, SjadamUndoMove};
use board::sjadam_move_gen;
use board::std_move_gen::move_gen;

use search_algorithms::board::EvalBoard;
use search_algorithms::board::Color;
use search_algorithms::board::Color::*;
use search_algorithms::board::GameResult;

use rand;
use uci::UciBoard;

#[derive(Debug, Eq, PartialEq, Clone, Hash)]
pub struct SjadamBoard {
    pub base_board: std_board::ChessBoard,
}

impl EvalBoard for SjadamBoard {
    type Move = SjadamMove;
    type UndoMove = SjadamUndoMove;

    fn to_move(&self) -> Color {
        self.base_board.to_move()
    }

    fn do_move(&mut self, mv: Self::Move) -> Self::UndoMove {
        let start_color = self.to_move();
        debug_assert!(mv.from != mv.sjadam_square || mv.sjadam_square != mv.to);

        let undo_move = SjadamUndoMove {
            from: mv.from, sjadam_square: mv.sjadam_square,
            capture: self.base_board[mv.to].piece_type(),
            to: mv.to, piece_moved: self.base_board[mv.from].piece_type(),
            old_castling_en_passant: self.base_board.castling_en_passant,
            old_half_move_clock: self.base_board.half_move_clock };

        // Do sjadam move. Has no effect if there is no sjadam jump
        self.base_board[mv.sjadam_square] = self.base_board[mv.from];
        if mv.from != mv.sjadam_square {
            self.base_board[mv.from] = Piece::empty();
        }

        // Remove castling priviledges on king moves
        if undo_move.piece_moved == PieceType::King {
            let color = self.to_move();
            self.base_board.disable_castling(color);
        }

        // Remove castling priviledges if anything moves from or to a corner

        let a1 = Square::from_alg("a1").unwrap();
        let h1 = Square::from_alg("h1").unwrap();
        let a8 = Square::from_alg("a8").unwrap();
        let h8 = Square::from_alg("h8").unwrap();
        match mv.from {
            square if square == a1 =>
                self.base_board.disable_castling_queenside(White),
            square if square == h1 =>
                self.base_board.disable_castling_kingside(White),
            square if square == a8 =>
                self.base_board.disable_castling_queenside(Black),
            square if square == h8 =>
                self.base_board.disable_castling_kingside(Black),
            _ => (),
        }

        match mv.to {
            square if square == a1 =>
                self.base_board.disable_castling_queenside(White),
            square if square == h1 =>
                self.base_board.disable_castling_kingside(White),
            square if square == a8 =>
                self.base_board.disable_castling_queenside(Black),
            square if square == h8 =>
                self.base_board.disable_castling_kingside(Black),
            _ => (),
        }

        let chess_move = mv.chess_move(self);

        match chess_move {
            None => {
                if undo_move.piece_moved != PieceType::Pawn {
                    self.base_board.half_move_clock += 1; // Pure sjadam moves are never captures
                }
                else {
                    self.base_board.half_move_clock = 0;
                }

                // Sjadam moves never create en passant
                self.base_board.set_en_passant_square(None);
                self.base_board.to_move = !self.base_board.to_move;

            }, // TODO: Figure out if anything else needs to be done here
            Some(chess_move) => {
                self.base_board.do_move(chess_move);
            },
        }
        if (start_color == White && mv.to.rank() == 0)
            || (start_color == Black && mv.to.rank() == 7) {
                debug_assert!(!self.base_board[mv.to].is_empty());
                if self.base_board[mv.to].piece_type() != PieceType::King {
                    self.base_board[mv.to] = Piece::new(PieceType::Queen, start_color);
                }
            }
        debug_assert_ne!(start_color, self.to_move());
        debug_assert!(self.base_board.castling_en_passant & 15 <= undo_move.old_castling_en_passant & 15);
        undo_move
    }

    fn undo_move(&mut self, mv: Self::UndoMove) {
        let start_color = self.to_move();
        match mv.chess_move(self) {
            None => {
                self.base_board.to_move = !self.base_board.to_move;
            },
            Some(chess_move) => self.base_board.undo_move(chess_move),
        }
        self.base_board.half_move_clock = mv.old_half_move_clock;
        self.base_board.castling_en_passant = mv.old_castling_en_passant;

        let color = self.base_board[mv.sjadam_square].color()
            .expect(&format!("Couldn't undo {:?} on\n{:?}", mv, self));
        self.base_board[mv.from] = Piece::new(mv.piece_moved, color);
        // Undo sjadam move
        if mv.from != mv.sjadam_square {
            self.base_board[mv.sjadam_square] = Piece::empty();
        }
        debug_assert_ne!(start_color, self.to_move());

    }

    fn start_board() -> Self {
        SjadamBoard {
            base_board: std_board::ChessBoard::start_board(),
        }
    }

    fn all_legal_moves(&self) -> Vec<Self::Move> {
        let mut cloned_board = self.clone();
        sjadam_move_gen::all_legal_moves(&mut cloned_board)
    }

    fn game_result(&self) -> Option<GameResult> {
        // In sjadam, king may be actually captured.
        // Check if the king is gone
        match self.king_on_board() {
            (true, false) => Some(GameResult::WhiteWin),
            (false, true) => Some(GameResult::BlackWin),
            (false, false) => panic!("Neither side has a king on the board:\n{:?}", self),
            (true, true) => {
                // First check if it is attacked by regular moves
                if !sjadam_move_gen::any_legal_moves(&mut self.clone()) {
                    if move_gen::is_attacked(&self.base_board, move_gen::king_pos(&self.base_board)) {
                        match self.to_move() {
                            Black => Some(GameResult::WhiteWin),
                            White => Some(GameResult::BlackWin),
                        }
                    }
                    else {
                        Some(GameResult::Draw)
                    }
                }
                // The king might still be directly captured by a sjadam move next move,
                // but that is fine
                else {
                    if self.base_board.half_move_clock > 50 {
                        Some(GameResult::Draw)
                    }
                    else { None }
                }
            },
        }
    }
    fn do_random_move<R: rand::Rng>(&mut self, rng: &mut R) {
        let moves = self.all_legal_moves();
        assert!(!moves.is_empty(),
                "Tried to do a random move, but there were no legal moves on {:?}",
                self);
        self.do_move(moves[rng.gen_range(0, moves.len())].clone());
    }
    fn eval_board(&self) -> f32 {
        self.base_board.eval_board()
    }

    fn branch_factor() -> u64 {
        40
    }
}

impl UciBoard for SjadamBoard {
    fn from_fen(input: &str) -> Result<Self, String> {
        Ok(SjadamBoard { base_board: try!(ChessBoard::from_fen(input)) })
    }

    fn to_fen(&self) -> String {
        self.base_board.to_fen()
    }
}

impl SjadamBoard {
    fn king_on_board(&self) -> (bool, bool) {
        let mut white_king = false;
        let mut black_king = false;
        let board = &self.base_board;
        for square in std_board::BoardIter::new() {
            if board[square].piece_type() == PieceType::King {
                match board[square].color().unwrap() {
                    White => white_king = true,
                    Black => black_king = true,
                }
            }
        }
        (white_king, black_king)
    }
}
<commit_msg>Revert "Added error message for unwrap() call" (Halved performance)<commit_after>use board::std_board;
use board::std_board::{ChessBoard, Piece, PieceType, Square};
use board::sjadam_move::{SjadamMove, SjadamUndoMove};
use board::sjadam_move_gen;
use board::std_move_gen::move_gen;

use search_algorithms::board::EvalBoard;
use search_algorithms::board::Color;
use search_algorithms::board::Color::*;
use search_algorithms::board::GameResult;

use rand;
use uci::UciBoard;

#[derive(Debug, Eq, PartialEq, Clone, Hash)]
pub struct SjadamBoard {
    pub base_board: std_board::ChessBoard,
}

impl EvalBoard for SjadamBoard {
    type Move = SjadamMove;
    type UndoMove = SjadamUndoMove;

    fn to_move(&self) -> Color {
        self.base_board.to_move()
    }

    fn do_move(&mut self, mv: Self::Move) -> Self::UndoMove {
        let start_color = self.to_move();
        debug_assert!(mv.from != mv.sjadam_square || mv.sjadam_square != mv.to);

        let undo_move = SjadamUndoMove {
            from: mv.from, sjadam_square: mv.sjadam_square,
            capture: self.base_board[mv.to].piece_type(),
            to: mv.to, piece_moved: self.base_board[mv.from].piece_type(),
            old_castling_en_passant: self.base_board.castling_en_passant,
            old_half_move_clock: self.base_board.half_move_clock };

        // Do sjadam move. Has no effect if there is no sjadam jump
        self.base_board[mv.sjadam_square] = self.base_board[mv.from];
        if mv.from != mv.sjadam_square {
            self.base_board[mv.from] = Piece::empty();
        }

        // Remove castling priviledges on king moves
        if undo_move.piece_moved == PieceType::King {
            let color = self.to_move();
            self.base_board.disable_castling(color);
        }

        // Remove castling priviledges if anything moves from or to a corner

        let a1 = Square::from_alg("a1").unwrap();
        let h1 = Square::from_alg("h1").unwrap();
        let a8 = Square::from_alg("a8").unwrap();
        let h8 = Square::from_alg("h8").unwrap();
        match mv.from {
            square if square == a1 =>
                self.base_board.disable_castling_queenside(White),
            square if square == h1 =>
                self.base_board.disable_castling_kingside(White),
            square if square == a8 =>
                self.base_board.disable_castling_queenside(Black),
            square if square == h8 =>
                self.base_board.disable_castling_kingside(Black),
            _ => (),
        }

        match mv.to {
            square if square == a1 =>
                self.base_board.disable_castling_queenside(White),
            square if square == h1 =>
                self.base_board.disable_castling_kingside(White),
            square if square == a8 =>
                self.base_board.disable_castling_queenside(Black),
            square if square == h8 =>
                self.base_board.disable_castling_kingside(Black),
            _ => (),
        }

        let chess_move = mv.chess_move(self);

        match chess_move {
            None => {
                if undo_move.piece_moved != PieceType::Pawn {
                    self.base_board.half_move_clock += 1; // Pure sjadam moves are never captures
                }
                else {
                    self.base_board.half_move_clock = 0;
                }

                // Sjadam moves never create en passant
                self.base_board.set_en_passant_square(None);
                self.base_board.to_move = !self.base_board.to_move;

            }, // TODO: Figure out if anything else needs to be done here
            Some(chess_move) => {
                self.base_board.do_move(chess_move);
            },
        }
        if (start_color == White && mv.to.rank() == 0)
            || (start_color == Black && mv.to.rank() == 7) {
                debug_assert!(!self.base_board[mv.to].is_empty());
                if self.base_board[mv.to].piece_type() != PieceType::King {
                    self.base_board[mv.to] = Piece::new(PieceType::Queen, start_color);
                }
            }
        debug_assert_ne!(start_color, self.to_move());
        debug_assert!(self.base_board.castling_en_passant & 15 <= undo_move.old_castling_en_passant & 15);
        undo_move
    }

    fn undo_move(&mut self, mv: Self::UndoMove) {
        let start_color = self.to_move();
        match mv.chess_move(self) {
            None => {
                self.base_board.to_move = !self.base_board.to_move;
            },
            Some(chess_move) => self.base_board.undo_move(chess_move),
        }
        self.base_board.half_move_clock = mv.old_half_move_clock;
        self.base_board.castling_en_passant = mv.old_castling_en_passant;

        let color = self.base_board[mv.sjadam_square].color().unwrap();
        self.base_board[mv.from] = Piece::new(mv.piece_moved, color);
        // Undo sjadam move
        if mv.from != mv.sjadam_square {
            self.base_board[mv.sjadam_square] = Piece::empty();
        }
        debug_assert_ne!(start_color, self.to_move());

    }

    fn start_board() -> Self {
        SjadamBoard {
            base_board: std_board::ChessBoard::start_board(),
        }
    }

    fn all_legal_moves(&self) -> Vec<Self::Move> {
        let mut cloned_board = self.clone();
        sjadam_move_gen::all_legal_moves(&mut cloned_board)
    }

    fn game_result(&self) -> Option<GameResult> {
        // In sjadam, king may be actually captured.
        // Check if the king is gone
        match self.king_on_board() {
            (true, false) => Some(GameResult::WhiteWin),
            (false, true) => Some(GameResult::BlackWin),
            (false, false) => panic!("Neither side has a king on the board:\n{:?}", self),
            (true, true) => {
                // First check if it is attacked by regular moves
                if !sjadam_move_gen::any_legal_moves(&mut self.clone()) {
                    if move_gen::is_attacked(&self.base_board, move_gen::king_pos(&self.base_board)) {
                        match self.to_move() {
                            Black => Some(GameResult::WhiteWin),
                            White => Some(GameResult::BlackWin),
                        }
                    }
                    else {
                        Some(GameResult::Draw)
                    }
                }
                // The king might still be directly captured by a sjadam move next move,
                // but that is fine
                else {
                    if self.base_board.half_move_clock > 50 {
                        Some(GameResult::Draw)
                    }
                    else { None }
                }
            },
        }
    }
    fn do_random_move<R: rand::Rng>(&mut self, rng: &mut R) {
        let moves = self.all_legal_moves();
        assert!(!moves.is_empty(),
                "Tried to do a random move, but there were no legal moves on {:?}",
                self);
        self.do_move(moves[rng.gen_range(0, moves.len())].clone());
    }
    fn eval_board(&self) -> f32 {
        self.base_board.eval_board()
    }

    fn branch_factor() -> u64 {
        40
    }
}

impl UciBoard for SjadamBoard {
    fn from_fen(input: &str) -> Result<Self, String> {
        Ok(SjadamBoard { base_board: try!(ChessBoard::from_fen(input)) })
    }

    fn to_fen(&self) -> String {
        self.base_board.to_fen()
    }
}

impl SjadamBoard {
    fn king_on_board(&self) -> (bool, bool) {
        let mut white_king = false;
        let mut black_king = false;
        let board = &self.base_board;
        for square in std_board::BoardIter::new() {
            if board[square].piece_type() == PieceType::King {
                match board[square].color().unwrap() {
                    White => white_king = true,
                    Black => black_king = true,
                }
            }
        }
        (white_king, black_king)
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! This module contains types and functions to support formatting specific line ranges.

use std::collections::HashMap;
use std::path::PathBuf;
use std::rc::Rc;
use std::{cmp, fmt, iter, str};

use serde::ser::{self, Serialize, Serializer};
use serde::de::{Deserialize, Deserializer};
use serde_json as json;

use syntax::codemap::{self, FileMap};

/// A range of lines in a file, inclusive of both ends.
pub struct LineRange {
    pub file: Rc<FileMap>,
    pub lo: usize,
    pub hi: usize,
}

/// Defines the name of an input - either a file or stdin.
#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub enum FileName {
    Real(PathBuf),
    Stdin,
}

impl From<codemap::FileName> for FileName {
    fn from(name: codemap::FileName) -> FileName {
        match name {
            codemap::FileName::Real(p) => FileName::Real(p),
            codemap::FileName::Custom(ref f) if f == "stdin" => FileName::Stdin,
            _ => unreachable!(),
        }
    }
}

impl fmt::Display for FileName {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            FileName::Real(p) => write!(f, "{}", p.to_str().unwrap()),
            FileName::Stdin => write!(f, "stdin"),
        }
    }
}

impl<'de> Deserialize<'de> for FileName {
    fn deserialize<D>(deserializer: D) -> Result<FileName, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;
        if s == "stdin" {
            Ok(FileName::Stdin)
        } else {
            Ok(FileName::Real(s.into()))
        }
    }
}

impl Serialize for FileName {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let s = match self {
            FileName::Stdin => Ok("stdin"),
            FileName::Real(path) => path.to_str().ok_or_else(||
                ser::Error::custom("path can't be serialized as UTF-8 string"))
        };

        s.and_then(|s| serializer.serialize_str(s))
    }
}

impl LineRange {
    pub fn file_name(&self) -> FileName {
        self.file.name.clone().into()
    }
}

/// A range that is inclusive of both ends.
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Deserialize)]
pub struct Range {
    lo: usize,
    hi: usize,
}

impl<'a> From<&'a LineRange> for Range {
    fn from(range: &'a LineRange) -> Range {
        Range::new(range.lo, range.hi)
    }
}

impl Range {
    pub fn new(lo: usize, hi: usize) -> Range {
        Range { lo, hi }
    }

    fn is_empty(self) -> bool {
        self.lo > self.hi
    }

    #[allow(dead_code)]
    fn contains(self, other: Range) -> bool {
        if other.is_empty() {
            true
        } else {
            !self.is_empty() && self.lo <= other.lo && self.hi >= other.hi
        }
    }

    fn intersects(self, other: Range) -> bool {
        if self.is_empty() || other.is_empty() {
            false
        } else {
            (self.lo <= other.hi && other.hi <= self.hi)
                || (other.lo <= self.hi && self.hi <= other.hi)
        }
    }

    fn adjacent_to(self, other: Range) -> bool {
        if self.is_empty() || other.is_empty() {
            false
        } else {
            self.hi + 1 == other.lo || other.hi + 1 == self.lo
        }
    }

    /// Returns a new `Range` with lines from `self` and `other` if they were adjacent or
    /// intersect; returns `None` otherwise.
    fn merge(self, other: Range) -> Option<Range> {
        if self.adjacent_to(other) || self.intersects(other) {
            Some(Range::new(
                cmp::min(self.lo, other.lo),
                cmp::max(self.hi, other.hi),
            ))
        } else {
            None
        }
    }
}

/// A set of lines in files.
///
/// It is represented as a multimap keyed on file names, with values a collection of
/// non-overlapping ranges sorted by their start point. An inner `None` is interpreted to mean all
/// lines in all files.
#[derive(Clone, Debug, Default, PartialEq)]
pub struct FileLines(Option<HashMap<FileName, Vec<Range>>>);

/// Normalizes the ranges so that the invariants for `FileLines` hold: ranges are non-overlapping,
/// and ordered by their start point.
fn normalize_ranges(ranges: &mut HashMap<FileName, Vec<Range>>) {
    for ranges in ranges.values_mut() {
        ranges.sort();
        let mut result = vec![];
        {
            let mut iter = ranges.into_iter().peekable();
            while let Some(next) = iter.next() {
                let mut next = *next;
                while let Some(&&mut peek) = iter.peek() {
                    if let Some(merged) = next.merge(peek) {
                        iter.next().unwrap();
                        next = merged;
                    } else {
                        break;
                    }
                }
                result.push(next)
            }
        }
        *ranges = result;
    }
}

impl FileLines {
    /// Creates a `FileLines` that contains all lines in all files.
    pub(crate) fn all() -> FileLines {
        FileLines(None)
    }

    /// Returns true if this `FileLines` contains all lines in all files.
    pub(crate) fn is_all(&self) -> bool {
        self.0.is_none()
    }

    pub fn from_ranges(mut ranges: HashMap<FileName, Vec<Range>>) -> FileLines {
        normalize_ranges(&mut ranges);
        FileLines(Some(ranges))
    }

    /// Returns an iterator over the files contained in `self`.
    pub fn files(&self) -> Files {
        Files(self.0.as_ref().map(|m| m.keys()))
    }

    /// Returns JSON representation as accepted by the `--file-lines JSON` arg.
    pub fn to_json_spans(&self) -> Vec<JsonSpan> {
        match &self.0 {
            None => vec![],
            Some(file_ranges) => file_ranges
                .iter()
                .flat_map(|(file, ranges)| ranges.iter().map(move |r| (file, r)))
                .map(|(file, range)| JsonSpan {
                    file: file.to_owned(),
                    range: (range.lo, range.hi),
                }).collect(),
        }
    }

    /// Returns true if `self` includes all lines in all files. Otherwise runs `f` on all ranges in
    /// the designated file (if any) and returns true if `f` ever does.
    fn file_range_matches<F>(&self, file_name: &FileName, f: F) -> bool
    where
        F: FnMut(&Range) -> bool,
    {
        let map = match self.0 {
            // `None` means "all lines in all files".
            None => return true,
            Some(ref map) => map,
        };

        match canonicalize_path_string(file_name).and_then(|file| map.get(&file)) {
            Some(ranges) => ranges.iter().any(f),
            None => false,
        }
    }

    /// Returns true if `range` is fully contained in `self`.
    #[allow(dead_code)]
    pub(crate) fn contains(&self, range: &LineRange) -> bool {
        self.file_range_matches(&range.file_name(), |r| r.contains(Range::from(range)))
    }

    /// Returns true if any lines in `range` are in `self`.
    pub(crate) fn intersects(&self, range: &LineRange) -> bool {
        self.file_range_matches(&range.file_name(), |r| r.intersects(Range::from(range)))
    }

    /// Returns true if `line` from `file_name` is in `self`.
    pub(crate) fn contains_line(&self, file_name: &FileName, line: usize) -> bool {
        self.file_range_matches(file_name, |r| r.lo <= line && r.hi >= line)
    }

    /// Returns true if all the lines between `lo` and `hi` from `file_name` are in `self`.
    pub(crate) fn contains_range(&self, file_name: &FileName, lo: usize, hi: usize) -> bool {
        self.file_range_matches(file_name, |r| r.contains(Range::new(lo, hi)))
    }
}

/// `FileLines` files iterator.
pub struct Files<'a>(Option<::std::collections::hash_map::Keys<'a, FileName, Vec<Range>>>);

impl<'a> iter::Iterator for Files<'a> {
    type Item = &'a FileName;

    fn next(&mut self) -> Option<&'a FileName> {
        self.0.as_mut().and_then(Iterator::next)
    }
}

fn canonicalize_path_string(file: &FileName) -> Option<FileName> {
    match *file {
        FileName::Real(ref path) => path.canonicalize().ok().map(FileName::Real),
        _ => Some(file.clone()),
    }
}

// This impl is needed for `Config::override_value` to work for use in tests.
impl str::FromStr for FileLines {
    type Err = String;

    fn from_str(s: &str) -> Result<FileLines, String> {
        let v: Vec<JsonSpan> = json::from_str(s).map_err(|e| e.to_string())?;
        let mut m = HashMap::new();
        for js in v {
            let (s, r) = JsonSpan::into_tuple(js)?;
            m.entry(s).or_insert_with(|| vec![]).push(r);
        }
        Ok(FileLines::from_ranges(m))
    }
}

// For JSON decoding.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct JsonSpan {
    file: FileName,
    range: (usize, usize),
}

impl JsonSpan {
    fn into_tuple(self) -> Result<(FileName, Range), String> {
        let (lo, hi) = self.range;
        let canonical = canonicalize_path_string(&self.file)
            .ok_or_else(|| format!("Can't canonicalize {}", &self.file))?;
        Ok((canonical, Range::new(lo, hi)))
    }
}

// This impl is needed for inclusion in the `Config` struct. We don't have a toml representation
// for `FileLines`, so it will just panic instead.
impl<'de> ::serde::de::Deserialize<'de> for FileLines {
    fn deserialize<D>(_: D) -> Result<Self, D::Error>
    where
        D: ::serde::de::Deserializer<'de>,
    {
        panic!(
            "FileLines cannot be deserialized from a project rustfmt.toml file: please \
             specify it via the `--file-lines` option instead"
        );
    }
}

// We also want to avoid attempting to serialize a FileLines to toml. The
// `Config` struct should ensure this impl is never reached.
impl ::serde::ser::Serialize for FileLines {
    fn serialize<S>(&self, _: S) -> Result<S::Ok, S::Error>
    where
        S: ::serde::ser::Serializer,
    {
        unreachable!("FileLines cannot be serialized. This is a rustfmt bug.");
    }
}

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

    #[test]
    fn test_range_intersects() {
        assert!(Range::new(1, 2).intersects(Range::new(1, 1)));
        assert!(Range::new(1, 2).intersects(Range::new(2, 2)));
        assert!(!Range::new(1, 2).intersects(Range::new(0, 0)));
        assert!(!Range::new(1, 2).intersects(Range::new(3, 10)));
        assert!(!Range::new(1, 3).intersects(Range::new(5, 5)));
    }

    #[test]
    fn test_range_adjacent_to() {
        assert!(!Range::new(1, 2).adjacent_to(Range::new(1, 1)));
        assert!(!Range::new(1, 2).adjacent_to(Range::new(2, 2)));
        assert!(Range::new(1, 2).adjacent_to(Range::new(0, 0)));
        assert!(Range::new(1, 2).adjacent_to(Range::new(3, 10)));
        assert!(!Range::new(1, 3).adjacent_to(Range::new(5, 5)));
    }

    #[test]
    fn test_range_contains() {
        assert!(Range::new(1, 2).contains(Range::new(1, 1)));
        assert!(Range::new(1, 2).contains(Range::new(2, 2)));
        assert!(!Range::new(1, 2).contains(Range::new(0, 0)));
        assert!(!Range::new(1, 2).contains(Range::new(3, 10)));
    }

    #[test]
    fn test_range_merge() {
        assert_eq!(None, Range::new(1, 3).merge(Range::new(5, 5)));
        assert_eq!(None, Range::new(4, 7).merge(Range::new(0, 1)));
        assert_eq!(
            Some(Range::new(3, 7)),
            Range::new(3, 5).merge(Range::new(4, 7))
        );
        assert_eq!(
            Some(Range::new(3, 7)),
            Range::new(3, 5).merge(Range::new(5, 7))
        );
        assert_eq!(
            Some(Range::new(3, 7)),
            Range::new(3, 5).merge(Range::new(6, 7))
        );
        assert_eq!(
            Some(Range::new(3, 7)),
            Range::new(3, 7).merge(Range::new(4, 5))
        );
    }

    use std::{collections::HashMap, path::PathBuf};
    use super::{FileName, FileLines};
    use super::json::{self, json, json_internal};

    #[test]
    fn file_lines_to_json() {
        let ranges: HashMap<FileName, Vec<Range>> = [
            (FileName::Real(PathBuf::from("src/main.rs")), vec![
                Range::new(1, 3),
                Range::new(5, 7)
            ]),
            (FileName::Real(PathBuf::from("src/lib.rs")), vec![
                Range::new(1, 7)
        ])].iter().cloned().collect();

        let file_lines = FileLines::from_ranges(ranges);
        let json = json::to_value(&file_lines.to_json_spans()).unwrap();
        assert_eq!(json, json! {[
            {"file": "src/main.rs", "range": [1, 3]},
            {"file": "src/main.rs", "range": [5, 7]},
            {"file": "src/lib.rs",  "range": [1, 7]},
        ]});
    }
}
<commit_msg>Make file_lines_to_json test deterministic<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! This module contains types and functions to support formatting specific line ranges.

use std::collections::HashMap;
use std::path::PathBuf;
use std::rc::Rc;
use std::{cmp, fmt, iter, str};

use serde::ser::{self, Serialize, Serializer};
use serde::de::{Deserialize, Deserializer};
use serde_json as json;

use syntax::codemap::{self, FileMap};

/// A range of lines in a file, inclusive of both ends.
pub struct LineRange {
    pub file: Rc<FileMap>,
    pub lo: usize,
    pub hi: usize,
}

/// Defines the name of an input - either a file or stdin.
#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub enum FileName {
    Real(PathBuf),
    Stdin,
}

impl From<codemap::FileName> for FileName {
    fn from(name: codemap::FileName) -> FileName {
        match name {
            codemap::FileName::Real(p) => FileName::Real(p),
            codemap::FileName::Custom(ref f) if f == "stdin" => FileName::Stdin,
            _ => unreachable!(),
        }
    }
}

impl fmt::Display for FileName {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            FileName::Real(p) => write!(f, "{}", p.to_str().unwrap()),
            FileName::Stdin => write!(f, "stdin"),
        }
    }
}

impl<'de> Deserialize<'de> for FileName {
    fn deserialize<D>(deserializer: D) -> Result<FileName, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;
        if s == "stdin" {
            Ok(FileName::Stdin)
        } else {
            Ok(FileName::Real(s.into()))
        }
    }
}

impl Serialize for FileName {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let s = match self {
            FileName::Stdin => Ok("stdin"),
            FileName::Real(path) => path.to_str().ok_or_else(||
                ser::Error::custom("path can't be serialized as UTF-8 string"))
        };

        s.and_then(|s| serializer.serialize_str(s))
    }
}

impl LineRange {
    pub fn file_name(&self) -> FileName {
        self.file.name.clone().into()
    }
}

/// A range that is inclusive of both ends.
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Deserialize)]
pub struct Range {
    lo: usize,
    hi: usize,
}

impl<'a> From<&'a LineRange> for Range {
    fn from(range: &'a LineRange) -> Range {
        Range::new(range.lo, range.hi)
    }
}

impl Range {
    pub fn new(lo: usize, hi: usize) -> Range {
        Range { lo, hi }
    }

    fn is_empty(self) -> bool {
        self.lo > self.hi
    }

    #[allow(dead_code)]
    fn contains(self, other: Range) -> bool {
        if other.is_empty() {
            true
        } else {
            !self.is_empty() && self.lo <= other.lo && self.hi >= other.hi
        }
    }

    fn intersects(self, other: Range) -> bool {
        if self.is_empty() || other.is_empty() {
            false
        } else {
            (self.lo <= other.hi && other.hi <= self.hi)
                || (other.lo <= self.hi && self.hi <= other.hi)
        }
    }

    fn adjacent_to(self, other: Range) -> bool {
        if self.is_empty() || other.is_empty() {
            false
        } else {
            self.hi + 1 == other.lo || other.hi + 1 == self.lo
        }
    }

    /// Returns a new `Range` with lines from `self` and `other` if they were adjacent or
    /// intersect; returns `None` otherwise.
    fn merge(self, other: Range) -> Option<Range> {
        if self.adjacent_to(other) || self.intersects(other) {
            Some(Range::new(
                cmp::min(self.lo, other.lo),
                cmp::max(self.hi, other.hi),
            ))
        } else {
            None
        }
    }
}

/// A set of lines in files.
///
/// It is represented as a multimap keyed on file names, with values a collection of
/// non-overlapping ranges sorted by their start point. An inner `None` is interpreted to mean all
/// lines in all files.
#[derive(Clone, Debug, Default, PartialEq)]
pub struct FileLines(Option<HashMap<FileName, Vec<Range>>>);

/// Normalizes the ranges so that the invariants for `FileLines` hold: ranges are non-overlapping,
/// and ordered by their start point.
fn normalize_ranges(ranges: &mut HashMap<FileName, Vec<Range>>) {
    for ranges in ranges.values_mut() {
        ranges.sort();
        let mut result = vec![];
        {
            let mut iter = ranges.into_iter().peekable();
            while let Some(next) = iter.next() {
                let mut next = *next;
                while let Some(&&mut peek) = iter.peek() {
                    if let Some(merged) = next.merge(peek) {
                        iter.next().unwrap();
                        next = merged;
                    } else {
                        break;
                    }
                }
                result.push(next)
            }
        }
        *ranges = result;
    }
}

impl FileLines {
    /// Creates a `FileLines` that contains all lines in all files.
    pub(crate) fn all() -> FileLines {
        FileLines(None)
    }

    /// Returns true if this `FileLines` contains all lines in all files.
    pub(crate) fn is_all(&self) -> bool {
        self.0.is_none()
    }

    pub fn from_ranges(mut ranges: HashMap<FileName, Vec<Range>>) -> FileLines {
        normalize_ranges(&mut ranges);
        FileLines(Some(ranges))
    }

    /// Returns an iterator over the files contained in `self`.
    pub fn files(&self) -> Files {
        Files(self.0.as_ref().map(|m| m.keys()))
    }

    /// Returns JSON representation as accepted by the `--file-lines JSON` arg.
    pub fn to_json_spans(&self) -> Vec<JsonSpan> {
        match &self.0 {
            None => vec![],
            Some(file_ranges) => file_ranges
                .iter()
                .flat_map(|(file, ranges)| ranges.iter().map(move |r| (file, r)))
                .map(|(file, range)| JsonSpan {
                    file: file.to_owned(),
                    range: (range.lo, range.hi),
                }).collect(),
        }
    }

    /// Returns true if `self` includes all lines in all files. Otherwise runs `f` on all ranges in
    /// the designated file (if any) and returns true if `f` ever does.
    fn file_range_matches<F>(&self, file_name: &FileName, f: F) -> bool
    where
        F: FnMut(&Range) -> bool,
    {
        let map = match self.0 {
            // `None` means "all lines in all files".
            None => return true,
            Some(ref map) => map,
        };

        match canonicalize_path_string(file_name).and_then(|file| map.get(&file)) {
            Some(ranges) => ranges.iter().any(f),
            None => false,
        }
    }

    /// Returns true if `range` is fully contained in `self`.
    #[allow(dead_code)]
    pub(crate) fn contains(&self, range: &LineRange) -> bool {
        self.file_range_matches(&range.file_name(), |r| r.contains(Range::from(range)))
    }

    /// Returns true if any lines in `range` are in `self`.
    pub(crate) fn intersects(&self, range: &LineRange) -> bool {
        self.file_range_matches(&range.file_name(), |r| r.intersects(Range::from(range)))
    }

    /// Returns true if `line` from `file_name` is in `self`.
    pub(crate) fn contains_line(&self, file_name: &FileName, line: usize) -> bool {
        self.file_range_matches(file_name, |r| r.lo <= line && r.hi >= line)
    }

    /// Returns true if all the lines between `lo` and `hi` from `file_name` are in `self`.
    pub(crate) fn contains_range(&self, file_name: &FileName, lo: usize, hi: usize) -> bool {
        self.file_range_matches(file_name, |r| r.contains(Range::new(lo, hi)))
    }
}

/// `FileLines` files iterator.
pub struct Files<'a>(Option<::std::collections::hash_map::Keys<'a, FileName, Vec<Range>>>);

impl<'a> iter::Iterator for Files<'a> {
    type Item = &'a FileName;

    fn next(&mut self) -> Option<&'a FileName> {
        self.0.as_mut().and_then(Iterator::next)
    }
}

fn canonicalize_path_string(file: &FileName) -> Option<FileName> {
    match *file {
        FileName::Real(ref path) => path.canonicalize().ok().map(FileName::Real),
        _ => Some(file.clone()),
    }
}

// This impl is needed for `Config::override_value` to work for use in tests.
impl str::FromStr for FileLines {
    type Err = String;

    fn from_str(s: &str) -> Result<FileLines, String> {
        let v: Vec<JsonSpan> = json::from_str(s).map_err(|e| e.to_string())?;
        let mut m = HashMap::new();
        for js in v {
            let (s, r) = JsonSpan::into_tuple(js)?;
            m.entry(s).or_insert_with(|| vec![]).push(r);
        }
        Ok(FileLines::from_ranges(m))
    }
}

// For JSON decoding.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Deserialize, Serialize)]
pub struct JsonSpan {
    file: FileName,
    range: (usize, usize),
}

impl JsonSpan {
    fn into_tuple(self) -> Result<(FileName, Range), String> {
        let (lo, hi) = self.range;
        let canonical = canonicalize_path_string(&self.file)
            .ok_or_else(|| format!("Can't canonicalize {}", &self.file))?;
        Ok((canonical, Range::new(lo, hi)))
    }
}

// This impl is needed for inclusion in the `Config` struct. We don't have a toml representation
// for `FileLines`, so it will just panic instead.
impl<'de> ::serde::de::Deserialize<'de> for FileLines {
    fn deserialize<D>(_: D) -> Result<Self, D::Error>
    where
        D: ::serde::de::Deserializer<'de>,
    {
        panic!(
            "FileLines cannot be deserialized from a project rustfmt.toml file: please \
             specify it via the `--file-lines` option instead"
        );
    }
}

// We also want to avoid attempting to serialize a FileLines to toml. The
// `Config` struct should ensure this impl is never reached.
impl ::serde::ser::Serialize for FileLines {
    fn serialize<S>(&self, _: S) -> Result<S::Ok, S::Error>
    where
        S: ::serde::ser::Serializer,
    {
        unreachable!("FileLines cannot be serialized. This is a rustfmt bug.");
    }
}

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

    #[test]
    fn test_range_intersects() {
        assert!(Range::new(1, 2).intersects(Range::new(1, 1)));
        assert!(Range::new(1, 2).intersects(Range::new(2, 2)));
        assert!(!Range::new(1, 2).intersects(Range::new(0, 0)));
        assert!(!Range::new(1, 2).intersects(Range::new(3, 10)));
        assert!(!Range::new(1, 3).intersects(Range::new(5, 5)));
    }

    #[test]
    fn test_range_adjacent_to() {
        assert!(!Range::new(1, 2).adjacent_to(Range::new(1, 1)));
        assert!(!Range::new(1, 2).adjacent_to(Range::new(2, 2)));
        assert!(Range::new(1, 2).adjacent_to(Range::new(0, 0)));
        assert!(Range::new(1, 2).adjacent_to(Range::new(3, 10)));
        assert!(!Range::new(1, 3).adjacent_to(Range::new(5, 5)));
    }

    #[test]
    fn test_range_contains() {
        assert!(Range::new(1, 2).contains(Range::new(1, 1)));
        assert!(Range::new(1, 2).contains(Range::new(2, 2)));
        assert!(!Range::new(1, 2).contains(Range::new(0, 0)));
        assert!(!Range::new(1, 2).contains(Range::new(3, 10)));
    }

    #[test]
    fn test_range_merge() {
        assert_eq!(None, Range::new(1, 3).merge(Range::new(5, 5)));
        assert_eq!(None, Range::new(4, 7).merge(Range::new(0, 1)));
        assert_eq!(
            Some(Range::new(3, 7)),
            Range::new(3, 5).merge(Range::new(4, 7))
        );
        assert_eq!(
            Some(Range::new(3, 7)),
            Range::new(3, 5).merge(Range::new(5, 7))
        );
        assert_eq!(
            Some(Range::new(3, 7)),
            Range::new(3, 5).merge(Range::new(6, 7))
        );
        assert_eq!(
            Some(Range::new(3, 7)),
            Range::new(3, 7).merge(Range::new(4, 5))
        );
    }

    use std::{collections::HashMap, path::PathBuf};
    use super::{FileName, FileLines};
    use super::json::{self, json, json_internal};

    #[test]
    fn file_lines_to_json() {
        let ranges: HashMap<FileName, Vec<Range>> = [
            (FileName::Real(PathBuf::from("src/main.rs")), vec![
                Range::new(1, 3),
                Range::new(5, 7)
            ]),
            (FileName::Real(PathBuf::from("src/lib.rs")), vec![
                Range::new(1, 7)
        ])].iter().cloned().collect();

        let file_lines = FileLines::from_ranges(ranges);
        let mut spans = file_lines.to_json_spans();
        spans.sort();
        let json = json::to_value(&spans).unwrap();
        assert_eq!(json, json! {[
            {"file": "src/lib.rs",  "range": [1, 7]},
            {"file": "src/main.rs", "range": [1, 3]},
            {"file": "src/main.rs", "range": [5, 7]},
        ]});
    }
}
<|endoftext|>


<commit_before>use to_str::to_str;
use ptr::to_unsafe_ptr;
use comm::{Port, Chan};
use result::{Result, Ok, Err};
use std::net::ip::{
    get_addr, format_addr,
    IpAddr, IpGetAddrErr, Ipv4, Ipv6
};
use std::net::tcp::{connect, TcpSocket};
use std::net::url;
use std::net::url::Url;
use std::uv_global_loop;
use connection::{
    Connection, ConnectionFactory, UvConnectionFactory,
    MockConnection, MockConnectionFactory
};
use parser::{Parser, ParserCallbacks};
use request::build_request;

pub const timeout: uint = 2000;

/// HTTP status codes
pub enum StatusCode {
    StatusOk = 200,
    StatusFound = 302,
    StatusUnknown
}

/// HTTP request error conditions
pub enum RequestError {
    ErrorDnsResolution,
    ErrorConnect,
    ErrorMisc
}

/// Request
pub enum RequestEvent {
    Status(StatusCode),
    Payload(~mut Option<~[u8]>),
    Error(RequestError)
}

impl StatusCode: cmp::Eq {
    pure fn eq(other: &StatusCode) -> bool {
        self as uint == (*other) as uint
    }
    pure fn ne(other: &StatusCode) -> bool {
        self as uint != (*other) as uint
    }
}

impl RequestError: cmp::Eq {
    pure fn eq(other: &RequestError) -> bool {
        self as uint == (*other) as uint
    }
    pure fn ne(other: &RequestError) -> bool {
        self as uint != (*other) as uint
    }
}

impl RequestEvent: cmp::Eq {
    pure fn eq(other: &RequestEvent) -> bool {
        // FIXME: bad copy
        match (copy self, copy *other) {
          (Status(a), Status(b)) => a == b,
          (Payload(a), Payload(b)) => a == b,
          (Error(a), Error(b)) => a == b,

          (Status(*), _)
          | (Payload(*), _)
          | (Error(*), _) => false
        }
    }
    pure fn ne(other: &RequestEvent) -> bool {
        !self.eq(other)
    }
}

pub type DnsResolver = fn@(host: ~str) -> Result<~[IpAddr], IpGetAddrErr>;

pub fn uv_dns_resolver() -> DnsResolver {
    |host: ~str| {
        let iotask = uv_global_loop::get();
        get_addr(host.to_str(), iotask)
    }
}

pub fn uv_http_request(url: Url) -> HttpRequest<TcpSocket, UvConnectionFactory> {
    HttpRequest(uv_dns_resolver(), UvConnectionFactory, move url)
}

#[allow(non_implicitly_copyable_typarams)]
pub struct HttpRequest<C: Connection, CF: ConnectionFactory<C>> {
    resolve_ip_addr: DnsResolver,
    connection_factory: CF,
    url: Url,
    parser: Parser,
    mut cb: fn@(+ev: RequestEvent)
}

pub fn HttpRequest<C: Connection, CF: ConnectionFactory<C>>(resolver: DnsResolver,
                                                            connection_factory: CF,
                                                            url: Url) ->
                                                            HttpRequest<C,CF> {
    HttpRequest {
        resolve_ip_addr: move resolver,
        connection_factory: move connection_factory,
        url: move url,
        parser: move Parser(),
        cb: |_event| { }
    }
}

#[allow(non_implicitly_copyable_typarams)]
impl<C: Connection, CF: ConnectionFactory<C>> HttpRequest<C, CF> {
    fn begin(cb: fn@(ev: RequestEvent)) {
        #debug("http_client: looking up url %?", self.url.to_str());
        let ip_addr = match self.get_ip() {
          Ok(addr) => { copy addr }
          Err(e) => { cb(Error(e)); return }
        };

        #debug("http_client: using IP %? for %?", format_addr(&ip_addr), self.url.to_str());

        let socket = {
            #debug("http_client: connecting to %?", ip_addr);
            let socket = self.connection_factory.connect(copy ip_addr, 80);
            if socket.is_ok() {
                result::unwrap(move socket)
            } else {
                #debug("http_client: unable to connect to %?: %?", ip_addr, socket);
                cb(Error(ErrorConnect));
                return;
            }
        };

        #debug("http_client: got socket for %?", ip_addr);

        let request_header = build_request(copy self.url);
        #debug("http_client: writing request header: %?", request_header);
        let request_header_bytes = str::to_bytes(request_header);
        match socket.write_(move request_header_bytes) {
          result::Ok(*) => { }
          result::Err(*) => {
            // FIXME: Need test
            cb(Error(ErrorMisc));
            return;
          }
        }

        let read_port = {
            let read_port = socket.read_start_();
            if read_port.is_ok() {
                result::unwrap(move read_port)
            } else {
                cb(Error(ErrorMisc));
                return;
            }
        };

        // This unsafety is unfortunate but we can't capture self
        // into shared closures
        let unsafe_self = to_unsafe_ptr(&self);
        let callbacks: ParserCallbacks = {
            on_message_begin: || unsafe { (*unsafe_self).on_message_begin() },
            on_url: |data| unsafe { (*unsafe_self).on_url(move data) },
            on_header_field: |data| unsafe { (*unsafe_self).on_header_field(move data) },
            on_header_value: |data| unsafe { (*unsafe_self).on_header_value(move data) },
            on_headers_complete: || unsafe { (*unsafe_self).on_headers_complete() },
            on_body: |data| unsafe { (*unsafe_self).on_body(move data) },
            on_message_complete: || unsafe { (*unsafe_self).on_message_complete() }
        };

        // Set the callback used by the parser event handlers
        self.cb = cb;

        loop {
            let next_data = read_port.recv();

            if next_data.is_ok() {
                let next_data = result::unwrap(move next_data);
                #debug("data: %?", next_data);
                let bytes_parsed = self.parser.execute(next_data, &callbacks);
                if bytes_parsed != next_data.len() {
                    // FIXME: Need tests
                    fail ~"http parse failure";
                }
            } else {
                #debug("http_client: read error: %?", next_data);

                // This method of detecting EOF is lame
                match next_data {
                  result::Err({err_name: ~"EOF", _}) => {
                    self.parser.execute(~[], &callbacks);
                    break;
                  }
                  _ => {
                    // FIXME: Need tests and error handling
                    socket.read_stop_(read_port);
                    cb(Error(ErrorMisc));
                    return;
                  }
                }
            }
        }
        socket.read_stop_(read_port);
    }

    fn get_ip() -> Result<IpAddr, RequestError> {
        let ip_addrs = self.resolve_ip_addr(copy self.url.host);
        if ip_addrs.is_ok() {
            let ip_addrs = result::unwrap(move ip_addrs);
            // FIXME: This log crashes
            //#debug("http_client: got IP addresses for %?: %?", self.url, ip_addrs);
            if ip_addrs.is_not_empty() {
                // FIXME: Which address should we really pick?
                let best_ip = do ip_addrs.find |ip| {
                    match ip {
                      Ipv4(*) => { true }
                      Ipv6(*) => { false }
                    }
                };

                if best_ip.is_some() {
                    return Ok(option::unwrap(best_ip));
                } else {
                    // FIXME: Need test
                    return Err(ErrorMisc);
                }
            } else {
                #debug("http_client: got no IP addresses for %?", self.url);
                // FIXME: Need test
                return Err(ErrorMisc);
            }
        } else {
            #debug("http_client: DNS lookup failure: %?", ip_addrs.get_err());
            return Err(ErrorDnsResolution);
        }
    }

    fn on_message_begin() -> bool {
        #debug("on_message_begin");
        true
    }

    fn on_url(_data: ~[u8]) -> bool {
        #debug("on_url");
        true
    }

    fn on_header_field(data: ~[u8]) -> bool {
        let header_field = str::from_bytes(data);
        #debug("on_header_field: %?", header_field);
        true
    }

    fn on_header_value(data: ~[u8]) -> bool {
        let header_value = str::from_bytes(data);
        #debug("on_header_value: %?", header_value);
        true
    }

    fn on_headers_complete() -> bool {
        #debug("on_headers_complete");
        true
    }

    fn on_body(data: ~[u8]) -> bool {
        #debug("on_body");
        let the_payload = Payload(~mut Some(move data));
        self.cb(move the_payload);
        true
    }

    fn on_message_complete() -> bool {
        #debug("on_message_complete");
        true
    }
}

#[allow(non_implicitly_copyable_typarams)]
pub fn sequence<C: Connection, CF: ConnectionFactory<C>>(request: HttpRequest<C, CF>) ->
    ~[RequestEvent] {

    let events = @mut ~[];
    do request.begin |event| {
        vec::push(&mut *events, move event)
    }
    return copy *events;
}

#[test]
#[allow(non_implicitly_copyable_typarams)]
pub fn test_resolve_error() {
    let url = url::from_str(~"http://example.com_not_real/").get();
    let request = uv_http_request(url);
    let events = sequence(request);

    assert events == ~[
        Error(ErrorDnsResolution),
    ];
}

#[test]
#[allow(non_implicitly_copyable_typarams)]
pub fn test_connect_error() {
    // This address is invalid because the first octet
    // of a class A address cannot be 0
    let url = url::from_str(~"http://0.42.42.42/").get();
    let request = uv_http_request(url);
    let events = sequence(request);

    assert events == ~[
        Error(ErrorConnect),
    ];
}

#[test]
#[allow(non_implicitly_copyable_typarams)]
pub fn test_connect_success() {
    let url = url::from_str(~"http://example.com/").get();
    let request = uv_http_request(url);
    let events = sequence(request);

    for events.each |ev| {
        match *ev {
          Error(*) => { fail }
          _ => { }
        }
    }
}

#[test]
#[allow(non_implicitly_copyable_typarams)]
pub fn test_simple_body() {
    let url = url::from_str(~"http://www.iana.org/").get();
    let request = uv_http_request(url);
    let events = sequence(request);

    let mut found = false;

    for events.each |ev| {
        match *ev {
          Payload(value) => {
            if str::from_bytes(value.get()).contains(~"DOCTYPE html") {
                found = true
            }
          }
          _ => { }
        }
    }

    assert found;
}

#[test]
#[ignore(reason = "ICE")]
#[allow(non_implicitly_copyable_typarams)]
pub fn test_simple_response() {
    let _url = url::from_str(~"http://whatever/").get();
    let _mock_connection: MockConnection = {
        write_fn: |_data| { Ok(()) },
        read_start_fn: || {
            let port = Port();
            let chan = port.chan();

            let response = ~"HTTP/1.0 200 OK\
                            \
                            Test";
            chan.send(Ok(str::to_bytes(response)));

            Ok(port)
        },
        read_stop_fn: |_port| { Ok(()) }
    };

    let _mock_connection_factory: MockConnectionFactory = {
        connect_fn: |_ip, _port| {

            // FIXME this doesn't work
            fail;//ok(mock_connection)
        }
    };
}
<commit_msg>Explicit self Eq<commit_after>use to_str::to_str;
use ptr::to_unsafe_ptr;
use comm::{Port, Chan};
use result::{Result, Ok, Err};
use std::net::ip::{
    get_addr, format_addr,
    IpAddr, IpGetAddrErr, Ipv4, Ipv6
};
use std::net::tcp::{connect, TcpSocket};
use std::net::url;
use std::net::url::Url;
use std::uv_global_loop;
use connection::{
    Connection, ConnectionFactory, UvConnectionFactory,
    MockConnection, MockConnectionFactory
};
use parser::{Parser, ParserCallbacks};
use request::build_request;

pub const timeout: uint = 2000;

/// HTTP status codes
pub enum StatusCode {
    StatusOk = 200,
    StatusFound = 302,
    StatusUnknown
}

/// HTTP request error conditions
pub enum RequestError {
    ErrorDnsResolution,
    ErrorConnect,
    ErrorMisc
}

/// Request
pub enum RequestEvent {
    Status(StatusCode),
    Payload(~mut Option<~[u8]>),
    Error(RequestError)
}

impl StatusCode: cmp::Eq {
    pure fn eq(&self, other: &StatusCode) -> bool {
        (*self) as uint == (*other) as uint
    }
    pure fn ne(&self, other: &StatusCode) -> bool {
        (*self) as uint != (*other) as uint
    }
}

impl RequestError: cmp::Eq {
    pure fn eq(&self, other: &RequestError) -> bool {
        (*self) as uint == (*other) as uint
    }
    pure fn ne(&self, other: &RequestError) -> bool {
        (*self) as uint != (*other) as uint
    }
}

impl RequestEvent: cmp::Eq {
    pure fn eq(&self, other: &RequestEvent) -> bool {
        match (self, other) {
          (&Status(a), &Status(b)) => a == b,
          (&Payload(a), &Payload(b)) => a == b,
          (&Error(a), &Error(b)) => a == b,

          (&Status(*), _)
          | (&Payload(*), _)
          | (&Error(*), _) => false
        }
    }
    pure fn ne(&self, other: &RequestEvent) -> bool {
        !self.eq(other)
    }
}

pub type DnsResolver = fn@(host: ~str) -> Result<~[IpAddr], IpGetAddrErr>;

pub fn uv_dns_resolver() -> DnsResolver {
    |host: ~str| {
        let iotask = uv_global_loop::get();
        get_addr(host.to_str(), iotask)
    }
}

pub fn uv_http_request(url: Url) -> HttpRequest<TcpSocket, UvConnectionFactory> {
    HttpRequest(uv_dns_resolver(), UvConnectionFactory, move url)
}

#[allow(non_implicitly_copyable_typarams)]
pub struct HttpRequest<C: Connection, CF: ConnectionFactory<C>> {
    resolve_ip_addr: DnsResolver,
    connection_factory: CF,
    url: Url,
    parser: Parser,
    mut cb: fn@(+ev: RequestEvent)
}

pub fn HttpRequest<C: Connection, CF: ConnectionFactory<C>>(resolver: DnsResolver,
                                                            connection_factory: CF,
                                                            url: Url) ->
                                                            HttpRequest<C,CF> {
    HttpRequest {
        resolve_ip_addr: move resolver,
        connection_factory: move connection_factory,
        url: move url,
        parser: move Parser(),
        cb: |_event| { }
    }
}

#[allow(non_implicitly_copyable_typarams)]
impl<C: Connection, CF: ConnectionFactory<C>> HttpRequest<C, CF> {
    fn begin(cb: fn@(ev: RequestEvent)) {
        #debug("http_client: looking up url %?", self.url.to_str());
        let ip_addr = match self.get_ip() {
          Ok(addr) => { copy addr }
          Err(e) => { cb(Error(e)); return }
        };

        #debug("http_client: using IP %? for %?", format_addr(&ip_addr), self.url.to_str());

        let socket = {
            #debug("http_client: connecting to %?", ip_addr);
            let socket = self.connection_factory.connect(copy ip_addr, 80);
            if socket.is_ok() {
                result::unwrap(move socket)
            } else {
                #debug("http_client: unable to connect to %?: %?", ip_addr, socket);
                cb(Error(ErrorConnect));
                return;
            }
        };

        #debug("http_client: got socket for %?", ip_addr);

        let request_header = build_request(copy self.url);
        #debug("http_client: writing request header: %?", request_header);
        let request_header_bytes = str::to_bytes(request_header);
        match socket.write_(move request_header_bytes) {
          result::Ok(*) => { }
          result::Err(*) => {
            // FIXME: Need test
            cb(Error(ErrorMisc));
            return;
          }
        }

        let read_port = {
            let read_port = socket.read_start_();
            if read_port.is_ok() {
                result::unwrap(move read_port)
            } else {
                cb(Error(ErrorMisc));
                return;
            }
        };

        // This unsafety is unfortunate but we can't capture self
        // into shared closures
        let unsafe_self = to_unsafe_ptr(&self);
        let callbacks: ParserCallbacks = {
            on_message_begin: || unsafe { (*unsafe_self).on_message_begin() },
            on_url: |data| unsafe { (*unsafe_self).on_url(move data) },
            on_header_field: |data| unsafe { (*unsafe_self).on_header_field(move data) },
            on_header_value: |data| unsafe { (*unsafe_self).on_header_value(move data) },
            on_headers_complete: || unsafe { (*unsafe_self).on_headers_complete() },
            on_body: |data| unsafe { (*unsafe_self).on_body(move data) },
            on_message_complete: || unsafe { (*unsafe_self).on_message_complete() }
        };

        // Set the callback used by the parser event handlers
        self.cb = cb;

        loop {
            let next_data = read_port.recv();

            if next_data.is_ok() {
                let next_data = result::unwrap(move next_data);
                #debug("data: %?", next_data);
                let bytes_parsed = self.parser.execute(next_data, &callbacks);
                if bytes_parsed != next_data.len() {
                    // FIXME: Need tests
                    fail ~"http parse failure";
                }
            } else {
                #debug("http_client: read error: %?", next_data);

                // This method of detecting EOF is lame
                match next_data {
                  result::Err({err_name: ~"EOF", _}) => {
                    self.parser.execute(~[], &callbacks);
                    break;
                  }
                  _ => {
                    // FIXME: Need tests and error handling
                    socket.read_stop_(read_port);
                    cb(Error(ErrorMisc));
                    return;
                  }
                }
            }
        }
        socket.read_stop_(read_port);
    }

    fn get_ip() -> Result<IpAddr, RequestError> {
        let ip_addrs = self.resolve_ip_addr(copy self.url.host);
        if ip_addrs.is_ok() {
            let ip_addrs = result::unwrap(move ip_addrs);
            // FIXME: This log crashes
            //#debug("http_client: got IP addresses for %?: %?", self.url, ip_addrs);
            if ip_addrs.is_not_empty() {
                // FIXME: Which address should we really pick?
                let best_ip = do ip_addrs.find |ip| {
                    match ip {
                      Ipv4(*) => { true }
                      Ipv6(*) => { false }
                    }
                };

                if best_ip.is_some() {
                    return Ok(option::unwrap(best_ip));
                } else {
                    // FIXME: Need test
                    return Err(ErrorMisc);
                }
            } else {
                #debug("http_client: got no IP addresses for %?", self.url);
                // FIXME: Need test
                return Err(ErrorMisc);
            }
        } else {
            #debug("http_client: DNS lookup failure: %?", ip_addrs.get_err());
            return Err(ErrorDnsResolution);
        }
    }

    fn on_message_begin() -> bool {
        #debug("on_message_begin");
        true
    }

    fn on_url(_data: ~[u8]) -> bool {
        #debug("on_url");
        true
    }

    fn on_header_field(data: ~[u8]) -> bool {
        let header_field = str::from_bytes(data);
        #debug("on_header_field: %?", header_field);
        true
    }

    fn on_header_value(data: ~[u8]) -> bool {
        let header_value = str::from_bytes(data);
        #debug("on_header_value: %?", header_value);
        true
    }

    fn on_headers_complete() -> bool {
        #debug("on_headers_complete");
        true
    }

    fn on_body(data: ~[u8]) -> bool {
        #debug("on_body");
        let the_payload = Payload(~mut Some(move data));
        self.cb(move the_payload);
        true
    }

    fn on_message_complete() -> bool {
        #debug("on_message_complete");
        true
    }
}

#[allow(non_implicitly_copyable_typarams)]
pub fn sequence<C: Connection, CF: ConnectionFactory<C>>(request: HttpRequest<C, CF>) ->
    ~[RequestEvent] {

    let events = @mut ~[];
    do request.begin |event| {
        vec::push(&mut *events, move event)
    }
    return copy *events;
}

#[test]
#[allow(non_implicitly_copyable_typarams)]
pub fn test_resolve_error() {
    let url = url::from_str(~"http://example.com_not_real/").get();
    let request = uv_http_request(url);
    let events = sequence(request);

    assert events == ~[
        Error(ErrorDnsResolution),
    ];
}

#[test]
#[allow(non_implicitly_copyable_typarams)]
pub fn test_connect_error() {
    // This address is invalid because the first octet
    // of a class A address cannot be 0
    let url = url::from_str(~"http://0.42.42.42/").get();
    let request = uv_http_request(url);
    let events = sequence(request);

    assert events == ~[
        Error(ErrorConnect),
    ];
}

#[test]
#[allow(non_implicitly_copyable_typarams)]
pub fn test_connect_success() {
    let url = url::from_str(~"http://example.com/").get();
    let request = uv_http_request(url);
    let events = sequence(request);

    for events.each |ev| {
        match *ev {
          Error(*) => { fail }
          _ => { }
        }
    }
}

#[test]
#[allow(non_implicitly_copyable_typarams)]
pub fn test_simple_body() {
    let url = url::from_str(~"http://www.iana.org/").get();
    let request = uv_http_request(url);
    let events = sequence(request);

    let mut found = false;

    for events.each |ev| {
        match *ev {
          Payload(value) => {
            if str::from_bytes(value.get()).contains(~"DOCTYPE html") {
                found = true
            }
          }
          _ => { }
        }
    }

    assert found;
}

#[test]
#[ignore(reason = "ICE")]
#[allow(non_implicitly_copyable_typarams)]
pub fn test_simple_response() {
    let _url = url::from_str(~"http://whatever/").get();
    let _mock_connection: MockConnection = {
        write_fn: |_data| { Ok(()) },
        read_start_fn: || {
            let port = Port();
            let chan = port.chan();

            let response = ~"HTTP/1.0 200 OK\
                            \
                            Test";
            chan.send(Ok(str::to_bytes(response)));

            Ok(port)
        },
        read_stop_fn: |_port| { Ok(()) }
    };

    let _mock_connection_factory: MockConnectionFactory = {
        connect_fn: |_ip, _port| {

            // FIXME this doesn't work
            fail;//ok(mock_connection)
        }
    };
}
<|endoftext|>


<commit_before>use std::ptr;
use std::ffi::CStr;
use llvm::core::*;
use llvm::prelude::*;
use libc;

use ast::*;
use codegen::*;
use compileerror::*;

pub unsafe fn type_name(tr: LLVMTypeRef) -> String
{
    let n = LLVMPrintTypeToString(tr);
    let name = CStr::from_ptr(n).to_str().expect("Invalid C String").to_owned();
    LLVMDisposeMessage(n);
    name
}

#[allow(unused_variables)]
fn gen_import(ctx: &mut Context, import: &Import) -> Result<(), CompileError>
{
     err(Pos::new(0, 0), ErrorType::UnexpectedEOF)
}

unsafe fn gen_variable(ctx: &mut Context, v: &Variable) -> Result<(), CompileError>
{
    if ctx.has_variable(&v.name) {
        return err(v.span.start, ErrorType::RedefinitionOfVariable(v.name.clone()));
    }

    let initial_value = try!(gen_expression(ctx, &v.init));
    let initial_value_type = LLVMTypeOf(initial_value);

    if let Some(ref tr) = v.typ {
        if let Some(llvm_type_ref) = ctx.resolve_type(&tr) {
            if llvm_type_ref != initial_value_type {
                return err(v.span.start, ErrorType::TypeError(format!("Mismatched types in initialization")))
            }
        } else {
            return err(v.span.start, ErrorType::TypeError(format!("Unknown type '{}'", tr)));
        }
    }

    let var = LLVMBuildAlloca(ctx.builder, initial_value_type, cstr("var"));
    LLVMBuildStore(ctx.builder, initial_value, var);

    let mut sf = ctx.top_stack_frame();
    sf.add_variable(&v.name, var, v.is_const);
    Ok(())
}

unsafe fn gen_function_sig(ctx: &mut Context, sig: &FunctionSignature, span: &Span) -> Result<(LLVMValueRef, LLVMTypeRef, Vec<LLVMTypeRef>), CompileError>
{
    let ret_type = try!(ctx
        .resolve_type(&sig.return_type)
        .ok_or(CompileError::new(span.start, ErrorType::TypeError(format!("Cannot resolve the return type of function '{}'", sig.name)))));

    let mut arg_types = Vec::new();
    for arg in &sig.args {
        let arg_type = try!(ctx
            .resolve_type(&arg.typ)
            .ok_or(CompileError::new(arg.span.start, ErrorType::TypeError(format!("Cannot resolve the type of argument '{}'", arg.name)))));
        arg_types.push(arg_type);
    }

    let function_type = LLVMFunctionType(ret_type, arg_types.as_mut_ptr(), arg_types.len() as libc::c_uint, 0);
    let function = LLVMAddFunction(ctx.module, cstr(&sig.name), function_type);
    Ok((function, ret_type, arg_types))
}

unsafe fn gen_function(ctx: &mut Context, f: &Function) -> Result<(), CompileError>
{
    if ctx.has_function(&f.sig.name) {
        return err(f.span.start, ErrorType::RedefinitionOfFunction(f.sig.name.clone()));
    }

    let (function, ret_type, arg_types) = try!(gen_function_sig(ctx, &f.sig, &f.span));
    let bb = LLVMAppendBasicBlockInContext(ctx.context, function, cstr("entry"));
    LLVMPositionBuilderAtEnd(ctx.builder, bb);

    ctx.top_stack_frame().add_function(FunctionInstance{
        name: f.sig.name.clone(),
        args: arg_types.clone(),
        return_type: ret_type,
        function: function,
    });

    ctx.push_stack_frame(function, bb);

    for (i, arg) in f.sig.args.iter().enumerate() {
        let var = LLVMGetParam(function, i as libc::c_uint);
        let alloc = LLVMBuildAlloca(ctx.builder, arg_types[i], cstr("argtmp"));
        LLVMBuildStore(ctx.builder, var, alloc);
        ctx.top_stack_frame().add_variable(&arg.name, alloc, arg.constant);
    }

    for s in &f.block.statements {
        try!(gen_statement(ctx, s));
    }

    if f.sig.return_type == Type::Void {
        LLVMBuildRetVoid(ctx.builder);
    }

    ctx.pop_stack_frame();
    LLVMPositionBuilderAtEnd(ctx.builder, ctx.top_stack_frame().get_current_bb());
    Ok(())
}

#[allow(unused_variables)]
unsafe fn gen_external_function(ctx: &mut Context, f: &ExternalFunction) -> Result<(), CompileError>
{
    let (function, ret_type, arg_types) = try!(gen_function_sig(ctx, &f.sig, &f.span));

    ctx.top_stack_frame().add_function(FunctionInstance{
        name: f.sig.name.clone(),
        args: arg_types.clone(),
        return_type: ret_type,
        function: function,
    });
    Ok(())
}

#[allow(unused_variables)]
fn gen_while(ctx: &mut Context, f: &While) -> Result<(), CompileError>
{
     err(Pos::new(0, 0), ErrorType::UnexpectedEOF)
}

#[allow(unused_variables)]
fn gen_if(ctx: &mut Context, f: &If) -> Result<(), CompileError>
{
     err(Pos::new(0, 0), ErrorType::UnexpectedEOF)
}

unsafe fn gen_return(ctx: &mut Context, f: &Return) -> Result<(), CompileError>
{
    let ret = try!(gen_expression(ctx, &f.expr));
    let builder = ctx.builder;
    let sf = ctx.top_stack_frame();
    let ret_type =  LLVMTypeOf(ret);
    let func_type = sf.return_type();
    if ret_type != func_type {
        err(f.span.start, ErrorType::TypeError(
            format!("Attempting to return type '{}' expecting '{}'", type_name(ret_type), type_name(func_type))))
    } else {
        LLVMBuildRet(builder, ret);
        Ok(())
    }
}

#[allow(unused_variables)]
fn gen_struct(ctx: &mut Context, f: &Struct) -> Result<(), CompileError>
{
     err(Pos::new(0, 0), ErrorType::UnexpectedEOF)
}

#[allow(unused_variables)]
fn gen_union(ctx: &mut Context, f: &Union) -> Result<(), CompileError>
{
     err(Pos::new(0, 0), ErrorType::UnexpectedEOF)
}

#[allow(unused_variables)]
fn gen_match(ctx: &mut Context, f: &Match) -> Result<(), CompileError>
{
     err(Pos::new(0, 0), ErrorType::UnexpectedEOF)
}

unsafe fn gen_statement(ctx: &mut Context, stmt: &Statement) -> Result<(), CompileError>
{
    match *stmt {
        Statement::Import(ref i) => gen_import(ctx, i),
        Statement::Variable(ref vars) => {
            for v in vars {
                try!(gen_variable(ctx, v))
            }
            Ok(())
        },
        Statement::Function(ref fun) => gen_function(ctx, fun),
        Statement::ExternalFunction(ref fun) => gen_external_function(ctx, fun),
        Statement::While(ref w) => gen_while(ctx, w),
        Statement::If(ref i) => gen_if(ctx, i),
        Statement::Return(ref r) => gen_return(ctx, r),
        Statement::Struct(ref s) => gen_struct(ctx, s),
        Statement::Union(ref u) => gen_union(ctx, u),
        Statement::Match(ref m) => gen_match(ctx, m),
        Statement::Expression(ref e) => gen_expression(ctx, e).map(|_| ()),
    }
}

pub unsafe fn gen_program(ctx: &mut Context, prog: &Program) -> Result<(), CompileError>
{
    let main_ret_type = LLVMInt64TypeInContext(ctx.context);
    let function_type = LLVMFunctionType(main_ret_type, ptr::null_mut(), 0, 0);
    let function = LLVMAddFunction(ctx.module, cstr("main"), function_type);
    let bb = LLVMAppendBasicBlockInContext(ctx.context, function, cstr("entry"));
    LLVMPositionBuilderAtEnd(ctx.builder, bb);

    ctx.push_stack_frame(function, bb);
    for s in &prog.block.statements {
        try!(gen_statement(ctx, s));
    }
    Ok(())
}
<commit_msg>Unduplicate function adding to context code<commit_after>use std::ptr;
use std::ffi::CStr;
use llvm::core::*;
use llvm::prelude::*;
use libc;

use ast::*;
use codegen::*;
use compileerror::*;

pub unsafe fn type_name(tr: LLVMTypeRef) -> String
{
    let n = LLVMPrintTypeToString(tr);
    let name = CStr::from_ptr(n).to_str().expect("Invalid C String").to_owned();
    LLVMDisposeMessage(n);
    name
}

#[allow(unused_variables)]
fn gen_import(ctx: &mut Context, import: &Import) -> Result<(), CompileError>
{
     err(Pos::new(0, 0), ErrorType::UnexpectedEOF)
}

unsafe fn gen_variable(ctx: &mut Context, v: &Variable) -> Result<(), CompileError>
{
    if ctx.has_variable(&v.name) {
        return err(v.span.start, ErrorType::RedefinitionOfVariable(v.name.clone()));
    }

    let initial_value = try!(gen_expression(ctx, &v.init));
    let initial_value_type = LLVMTypeOf(initial_value);

    if let Some(ref tr) = v.typ {
        if let Some(llvm_type_ref) = ctx.resolve_type(&tr) {
            if llvm_type_ref != initial_value_type {
                return err(v.span.start, ErrorType::TypeError(format!("Mismatched types in initialization")))
            }
        } else {
            return err(v.span.start, ErrorType::TypeError(format!("Unknown type '{}'", tr)));
        }
    }

    let var = LLVMBuildAlloca(ctx.builder, initial_value_type, cstr("var"));
    LLVMBuildStore(ctx.builder, initial_value, var);

    let mut sf = ctx.top_stack_frame();
    sf.add_variable(&v.name, var, v.is_const);
    Ok(())
}

unsafe fn gen_function_sig(ctx: &mut Context, sig: &FunctionSignature, span: &Span) -> Result<(LLVMValueRef, Vec<LLVMTypeRef>), CompileError>
{
    let ret_type = try!(ctx
        .resolve_type(&sig.return_type)
        .ok_or(CompileError::new(span.start, ErrorType::TypeError(format!("Cannot resolve the return type of function '{}'", sig.name)))));

    let mut arg_types = Vec::new();
    for arg in &sig.args {
        let arg_type = try!(ctx
            .resolve_type(&arg.typ)
            .ok_or(CompileError::new(arg.span.start, ErrorType::TypeError(format!("Cannot resolve the type of argument '{}'", arg.name)))));
        arg_types.push(arg_type);
    }

    let function_type = LLVMFunctionType(ret_type, arg_types.as_mut_ptr(), arg_types.len() as libc::c_uint, 0);
    let function = LLVMAddFunction(ctx.module, cstr(&sig.name), function_type);

    ctx.top_stack_frame().add_function(FunctionInstance{
        name: sig.name.clone(),
        args: arg_types.clone(),
        return_type: ret_type,
        function: function,
    });

    Ok((function, arg_types))
}

unsafe fn gen_function(ctx: &mut Context, f: &Function) -> Result<(), CompileError>
{
    if ctx.has_function(&f.sig.name) {
        return err(f.span.start, ErrorType::RedefinitionOfFunction(f.sig.name.clone()));
    }

    let (function, arg_types) = try!(gen_function_sig(ctx, &f.sig, &f.span));
    let bb = LLVMAppendBasicBlockInContext(ctx.context, function, cstr("entry"));
    LLVMPositionBuilderAtEnd(ctx.builder, bb);

    ctx.push_stack_frame(function, bb);

    for (i, arg) in f.sig.args.iter().enumerate() {
        let var = LLVMGetParam(function, i as libc::c_uint);
        let alloc = LLVMBuildAlloca(ctx.builder, arg_types[i], cstr("argtmp"));
        LLVMBuildStore(ctx.builder, var, alloc);
        ctx.top_stack_frame().add_variable(&arg.name, alloc, arg.constant);
    }

    for s in &f.block.statements {
        try!(gen_statement(ctx, s));
    }

    if f.sig.return_type == Type::Void {
        LLVMBuildRetVoid(ctx.builder);
    }

    ctx.pop_stack_frame();
    LLVMPositionBuilderAtEnd(ctx.builder, ctx.top_stack_frame().get_current_bb());
    Ok(())
}

#[allow(unused_variables)]
unsafe fn gen_external_function(ctx: &mut Context, f: &ExternalFunction) -> Result<(), CompileError>
{
    let _ = try!(gen_function_sig(ctx, &f.sig, &f.span));
    Ok(())
}

#[allow(unused_variables)]
fn gen_while(ctx: &mut Context, f: &While) -> Result<(), CompileError>
{
     err(Pos::new(0, 0), ErrorType::UnexpectedEOF)
}

#[allow(unused_variables)]
fn gen_if(ctx: &mut Context, f: &If) -> Result<(), CompileError>
{
     err(Pos::new(0, 0), ErrorType::UnexpectedEOF)
}

unsafe fn gen_return(ctx: &mut Context, f: &Return) -> Result<(), CompileError>
{
    let ret = try!(gen_expression(ctx, &f.expr));
    let builder = ctx.builder;
    let sf = ctx.top_stack_frame();
    let ret_type =  LLVMTypeOf(ret);
    let func_type = sf.return_type();
    if ret_type != func_type {
        err(f.span.start, ErrorType::TypeError(
            format!("Attempting to return type '{}' expecting '{}'", type_name(ret_type), type_name(func_type))))
    } else {
        LLVMBuildRet(builder, ret);
        Ok(())
    }
}

#[allow(unused_variables)]
fn gen_struct(ctx: &mut Context, f: &Struct) -> Result<(), CompileError>
{
     err(Pos::new(0, 0), ErrorType::UnexpectedEOF)
}

#[allow(unused_variables)]
fn gen_union(ctx: &mut Context, f: &Union) -> Result<(), CompileError>
{
     err(Pos::new(0, 0), ErrorType::UnexpectedEOF)
}

#[allow(unused_variables)]
fn gen_match(ctx: &mut Context, f: &Match) -> Result<(), CompileError>
{
     err(Pos::new(0, 0), ErrorType::UnexpectedEOF)
}

unsafe fn gen_statement(ctx: &mut Context, stmt: &Statement) -> Result<(), CompileError>
{
    match *stmt {
        Statement::Import(ref i) => gen_import(ctx, i),
        Statement::Variable(ref vars) => {
            for v in vars {
                try!(gen_variable(ctx, v))
            }
            Ok(())
        },
        Statement::Function(ref fun) => gen_function(ctx, fun),
        Statement::ExternalFunction(ref fun) => gen_external_function(ctx, fun),
        Statement::While(ref w) => gen_while(ctx, w),
        Statement::If(ref i) => gen_if(ctx, i),
        Statement::Return(ref r) => gen_return(ctx, r),
        Statement::Struct(ref s) => gen_struct(ctx, s),
        Statement::Union(ref u) => gen_union(ctx, u),
        Statement::Match(ref m) => gen_match(ctx, m),
        Statement::Expression(ref e) => gen_expression(ctx, e).map(|_| ()),
    }
}

pub unsafe fn gen_program(ctx: &mut Context, prog: &Program) -> Result<(), CompileError>
{
    let main_ret_type = LLVMInt64TypeInContext(ctx.context);
    let function_type = LLVMFunctionType(main_ret_type, ptr::null_mut(), 0, 0);
    let function = LLVMAddFunction(ctx.module, cstr("main"), function_type);
    let bb = LLVMAppendBasicBlockInContext(ctx.context, function, cstr("entry"));
    LLVMPositionBuilderAtEnd(ctx.builder, bb);

    ctx.push_stack_frame(function, bb);
    for s in &prog.block.statements {
        try!(gen_statement(ctx, s));
    }
    Ok(())
}
<|endoftext|>


<commit_before>use std::collections::{BTreeMap, HashSet};

use core::PackageId;
use core::registry::PackageRegistry;
use core::{Resolve, SourceId, Workspace};
use core::resolver::Method;
use ops;
use util::config::Config;
use util::CargoResult;

pub struct UpdateOptions<'a> {
    pub config: &'a Config,
    pub to_update: &'a [String],
    pub precise: Option<&'a str>,
    pub aggressive: bool,
}

pub fn generate_lockfile(ws: &Workspace) -> CargoResult<()> {
    let mut registry = PackageRegistry::new(ws.config())?;
    let resolve = ops::resolve_with_previous(&mut registry, ws,
                                             Method::Everything,
                                             None, None, &[], true)?;
    ops::write_pkg_lockfile(ws, &resolve)?;
    Ok(())
}

pub fn update_lockfile(ws: &Workspace, opts: &UpdateOptions)
                       -> CargoResult<()> {

    if opts.aggressive && opts.precise.is_some() {
        bail!("cannot specify both aggressive and precise simultaneously")
    }

    if ws.members().is_empty() {
        bail!("you can't generate a lockfile for an empty workspace.")
    }

    let previous_resolve = match ops::load_pkg_lockfile(ws)? {
        Some(resolve) => resolve,
        None => return generate_lockfile(ws),
    };
    let mut registry = PackageRegistry::new(opts.config)?;
    let mut to_avoid = HashSet::new();

    if opts.to_update.is_empty() {
        to_avoid.extend(previous_resolve.iter());
    } else {
        let mut sources = Vec::new();
        for name in opts.to_update {
            let dep = previous_resolve.query(name)?;
            if opts.aggressive {
                fill_with_deps(&previous_resolve, dep, &mut to_avoid,
                               &mut HashSet::new());
            } else {
                to_avoid.insert(dep);
                sources.push(match opts.precise {
                    Some(precise) => {
                        // TODO: see comment in `resolve.rs` as well, but this
                        //       seems like a pretty hokey reason to single out
                        //       the registry as well.
                        let precise = if dep.source_id().is_registry() {
                            format!("{}={}", dep.name(), precise)
                        } else {
                            precise.to_string()
                        };
                        dep.source_id().clone().with_precise(Some(precise))
                    }
                    None => {
                        dep.source_id().clone().with_precise(None)
                    }
                });
            }
        }
        registry.add_sources(&sources)?;
    }

    let resolve = ops::resolve_with_previous(&mut registry,
                                                  ws,
                                                  Method::Everything,
                                                  Some(&previous_resolve),
                                                  Some(&to_avoid),
                                                  &[],
                                                  true)?;

    // Summarize what is changing for the user.
    let print_change = |status: &str, msg: String| {
        opts.config.shell().status(status, msg)
    };
    for (removed, added) in compare_dependency_graphs(&previous_resolve, &resolve) {
        if removed.len() == 1 && added.len() == 1 {
            let msg = if removed[0].source_id().is_git() {
                format!("{} -> #{}", removed[0],
                        &added[0].source_id().precise().unwrap()[..8])
            } else {
                format!("{} -> v{}", removed[0], added[0].version())
            };
            print_change("Updating", msg)?;
        } else {
            for package in removed.iter() {
                print_change("Removing", format!("{}", package))?;
            }
            for package in added.iter() {
                print_change("Adding", format!("{}", package))?;
            }
        }
    }

    ops::write_pkg_lockfile(ws, &resolve)?;
    return Ok(());

    fn fill_with_deps<'a>(resolve: &'a Resolve, dep: &'a PackageId,
                          set: &mut HashSet<&'a PackageId>,
                          visited: &mut HashSet<&'a PackageId>) {
        if !visited.insert(dep) {
            return
        }
        set.insert(dep);
        for dep in resolve.deps(dep) {
            fill_with_deps(resolve, dep, set, visited);
        }
    }

    fn compare_dependency_graphs<'a>(previous_resolve: &'a Resolve,
                                     resolve: &'a Resolve) ->
                                     Vec<(Vec<&'a PackageId>, Vec<&'a PackageId>)> {
        fn key(dep: &PackageId) -> (&str, &SourceId) {
            (dep.name(), dep.source_id())
        }

        // Removes all package ids in `b` from `a`. Note that this is somewhat
        // more complicated because the equality for source ids does not take
        // precise versions into account (e.g. git shas), but we want to take
        // that into account here.
        fn vec_subtract<'a>(a: &[&'a PackageId],
                            b: &[&'a PackageId]) -> Vec<&'a PackageId> {
            a.iter().filter(|a| {
                // If this package id is not found in `b`, then it's definitely
                // in the subtracted set
                let i = match b.binary_search(a) {
                    Ok(i) => i,
                    Err(..) => return true,
                };

                // If we've found `a` in `b`, then we iterate over all instances
                // (we know `b` is sorted) and see if they all have different
                // precise versions. If so, then `a` isn't actually in `b` so
                // we'll let it through.
                //
                // Note that we only check this for non-registry sources,
                // however, as registries contain enough version information in
                // the package id to disambiguate
                if a.source_id().is_registry() {
                    return false
                }
                b[i..].iter().take_while(|b| a == b).all(|b| {
                    a.source_id().precise() != b.source_id().precise()
                })
            }).cloned().collect()
        }

        // Map (package name, package source) to (removed versions, added versions).
        let mut changes = BTreeMap::new();
        let empty = (Vec::new(), Vec::new());
        for dep in previous_resolve.iter() {
            changes.entry(key(dep)).or_insert(empty.clone()).0.push(dep);
        }
        for dep in resolve.iter() {
            changes.entry(key(dep)).or_insert(empty.clone()).1.push(dep);
        }

        for v in changes.values_mut() {
            let (ref mut old, ref mut new) = *v;
            old.sort();
            new.sort();
            let removed = vec_subtract(old, new);
            let added = vec_subtract(new, old);
            *old = removed;
            *new = added;
        }
        debug!("{:#?}", changes);

        changes.into_iter().map(|(_, v)| v).collect()
    }
}
<commit_msg>Auto merge of #4701 - mathstuf:update-lockfile-color-reflects-change, r=alexcrichton<commit_after>use std::collections::{BTreeMap, HashSet};

use termcolor::Color::{self, Cyan, Green, Red};

use core::PackageId;
use core::registry::PackageRegistry;
use core::{Resolve, SourceId, Workspace};
use core::resolver::Method;
use ops;
use util::config::Config;
use util::CargoResult;

pub struct UpdateOptions<'a> {
    pub config: &'a Config,
    pub to_update: &'a [String],
    pub precise: Option<&'a str>,
    pub aggressive: bool,
}

pub fn generate_lockfile(ws: &Workspace) -> CargoResult<()> {
    let mut registry = PackageRegistry::new(ws.config())?;
    let resolve = ops::resolve_with_previous(&mut registry, ws,
                                             Method::Everything,
                                             None, None, &[], true)?;
    ops::write_pkg_lockfile(ws, &resolve)?;
    Ok(())
}

pub fn update_lockfile(ws: &Workspace, opts: &UpdateOptions)
                       -> CargoResult<()> {

    if opts.aggressive && opts.precise.is_some() {
        bail!("cannot specify both aggressive and precise simultaneously")
    }

    if ws.members().is_empty() {
        bail!("you can't generate a lockfile for an empty workspace.")
    }

    let previous_resolve = match ops::load_pkg_lockfile(ws)? {
        Some(resolve) => resolve,
        None => return generate_lockfile(ws),
    };
    let mut registry = PackageRegistry::new(opts.config)?;
    let mut to_avoid = HashSet::new();

    if opts.to_update.is_empty() {
        to_avoid.extend(previous_resolve.iter());
    } else {
        let mut sources = Vec::new();
        for name in opts.to_update {
            let dep = previous_resolve.query(name)?;
            if opts.aggressive {
                fill_with_deps(&previous_resolve, dep, &mut to_avoid,
                               &mut HashSet::new());
            } else {
                to_avoid.insert(dep);
                sources.push(match opts.precise {
                    Some(precise) => {
                        // TODO: see comment in `resolve.rs` as well, but this
                        //       seems like a pretty hokey reason to single out
                        //       the registry as well.
                        let precise = if dep.source_id().is_registry() {
                            format!("{}={}", dep.name(), precise)
                        } else {
                            precise.to_string()
                        };
                        dep.source_id().clone().with_precise(Some(precise))
                    }
                    None => {
                        dep.source_id().clone().with_precise(None)
                    }
                });
            }
        }
        registry.add_sources(&sources)?;
    }

    let resolve = ops::resolve_with_previous(&mut registry,
                                                  ws,
                                                  Method::Everything,
                                                  Some(&previous_resolve),
                                                  Some(&to_avoid),
                                                  &[],
                                                  true)?;

    // Summarize what is changing for the user.
    let print_change = |status: &str, msg: String, color: Color| {
        opts.config.shell().status_with_color(status, msg, color)
    };
    for (removed, added) in compare_dependency_graphs(&previous_resolve, &resolve) {
        if removed.len() == 1 && added.len() == 1 {
            let msg = if removed[0].source_id().is_git() {
                format!("{} -> #{}", removed[0],
                        &added[0].source_id().precise().unwrap()[..8])
            } else {
                format!("{} -> v{}", removed[0], added[0].version())
            };
            print_change("Updating", msg, Cyan)?;
        } else {
            for package in removed.iter() {
                print_change("Removing", format!("{}", package), Red)?;
            }
            for package in added.iter() {
                print_change("Adding", format!("{}", package), Green)?;
            }
        }
    }

    ops::write_pkg_lockfile(ws, &resolve)?;
    return Ok(());

    fn fill_with_deps<'a>(resolve: &'a Resolve, dep: &'a PackageId,
                          set: &mut HashSet<&'a PackageId>,
                          visited: &mut HashSet<&'a PackageId>) {
        if !visited.insert(dep) {
            return
        }
        set.insert(dep);
        for dep in resolve.deps(dep) {
            fill_with_deps(resolve, dep, set, visited);
        }
    }

    fn compare_dependency_graphs<'a>(previous_resolve: &'a Resolve,
                                     resolve: &'a Resolve) ->
                                     Vec<(Vec<&'a PackageId>, Vec<&'a PackageId>)> {
        fn key(dep: &PackageId) -> (&str, &SourceId) {
            (dep.name(), dep.source_id())
        }

        // Removes all package ids in `b` from `a`. Note that this is somewhat
        // more complicated because the equality for source ids does not take
        // precise versions into account (e.g. git shas), but we want to take
        // that into account here.
        fn vec_subtract<'a>(a: &[&'a PackageId],
                            b: &[&'a PackageId]) -> Vec<&'a PackageId> {
            a.iter().filter(|a| {
                // If this package id is not found in `b`, then it's definitely
                // in the subtracted set
                let i = match b.binary_search(a) {
                    Ok(i) => i,
                    Err(..) => return true,
                };

                // If we've found `a` in `b`, then we iterate over all instances
                // (we know `b` is sorted) and see if they all have different
                // precise versions. If so, then `a` isn't actually in `b` so
                // we'll let it through.
                //
                // Note that we only check this for non-registry sources,
                // however, as registries contain enough version information in
                // the package id to disambiguate
                if a.source_id().is_registry() {
                    return false
                }
                b[i..].iter().take_while(|b| a == b).all(|b| {
                    a.source_id().precise() != b.source_id().precise()
                })
            }).cloned().collect()
        }

        // Map (package name, package source) to (removed versions, added versions).
        let mut changes = BTreeMap::new();
        let empty = (Vec::new(), Vec::new());
        for dep in previous_resolve.iter() {
            changes.entry(key(dep)).or_insert(empty.clone()).0.push(dep);
        }
        for dep in resolve.iter() {
            changes.entry(key(dep)).or_insert(empty.clone()).1.push(dep);
        }

        for v in changes.values_mut() {
            let (ref mut old, ref mut new) = *v;
            old.sort();
            new.sort();
            let removed = vec_subtract(old, new);
            let added = vec_subtract(new, old);
            *old = removed;
            *new = added;
        }
        debug!("{:#?}", changes);

        changes.into_iter().map(|(_, v)| v).collect()
    }
}
<|endoftext|>