<commit_before>use std::io;

#[derive(Copy, Clone, Debug)]
enum Player {
    YeonSeung,
    JunSeok,
}

impl Player {
    fn opponent(&self) -> Player {
        match *self {
            Player::YeonSeung => Player::JunSeok,
            Player::JunSeok => Player::YeonSeung,
        }
    }
}

#[derive(Copy, Clone, Eq, PartialEq, Debug)]
struct Coordinate {
    row: usize,
    col: usize,
}

impl Coordinate {
    fn move_in(&self, dir: Direction, delta: usize) -> Coordinate {
        match dir {
            Direction::Up => Coordinate{row: self.row - delta, col: self.col},
            Direction::Down => Coordinate{row: self.row + delta, col: self.col},
            Direction::Left => Coordinate{row: self.row, col: self.col - delta},
            Direction::Right => Coordinate{row: self.row, col: self.col + delta},
        }
    }
    fn induces_board_type(&self) -> bool {
        *self == Coordinate{row: 1, col: 1} ||
        *self == Coordinate{row: 2, col: 1} ||
        *self == Coordinate{row: 2, col: 0} ||
        *self == Coordinate{row: 3, col: 0}
    }
}

#[derive(Copy, Clone)]
enum Direction {
    Up,
    Down,
    Left,
    Right,
}
const POSSIBLE_DIRECTIONS: [Direction; 4] = [
    Direction::Up,
    Direction::Down,
    Direction::Left,
    Direction::Right,
];

#[derive(Copy, Clone, Debug)]
enum MoveType {
    Single,
    OneUp,
    OneDown,
    OneLeft,
    OneRight,
    TwoUp,
    TwoDown,
    TwoLeft,
    TwoRight,
    UpAndDown,
    LeftAndRight,
}
const POSSIBLE_MOVE_TYPES: [MoveType; 11] = [
    MoveType::Single,
    MoveType::OneUp,
    MoveType::OneDown,
    MoveType::OneLeft,
    MoveType::OneRight,
    MoveType::TwoUp,
    MoveType::TwoDown,
    MoveType::TwoLeft,
    MoveType::TwoRight,
    MoveType::UpAndDown,
    MoveType::LeftAndRight,
];

#[derive(Copy, Clone, Debug)]
struct Move {
    coord: Coordinate,
    move_type: MoveType,
    board_type: Option<BoardType>,
}
impl Move {
    fn coords(&self) -> Vec<Coordinate> {
        match self.move_type {
            MoveType::Single => vec![],
            MoveType::OneUp => vec![self.coord.move_in(Direction::Up, 1)],
            MoveType::OneDown => vec![self.coord.move_in(Direction::Down, 1)],
            MoveType::OneLeft => vec![self.coord.move_in(Direction::Left, 1)],
            MoveType::OneRight => vec![self.coord.move_in(Direction::Right, 1)],
            MoveType::TwoUp => vec![self.coord.move_in(Direction::Up, 1), self.coord.move_in(Direction::Up, 2)],
            MoveType::TwoDown => vec![self.coord.move_in(Direction::Down, 1), self.coord.move_in(Direction::Down, 2)],
            MoveType::TwoLeft => vec![self.coord.move_in(Direction::Left, 1), self.coord.move_in(Direction::Left, 2)],
            MoveType::TwoRight => vec![self.coord.move_in(Direction::Right, 1), self.coord.move_in(Direction::Right, 2)],
            MoveType::UpAndDown => vec![self.coord.move_in(Direction::Up, 1), self.coord.move_in(Direction::Down, 1)],
            MoveType::LeftAndRight => vec![self.coord.move_in(Direction::Left, 1), self.coord.move_in(Direction::Right, 1)],
        }
    }
}

const NUM_COLS: usize = 5;
const NUM_ROWS: usize = 4;

// Hacks for the three states of the lower-left of the board in JunSeok vs YeonSeung game
#[derive(Copy, Clone, Debug)]
enum BoardType {
    Left,
    Middle,
    Right,
}
const POSSIBLE_BOARD_TYPES: [BoardType; 3] = [
    BoardType::Left,
    BoardType::Middle,
    BoardType::Right,
];

type BoardArray = [[bool; NUM_COLS]; NUM_ROWS];
struct Board {
    board: BoardArray,
    board_type: Option<BoardType>,
}

impl Board {
    fn make_move(&mut self, m: Move) {
        match m.board_type {
            Some(_) => self.board_type = m.board_type,
            None => (),
        }
        self.set_squares(m, true)
    }

    fn undo_move(&mut self, m: Move, bt: Option<BoardType>) {
        self.board_type = bt;
        self.set_squares(m, false)
    }

    fn set_squares(&mut self, m: Move, mode: bool) {
        self.board[m.coord.row][m.coord.col] = mode;
        for other_space in m.coords().iter() {
            self.board[other_space.row][other_space.col] = mode;
        }
    }

    fn occupied(&self, c: Coordinate) -> bool {
        self.in_bounds(c) && self.board[c.row][c.col]
    }

    fn in_bounds(&self, c: Coordinate) -> bool {
        // >= 0 is always true due to type limits.
        // c.row >= 0 && c.row < NUM_ROWS && c.col >= 0 && c.col < NUM_COLS
        c.row < NUM_ROWS && c.col < NUM_COLS
    }

    // Assuming that m is a move with an unoccupied coordinate!
    // This doesn't check whether the target squares are occupied.
    // Advantage: It's quicker. Disadvantage: It allows some illegal moves.
    fn move_in_bounds(&self, m: Move) -> bool {
        match m.move_type {
            MoveType::Single => true,
            MoveType::OneUp => m.coord.row >= 1,
            MoveType::OneDown => m.coord.row < NUM_ROWS - 1,
            MoveType::OneLeft => m.coord.col >= 1,
            MoveType::OneRight => m.coord.col < NUM_COLS - 1,
            MoveType::TwoUp => m.coord.row >= 2,
            MoveType::TwoDown => m.coord.row < NUM_ROWS - 2,
            MoveType::TwoLeft => m.coord.col >= 2,
            MoveType::TwoRight => m.coord.col < NUM_COLS - 2,
            MoveType::UpAndDown => m.coord.row >= 1 && m.coord.row < NUM_ROWS - 1,
            MoveType::LeftAndRight => m.coord.col >= 1 && m.coord.col < NUM_COLS - 1,
        }
    }

    // Unfortunately I kind of have to hard-code this.
    fn makes_unsolvable(c: Coordinate, b: Option<BoardType>) -> bool {
        match b {
            Some(BoardType::Left)   => c == Coordinate{row: 2, col: 1} || c == Coordinate{row: 1, col: 1},
            Some(BoardType::Middle) => c == Coordinate{row: 3, col: 0} || c == Coordinate{row: 1, col: 1},
            Some(BoardType::Right)  => c == Coordinate{row: 3, col: 0} || c == Coordinate{row: 2, col: 0},
            None => false,
        }
    }

    fn frontier(&self) -> Vec<Coordinate> {
        let mut results = Vec::new();
        for row in 0..NUM_ROWS {
            for col in 0..NUM_COLS {
                let coord = Coordinate{row: row, col: col};
                if self.occupied(coord) || Board::makes_unsolvable(coord, self.board_type) {
                    continue;
                }
                let mut have_neighbor = false;
                for dir in POSSIBLE_DIRECTIONS.iter() {
                    let new_coord = coord.move_in(*dir, 1);
                    if self.occupied(new_coord) {
                        have_neighbor = true;
                        break;
                    }
                }
                if have_neighbor {
                    results.push(coord);
                }
            }
        }
        results
    }

    fn legal_moves(&self) -> Vec<Move> {
        let mut results = Vec::new();
        for frontier_space in self.frontier().iter() {
            for move_type in POSSIBLE_MOVE_TYPES.iter() {
                let mov = Move{coord: *frontier_space, move_type: *move_type, board_type: None};
                if !self.move_in_bounds(mov) {
                    continue;
                }
                let mut other_space_taken = false;
                let mut induces_board_type = frontier_space.induces_board_type();
                for other_space in mov.coords().iter() {
                    if other_space.induces_board_type() {
                        induces_board_type = true;
                    }
                    if self.occupied(*other_space) {
                        other_space_taken = true;
                        break;
                    }
                }
                if !other_space_taken {
                    if induces_board_type && self.board_type.is_none() {
                        for board_type in POSSIBLE_BOARD_TYPES.iter() {
                            if Board::makes_unsolvable(*frontier_space, Some(*board_type)) {
                                continue;
                            }
                            let mut other_spaces_ok = true;
                            for other_space in mov.coords().iter() {
                                if Board::makes_unsolvable(*other_space, Some(*board_type)) {
                                    other_spaces_ok = false;
                                    break;
                                }
                            }
                            if other_spaces_ok {
                                results.push(Move{coord: mov.coord, move_type: mov.move_type, board_type: Some(*board_type)});
                            }
                        }

                    } else {
                        results.push(mov);
                    }
                }
            }
        }
        results
    }

    fn print(&self) {
        for row in self.board.iter() {
            for col in row.iter() {
                print!("{: >5} ", col);
            }
            println!("");
        }
        println!("{:?}", self.board_type);
    }
}

#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
enum GameResult {
    PlaceholderJunSeok,
    JunSeokWin,
    YeonSeungWin,
    PlaceholderYeonSeung,
}

fn minimax_alpha_beta(player: Player, board: &mut Board, initial_alpha: GameResult, initial_beta: GameResult) -> (GameResult, Option<Move>) {
    let moves = board.legal_moves();
    // There are no more moves, which means my opponent completed the railroad.
    // So I lose.
    if moves.is_empty() {
        return match player {
            Player::YeonSeung => (GameResult::JunSeokWin, None),
            Player::JunSeok => (GameResult::YeonSeungWin, None),
        }
    }
    let mut best = match player {
        Player::YeonSeung => initial_alpha,
        Player::JunSeok => initial_beta,
    };
    let mut alpha = initial_alpha;
    let mut beta = initial_beta;
    let mut best_move = None;

    for possible_move in moves.iter() {
        let bt = board.board_type;
        board.make_move(*possible_move);
        let (reply, _) = minimax_alpha_beta(player.opponent(), board, alpha, beta);
        board.undo_move(*possible_move, bt);

        match player {
            Player::YeonSeung => {
                if reply > best {
                    best = reply;
                    alpha = reply;
                    best_move = Some(*possible_move);
                }
                if best >= GameResult::YeonSeungWin {
                    return (best, best_move);
                }
            },
            Player::JunSeok => {
                if reply < best {
                    best = reply;
                    beta = reply;
                    best_move = Some(*possible_move);
                }
                if best <= GameResult::JunSeokWin {
                    return (best, best_move);
                }
            },
        }

        if alpha >= beta {
            return (best, best_move);
        }
    }

    (best, best_move)
}

fn main() {
    let mut starting_board = Board{
        board: [
            [false,  true,  true,  true, false],
            [false, false, false,  true, false],
            [false, false, false,  true, false],
            [false, false, false, false, false],
        ],
        board_type: None,
    };
    let starting_player = Player::YeonSeung;

    let all_responses = false;
    let best_move = false;
    let legal_moves = false;
    let interactive = true;

    if legal_moves {
        for legal_move in starting_board.legal_moves().iter() {
            println!("{:?}", legal_move);
        }
    }

    if best_move {
        let (result, best_move) = minimax_alpha_beta(starting_player, &mut starting_board, GameResult::PlaceholderJunSeok, GameResult::PlaceholderYeonSeung);
        println!("{:?}", result);
        println!("{:?}", best_move);
        match best_move {
            Some(x) => {
                starting_board.make_move(x);
                starting_board.print();
            },
            None => (),
        }
    }

    if all_responses {
        for legal_move in starting_board.legal_moves().iter() {
            print!("{:?} does: {:?}, ", starting_player, legal_move);
            let bt = starting_board.board_type;
            starting_board.make_move(*legal_move);
            let (result, best_move) = minimax_alpha_beta(starting_player.opponent(), &mut starting_board, GameResult::PlaceholderJunSeok, GameResult::PlaceholderYeonSeung);
            match best_move {
                Some(x) => {
                    println!("{:?} does: {:?}, {:?}", starting_player.opponent(), x, result);
                    starting_board.make_move(x);
                    starting_board.print();
                    starting_board.undo_move(x, bt);
                }
                None => (),
            }
            starting_board.undo_move(*legal_move, bt);
        }
    }

    if interactive {
        let mut player = starting_player;
        loop {
            let (result, best_move) = minimax_alpha_beta(player, &mut starting_board, GameResult::PlaceholderJunSeok, GameResult::PlaceholderYeonSeung);
            println!("{:?}", result);
            println!("{:?}", best_move);
            match best_move {
                Some(x) => {
                    starting_board.make_move(x);
                    starting_board.print();
                },
                None => println!("No move, guess {:?} wins?", player.opponent()),
            }

            player = player.opponent();

            let moves = starting_board.legal_moves();
            if moves.is_empty() {
                println!("No moves left, {:?} wins", player.opponent());
                break;
            }
            for (i, legal_move) in moves.iter().enumerate() {
                println!("{} {:?}", i, legal_move);
            }
            println!("It's {:?}'s turn. What move?", player);
            let mut input_move = String::new();
            io::stdin().read_line(&mut input_move).ok().expect("Failed to read line");
            let input_move: usize = input_move.trim().parse().ok().expect("Give me a number");
            let mut found = false;
            for (i, legal_move) in moves.iter().enumerate() {
                if i == input_move {
                    starting_board.make_move(*legal_move);
                    player = player.opponent();
                    found = true;
                }
            }
            if !found {
                println!("Not found. Auto-calculating a move.")
            }
        }
    }
}
<commit_msg>Don't allow moves that will extend into unsolvable area<commit_after>use std::io;

#[derive(Copy, Clone, Debug)]
enum Player {
    YeonSeung,
    JunSeok,
}

impl Player {
    fn opponent(&self) -> Player {
        match *self {
            Player::YeonSeung => Player::JunSeok,
            Player::JunSeok => Player::YeonSeung,
        }
    }
}

#[derive(Copy, Clone, Eq, PartialEq, Debug)]
struct Coordinate {
    row: usize,
    col: usize,
}

impl Coordinate {
    fn move_in(&self, dir: Direction, delta: usize) -> Coordinate {
        match dir {
            Direction::Up => Coordinate{row: self.row - delta, col: self.col},
            Direction::Down => Coordinate{row: self.row + delta, col: self.col},
            Direction::Left => Coordinate{row: self.row, col: self.col - delta},
            Direction::Right => Coordinate{row: self.row, col: self.col + delta},
        }
    }
    fn induces_board_type(&self) -> bool {
        *self == Coordinate{row: 1, col: 1} ||
        *self == Coordinate{row: 2, col: 1} ||
        *self == Coordinate{row: 2, col: 0} ||
        *self == Coordinate{row: 3, col: 0}
    }
}

#[derive(Copy, Clone)]
enum Direction {
    Up,
    Down,
    Left,
    Right,
}
const POSSIBLE_DIRECTIONS: [Direction; 4] = [
    Direction::Up,
    Direction::Down,
    Direction::Left,
    Direction::Right,
];

#[derive(Copy, Clone, Debug)]
enum MoveType {
    Single,
    OneUp,
    OneDown,
    OneLeft,
    OneRight,
    TwoUp,
    TwoDown,
    TwoLeft,
    TwoRight,
    UpAndDown,
    LeftAndRight,
}
const POSSIBLE_MOVE_TYPES: [MoveType; 11] = [
    MoveType::Single,
    MoveType::OneUp,
    MoveType::OneDown,
    MoveType::OneLeft,
    MoveType::OneRight,
    MoveType::TwoUp,
    MoveType::TwoDown,
    MoveType::TwoLeft,
    MoveType::TwoRight,
    MoveType::UpAndDown,
    MoveType::LeftAndRight,
];

#[derive(Copy, Clone, Debug)]
struct Move {
    coord: Coordinate,
    move_type: MoveType,
    board_type: Option<BoardType>,
}
impl Move {
    fn coords(&self) -> Vec<Coordinate> {
        match self.move_type {
            MoveType::Single => vec![],
            MoveType::OneUp => vec![self.coord.move_in(Direction::Up, 1)],
            MoveType::OneDown => vec![self.coord.move_in(Direction::Down, 1)],
            MoveType::OneLeft => vec![self.coord.move_in(Direction::Left, 1)],
            MoveType::OneRight => vec![self.coord.move_in(Direction::Right, 1)],
            MoveType::TwoUp => vec![self.coord.move_in(Direction::Up, 1), self.coord.move_in(Direction::Up, 2)],
            MoveType::TwoDown => vec![self.coord.move_in(Direction::Down, 1), self.coord.move_in(Direction::Down, 2)],
            MoveType::TwoLeft => vec![self.coord.move_in(Direction::Left, 1), self.coord.move_in(Direction::Left, 2)],
            MoveType::TwoRight => vec![self.coord.move_in(Direction::Right, 1), self.coord.move_in(Direction::Right, 2)],
            MoveType::UpAndDown => vec![self.coord.move_in(Direction::Up, 1), self.coord.move_in(Direction::Down, 1)],
            MoveType::LeftAndRight => vec![self.coord.move_in(Direction::Left, 1), self.coord.move_in(Direction::Right, 1)],
        }
    }
}

const NUM_COLS: usize = 5;
const NUM_ROWS: usize = 4;

// Hacks for the three states of the lower-left of the board in JunSeok vs YeonSeung game
#[derive(Copy, Clone, Debug)]
enum BoardType {
    Left,
    Middle,
    Right,
}
const POSSIBLE_BOARD_TYPES: [BoardType; 3] = [
    BoardType::Left,
    BoardType::Middle,
    BoardType::Right,
];

type BoardArray = [[bool; NUM_COLS]; NUM_ROWS];
struct Board {
    board: BoardArray,
    board_type: Option<BoardType>,
}

impl Board {
    fn make_move(&mut self, m: Move) {
        match m.board_type {
            Some(_) => self.board_type = m.board_type,
            None => (),
        }
        self.set_squares(m, true)
    }

    fn undo_move(&mut self, m: Move, bt: Option<BoardType>) {
        self.board_type = bt;
        self.set_squares(m, false)
    }

    fn set_squares(&mut self, m: Move, mode: bool) {
        self.board[m.coord.row][m.coord.col] = mode;
        for other_space in m.coords().iter() {
            self.board[other_space.row][other_space.col] = mode;
        }
    }

    fn occupied(&self, c: Coordinate) -> bool {
        self.in_bounds(c) && self.board[c.row][c.col]
    }

    fn in_bounds(&self, c: Coordinate) -> bool {
        // >= 0 is always true due to type limits.
        // c.row >= 0 && c.row < NUM_ROWS && c.col >= 0 && c.col < NUM_COLS
        c.row < NUM_ROWS && c.col < NUM_COLS
    }

    // Assuming that m is a move with an unoccupied coordinate!
    // This doesn't check whether the target squares are occupied.
    // Advantage: It's quicker. Disadvantage: It allows some illegal moves.
    fn move_in_bounds(&self, m: Move) -> bool {
        match m.move_type {
            MoveType::Single => true,
            MoveType::OneUp => m.coord.row >= 1,
            MoveType::OneDown => m.coord.row < NUM_ROWS - 1,
            MoveType::OneLeft => m.coord.col >= 1,
            MoveType::OneRight => m.coord.col < NUM_COLS - 1,
            MoveType::TwoUp => m.coord.row >= 2,
            MoveType::TwoDown => m.coord.row < NUM_ROWS - 2,
            MoveType::TwoLeft => m.coord.col >= 2,
            MoveType::TwoRight => m.coord.col < NUM_COLS - 2,
            MoveType::UpAndDown => m.coord.row >= 1 && m.coord.row < NUM_ROWS - 1,
            MoveType::LeftAndRight => m.coord.col >= 1 && m.coord.col < NUM_COLS - 1,
        }
    }

    // Unfortunately I kind of have to hard-code this.
    fn makes_unsolvable(c: Coordinate, b: Option<BoardType>) -> bool {
        match b {
            Some(BoardType::Left)   => c == Coordinate{row: 2, col: 1} || c == Coordinate{row: 1, col: 1},
            Some(BoardType::Middle) => c == Coordinate{row: 3, col: 0} || c == Coordinate{row: 1, col: 1},
            Some(BoardType::Right)  => c == Coordinate{row: 3, col: 0} || c == Coordinate{row: 2, col: 0},
            None => false,
        }
    }

    fn frontier(&self) -> Vec<Coordinate> {
        let mut results = Vec::new();
        for row in 0..NUM_ROWS {
            for col in 0..NUM_COLS {
                let coord = Coordinate{row: row, col: col};
                if self.occupied(coord) || Board::makes_unsolvable(coord, self.board_type) {
                    continue;
                }
                let mut have_neighbor = false;
                for dir in POSSIBLE_DIRECTIONS.iter() {
                    let new_coord = coord.move_in(*dir, 1);
                    if self.occupied(new_coord) {
                        have_neighbor = true;
                        break;
                    }
                }
                if have_neighbor {
                    results.push(coord);
                }
            }
        }
        results
    }

    fn legal_moves(&self) -> Vec<Move> {
        let mut results = Vec::new();
        for frontier_space in self.frontier().iter() {
            for move_type in POSSIBLE_MOVE_TYPES.iter() {
                let mov = Move{coord: *frontier_space, move_type: *move_type, board_type: None};
                if !self.move_in_bounds(mov) {
                    continue;
                }
                let mut other_space_taken = false;
                let mut induces_board_type = frontier_space.induces_board_type();
                for other_space in mov.coords().iter() {
                    if other_space.induces_board_type() {
                        induces_board_type = true;
                    }
                    if self.occupied(*other_space) || Board::makes_unsolvable(*other_space, self.board_type) {
                        other_space_taken = true;
                        break;
                    }
                }
                if !other_space_taken {
                    if induces_board_type && self.board_type.is_none() {
                        for board_type in POSSIBLE_BOARD_TYPES.iter() {
                            if Board::makes_unsolvable(*frontier_space, Some(*board_type)) {
                                continue;
                            }
                            let mut other_spaces_ok = true;
                            for other_space in mov.coords().iter() {
                                if Board::makes_unsolvable(*other_space, Some(*board_type)) {
                                    other_spaces_ok = false;
                                    break;
                                }
                            }
                            if other_spaces_ok {
                                results.push(Move{coord: mov.coord, move_type: mov.move_type, board_type: Some(*board_type)});
                            }
                        }

                    } else {
                        results.push(mov);
                    }
                }
            }
        }
        results
    }

    fn print(&self) {
        for row in self.board.iter() {
            for col in row.iter() {
                print!("{: >5} ", col);
            }
            println!("");
        }
        println!("{:?}", self.board_type);
    }
}

#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
enum GameResult {
    PlaceholderJunSeok,
    JunSeokWin,
    YeonSeungWin,
    PlaceholderYeonSeung,
}

fn minimax_alpha_beta(player: Player, board: &mut Board, initial_alpha: GameResult, initial_beta: GameResult) -> (GameResult, Option<Move>) {
    let moves = board.legal_moves();
    // There are no more moves, which means my opponent completed the railroad.
    // So I lose.
    if moves.is_empty() {
        return match player {
            Player::YeonSeung => (GameResult::JunSeokWin, None),
            Player::JunSeok => (GameResult::YeonSeungWin, None),
        }
    }
    let mut best = match player {
        Player::YeonSeung => initial_alpha,
        Player::JunSeok => initial_beta,
    };
    let mut alpha = initial_alpha;
    let mut beta = initial_beta;
    let mut best_move = None;

    for possible_move in moves.iter() {
        let bt = board.board_type;
        board.make_move(*possible_move);
        let (reply, _) = minimax_alpha_beta(player.opponent(), board, alpha, beta);
        board.undo_move(*possible_move, bt);

        match player {
            Player::YeonSeung => {
                if reply > best {
                    best = reply;
                    alpha = reply;
                    best_move = Some(*possible_move);
                }
                if best >= GameResult::YeonSeungWin {
                    return (best, best_move);
                }
            },
            Player::JunSeok => {
                if reply < best {
                    best = reply;
                    beta = reply;
                    best_move = Some(*possible_move);
                }
                if best <= GameResult::JunSeokWin {
                    return (best, best_move);
                }
            },
        }

        if alpha >= beta {
            return (best, best_move);
        }
    }

    (best, best_move)
}

fn main() {
    let mut starting_board = Board{
        board: [
            [false,  true,  true,  true, false],
            [false, false, false,  true, false],
            [false, false, false,  true, false],
            [false, false, false, false, false],
        ],
        board_type: None,
    };
    let starting_player = Player::YeonSeung;

    let all_responses = false;
    let best_move = false;
    let legal_moves = false;
    let interactive = true;

    if legal_moves {
        for legal_move in starting_board.legal_moves().iter() {
            println!("{:?}", legal_move);
        }
    }

    if best_move {
        let (result, best_move) = minimax_alpha_beta(starting_player, &mut starting_board, GameResult::PlaceholderJunSeok, GameResult::PlaceholderYeonSeung);
        println!("{:?}", result);
        println!("{:?}", best_move);
        match best_move {
            Some(x) => {
                starting_board.make_move(x);
                starting_board.print();
            },
            None => (),
        }
    }

    if all_responses {
        for legal_move in starting_board.legal_moves().iter() {
            print!("{:?} does: {:?}, ", starting_player, legal_move);
            let bt = starting_board.board_type;
            starting_board.make_move(*legal_move);
            let (result, best_move) = minimax_alpha_beta(starting_player.opponent(), &mut starting_board, GameResult::PlaceholderJunSeok, GameResult::PlaceholderYeonSeung);
            match best_move {
                Some(x) => {
                    println!("{:?} does: {:?}, {:?}", starting_player.opponent(), x, result);
                    starting_board.make_move(x);
                    starting_board.print();
                    starting_board.undo_move(x, bt);
                }
                None => (),
            }
            starting_board.undo_move(*legal_move, bt);
        }
    }

    if interactive {
        let mut player = starting_player;
        loop {
            let (result, best_move) = minimax_alpha_beta(player, &mut starting_board, GameResult::PlaceholderJunSeok, GameResult::PlaceholderYeonSeung);
            println!("{:?}", result);
            println!("{:?}", best_move);
            match best_move {
                Some(x) => {
                    starting_board.make_move(x);
                    starting_board.print();
                },
                None => println!("No move, guess {:?} wins?", player.opponent()),
            }

            player = player.opponent();

            let moves = starting_board.legal_moves();
            if moves.is_empty() {
                println!("No moves left, {:?} wins", player.opponent());
                break;
            }
            for (i, legal_move) in moves.iter().enumerate() {
                println!("{} {:?}", i, legal_move);
            }
            println!("It's {:?}'s turn. What move?", player);
            let mut input_move = String::new();
            io::stdin().read_line(&mut input_move).ok().expect("Failed to read line");
            let input_move: usize = input_move.trim().parse().ok().expect("Give me a number");
            let mut found = false;
            for (i, legal_move) in moves.iter().enumerate() {
                if i == input_move {
                    starting_board.make_move(*legal_move);
                    player = player.opponent();
                    found = true;
                }
            }
            if !found {
                println!("Not found. Auto-calculating a move.")
            }
        }
    }
}
<|endoftext|>


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

//! A little class that rate limits the number of resize events sent to the script task
/// based on how fast script dispatches those events. It waits until each event is handled
/// before sending the next. If the window is resized multiple times before an event is handled
/// then some events will never be sent.

use core::comm::{Port};
use script::dom::event::ResizeEvent;
use script::script_task::{ScriptChan, ScriptMsg, SendEventMsg};

pub struct ResizeRateLimiter {
    /// The channel we send resize events on
    priv script_chan: ScriptChan,
    /// The port we are waiting on for a response to the last resize event
    priv last_response_port: Option<Port<()>>,
    /// The next window resize event we should fire
    priv next_resize_event: Option<(uint, uint)>
}

pub fn ResizeRateLimiter(script_chan: ScriptChan) -> ResizeRateLimiter {
    ResizeRateLimiter {
        script_chan: script_chan,
        last_response_port: None,
        next_resize_event: None
    }
}

pub impl ResizeRateLimiter {
    fn window_resized(&mut self, width: uint, height: uint) {
        match self.last_response_port {
            None => {
                assert!(self.next_resize_event.is_none());
                self.send_event(width, height);
            }
            Some(*) => {
                if self.last_response_port.get_ref().peek() {
                    self.send_event(width, height);
                    self.next_resize_event = None;
                } else {
                    if self.next_resize_event.is_some() {
                        warn!("osmain: script task can't keep up. skipping resize event");
                    }
                    self.next_resize_event = Some((width, height));
                }
            }
        }
    }

    fn check_resize_response(&mut self) {
        match self.next_resize_event {
            Some((copy width, copy height)) => {
                assert!(self.last_response_port.is_some());
                if self.last_response_port.get_ref().peek() {
                    self.send_event(width, height);
                    self.next_resize_event = None;
                }
            }
            None => ()
        }
    }

    priv fn send_event(&mut self, width: uint, height: uint) {
        let (port, chan) = comm::stream();
        self.script_chan.send(SendEventMsg(ResizeEvent(width, height, chan)));
        self.last_response_port = Some(port);
    }
}
<commit_msg>auto merge of #929 : kmcallister/servo/rm-resize-limiter, r=metajack<commit_after><|endoftext|>


<commit_before>// Copyright 2014 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.

// ignore-tidy-linelength

#![feature(box_patterns)]
#![feature(box_syntax)]

struct S {
    x: Box<E>
}

enum E {
    Foo(Box<S>),
    Bar(Box<isize>),
    Baz
}

fn f<G>(s: &S, g: G) where G: FnOnce(&S) {
    g(s)
}

fn main() {
    let s = S { x: box E::Bar(box 42) };
    loop {
        f(&s, |hellothere| {
            match hellothere.x { //~ ERROR cannot move out
                                 //~| cannot move out of borrowed content
                box E::Foo(_) => {}
                box E::Bar(x) => println!("{}", x.to_string()),
                //~^ NOTE to prevent move
                box E::Baz => {}
            }
        })
    }
}
<commit_msg>Fix tidy error<commit_after>// Copyright 2014 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.

// ignore-tidy-linelength

#![feature(box_patterns)]
#![feature(box_syntax)]

struct S {
    x: Box<E>
}

enum E {
    Foo(Box<S>),
    Bar(Box<isize>),
    Baz
}

fn f<G>(s: &S, g: G) where G: FnOnce(&S) {
    g(s)
}

fn main() {
    let s = S { x: box E::Bar(box 42) };
    loop {
        f(&s, |hellothere| {
            match hellothere.x { //~ ERROR cannot move out
                                 //~| cannot move out of borrowed content
                box E::Foo(_) => {}
                box E::Bar(x) => println!("{}", x.to_string()),
                //~^ NOTE to prevent move
                box E::Baz => {}
            }
        })
    }
}
<|endoftext|>


<commit_before>/* diosix hypervisor's system for passing messages between physical CPU cores and services
 *
 * (c) Chris Williams, 2019-2020.
 *
 * See LICENSE for usage and copying.
 */

use super::lock::Mutex;
use alloc::collections::vec_deque::VecDeque;
use hashbrown::hash_map::HashMap;
use super::error::Cause;
use super::service::{self, ServiceID};
use super::capsule::CapsuleID;
use super::pcore::{PhysicalCoreID, PhysicalCore};

/* here's how message passing works, depending on the target:
    * To an individual physical core:
        1. locate the physical core's message queue in MAILBOXES
        2. insert the message at the end of the queue
        3. interrupt the physical CPU core to check its mailbox
    * To all physical cores:
        1. iterate over each physical core in MAILBOXES
        2. insert a copy of the message in the message queue of each physical CPU
        3. interrupt each physical CPU core to check its mailbox
    * To a service registered by a capsule:
        1. locate the service's mailbox
        2. insert the message into the mailbox
        3. raise an interrupt or wait for the capsule to poll the mailbox
*/

/* maintain a mailbox of messages per physical CPU core */
lazy_static!
{
    static ref MAILBOXES: Mutex<HashMap<PhysicalCoreID, VecDeque<Message>>> = Mutex::new("mailbox", HashMap::new());
}

/* create a mailbox for physical CPU core coreid */
pub fn create_mailbox(coreid: PhysicalCoreID)
{
    MAILBOXES.lock().insert(coreid, VecDeque::<Message>::new());
}

#[derive(Clone)]
pub enum Sender
{
    PhysicalCore(PhysicalCoreID),
    Capsule(CapsuleID)
}

#[derive(Clone, Copy)]
pub enum Recipient
{
    Broadcast,                      /* send to all physical CPU cores */
    PhysicalCore(PhysicalCoreID),   /* send to a single physical CPU core */
    Service(ServiceID)              /* send to a single registered service */
}

impl Recipient
{
    /* broadcast message to all physical cores */
    pub fn send_to_all() -> Recipient { Recipient::Broadcast }

    /* send to a particular physical core */
    pub fn send_to_pcore(id: PhysicalCoreID) -> Recipient
    {
        Recipient::PhysicalCore(id)
    }

    /* send to a particular capsule-hosted service */
    pub fn send_to_service(id: ServiceID) -> Recipient
    {
        Recipient::Service(id)
    }
}

#[derive(Clone, Copy)]
pub enum MessageContent
{
    /* warn all physical CPUs capsule is dying */
    CapsuleTeardown(CapsuleID),
    /* return any queued virtual core to the global queue for other physical CPUs to schedule */
    DisownQueuedVirtualCore
}

#[derive(Clone)]
pub struct Message
{
    sender: Sender,
    receiver: Recipient,
    code: MessageContent
}

impl Message
{
    /* create a new message
       => recv = end point to send the message to
          data = message to send to the recipient
       <= returns message structure
    */
    pub fn new(recv: Recipient, data: MessageContent) -> Message
    {
        Message
        {
            receiver: recv,
            code: data,

            /* determine sender from message type */
            sender: match data
            {
                MessageContent::CapsuleTeardown(_) => Sender::PhysicalCore(PhysicalCore::get_id()),
                MessageContent::DisownQueuedVirtualCore => Sender::PhysicalCore(PhysicalCore::get_id())
            }
        }
    }

    pub fn get_receiver(&self) -> Recipient
    {
        self.receiver
    }
}

/* send the given message msg, consuming it so it can't be reused or resent */
pub fn send(msg: Message) -> Result<(), Cause>
{
    let receiver = msg.receiver;
    match receiver
    {
        /* iterate over all physical CPU cores */
        Recipient::Broadcast =>
        {
            for (_, mailbox) in MAILBOXES.lock().iter_mut()
            {
                mailbox.push_back(msg.clone())
            }
        },

        /* send to a particular physical CPU core */
        Recipient::PhysicalCore(pid) =>
        {
            if let Some(mailbox) = MAILBOXES.lock().get_mut(&pid)
            {
                mailbox.push_back(msg);
            }
            else
            {
                return Err(Cause::PhysicalCoreBadID);
            }
        },

        /* send to a service */
        Recipient::Service(_) =>
        {
            return service::send(msg);
        }
    };

    Ok(())
}
<commit_msg>Differentiate between output text from capsules and the hypervisor<commit_after>/* diosix hypervisor's system for passing messages between physical CPU cores and services
 *
 * (c) Chris Williams, 2019-2020.
 *
 * See LICENSE for usage and copying.
 */

use super::lock::Mutex;
use alloc::collections::vec_deque::VecDeque;
use alloc::string::String;
use hashbrown::hash_map::HashMap;
use super::error::Cause;
use super::service::{self, ServiceType};
use super::capsule::CapsuleID;
use super::pcore::{PhysicalCoreID, PhysicalCore};

/* here's how message passing works, depending on the target:
    * To an individual physical core:
        1. locate the physical core's message queue in MAILBOXES
        2. insert the message at the end of the queue
        3. interrupt the physical CPU core to check its mailbox
    * To all physical cores:
        1. iterate over each physical core in MAILBOXES
        2. insert a copy of the message in the message queue of each physical CPU
        3. interrupt each physical CPU core to check its mailbox
    * To a service registered by a capsule:
        1. locate the service's mailbox
        2. insert the message into the mailbox
        3. raise an interrupt or wait for the capsule to poll the mailbox
*/

/* maintain a mailbox of messages per physical CPU core */
lazy_static!
{
    static ref MAILBOXES: Mutex<HashMap<PhysicalCoreID, VecDeque<Message>>> = Mutex::new("mailbox", HashMap::new());
}

/* create a mailbox for physical CPU core coreid */
pub fn create_mailbox(coreid: PhysicalCoreID)
{
    MAILBOXES.lock().insert(coreid, VecDeque::<Message>::new());
}

#[derive(Clone)]
pub enum Sender
{
    PhysicalCore(PhysicalCoreID),
    Capsule(CapsuleID),
    Hypervisor
}

#[derive(Clone, Copy)]
pub enum Recipient
{
    Broadcast,                      /* send to all physical CPU cores */
    PhysicalCore(PhysicalCoreID),   /* send to a single physical CPU core */
    Service(ServiceType)              /* send to a single registered service */
}

impl Recipient
{
    /* broadcast message to all physical cores */
    pub fn send_to_all() -> Recipient { Recipient::Broadcast }

    /* send to a particular physical core */
    pub fn send_to_pcore(id: PhysicalCoreID) -> Recipient
    {
        Recipient::PhysicalCore(id)
    }

    /* send to a particular capsule-hosted service */
    pub fn send_to_service(stype: ServiceType) -> Recipient
    {
        Recipient::Service(stype)
    }
}

#[derive(Clone, Debug)]
pub enum MessageContent
{
    HypervisorDebugStr(String),
    CapsuleConsoleStr(String),
    DisownQueuedVirtualCore
}

#[derive(Clone)]
pub struct Message
{
    sender: Sender,
    receiver: Recipient,
    data: MessageContent
}

impl Message
{
    /* create a new message
       => recv = end point to send the message to
          data = message to send to the recipient
       <= returns message structure
    */
    pub fn new(recv: Recipient, data: MessageContent) -> Result<Message, Cause>
    {
        Ok(Message
        {
            receiver: recv,

            /* determine sender from message type */
            sender: match data
            {
                MessageContent::HypervisorDebugStr(_) => Sender::Hypervisor,
                MessageContent::CapsuleConsoleStr(_) => match PhysicalCore::get_capsule_id()
                {
                    Some(id) => Sender::Capsule(id),
                    None =>
                    {
                        hvdebug!("BUG: Sending {:?} from non-existent capsule", data);
                        return Err(Cause::CapsuleBadID);
                    }
                },
                MessageContent::DisownQueuedVirtualCore => Sender::PhysicalCore(PhysicalCore::get_id())
            },

            data
        })
    }

    pub fn get_receiver(&self) -> Recipient
    {
        self.receiver
    }
}

/* send the given message msg, consuming it so it can't be reused or resent */
pub fn send(msg: Message) -> Result<(), Cause>
{
    let receiver = msg.receiver;
    match receiver
    {
        /* iterate over all physical CPU cores */
        Recipient::Broadcast =>
        {
            for (_, mailbox) in MAILBOXES.lock().iter_mut()
            {
                mailbox.push_back(msg.clone())
            }
        },

        /* send to a particular physical CPU core */
        Recipient::PhysicalCore(pid) =>
        {
            if let Some(mailbox) = MAILBOXES.lock().get_mut(&pid)
            {
                mailbox.push_back(msg);
            }
            else
            {
                return Err(Cause::PhysicalCoreBadID);
            }
        },

        /* send to a service */
        Recipient::Service(_) =>
        {
            return service::send(msg);
        }
    };

    Ok(())
}
<|endoftext|>


<commit_before><commit_msg>Update dart_runtime test to follow conventions<commit_after><|endoftext|>


<commit_before>#![crate_id = "jit_macro"]
#![comment = "LibJIT Macro"]
#![crate_type = "dylib"]
#![feature(quote, globs, plugin_registrar, managed_boxes)]
#![deny(non_uppercase_statics, missing_doc, unnecessary_parens, unrecognized_lint, unreachable_code, unnecessary_allocation, unnecessary_typecast, unnecessary_allocation, uppercase_variables, non_camel_case_types, unused_must_use)]
//! This crate provides a macro `jit_type` which can compile a Rust type
//! into its LibJIT counterpart. It even supports functions *and* structs!
//!
//! For example:
//!
//! ```rust
//! #![feature(phase)]
//! extern crate jit;
//! #[phase(syntax)]
//! extern crate jit_macro;
//! fn main() {
//! 	let ty = jit_type!(i64);
//! 	assert_eq(ty.get_size(), 8);
//! 	let floor_sig = jit_type!((f64) -> i32);
//! 	let double_array_ty = jit_type!({
//! 		len: int,
//! 		ptr: **f64
//! 	});
//! }
//! ```
extern crate syntax;
extern crate rustc;
use rustc::plugin::Registry;
use syntax::ext::quote::rt::ToSource;
use syntax::ast::*;
use syntax::codemap::Span;
use syntax::ext::base::*;
use syntax::ext::build::AstBuilder;
use syntax::parse::token::*;
use std::iter::Peekable;
use std::slice::Items;
#[plugin_registrar]
/// Register the `jit_type` macro on the registry
pub fn plugin_registrar(reg:&mut Registry) {
	reg.register_macro("jit_type", jit_type)
}
fn error(e:Option<TokenTree>, tok:Token) -> Result<(), String> {
	match e {
		Some(TTTok(_, ref got)) => Err(format!("Bad syntax - got {} but expected {}", got, tok)),
		Some(TTNonterminal(_, ref got)) => Err(format!("Bad syntax - got {} but expected {}", got, tok)),
		Some(TTDelim(ref trees)) => error(Some(trees.deref().get(0).clone()), tok),
		Some(_) => Err("Bad syntax".into_string()),
		None => Err("Abrupt end".into_string())
	}
}
fn expect<'a>(_: &mut ExtCtxt, tts:&mut Peekable<&'a TokenTree, Items<'a, TokenTree>>, tok:Token) -> Result<(), String> {
	match tts.next().map(|v| v.clone()) {
		Some(TTTok(_, ref got)) if *got == tok => {
			Ok(())
		},
		Some(TTDelim(ref trees)) => {
			let curr = trees.deref().get(0).clone();
			match curr {
				TTTok(_, ref got) if *got == tok =>
					Ok(()),
				TTTok(_, ref tok) =>
					Err(format!("Bad result {}", tok)),
				_ =>
					Err("Bad result".into_string())
			}
		},
		v => error(v, tok)
	}
}
fn jit_parse_type<'a>(cx: &mut ExtCtxt, tts:&mut Peekable<&'a TokenTree, Items<'a, TokenTree>>) -> Result<P<Expr>, String> {
	let val:Option<&TokenTree> = tts.peek().map(|v|*v);
	match val {
		Some(ref val) => {
			match **val {
				TTDelim(ref toks) => {
					match *toks.get(0) {
						TTTok(span, LPAREN) => {
							let toks = toks.slice(1, toks.len() - 1);
							if toks.len() == 0 {
								Ok(quote_expr!(cx, ::jit::Types::get_void()))
							} else {
								let mut arg_types : Vec<P<Expr>> = Vec::new();
								{
									let mut tts = toks.iter().peekable();
									loop {
										let parsed = try!(jit_parse_type(cx, &mut tts));
										arg_types.push(cx.expr_addr_of(parsed.span, parsed));
										match tts.next() {
											Some(v) =>
												match *v {
													TTTok(_, COMMA) => {
														continue;
													},
													_ => return Err("Unexpected token in function decl".into_string())
												},
											None => break
										}
									}
								}
								tts.next();
								try!(expect(cx, tts, RARROW));
								let parsed_ret = try!(jit_parse_type(cx, tts));
								let ret = cx.expr_addr_of(parsed_ret.span, parsed_ret);
								let arg_types_ex = cx.expr(span, ExprVec(arg_types));
								let func = quote_expr!(&mut*cx, ::jit::Type::create_signature);
								let cdecl = quote_expr!(&mut*cx, ::jit::CDECL);
								let arg_types_ex = cx.expr_mut_addr_of(arg_types_ex.span, arg_types_ex);
								let arg_types_ex = cx.expr_method_call(span, arg_types_ex, cx.ident_of("as_mut_slice"), vec!());
								let call = cx.expr_call(span, func, vec!(cdecl, ret, arg_types_ex));
								Ok(call)

							}
						},
						TTTok(span, LBRACE) => {
							let toks = toks.slice(1, toks.len() - 1);
							let mut tts = toks.iter().peekable();
							let mut names:Vec<P<Expr>> = Vec::new();
							let mut types:Vec<P<Expr>> = Vec::new();
							loop {
								match tts.next() {
									Some(ref v) => {
										match **v {
											TTTok(_, COMMA) =>
												continue,
											TTTok(span, IDENT(field_name, _)) => {
												try!(expect(cx, &mut tts, COLON));
												let field_type = try!(jit_parse_type(cx, &mut tts));
												names.push(cx.expr_method_call(span, cx.expr_str(span, get_ident(field_name)), cx.ident_of("into_string"), vec!()));
												types.push(cx.expr_addr_of(field_type.span, field_type));
											},
											_ => return Err("Expected ident".into_string())
										}
									},
									None => break
								}
							}
							let func = quote_expr!(&mut*cx, ::jit::Type::create_struct);
							let types_vec = cx.expr(span, ExprVec(types));
							let types_vec_slices = cx.expr_method_call(span, types_vec, cx.ident_of("as_mut_slice"), vec!());
							let names_vec = cx.expr(span, ExprVec(names));
							let ident_type = cx.ident_of("ty");
							let final = cx.expr_block(cx.block(span, vec!(
								cx.stmt_let(span, false, ident_type, cx.expr_call(span, func, vec!(types_vec_slices))),
								cx.stmt_expr(cx.expr_method_call(span, cx.expr_ident(span, ident_type), cx.ident_of("set_names"), vec!(names_vec)))
							), Some(cx.expr_ident(span, ident_type))));
							println!("{}", final.to_source());
							Ok(final)
						},
						_ => Err("Expected bracket".into_string())
					}
				},
				TTTok(_, IDENT(ident, _)) => {
					let ident = ident.to_source();
					let type_id = match ident.as_slice() {
						"void" => "void",
						"int" | "i32" => "int",
						"uint" | "u32" => "uint",
						"i64" => "long",
						"u64" => "ulong",
						"char" => "char",
						"bool" => "bool",
						"f64" => "float64",
						"f32" => "float32",
						"String" => "cstring",
						"Vec" => "vec",
						id => return Err(format!("Unexpected identifier {}", id))
					};
					let full_type_id = cx.ident_of("get_".into_string().append(type_id).as_slice());
					tts.next();
					Ok(quote_expr!(cx, ::jit::Types::$full_type_id()))
				},
				TTTok(span, BINOP(STAR)) => {
					tts.next();
					let func = quote_expr!(&mut*cx, ::jit::Type::create_pointer);
					let parsed_type = try!(jit_parse_type(&mut*cx, tts));
					let wv = cx.expr_addr_of(span, parsed_type);
					Ok(cx.expr_call(span, func, vec!(wv)))
				},
				TTTok(_, DOLLAR) => {
					tts.next();
					match tts.next() {
						Some(ref tks) => {
							match **tks {
								TTTok(span, IDENT(ident, _)) => {
									Ok(cx.expr_ident(span, ident))
								}
								_ => Err("Expected next".into_string())
							}
						},
						None => Err("Abrupt end".into_string())
					}
				},
				ref token => {
					try!(error(Some(token.clone()), DOLLAR));
					Err("...".into_string())
				}
			}
		},
		None =>
			Err("Not a type".into_string())
	}
}
fn jit_type(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult> {
	let mut iter = tts.iter().peekable();
	MacExpr::new(match jit_parse_type(cx, &mut iter) {
		Ok(v) => v,
		Err(e) => {
			cx.span_err(sp, e.as_slice());
			quote_expr!(cx, fail!("Invalid"))
		}
	})
}<commit_msg>Rename `jit_type` generator function to `expand_jit_type`<commit_after>#![crate_id = "jit_macro"]
#![comment = "LibJIT Macro"]
#![crate_type = "dylib"]
#![feature(quote, globs, plugin_registrar, managed_boxes)]
#![deny(non_uppercase_statics, missing_doc, unnecessary_parens, unrecognized_lint, unreachable_code, unnecessary_allocation, unnecessary_typecast, unnecessary_allocation, uppercase_variables, non_camel_case_types, unused_must_use)]
//! This crate provides a macro `jit_type` which can compile a Rust type
//! into its LibJIT counterpart. It even supports functions *and* structs!
//!
//! For example:
//!
//! ```rust
//! #![feature(phase)]
//! extern crate jit;
//! #[phase(syntax)]
//! extern crate jit_macro;
//! fn main() {
//! 	let ty = jit_type!(i64);
//! 	assert_eq(ty.get_size(), 8);
//! 	let floor_sig = jit_type!((f64) -> i32);
//! 	let double_array_ty = jit_type!({
//! 		len: int,
//! 		ptr: **f64
//! 	});
//! }
//! ```
extern crate syntax;
extern crate rustc;
use rustc::plugin::Registry;
use syntax::ext::quote::rt::ToSource;
use syntax::ast::*;
use syntax::codemap::Span;
use syntax::ext::base::*;
use syntax::ext::build::AstBuilder;
use syntax::parse::token::*;
use std::iter::Peekable;
use std::slice::Items;
#[plugin_registrar]
/// Register the `jit_type` macro on the registry
pub fn plugin_registrar(reg:&mut Registry) {
	reg.register_macro("jit_type", expand_jit_type)
}
fn error(e:Option<TokenTree>, tok:Token) -> Result<(), String> {
	match e {
		Some(TTTok(_, ref got)) => Err(format!("Bad syntax - got {} but expected {}", got, tok)),
		Some(TTNonterminal(_, ref got)) => Err(format!("Bad syntax - got {} but expected {}", got, tok)),
		Some(TTDelim(ref trees)) => error(Some(trees.deref().get(0).clone()), tok),
		Some(_) => Err("Bad syntax".into_string()),
		None => Err("Abrupt end".into_string())
	}
}
fn expect<'a>(_: &mut ExtCtxt, tts:&mut Peekable<&'a TokenTree, Items<'a, TokenTree>>, tok:Token) -> Result<(), String> {
	match tts.next().map(|v| v.clone()) {
		Some(TTTok(_, ref got)) if *got == tok => {
			Ok(())
		},
		Some(TTDelim(ref trees)) => {
			let curr = trees.deref().get(0).clone();
			match curr {
				TTTok(_, ref got) if *got == tok =>
					Ok(()),
				TTTok(_, ref tok) =>
					Err(format!("Bad result {}", tok)),
				_ =>
					Err("Bad result".into_string())
			}
		},
		v => error(v, tok)
	}
}
fn jit_parse_type<'a>(cx: &mut ExtCtxt, tts:&mut Peekable<&'a TokenTree, Items<'a, TokenTree>>) -> Result<P<Expr>, String> {
	let val:Option<&TokenTree> = tts.peek().map(|v|*v);
	match val {
		Some(ref val) => {
			match **val {
				TTDelim(ref toks) => {
					match *toks.get(0) {
						TTTok(span, LPAREN) => {
							let toks = toks.slice(1, toks.len() - 1);
							if toks.len() == 0 {
								Ok(quote_expr!(cx, ::jit::Types::get_void()))
							} else {
								let mut arg_types : Vec<P<Expr>> = Vec::new();
								{
									let mut tts = toks.iter().peekable();
									loop {
										let parsed = try!(jit_parse_type(cx, &mut tts));
										arg_types.push(cx.expr_addr_of(parsed.span, parsed));
										match tts.next() {
											Some(v) =>
												match *v {
													TTTok(_, COMMA) => {
														continue;
													},
													_ => return Err("Unexpected token in function decl".into_string())
												},
											None => break
										}
									}
								}
								tts.next();
								try!(expect(cx, tts, RARROW));
								let parsed_ret = try!(jit_parse_type(cx, tts));
								let ret = cx.expr_addr_of(parsed_ret.span, parsed_ret);
								let arg_types_ex = cx.expr(span, ExprVec(arg_types));
								let func = quote_expr!(&mut*cx, ::jit::Type::create_signature);
								let cdecl = quote_expr!(&mut*cx, ::jit::CDECL);
								let arg_types_ex = cx.expr_mut_addr_of(arg_types_ex.span, arg_types_ex);
								let arg_types_ex = cx.expr_method_call(span, arg_types_ex, cx.ident_of("as_mut_slice"), vec!());
								let call = cx.expr_call(span, func, vec!(cdecl, ret, arg_types_ex));
								Ok(call)

							}
						},
						TTTok(span, LBRACE) => {
							let toks = toks.slice(1, toks.len() - 1);
							let mut tts = toks.iter().peekable();
							let mut names:Vec<P<Expr>> = Vec::new();
							let mut types:Vec<P<Expr>> = Vec::new();
							loop {
								match tts.next() {
									Some(ref v) => {
										match **v {
											TTTok(_, COMMA) =>
												continue,
											TTTok(span, IDENT(field_name, _)) => {
												try!(expect(cx, &mut tts, COLON));
												let field_type = try!(jit_parse_type(cx, &mut tts));
												names.push(cx.expr_method_call(span, cx.expr_str(span, get_ident(field_name)), cx.ident_of("into_string"), vec!()));
												types.push(cx.expr_addr_of(field_type.span, field_type));
											},
											_ => return Err("Expected ident".into_string())
										}
									},
									None => break
								}
							}
							let func = quote_expr!(&mut*cx, ::jit::Type::create_struct);
							let types_vec = cx.expr(span, ExprVec(types));
							let types_vec_slices = cx.expr_method_call(span, types_vec, cx.ident_of("as_mut_slice"), vec!());
							let names_vec = cx.expr(span, ExprVec(names));
							let ident_type = cx.ident_of("ty");
							let final = cx.expr_block(cx.block(span, vec!(
								cx.stmt_let(span, false, ident_type, cx.expr_call(span, func, vec!(types_vec_slices))),
								cx.stmt_expr(cx.expr_method_call(span, cx.expr_ident(span, ident_type), cx.ident_of("set_names"), vec!(names_vec)))
							), Some(cx.expr_ident(span, ident_type))));
							println!("{}", final.to_source());
							Ok(final)
						},
						_ => Err("Expected bracket".into_string())
					}
				},
				TTTok(_, IDENT(ident, _)) => {
					let ident = ident.to_source();
					let type_id = match ident.as_slice() {
						"void" => "void",
						"int" | "i32" => "int",
						"uint" | "u32" => "uint",
						"i64" => "long",
						"u64" => "ulong",
						"char" => "char",
						"bool" => "bool",
						"f64" => "float64",
						"f32" => "float32",
						"String" => "cstring",
						"Vec" => "vec",
						id => return Err(format!("Unexpected identifier {}", id))
					};
					let full_type_id = cx.ident_of("get_".into_string().append(type_id).as_slice());
					tts.next();
					Ok(quote_expr!(cx, ::jit::Types::$full_type_id()))
				},
				TTTok(span, BINOP(STAR)) => {
					tts.next();
					let func = quote_expr!(&mut*cx, ::jit::Type::create_pointer);
					let parsed_type = try!(jit_parse_type(&mut*cx, tts));
					let wv = cx.expr_addr_of(span, parsed_type);
					Ok(cx.expr_call(span, func, vec!(wv)))
				},
				TTTok(_, DOLLAR) => {
					tts.next();
					match tts.next() {
						Some(ref tks) => {
							match **tks {
								TTTok(span, IDENT(ident, _)) => {
									Ok(cx.expr_ident(span, ident))
								}
								_ => Err("Expected next".into_string())
							}
						},
						None => Err("Abrupt end".into_string())
					}
				},
				ref token => {
					try!(error(Some(token.clone()), DOLLAR));
					Err("...".into_string())
				}
			}
		},
		None =>
			Err("Not a type".into_string())
	}
}
fn expand_jit_type(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult> {
	let mut iter = tts.iter().peekable();
	MacExpr::new(match jit_parse_type(cx, &mut iter) {
		Ok(v) => v,
		Err(e) => {
			cx.span_err(sp, e.as_slice());
			quote_expr!(cx, fail!("Invalid"))
		}
	})
}<|endoftext|>


<commit_before>//! Some lints that are only useful in the compiler or crates that use compiler internals, such as
//! Clippy.

use crate::hir::{HirId, Path, QPath, Ty, TyKind};
use crate::lint::{
    EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintArray, LintContext, LintPass,
};
use errors::Applicability;
use rustc_data_structures::fx::FxHashMap;
use syntax::ast::Ident;

declare_lint! {
    pub DEFAULT_HASH_TYPES,
    Allow,
    "forbid HashMap and HashSet and suggest the FxHash* variants"
}

pub struct DefaultHashTypes {
    map: FxHashMap<String, String>,
}

impl DefaultHashTypes {
    pub fn new() -> Self {
        let mut map = FxHashMap::default();
        map.insert("HashMap".to_string(), "FxHashMap".to_string());
        map.insert("HashSet".to_string(), "FxHashSet".to_string());
        Self { map }
    }
}

impl LintPass for DefaultHashTypes {
    fn get_lints(&self) -> LintArray {
        lint_array!(DEFAULT_HASH_TYPES)
    }

    fn name(&self) -> &'static str {
        "DefaultHashTypes"
    }
}

impl EarlyLintPass for DefaultHashTypes {
    fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) {
        let ident_string = ident.to_string();
        if let Some(replace) = self.map.get(&ident_string) {
            let msg = format!(
                "Prefer {} over {}, it has better performance",
                replace, ident_string
            );
            let mut db = cx.struct_span_lint(DEFAULT_HASH_TYPES, ident.span, &msg);
            db.span_suggestion(
                ident.span,
                "use",
                replace.to_string(),
                Applicability::MaybeIncorrect, // FxHashMap, ... needs another import
            );
            db.note(&format!(
                "a `use rustc_data_structures::fx::{}` may be necessary",
                replace
            ))
            .emit();
        }
    }
}

declare_lint! {
    pub USAGE_OF_TY_TYKIND,
    Allow,
    "Usage of `ty::TyKind` outside of the `ty::sty` module"
}

pub struct TyKindUsage;

impl LintPass for TyKindUsage {
    fn get_lints(&self) -> LintArray {
        lint_array!(USAGE_OF_TY_TYKIND)
    }

    fn name(&self) -> &'static str {
        "TyKindUsage"
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TyKindUsage {
    fn check_path(&mut self, cx: &LateContext<'_, '_>, path: &'tcx Path, _: HirId) {
        let segments_iter = path.segments.iter().rev().skip(1).rev();

        if let Some(last) = segments_iter.clone().last() {
            if last.ident.as_str() == "TyKind" {
                let path = Path {
                    span: path.span.with_hi(last.ident.span.hi()),
                    def: path.def,
                    segments: segments_iter.cloned().collect(),
                };

                if let Some(def) = last.def {
                    if def
                        .def_id()
                        .match_path(cx.tcx, &["rustc", "ty", "sty", "TyKind"])
                    {
                        cx.struct_span_lint(
                            USAGE_OF_TY_TYKIND,
                            path.span,
                            "usage of `ty::TyKind::<kind>`",
                        )
                        .span_suggestion(
                            path.span,
                            "try using ty::<kind> directly",
                            "ty".to_string(),
                            Applicability::MaybeIncorrect, // ty maybe needs an import
                        )
                        .emit();
                    }
                }
            }
        }
    }

    fn check_ty(&mut self, cx: &LateContext<'_, '_>, ty: &'tcx Ty) {
        if let TyKind::Path(qpath) = &ty.node {
            if let QPath::Resolved(_, path) = qpath {
                if let Some(last) = path.segments.iter().last() {
                    if last.ident.as_str() == "TyKind" {
                        if let Some(def) = last.def {
                            if def
                                .def_id()
                                .match_path(cx.tcx, &["rustc", "ty", "sty", "TyKind"])
                            {
                                cx.struct_span_lint(
                                    USAGE_OF_TY_TYKIND,
                                    path.span,
                                    "usage of `ty::TyKind`",
                                )
                                .help("try using `ty::Ty` instead")
                                .emit();
                            }
                        }
                    }
                }
            }
        }
    }
}
<commit_msg>Fix bug in TyKind lint<commit_after>//! Some lints that are only useful in the compiler or crates that use compiler internals, such as
//! Clippy.

use crate::hir::{def::Def, HirId, Path, QPath, Ty, TyKind};
use crate::lint::{
    EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintArray, LintContext, LintPass,
};
use errors::Applicability;
use rustc_data_structures::fx::FxHashMap;
use syntax::ast::Ident;

declare_lint! {
    pub DEFAULT_HASH_TYPES,
    Allow,
    "forbid HashMap and HashSet and suggest the FxHash* variants"
}

pub struct DefaultHashTypes {
    map: FxHashMap<String, String>,
}

impl DefaultHashTypes {
    pub fn new() -> Self {
        let mut map = FxHashMap::default();
        map.insert("HashMap".to_string(), "FxHashMap".to_string());
        map.insert("HashSet".to_string(), "FxHashSet".to_string());
        Self { map }
    }
}

impl LintPass for DefaultHashTypes {
    fn get_lints(&self) -> LintArray {
        lint_array!(DEFAULT_HASH_TYPES)
    }

    fn name(&self) -> &'static str {
        "DefaultHashTypes"
    }
}

impl EarlyLintPass for DefaultHashTypes {
    fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) {
        let ident_string = ident.to_string();
        if let Some(replace) = self.map.get(&ident_string) {
            let msg = format!(
                "Prefer {} over {}, it has better performance",
                replace, ident_string
            );
            let mut db = cx.struct_span_lint(DEFAULT_HASH_TYPES, ident.span, &msg);
            db.span_suggestion(
                ident.span,
                "use",
                replace.to_string(),
                Applicability::MaybeIncorrect, // FxHashMap, ... needs another import
            );
            db.note(&format!(
                "a `use rustc_data_structures::fx::{}` may be necessary",
                replace
            ))
            .emit();
        }
    }
}

declare_lint! {
    pub USAGE_OF_TY_TYKIND,
    Allow,
    "Usage of `ty::TyKind` outside of the `ty::sty` module"
}

pub struct TyKindUsage;

impl LintPass for TyKindUsage {
    fn get_lints(&self) -> LintArray {
        lint_array!(USAGE_OF_TY_TYKIND)
    }

    fn name(&self) -> &'static str {
        "TyKindUsage"
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TyKindUsage {
    fn check_path(&mut self, cx: &LateContext<'_, '_>, path: &'tcx Path, _: HirId) {
        let segments_iter = path.segments.iter().rev().skip(1).rev();

        if let Some(last) = segments_iter.clone().last() {
            if last.ident.as_str() == "TyKind" {
                let path = Path {
                    span: path.span.with_hi(last.ident.span.hi()),
                    def: path.def,
                    segments: segments_iter.cloned().collect(),
                };

                match last.def {
                    Some(Def::Err) => (),
                    Some(def)
                        if def
                            .def_id()
                            .match_path(cx.tcx, &["rustc", "ty", "sty", "TyKind"]) =>
                    {
                        cx.struct_span_lint(
                            USAGE_OF_TY_TYKIND,
                            path.span,
                            "usage of `ty::TyKind::<kind>`",
                        )
                        .span_suggestion(
                            path.span,
                            "try using ty::<kind> directly",
                            "ty".to_string(),
                            Applicability::MaybeIncorrect, // ty maybe needs an import
                        )
                        .emit();
                    }
                    _ => (),
                }
            }
        }
    }

    fn check_ty(&mut self, cx: &LateContext<'_, '_>, ty: &'tcx Ty) {
        if let TyKind::Path(qpath) = &ty.node {
            if let QPath::Resolved(_, path) = qpath {
                if let Some(last) = path.segments.iter().last() {
                    if last.ident.as_str() == "TyKind" {
                        if let Some(def) = last.def {
                            if def
                                .def_id()
                                .match_path(cx.tcx, &["rustc", "ty", "sty", "TyKind"])
                            {
                                cx.struct_span_lint(
                                    USAGE_OF_TY_TYKIND,
                                    path.span,
                                    "usage of `ty::TyKind`",
                                )
                                .help("try using `ty::Ty` instead")
                                .emit();
                            }
                        }
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Tests for the `cargo login` command.

use std::fs::{self, File};
use std::io::prelude::*;
use std::path::PathBuf;

use cargo::core::Shell;
use cargo::util::config::Config;
use cargo_test_support::install::cargo_home;
use cargo_test_support::registry::{self, registry_url};
use cargo_test_support::{cargo_process, t};
use toml;

const TOKEN: &str = "test-token";
const TOKEN2: &str = "test-token2";
const ORIGINAL_TOKEN: &str = "api-token";

fn setup_new_credentials() {
    let config = cargo_home().join("credentials");
    setup_new_credentials_at(config);
}

fn setup_new_credentials_toml() {
    let config = cargo_home().join("credentials.toml");
    setup_new_credentials_at(config);
}

fn setup_new_credentials_at(config: PathBuf) {
    t!(fs::create_dir_all(config.parent().unwrap()));
    t!(t!(File::create(&config))
        .write_all(format!(r#"token = "{token}""#, token = ORIGINAL_TOKEN).as_bytes()));
}

fn check_token(expected_token: &str, registry: Option<&str>) -> bool {
    let credentials = cargo_home().join("credentials");
    assert!(credentials.is_file());

    let mut contents = String::new();
    File::open(&credentials)
        .unwrap()
        .read_to_string(&mut contents)
        .unwrap();
    let toml: toml::Value = contents.parse().unwrap();

    let token = match (registry, toml) {
        // A registry has been provided, so check that the token exists in a
        // table for the registry.
        (Some(registry), toml::Value::Table(table)) => table
            .get("registries")
            .and_then(|registries_table| registries_table.get(registry))
            .and_then(|registry_table| match registry_table.get("token") {
                Some(&toml::Value::String(ref token)) => Some(token.as_str().to_string()),
                _ => None,
            }),
        // There is no registry provided, so check the global token instead.
        (None, toml::Value::Table(table)) => table
            .get("registry")
            .and_then(|registry_table| registry_table.get("token"))
            .and_then(|v| match v {
                toml::Value::String(ref token) => Some(token.as_str().to_string()),
                _ => None,
            }),
        _ => None,
    };

    if let Some(token_val) = token {
        token_val == expected_token
    } else {
        false
    }
}

#[cargo_test]
fn login_with_old_credentials() {
    registry::init();

    cargo_process("login --host")
        .arg(registry_url().to_string())
        .arg(TOKEN)
        .run();

    // Ensure that we get the new token for the registry
    assert!(check_token(TOKEN, None));
}

#[cargo_test]
fn login_with_new_credentials() {
    registry::init();
    setup_new_credentials();

    cargo_process("login --host")
        .arg(registry_url().to_string())
        .arg(TOKEN)
        .run();

    // Ensure that we get the new token for the registry
    assert!(check_token(TOKEN, None));
}

#[cargo_test]
fn credentials_work_with_extension() {
    registry::init();
    setup_new_credentials_toml();

    cargo_process("login --host")
        .arg(registry_url().to_string())
        .arg(TOKEN)
        .run();

    // Ensure that we get the new token for the registry
    assert!(check_token(TOKEN, None));
}

#[cargo_test]
fn credentials_ambiguous_filename() {
    registry::init();
    setup_new_credentials();
    setup_new_credentials_toml();

    cargo_process("login --host")
        .arg(registry_url().to_string())
        .arg(TOKEN)
        .with_stderr_contains(
            "\
[WARNING] Both `[..]/credentials` and `[..]/credentials.toml` exist. Using `[..]/credentials`
",
        )
        .run();

    // It should use the value from the one without the extension
    // for backwards compatibility. check_token explicitly checks
    // 'credentials' without the extension, which is what we want.
    assert!(check_token(TOKEN, None));
}

#[cargo_test]
fn login_with_old_and_new_credentials() {
    setup_new_credentials();
    login_with_old_credentials();
}

#[cargo_test]
fn login_without_credentials() {
    registry::init();
    cargo_process("login --host")
        .arg(registry_url().to_string())
        .arg(TOKEN)
        .run();

    // Ensure that we get the new token for the registry
    assert!(check_token(TOKEN, None));
}

#[cargo_test]
fn new_credentials_is_used_instead_old() {
    registry::init();
    setup_new_credentials();

    cargo_process("login --host")
        .arg(registry_url().to_string())
        .arg(TOKEN)
        .run();

    let config = Config::new(Shell::new(), cargo_home(), cargo_home());

    let token = config.get_string("registry.token").unwrap().map(|p| p.val);
    assert_eq!(token.unwrap(), TOKEN);
}

#[cargo_test]
fn registry_credentials() {
    registry::init();
    registry::init_alt2_registry();
    setup_new_credentials();

    let reg = "alternative";

    cargo_process("login --registry")
        .arg(reg)
        .arg(TOKEN)
        .arg("-Zunstable-options")
        .masquerade_as_nightly_cargo()
        .run();

    // Ensure that we have not updated the default token
    assert!(check_token(ORIGINAL_TOKEN, None));

    // Also ensure that we get the new token for the registry
    assert!(check_token(TOKEN, Some(reg)));

    let reg2 = "alternative2";
    cargo_process("login --registry")
        .arg(reg2)
        .arg(TOKEN2)
        .arg("-Zunstable-options")
        .masquerade_as_nightly_cargo()
        .run();

    // Ensure not overwriting 1st alternate registry token with
    // 2nd alternate registry token (see rust-lang/cargo#7701).
    assert!(check_token(ORIGINAL_TOKEN, None));
    assert!(check_token(TOKEN, Some(reg)));
    assert!(check_token(TOKEN2, Some(reg2)));
}
<commit_msg>Remove unneeded flags<commit_after>//! Tests for the `cargo login` command.

use std::fs::{self, File};
use std::io::prelude::*;
use std::path::PathBuf;

use cargo::core::Shell;
use cargo::util::config::Config;
use cargo_test_support::install::cargo_home;
use cargo_test_support::registry::{self, registry_url};
use cargo_test_support::{cargo_process, t};
use toml;

const TOKEN: &str = "test-token";
const TOKEN2: &str = "test-token2";
const ORIGINAL_TOKEN: &str = "api-token";

fn setup_new_credentials() {
    let config = cargo_home().join("credentials");
    setup_new_credentials_at(config);
}

fn setup_new_credentials_toml() {
    let config = cargo_home().join("credentials.toml");
    setup_new_credentials_at(config);
}

fn setup_new_credentials_at(config: PathBuf) {
    t!(fs::create_dir_all(config.parent().unwrap()));
    t!(t!(File::create(&config))
        .write_all(format!(r#"token = "{token}""#, token = ORIGINAL_TOKEN).as_bytes()));
}

fn check_token(expected_token: &str, registry: Option<&str>) -> bool {
    let credentials = cargo_home().join("credentials");
    assert!(credentials.is_file());

    let mut contents = String::new();
    File::open(&credentials)
        .unwrap()
        .read_to_string(&mut contents)
        .unwrap();
    let toml: toml::Value = contents.parse().unwrap();

    let token = match (registry, toml) {
        // A registry has been provided, so check that the token exists in a
        // table for the registry.
        (Some(registry), toml::Value::Table(table)) => table
            .get("registries")
            .and_then(|registries_table| registries_table.get(registry))
            .and_then(|registry_table| match registry_table.get("token") {
                Some(&toml::Value::String(ref token)) => Some(token.as_str().to_string()),
                _ => None,
            }),
        // There is no registry provided, so check the global token instead.
        (None, toml::Value::Table(table)) => table
            .get("registry")
            .and_then(|registry_table| registry_table.get("token"))
            .and_then(|v| match v {
                toml::Value::String(ref token) => Some(token.as_str().to_string()),
                _ => None,
            }),
        _ => None,
    };

    if let Some(token_val) = token {
        token_val == expected_token
    } else {
        false
    }
}

#[cargo_test]
fn login_with_old_credentials() {
    registry::init();

    cargo_process("login --host")
        .arg(registry_url().to_string())
        .arg(TOKEN)
        .run();

    // Ensure that we get the new token for the registry
    assert!(check_token(TOKEN, None));
}

#[cargo_test]
fn login_with_new_credentials() {
    registry::init();
    setup_new_credentials();

    cargo_process("login --host")
        .arg(registry_url().to_string())
        .arg(TOKEN)
        .run();

    // Ensure that we get the new token for the registry
    assert!(check_token(TOKEN, None));
}

#[cargo_test]
fn credentials_work_with_extension() {
    registry::init();
    setup_new_credentials_toml();

    cargo_process("login --host")
        .arg(registry_url().to_string())
        .arg(TOKEN)
        .run();

    // Ensure that we get the new token for the registry
    assert!(check_token(TOKEN, None));
}

#[cargo_test]
fn credentials_ambiguous_filename() {
    registry::init();
    setup_new_credentials();
    setup_new_credentials_toml();

    cargo_process("login --host")
        .arg(registry_url().to_string())
        .arg(TOKEN)
        .with_stderr_contains(
            "\
[WARNING] Both `[..]/credentials` and `[..]/credentials.toml` exist. Using `[..]/credentials`
",
        )
        .run();

    // It should use the value from the one without the extension
    // for backwards compatibility. check_token explicitly checks
    // 'credentials' without the extension, which is what we want.
    assert!(check_token(TOKEN, None));
}

#[cargo_test]
fn login_with_old_and_new_credentials() {
    setup_new_credentials();
    login_with_old_credentials();
}

#[cargo_test]
fn login_without_credentials() {
    registry::init();
    cargo_process("login --host")
        .arg(registry_url().to_string())
        .arg(TOKEN)
        .run();

    // Ensure that we get the new token for the registry
    assert!(check_token(TOKEN, None));
}

#[cargo_test]
fn new_credentials_is_used_instead_old() {
    registry::init();
    setup_new_credentials();

    cargo_process("login --host")
        .arg(registry_url().to_string())
        .arg(TOKEN)
        .run();

    let config = Config::new(Shell::new(), cargo_home(), cargo_home());

    let token = config.get_string("registry.token").unwrap().map(|p| p.val);
    assert_eq!(token.unwrap(), TOKEN);
}

#[cargo_test]
fn registry_credentials() {
    registry::init();
    registry::init_alt2_registry();
    setup_new_credentials();

    let reg = "alternative";

    cargo_process("login --registry").arg(reg).arg(TOKEN).run();

    // Ensure that we have not updated the default token
    assert!(check_token(ORIGINAL_TOKEN, None));

    // Also ensure that we get the new token for the registry
    assert!(check_token(TOKEN, Some(reg)));

    let reg2 = "alternative2";
    cargo_process("login --registry")
        .arg(reg2)
        .arg(TOKEN2)
        .run();

    // Ensure not overwriting 1st alternate registry token with
    // 2nd alternate registry token (see rust-lang/cargo#7701).
    assert!(check_token(ORIGINAL_TOKEN, None));
    assert!(check_token(TOKEN, Some(reg)));
    assert!(check_token(TOKEN2, Some(reg2)));
}
<|endoftext|>