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

//! The Rust prelude
//!
//! Because `std` is required by most serious Rust software, it is
//! imported at the topmost level of every crate by default, as if the
//! first line of each crate was
//!
//! ```ignore
//! extern crate std;
//! ```
//!
//! This means that the contents of std can be accessed from any context
//! with the `std::` path prefix, as in `use std::vec`, `use std::task::spawn`,
//! etc.
//!
//! Additionally, `std` contains a `prelude` module that reexports many of the
//! most common traits, types and functions. The contents of the prelude are
//! imported into every *module* by default.  Implicitly, all modules behave as if
//! they contained the following prologue:
//!
//! ```ignore
//! use std::prelude::*;
//! ```
//!
//! The prelude is primarily concerned with exporting *traits* that are so
//! pervasive that it would be obnoxious to import for every use, particularly
//! those that define methods on primitive types. It does include a few
//! particularly useful standalone functions, like `from_str`, `range`, and
//! `drop`, `spawn`, and `channel`.

// Reexported core operators
pub use kinds::{Copy, Send, Sized, Share};
pub use ops::{Add, Sub, Mul, Div, Rem, Neg, Not};
pub use ops::{BitAnd, BitOr, BitXor};
pub use ops::{Drop, Deref, DerefMut};
pub use ops::{Shl, Shr, Index};
pub use option::{Option, Some, None};
pub use result::{Result, Ok, Err};

// Reexported functions
pub use from_str::from_str;
pub use iter::range;
pub use mem::drop;

// Reexported types and traits

pub use ascii::{Ascii, AsciiCast, OwnedAsciiCast, AsciiStr, IntoBytes};
pub use c_str::ToCStr;
pub use char::Char;
pub use clone::Clone;
pub use cmp::{Eq, Ord, TotalEq, TotalOrd, Ordering, Less, Equal, Greater, Equiv};
pub use container::{Container, Mutable, Map, MutableMap, Set, MutableSet};
pub use iter::{FromIterator, Extendable};
pub use iter::{Iterator, DoubleEndedIterator, RandomAccessIterator, CloneableIterator};
pub use iter::{OrdIterator, MutableDoubleEndedIterator, ExactSize};
pub use num::{Num, NumCast, CheckedAdd, CheckedSub, CheckedMul};
pub use num::{Signed, Unsigned};
pub use num::{Primitive, Int, Float, FloatMath, ToPrimitive, FromPrimitive};
pub use option::Expect;
pub use owned::Box;
pub use path::{GenericPath, Path, PosixPath, WindowsPath};
pub use ptr::RawPtr;
pub use io::{Buffer, Writer, Reader, Seek};
pub use str::{Str, StrVector, StrSlice, OwnedStr, IntoMaybeOwned};
pub use str::{StrAllocating};
pub use to_str::{ToStr, IntoStr};
pub use tuple::{Tuple1, Tuple2, Tuple3, Tuple4};
pub use tuple::{Tuple5, Tuple6, Tuple7, Tuple8};
pub use tuple::{Tuple9, Tuple10, Tuple11, Tuple12};
pub use slice::{CloneableVector, ImmutableCloneableVector, MutableCloneableVector};
pub use slice::{ImmutableVector, MutableVector};
pub use slice::{ImmutableEqVector, ImmutableTotalOrdVector, MutableTotalOrdVector};
pub use slice::{Vector, VectorVector, OwnedVector, MutableVectorAllocating};
pub use string::String;
pub use vec::Vec;

// Reexported runtime types
pub use comm::{sync_channel, channel, SyncSender, Sender, Receiver};
pub use task::spawn;

// Reexported statics
#[cfg(not(test))]
pub use gc::GC;
<commit_msg>std: Add doc(noinline) to the prelude reexports<commit_after>// Copyright 2013 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.

//! The Rust prelude
//!
//! Because `std` is required by most serious Rust software, it is
//! imported at the topmost level of every crate by default, as if the
//! first line of each crate was
//!
//! ```ignore
//! extern crate std;
//! ```
//!
//! This means that the contents of std can be accessed from any context
//! with the `std::` path prefix, as in `use std::vec`, `use std::task::spawn`,
//! etc.
//!
//! Additionally, `std` contains a `prelude` module that reexports many of the
//! most common traits, types and functions. The contents of the prelude are
//! imported into every *module* by default.  Implicitly, all modules behave as if
//! they contained the following prologue:
//!
//! ```ignore
//! use std::prelude::*;
//! ```
//!
//! The prelude is primarily concerned with exporting *traits* that are so
//! pervasive that it would be obnoxious to import for every use, particularly
//! those that define methods on primitive types. It does include a few
//! particularly useful standalone functions, like `from_str`, `range`, and
//! `drop`, `spawn`, and `channel`.

// Reexported core operators
#[doc(noinline)] pub use kinds::{Copy, Send, Sized, Share};
#[doc(noinline)] pub use ops::{Add, Sub, Mul, Div, Rem, Neg, Not};
#[doc(noinline)] pub use ops::{BitAnd, BitOr, BitXor};
#[doc(noinline)] pub use ops::{Drop, Deref, DerefMut};
#[doc(noinline)] pub use ops::{Shl, Shr, Index};
#[doc(noinline)] pub use option::{Option, Some, None};
#[doc(noinline)] pub use result::{Result, Ok, Err};

// Reexported functions
#[doc(noinline)] pub use from_str::from_str;
#[doc(noinline)] pub use iter::range;
#[doc(noinline)] pub use mem::drop;

// Reexported types and traits

#[doc(noinline)] pub use ascii::{Ascii, AsciiCast, OwnedAsciiCast, AsciiStr};
#[doc(noinline)] pub use ascii::IntoBytes;
#[doc(noinline)] pub use c_str::ToCStr;
#[doc(noinline)] pub use char::Char;
#[doc(noinline)] pub use clone::Clone;
#[doc(noinline)] pub use cmp::{Eq, Ord, TotalEq, TotalOrd};
#[doc(noinline)] pub use cmp::{Ordering, Less, Equal, Greater, Equiv};
#[doc(noinline)] pub use container::{Container, Mutable, Map, MutableMap};
#[doc(noinline)] pub use container::{Set, MutableSet};
#[doc(noinline)] pub use iter::{FromIterator, Extendable, ExactSize};
#[doc(noinline)] pub use iter::{Iterator, DoubleEndedIterator};
#[doc(noinline)] pub use iter::{RandomAccessIterator, CloneableIterator};
#[doc(noinline)] pub use iter::{OrdIterator, MutableDoubleEndedIterator};
#[doc(noinline)] pub use num::{Num, NumCast, CheckedAdd, CheckedSub, CheckedMul};
#[doc(noinline)] pub use num::{Signed, Unsigned, Primitive, Int, Float};
#[doc(noinline)] pub use num::{FloatMath, ToPrimitive, FromPrimitive};
#[doc(noinline)] pub use option::Expect;
#[doc(noinline)] pub use owned::Box;
#[doc(noinline)] pub use path::{GenericPath, Path, PosixPath, WindowsPath};
#[doc(noinline)] pub use ptr::RawPtr;
#[doc(noinline)] pub use io::{Buffer, Writer, Reader, Seek};
#[doc(noinline)] pub use str::{Str, StrVector, StrSlice, OwnedStr};
#[doc(noinline)] pub use str::{IntoMaybeOwned, StrAllocating};
#[doc(noinline)] pub use to_str::{ToStr, IntoStr};
#[doc(noinline)] pub use tuple::{Tuple1, Tuple2, Tuple3, Tuple4};
#[doc(noinline)] pub use tuple::{Tuple5, Tuple6, Tuple7, Tuple8};
#[doc(noinline)] pub use tuple::{Tuple9, Tuple10, Tuple11, Tuple12};
#[doc(noinline)] pub use slice::{CloneableVector, ImmutableCloneableVector};
#[doc(noinline)] pub use slice::{MutableCloneableVector, MutableTotalOrdVector};
#[doc(noinline)] pub use slice::{ImmutableVector, MutableVector};
#[doc(noinline)] pub use slice::{ImmutableEqVector, ImmutableTotalOrdVector};
#[doc(noinline)] pub use slice::{Vector, VectorVector, OwnedVector};
#[doc(noinline)] pub use slice::MutableVectorAllocating;
#[doc(noinline)] pub use string::String;
#[doc(noinline)] pub use vec::Vec;

// Reexported runtime types
#[doc(noinline)] pub use comm::{sync_channel, channel};
#[doc(noinline)] pub use comm::{SyncSender, Sender, Receiver};
#[doc(noinline)] pub use task::spawn;

// Reexported statics
#[cfg(not(test))]
#[doc(noinline)] pub use gc::GC;
<|endoftext|>


<commit_before>//! Defines the `MoveGenerator` trait.

use std::mem::uninitialized;
use std::cmp::max;
use uci::SetOption;
use board::*;
use moves::*;
use value::*;
use evaluator::Evaluator;
use bitsets::*;
use utils::BoardGeometry;

/// A trait for move generators.
///
/// A `MoveGenerator` holds a chess position and can:
///
/// * Generate all legal moves, or a subset of all legal moves in the
///   current position.
///
/// * Perform static exchange evaluation for the generated moves.
///
/// * Play a selected move and take it back.
///
/// * Provide a static evaluator bound to the current position.
///
/// * Calculate Zobrist hashes.
///
/// **Important note:** `MoveGenerator` is unaware of repeating
/// positions and rule-50.
pub trait MoveGenerator: Sized + Send + Clone + SetOption {
    /// The type of static evaluator that the implementation works
    /// with.
    type Evaluator: Evaluator;

    /// Creates a new instance, consuming the supplied `Board`
    /// instance.
    ///
    /// Returns `Err(IllegalBoard)` if the position is illegal.
    fn from_board(board: Board) -> Result<Self, IllegalBoard>;

    /// Returns the Zobrist hash value for the underlying `Board`
    /// instance.
    ///
    /// Zobrist hashing is a technique to transform a board position
    /// into a number of a fixed length, with an equal distribution
    /// over all possible numbers, invented by Albert Zobrist. The key
    /// property of this method is that two similar positions generate
    /// entirely different hash numbers.
    ///
    /// **Important note:** This method will be relatively slow if the
    /// implementation calculates the hash value "from
    /// scratch". Inspect the implementation before using `hash` in
    /// time-critical paths. (See `do_move`.)
    fn hash(&self) -> u64;

    /// Returns a reference to the underlying `Board` instance.
    fn board(&self) -> &Board;

    /// Returns a bitboard with all pieces of color `us` that attack
    /// `square`.
    fn attacks_to(&self, us: Color, square: Square) -> Bitboard;

    /// Returns a bitboard with all enemy pieces that attack the king.
    ///
    /// **Important note:** The bitboard of checkers is calculated on
    /// the first call to `checkers`, and is stored in case another
    /// call is made before doing/undoing any moves. In that case
    /// `checkers` returns the saved bitboard instead of
    /// re-calculating it, thus saving time.
    fn checkers(&self) -> Bitboard;

    /// Returns a reference to a static evaluator bound to the current
    /// position.
    fn evaluator(&self) -> &Self::Evaluator;

    /// Generates all legal moves, possibly including some
    /// pseudo-legal moves too.
    ///
    /// The moves are added to `moves`. All generated moves with
    /// pieces other than the king will be legal. Some of the
    /// generated king's moves may be illegal because the destination
    /// square is under attack. This arrangement has two important
    /// advantages:
    ///
    /// * `do_move` can do its work without knowing the set of
    ///   checkers and pinned pieces, so there is no need to keep
    ///   those around.
    ///
    /// * A beta cut-off may make the verification that king's
    ///   destination square is not under attack unnecessary, thus
    ///   saving time.
    ///
    /// The initial move score for the generated moves is
    /// *unspecified*.
    ///
    /// **Note:** A pseudo-legal move is a move that is otherwise
    /// legal, except it might leave the king in check.
    fn generate_all<T: AddMove>(&self, moves: &mut T);

    /// Generates moves for the quiescence search.
    ///
    /// The moves are added to `moves`. This method always generates a
    /// **subset** of the moves generated by `generate_all`:
    ///
    /// * If the king is in check, all legal moves are included.
    ///
    /// * Captures and pawn promotions to queen are always included.
    ///
    /// * If `generate_checks` is `true`, moves that give check are
    ///   included too. Discovered checks and checks given by castling
    ///   can be omitted for speed.
    fn generate_forcing<T: AddMove>(&self, generate_checks: bool, moves: &mut T);

    /// Checks if `move_digest` represents a pseudo-legal move.
    ///
    /// If a move `m` exists that would be generated by
    /// `generate_all` if called for the current position on the
    /// board, and for that move `m.digest() == move_digest`, this
    /// method will return `Some(m)`. Otherwise it will return
    /// `None`. This is useful when playing moves from the
    /// transposition table, without calling `generate_all`.
    fn try_move_digest(&self, move_digest: MoveDigest) -> Option<Move>;

    /// Returns a null move.
    ///
    /// "Null move" is a pseudo-move that changes only the side to
    /// move. It is sometimes useful to include a speculative null
    /// move in the search tree so as to achieve more aggressive
    /// pruning. Null moves are represented as king's moves for which
    /// the origin and destination squares are the same.
    fn null_move(&self) -> Move;

    /// Plays a move on the board.
    ///
    /// It verifies if the move is legal. If the move is legal, the
    /// board is updated and an `u64` value is returned, which should
    /// be XOR-ed with old board's hash value to obtain new board's
    /// hash value. If the move is illegal, `None` is returned without
    /// updating the board. The move passed to this method **must**
    /// have been generated by `generate_all`, `generate_forcing`,
    /// `try_move_digest`, or `null_move` methods for the current
    /// position on the board.
    ///
    /// The moves generated by the `null_move` method are
    /// exceptions. For them `do_move` will return `None` if and only
    /// if the king is in check.
    fn do_move(&mut self, m: Move) -> Option<u64>;

    /// Takes back last played move.
    ///
    /// The move passed to this method **must** be the last move passed
    /// to `do_move`.
    fn undo_move(&mut self, m: Move);

    /// Returns the likely evaluation change (material) to be lost or
    /// gained as a result of a given move.
    ///
    /// This method performs static exchange evaluation (SEE). It
    /// examines the consequence of a series of exchanges on the
    /// destination square after a given move. A positive returned
    /// value indicates a "winning" move. For example, "PxQ" will
    /// always be a win, since the pawn side can choose to stop the
    /// exchange after its pawn is recaptured, and still be ahead. SEE
    /// is just an evaluation calculated without actually trying moves
    /// on the board, and therefore the returned value might be
    /// incorrect.
    ///
    /// The move passed to this method must have been generated by
    /// `generate_all`, `generate_forcing`, `try_move_digest`, or
    /// `null_move` methods for the current position on the board.
    fn evaluate_move(&self, m: Move) -> Value {
        debug_assert!(m.played_piece() < PIECE_NONE);
        debug_assert!(m.captured_piece() <= PIECE_NONE);
        const PIECE_VALUES: [Value; 8] = [10000, 975, 500, 325, 325, 100, 0, 0];

        // This is the square on which all the action takes place.
        let exchange_square = m.dest_square();

        unsafe {
            let geometry = BoardGeometry::get();
            let behind_blocker: &[Bitboard; 64] = geometry.squares_behind_blocker
                                                          .get_unchecked(exchange_square);
            let piece_type: &[Bitboard; 6] = &self.board().pieces.piece_type;
            let color: &[Bitboard; 2] = &self.board().pieces.color;
            let straight_sliders = piece_type[QUEEN] | piece_type[ROOK];
            let diag_sliders = piece_type[QUEEN] | piece_type[BISHOP];

            // These variables will be updated on each capture:
            let mut us = self.board().to_move;
            let mut depth = 0;
            let mut piece;
            let mut orig_square_bb = 1 << m.orig_square();
            let mut attackers_and_defenders = self.attacks_to(WHITE, exchange_square) |
                                              self.attacks_to(BLACK, exchange_square);

            // The `gain` array will hold the total material gained at
            // each `depth`, from the viewpoint of the side that made the
            // last capture (`us`).
            let mut gain: [Value; 34] = uninitialized();
            gain[0] = if m.move_type() == MOVE_PROMOTION {
                piece = Move::piece_from_aux_data(m.aux_data());
                PIECE_VALUES[m.captured_piece()] + PIECE_VALUES[piece] - PIECE_VALUES[PAWN]
            } else {
                piece = m.played_piece();
                *PIECE_VALUES.get_unchecked(m.captured_piece())
            };

            // Examine the possible exchanges, fill the `gain` array.
            'exchange: while orig_square_bb != 0 {
                let current_gain = *gain.get_unchecked(depth);

                // Store a speculative value that will be used if the
                // captured piece happens to be defended.
                let speculative_gain: &mut Value = gain.get_unchecked_mut(depth + 1);
                *speculative_gain = *PIECE_VALUES.get_unchecked(piece) - current_gain;

                if max(-current_gain, *speculative_gain) < 0 {
                    // The side that made the last capture wins even if
                    // the captured piece happens to be defended. So, we
                    // stop here to save precious CPU cycles. Note that
                    // here we may happen to return an incorrect SEE
                    // value, but the sign will be correct, which is by
                    // far the most important information.
                    break;
                }

                // Register that capturing piece's origin square is now vacant.
                attackers_and_defenders &= !orig_square_bb;

                // Consider adding new attackers/defenders, now that
                // capturing piece's origin square is vacant.
                let behind = self.board().occupied &
                             *behind_blocker.get_unchecked(bsf(orig_square_bb));
                if behind & (straight_sliders | diag_sliders) != 0 && piece != KING {
                    attackers_and_defenders |=
                        match behind & straight_sliders &
                              geometry.attacks_from_unsafe(ROOK, exchange_square, behind) {
                            0 => {
                                // Not a straight slider, possibly a diagonal slider.
                                behind & diag_sliders &
                                geometry.attacks_from_unsafe(BISHOP, exchange_square, behind)
                            }
                            bb => {
                                // A straight slider.
                                bb
                            }
                        };
                }

                // Change the side to move.
                us ^= 1;

                // Find the next piece to enter the exchange. (The least
                // valuable piece belonging to the side to move.)
                let candidates = attackers_and_defenders & *color.get_unchecked(us);
                if candidates != 0 {
                    for p in (KING..PIECE_NONE).rev() {
                        let bb = candidates & piece_type[p];
                        if bb != 0 {
                            depth += 1;
                            piece = p;
                            orig_square_bb = lsb(bb);
                            continue 'exchange;
                        }
                    }
                }
                break 'exchange;
            }

            // Negamax the `gain` array for the final static exchange
            // evaluation. (The `gain` array actually represents an unary
            // tree, at each node of which the player can either continue
            // the exchange or back off.)
            while depth > 0 {
                *gain.get_unchecked_mut(depth - 1) = -max(-*gain.get_unchecked(depth - 1),
                                                          *gain.get_unchecked(depth));
                depth -= 1;
            }
            gain[0]
        }
    }
}
<commit_msg>minor optimization in evaluate_move()<commit_after>//! Defines the `MoveGenerator` trait.

use std::mem::uninitialized;
use std::cmp::max;
use uci::SetOption;
use board::*;
use moves::*;
use value::*;
use evaluator::Evaluator;
use bitsets::*;
use utils::BoardGeometry;

/// A trait for move generators.
///
/// A `MoveGenerator` holds a chess position and can:
///
/// * Generate all legal moves, or a subset of all legal moves in the
///   current position.
///
/// * Perform static exchange evaluation for the generated moves.
///
/// * Play a selected move and take it back.
///
/// * Provide a static evaluator bound to the current position.
///
/// * Calculate Zobrist hashes.
///
/// **Important note:** `MoveGenerator` is unaware of repeating
/// positions and rule-50.
pub trait MoveGenerator: Sized + Send + Clone + SetOption {
    /// The type of static evaluator that the implementation works
    /// with.
    type Evaluator: Evaluator;

    /// Creates a new instance, consuming the supplied `Board`
    /// instance.
    ///
    /// Returns `Err(IllegalBoard)` if the position is illegal.
    fn from_board(board: Board) -> Result<Self, IllegalBoard>;

    /// Returns the Zobrist hash value for the underlying `Board`
    /// instance.
    ///
    /// Zobrist hashing is a technique to transform a board position
    /// into a number of a fixed length, with an equal distribution
    /// over all possible numbers, invented by Albert Zobrist. The key
    /// property of this method is that two similar positions generate
    /// entirely different hash numbers.
    ///
    /// **Important note:** This method will be relatively slow if the
    /// implementation calculates the hash value "from
    /// scratch". Inspect the implementation before using `hash` in
    /// time-critical paths. (See `do_move`.)
    fn hash(&self) -> u64;

    /// Returns a reference to the underlying `Board` instance.
    fn board(&self) -> &Board;

    /// Returns a bitboard with all pieces of color `us` that attack
    /// `square`.
    fn attacks_to(&self, us: Color, square: Square) -> Bitboard;

    /// Returns a bitboard with all enemy pieces that attack the king.
    ///
    /// **Important note:** The bitboard of checkers is calculated on
    /// the first call to `checkers`, and is stored in case another
    /// call is made before doing/undoing any moves. In that case
    /// `checkers` returns the saved bitboard instead of
    /// re-calculating it, thus saving time.
    fn checkers(&self) -> Bitboard;

    /// Returns a reference to a static evaluator bound to the current
    /// position.
    fn evaluator(&self) -> &Self::Evaluator;

    /// Generates all legal moves, possibly including some
    /// pseudo-legal moves too.
    ///
    /// The moves are added to `moves`. All generated moves with
    /// pieces other than the king will be legal. Some of the
    /// generated king's moves may be illegal because the destination
    /// square is under attack. This arrangement has two important
    /// advantages:
    ///
    /// * `do_move` can do its work without knowing the set of
    ///   checkers and pinned pieces, so there is no need to keep
    ///   those around.
    ///
    /// * A beta cut-off may make the verification that king's
    ///   destination square is not under attack unnecessary, thus
    ///   saving time.
    ///
    /// The initial move score for the generated moves is
    /// *unspecified*.
    ///
    /// **Note:** A pseudo-legal move is a move that is otherwise
    /// legal, except it might leave the king in check.
    fn generate_all<T: AddMove>(&self, moves: &mut T);

    /// Generates moves for the quiescence search.
    ///
    /// The moves are added to `moves`. This method always generates a
    /// **subset** of the moves generated by `generate_all`:
    ///
    /// * If the king is in check, all legal moves are included.
    ///
    /// * Captures and pawn promotions to queen are always included.
    ///
    /// * If `generate_checks` is `true`, moves that give check are
    ///   included too. Discovered checks and checks given by castling
    ///   can be omitted for speed.
    fn generate_forcing<T: AddMove>(&self, generate_checks: bool, moves: &mut T);

    /// Checks if `move_digest` represents a pseudo-legal move.
    ///
    /// If a move `m` exists that would be generated by
    /// `generate_all` if called for the current position on the
    /// board, and for that move `m.digest() == move_digest`, this
    /// method will return `Some(m)`. Otherwise it will return
    /// `None`. This is useful when playing moves from the
    /// transposition table, without calling `generate_all`.
    fn try_move_digest(&self, move_digest: MoveDigest) -> Option<Move>;

    /// Returns a null move.
    ///
    /// "Null move" is a pseudo-move that changes only the side to
    /// move. It is sometimes useful to include a speculative null
    /// move in the search tree so as to achieve more aggressive
    /// pruning. Null moves are represented as king's moves for which
    /// the origin and destination squares are the same.
    fn null_move(&self) -> Move;

    /// Plays a move on the board.
    ///
    /// It verifies if the move is legal. If the move is legal, the
    /// board is updated and an `u64` value is returned, which should
    /// be XOR-ed with old board's hash value to obtain new board's
    /// hash value. If the move is illegal, `None` is returned without
    /// updating the board. The move passed to this method **must**
    /// have been generated by `generate_all`, `generate_forcing`,
    /// `try_move_digest`, or `null_move` methods for the current
    /// position on the board.
    ///
    /// The moves generated by the `null_move` method are
    /// exceptions. For them `do_move` will return `None` if and only
    /// if the king is in check.
    fn do_move(&mut self, m: Move) -> Option<u64>;

    /// Takes back last played move.
    ///
    /// The move passed to this method **must** be the last move passed
    /// to `do_move`.
    fn undo_move(&mut self, m: Move);

    /// Returns the likely evaluation change (material) to be lost or
    /// gained as a result of a given move.
    ///
    /// This method performs static exchange evaluation (SEE). It
    /// examines the consequence of a series of exchanges on the
    /// destination square after a given move. A positive returned
    /// value indicates a "winning" move. For example, "PxQ" will
    /// always be a win, since the pawn side can choose to stop the
    /// exchange after its pawn is recaptured, and still be ahead. SEE
    /// is just an evaluation calculated without actually trying moves
    /// on the board, and therefore the returned value might be
    /// incorrect.
    ///
    /// The move passed to this method must have been generated by
    /// `generate_all`, `generate_forcing`, `try_move_digest`, or
    /// `null_move` methods for the current position on the board.
    fn evaluate_move(&self, m: Move) -> Value {
        debug_assert!(m.played_piece() < PIECE_NONE);
        debug_assert!(m.captured_piece() <= PIECE_NONE);
        const PIECE_VALUES: [Value; 8] = [10000, 975, 500, 325, 325, 100, 0, 0];

        unsafe {
            let mut piece = m.played_piece();
            let captured_piece = m.captured_piece();

            // Try not to waste CPU cycles when the played piece is
            // less valuable than the captured piece.
            if piece > captured_piece {
                return *PIECE_VALUES.get_unchecked(captured_piece);
            }

            // This is the square on which all the action takes place.
            let exchange_square = m.dest_square();

            let color: &[Bitboard; 2] = &self.board().pieces.color;
            let piece_type: &[Bitboard; 6] = &self.board().pieces.piece_type;
            let straight_sliders = piece_type[QUEEN] | piece_type[ROOK];
            let diag_sliders = piece_type[QUEEN] | piece_type[BISHOP];
            let geometry = BoardGeometry::get();
            let behind_blocker: &[Bitboard; 64] = geometry.squares_behind_blocker
                                                          .get_unchecked(exchange_square);

            // These variables (along with `piece`) will be updated on each capture:
            let mut us = self.board().to_move;
            let mut depth = 0;
            let mut orig_square_bb = 1 << m.orig_square();
            let mut attackers_and_defenders = self.attacks_to(WHITE, exchange_square) |
                                              self.attacks_to(BLACK, exchange_square);

            // The `gain` array will hold the total material gained at
            // each `depth`, from the viewpoint of the side that made the
            // last capture (`us`).
            let mut gain: [Value; 34] = uninitialized();
            gain[0] = if m.move_type() == MOVE_PROMOTION {
                piece = Move::piece_from_aux_data(m.aux_data());
                PIECE_VALUES[captured_piece] + PIECE_VALUES[piece] - PIECE_VALUES[PAWN]
            } else {
                *PIECE_VALUES.get_unchecked(captured_piece)
            };

            // Examine the possible exchanges, fill the `gain` array.
            'exchange: while orig_square_bb != 0 {
                let current_gain = *gain.get_unchecked(depth);

                // Store a speculative value that will be used if the
                // captured piece happens to be defended.
                let speculative_gain: &mut Value = gain.get_unchecked_mut(depth + 1);
                *speculative_gain = *PIECE_VALUES.get_unchecked(piece) - current_gain;

                if max(-current_gain, *speculative_gain) < 0 {
                    // The side that made the last capture wins even if
                    // the captured piece happens to be defended. So, we
                    // stop here to save precious CPU cycles. Note that
                    // here we may happen to return an incorrect SEE
                    // value, but the sign will be correct, which is by
                    // far the most important information.
                    break;
                }

                // Register that capturing piece's origin square is now vacant.
                attackers_and_defenders &= !orig_square_bb;

                // Consider adding new attackers/defenders, now that
                // capturing piece's origin square is vacant.
                let behind = self.board().occupied &
                             *behind_blocker.get_unchecked(bsf(orig_square_bb));
                if behind & (straight_sliders | diag_sliders) != 0 && piece != KING {
                    attackers_and_defenders |=
                        match behind & straight_sliders &
                              geometry.attacks_from_unsafe(ROOK, exchange_square, behind) {
                            0 => {
                                // Not a straight slider, possibly a diagonal slider.
                                behind & diag_sliders &
                                geometry.attacks_from_unsafe(BISHOP, exchange_square, behind)
                            }
                            bb => {
                                // A straight slider.
                                bb
                            }
                        };
                }

                // Change the side to move.
                us ^= 1;

                // Find the next piece to enter the exchange. (The least
                // valuable piece belonging to the side to move.)
                let candidates = attackers_and_defenders & *color.get_unchecked(us);
                if candidates != 0 {
                    for p in (KING..PIECE_NONE).rev() {
                        let bb = candidates & piece_type[p];
                        if bb != 0 {
                            depth += 1;
                            piece = p;
                            orig_square_bb = lsb(bb);
                            continue 'exchange;
                        }
                    }
                }
                break 'exchange;
            }

            // Negamax the `gain` array for the final static exchange
            // evaluation. (The `gain` array actually represents an unary
            // tree, at each node of which the player can either continue
            // the exchange or back off.)
            while depth > 0 {
                *gain.get_unchecked_mut(depth - 1) = -max(-*gain.get_unchecked(depth - 1),
                                                          *gain.get_unchecked(depth));
                depth -= 1;
            }
            gain[0]
        }
    }
}
<|endoftext|>


<commit_before>/// TODO: submit upstream

use std::io::{IoResult, Seek, SeekStyle};
use std::io::{MemReader, MemWriter};

/// Writes to an owned, growable byte vector but also implements read with fail-on-call methods.
struct MemWriterFakeStream(MemWriter);

impl MemWriterFakeStream {
    pub fn new() -> MemWriterFakeStream { MemWriterFakeStream(MemWriter::new()) }
}

impl Writer for MemWriterFakeStream {
    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
        let &MemWriterFakeStream(ref mut s) = self;
        s.write(buf)
    }

    fn flush(&mut self) -> IoResult<()> {
        let &MemWriterFakeStream(ref mut s) = self;
        s.flush()
    }
}

impl Seek for MemWriterFakeStream {
    fn tell(&self) -> IoResult<u64> {
        let &MemWriterFakeStream(ref s) = self;
        s.tell()
    }

    fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
        let &MemWriterFakeStream(ref mut s) = self;
        s.seek(pos, style)
    }
}

impl Reader for MemWriterFakeStream {
    fn read(&mut self, _buf: &mut [u8]) -> IoResult<uint> {
        fail!("Uh oh, you didn't aught to call MemWriterFakeStream.read()!")
    }
}

/// Reads from an owned byte vector, but also implements write with fail-on-call methods.
pub struct MemReaderFakeStream(MemReader);

impl MemReaderFakeStream {
    pub fn new(buf: Vec<u8>) -> MemReaderFakeStream { MemReaderFakeStream(MemReader::new(buf)) }
}

impl Reader for MemReaderFakeStream {
    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
        let &MemReaderFakeStream(ref mut s) = self;
        s.read(buf)
    }
}

impl Seek for MemReaderFakeStream {
    fn tell(&self) -> IoResult<u64> {
        let &MemReaderFakeStream(ref s) = self;
        s.tell()
    }

    fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
        let &MemReaderFakeStream(ref mut s) = self;
        s.seek(pos, style)
    }
}

impl Writer for MemReaderFakeStream {
    fn write(&mut self, _buf: &[u8]) -> IoResult<()> {
        fail!("Uh oh, you didn't aught to call MemReaderFakeStream.write()!")
    }
    fn flush(&mut self) -> IoResult<()> {
        fail!("Uh oh, you didn't aught to call MemReaderFakeStream.flush()!")
    }
}

#[cfg(test)]
mod test {
    use super::{MemReaderFakeStream, MemWriterFakeStream};

    #[test]
    fn test_mem_writer_fake_stream() {
        let mut writer = MemWriterFakeStream::new();
        assert_eq!(writer.tell(),              Ok(0));
        assert_eq!(writer.write([0]),          Ok(()));
        assert_eq!(writer.tell(),              Ok(1));
        assert_eq!(writer.write([1, 2, 3]),    Ok(()));
        assert_eq!(writer.write([4, 5, 6, 7]), Ok(()));
        assert_eq!(writer.tell(),              Ok(8));
    }

    #[test]
    fn test_mem_reader_fake_stream() {
        let mut reader = MemReaderFakeStream::new(vec!(0, 1, 2, 3, 4, 5, 6, 7));
        let mut buf = ~[];
        assert_eq!(reader.read(buf),      Ok(0));
        assert_eq!(reader.tell(),         Ok(0));
        let mut buf = ~[0];
        assert_eq!(reader.read(buf),      Ok(1));
        assert_eq!(reader.tell(),         Ok(1));
        assert_eq!(buf,                   ~[0]);
        let mut buf = ~[0, 0, 0, 0];
        assert_eq!(reader.read(buf),      Ok(4));
        assert_eq!(reader.tell(),         Ok(5));
        assert_eq!(buf,                   ~[1, 2, 3, 4]);
        assert_eq!(reader.read(buf),      Ok(3));
        assert_eq!(buf.slice(0, 3),       &[5, 6, 7]);
        assert_eq!(reader.read(buf).ok(), None);
    }
}
<commit_msg>Rust update: ~[T] is no longer in the language.<commit_after>/// TODO: submit upstream

use std::io::{IoResult, Seek, SeekStyle};
use std::io::{MemReader, MemWriter};

/// Writes to an owned, growable byte vector but also implements read with fail-on-call methods.
struct MemWriterFakeStream(MemWriter);

impl MemWriterFakeStream {
    pub fn new() -> MemWriterFakeStream { MemWriterFakeStream(MemWriter::new()) }
}

impl Writer for MemWriterFakeStream {
    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
        let &MemWriterFakeStream(ref mut s) = self;
        s.write(buf)
    }

    fn flush(&mut self) -> IoResult<()> {
        let &MemWriterFakeStream(ref mut s) = self;
        s.flush()
    }
}

impl Seek for MemWriterFakeStream {
    fn tell(&self) -> IoResult<u64> {
        let &MemWriterFakeStream(ref s) = self;
        s.tell()
    }

    fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
        let &MemWriterFakeStream(ref mut s) = self;
        s.seek(pos, style)
    }
}

impl Reader for MemWriterFakeStream {
    fn read(&mut self, _buf: &mut [u8]) -> IoResult<uint> {
        fail!("Uh oh, you didn't aught to call MemWriterFakeStream.read()!")
    }
}

/// Reads from an owned byte vector, but also implements write with fail-on-call methods.
pub struct MemReaderFakeStream(MemReader);

impl MemReaderFakeStream {
    pub fn new(buf: Vec<u8>) -> MemReaderFakeStream { MemReaderFakeStream(MemReader::new(buf)) }
}

impl Reader for MemReaderFakeStream {
    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
        let &MemReaderFakeStream(ref mut s) = self;
        s.read(buf)
    }
}

impl Seek for MemReaderFakeStream {
    fn tell(&self) -> IoResult<u64> {
        let &MemReaderFakeStream(ref s) = self;
        s.tell()
    }

    fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
        let &MemReaderFakeStream(ref mut s) = self;
        s.seek(pos, style)
    }
}

impl Writer for MemReaderFakeStream {
    fn write(&mut self, _buf: &[u8]) -> IoResult<()> {
        fail!("Uh oh, you didn't aught to call MemReaderFakeStream.write()!")
    }
    fn flush(&mut self) -> IoResult<()> {
        fail!("Uh oh, you didn't aught to call MemReaderFakeStream.flush()!")
    }
}

#[cfg(test)]
mod test {
    use super::{MemReaderFakeStream, MemWriterFakeStream};

    #[test]
    fn test_mem_writer_fake_stream() {
        let mut writer = MemWriterFakeStream::new();
        assert_eq!(writer.tell(),              Ok(0));
        assert_eq!(writer.write([0]),          Ok(()));
        assert_eq!(writer.tell(),              Ok(1));
        assert_eq!(writer.write([1, 2, 3]),    Ok(()));
        assert_eq!(writer.write([4, 5, 6, 7]), Ok(()));
        assert_eq!(writer.tell(),              Ok(8));
    }

    #[test]
    fn test_mem_reader_fake_stream() {
        let mut reader = MemReaderFakeStream::new(vec!(0, 1, 2, 3, 4, 5, 6, 7));
        let mut buf = vec![];
        assert_eq!(reader.read(buf),      Ok(0));
        assert_eq!(reader.tell(),         Ok(0));
        let mut buf = vec![0];
        assert_eq!(reader.read(buf),      Ok(1));
        assert_eq!(reader.tell(),         Ok(1));
        assert_eq!(buf,                   vec![0]);
        let mut buf = vec![0, 0, 0, 0];
        assert_eq!(reader.read(buf),      Ok(4));
        assert_eq!(reader.tell(),         Ok(5));
        assert_eq!(buf,                   vec[1, 2, 3, 4]);
        assert_eq!(reader.read(buf),      Ok(3));
        assert_eq!(buf.slice(0, 3),       &[5, 6, 7]);
        assert_eq!(reader.read(buf).ok(), None);
    }
}
<|endoftext|>


<commit_before>extern crate orbclient;

extern crate tetrahedrane;

use tetrahedrane::vid::*;
use tetrahedrane::start;

fn main() {

    let mut window = start::Window::new(640, 480, "Hello!", 1 as usize);

    window.window.set(Color::new(20, 40, 60).orb_color());

    let point1 = DepthPoint::new(0.0, -0.5, 2.0);
    let point2 = DepthPoint::new(0.5, 0.7, 2.0);
    let point3 = DepthPoint::new(-0.5, 2.0, 1.0);

    let triangle = Triangle::new(point1, point2, point3, 0.0, 0.0, 0.0, Color::new(200, 200, 200));

    'game_loop: loop {
        window.window.set(Color::new(20, 40, 60).orb_color());

        window.camera_z += 0.01;

        window.render_queue.push(triangle);

        window.render();

        window.window.sync();
        std::thread::sleep(std::time::Duration::from_millis(33));
    }
}<commit_msg>random<commit_after>extern crate orbclient;

extern crate tetrahedrane;

use tetrahedrane::vid::*;
use tetrahedrane::start;

fn main() {

    let mut window = start::Window::new(640, 480, "Hello!", 1 as usize);

    window.window.set(Color::new(20, 40, 60).orb_color());

    let point1 = DepthPoint::new(0.0, -0.5, 3.0);
    let point2 = DepthPoint::new(0.5, 0.7, 3.0);
    let point3 = DepthPoint::new(-0.5, 0.5, 3.0);

    let triangle = Triangle::new(point1, point2, point3, 0.0, 0.0, 0.0, Color::new(200, 200, 200));

    'game_loop: loop {
        window.window.set(Color::new(20, 40, 60).orb_color());

        window.camera_z += 0.01;

        window.render_queue.push(triangle);

        window.render();

        window.window.sync();
        std::thread::sleep(std::time::Duration::from_millis(33));
    }
}<|endoftext|>


<commit_before>/* AES CBC bitflipping attack
 *
 * Dmitry Vasiliev <dima@hlabs.org>
 */

extern crate aes_lib;

use std::rand::random;

use aes_lib::{AES_BLOCK_SIZE, encrypt_aes_cbc, decrypt_aes_cbc};

struct State {
    key: Vec<u8>,
    iv: Vec<u8>,
}

impl State {
    fn new() -> State {
        let key = random_bytes(AES_BLOCK_SIZE);
        let iv = random_bytes(AES_BLOCK_SIZE);
        State{key: key, iv: iv}
    }

    fn encrypt(&self, data: &str) -> Vec<u8> {
        let quoted = data.replace(";", "%3B").replace("=", "%3D");
        let prepend = "comment1=cooking%20MCs;userdata=";
        let append = ";comment2=%20like%20a%20pound%20of%20bacon";
        let to_enc = prepend.into_string() + quoted + append.into_string();
        encrypt_aes_cbc(to_enc.into_bytes().as_slice(), self.key.as_slice(),
                        self.iv.as_slice())
    }

    fn decrypt(&self, encrypted: &[u8]) -> Vec<u8> {
        decrypt_aes_cbc(encrypted, self.key.as_slice(), self.iv.as_slice())
    }

    fn is_admin(&self, dec: &[u8]) -> bool {
        let s = String::from_utf8_lossy(dec);
        s.as_slice().contains(";admin=true;")
    }
}

fn random_bytes(len: uint) -> Vec<u8> {
    Vec::from_fn(len, |_| random::<u8>())
}

fn add_admin(state: &State) -> Vec<u8> {
    let mut enc = state.encrypt("1234567890123456?admin?true");
    // Replace first '?' (0x3f) with ';' (0x3b)
    *enc.get_mut(32) ^= 4;
    // Replace second '?' (0x3f) with '=' (0x3d)
    *enc.get_mut(38) ^= 2;
    enc
}

fn main() {
    let state = State::new();
    let enc = add_admin(&state);
    let dec = state.decrypt(enc.as_slice());
    println!("Decrypted: {}", String::from_utf8_lossy(dec.as_slice()));
    println!("Is admin? {}", state.is_admin(dec.as_slice()));
}
<commit_msg>New slice syntax<commit_after>/* AES CBC bitflipping attack
 *
 * Dmitry Vasiliev <dima@hlabs.org>
 */

extern crate aes_lib;

use std::rand::random;

use aes_lib::{AES_BLOCK_SIZE, encrypt_aes_cbc, decrypt_aes_cbc};

struct State {
    key: Vec<u8>,
    iv: Vec<u8>,
}

impl State {
    fn new() -> State {
        let key = random_bytes(AES_BLOCK_SIZE);
        let iv = random_bytes(AES_BLOCK_SIZE);
        State{key: key, iv: iv}
    }

    fn encrypt(&self, data: &str) -> Vec<u8> {
        let quoted = data.replace(";", "%3B").replace("=", "%3D");
        let prepend = "comment1=cooking%20MCs;userdata=";
        let append = ";comment2=%20like%20a%20pound%20of%20bacon";
        let to_enc = prepend.into_string() + quoted + append.into_string();
        encrypt_aes_cbc(to_enc.into_bytes().as_slice(), self.key.as_slice(),
                        self.iv.as_slice())
    }

    fn decrypt(&self, encrypted: &[u8]) -> Vec<u8> {
        decrypt_aes_cbc(encrypted, self.key.as_slice(), self.iv.as_slice())
    }

    fn is_admin(&self, dec: &[u8]) -> bool {
        let s = String::from_utf8_lossy(dec);
        s.as_slice().contains(";admin=true;")
    }
}

fn random_bytes(len: uint) -> Vec<u8> {
    Vec::from_fn(len, |_| random::<u8>())
}

fn add_admin(state: &State) -> Vec<u8> {
    let mut enc = state.encrypt("1234567890123456?admin?true");
    // Replace first '?' (0x3f) with ';' (0x3b)
    enc[32] ^= 4;
    // Replace second '?' (0x3f) with '=' (0x3d)
    enc[38] ^= 2;
    enc
}

fn main() {
    let state = State::new();
    let enc = add_admin(&state);
    let dec = state.decrypt(enc.as_slice());
    println!("Decrypted: {}", String::from_utf8_lossy(dec.as_slice()));
    println!("Is admin? {}", state.is_admin(dec.as_slice()));
}
<|endoftext|>


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

//!
//! A symbol reader reads one symbol at a time from a source
//!

use std::slice::Iter;
use std::io::Read;
use std::io::Bytes;
use std::str::Chars;
use std::marker::PhantomData;

///
/// A symbol reader reads one symbol at a time from a source
///
pub trait SymbolReader<Symbol> {
    ///
    /// Reads the next symbol from this reader
    ///
    fn next_symbol(&mut self) -> Option<Symbol>;
}

///
/// Trait that can be implemented by things that can construct a symbol reader
///
pub trait SymbolSource<'a, Symbol> {
    /// The type of symbol reader that will be generated by this call
    type SymbolReader: SymbolReader<Symbol>+'a;

    /// Returns a new object that can read the symbols from this one
    fn read_symbols(self) -> Self::SymbolReader;
}

impl<'a, Symbol: Clone+'a> SymbolSource<'a, Symbol> for &'a Vec<Symbol> {
    type SymbolReader = Iter<'a, Symbol>;

    fn read_symbols(self) -> Self::SymbolReader {
        self.iter()
    }
}

impl<'a, Symbol: Clone+'a> SymbolReader<Symbol> for Iter<'a, Symbol> {
    fn next_symbol(&mut self) -> Option<Symbol> {
        if let Some(sym) = self.next() {
            Some(sym.clone())
        } else {
            None
        }
    }
}

// Can read from streams

//
// TODO: implementing SymbolSource for Read would be nice
// It's just a matter of calling bytes() on the appropriate type, but Rust's type system is simultaneously too clever and not clever enough
// to allow that in any sensible way. You can't do it an an arbitrary Read because the Bytes object is not sized without knowing its real
// type; I couldn't figure out a trick using the Self type to get around that, though those do work sometimes.
// `impl<X: Read> SymbolReader for X` sounds like a nice idea but Rust thinks X can be anything and won't allow any other implementations
// of the interface, which makes that useless too.
// Screwing around with lifetimes and references like below seems like it should work, but Rust doesn't seem to see the implementations on
// actual streams.
//

/*
impl<'a> SymbolSource<'a, u8> for &'a mut Read {
    type SymbolReader = ByteSymbolReader<Self>;

    fn read_symbols(self) -> Self::SymbolReader {
        ByteSymbolReader::new(self.bytes())
    }
}

impl<'a> SymbolSource<'a, u8> for &'a mut BufRead {
    type SymbolReader = ByteSymbolReader<Self>;

    fn read_symbols(self) -> Self::SymbolReader {
        ByteSymbolReader::new(self.bytes())
    }
}
*/

///
/// The ByteSymbolReader turns a `std::io::Bytes` object into a symbol reader
///
pub struct ByteSymbolReader<Reader: Read> {
    bytes: Bytes<Reader>
}

impl<Reader: Read> ByteSymbolReader<Reader> {
    pub fn new(bytes: Bytes<Reader>) -> ByteSymbolReader<Reader> {
        ByteSymbolReader { bytes: bytes }
    }

    pub fn from(reader: Reader) -> ByteSymbolReader<Reader> {
        Self::new(reader.bytes())
    }
}

impl<Reader: Read> SymbolReader<u8> for ByteSymbolReader<Reader> {
    fn next_symbol(&mut self) -> Option<u8> {
        if let Some(Ok(sym)) = self.bytes.next() {
            Some(sym.clone())
        } else {
            None
        }
    }
}

//
// Can read from strings
//
impl<'a> SymbolSource<'a, char> for &'a str {
    type SymbolReader = Chars<'a>;

    fn read_symbols(self) -> Self::SymbolReader {
        self.chars()
    }
}

impl<'a> SymbolReader<char> for Chars<'a> {
    fn next_symbol(&mut self) -> Option<char> {
        self.next()
    }
}

///
/// Converts symbol streams to vectors
///
pub trait ReaderToVector<Symbol> {
    /// Reads every symbol in this object and returns a vector
    fn to_vec(&mut self) -> Vec<Symbol>;
}

impl<Symbol, Reader: SymbolReader<Symbol>> ReaderToVector<Symbol> for Reader {
    #[inline]
    fn to_vec(&mut self) -> Vec<Symbol> {
        let mut result = vec![];

        while let Some(symbol) = self.next_symbol() {
            result.push(symbol);
        }

        result
    }
}

///
/// A symbol stream that performs mapping of symbols from a source stream
///
pub struct MappedStream<'a, MapFunction, Reader> {
    /// The source stream
    source_stream: &'a mut Reader,

    /// A function used to map a symbol from the source stream to a symbol in the output stream
    mapping_function: MapFunction,
}

///
/// Provides a way to map symbol streams to streams of other types
///
pub trait MapSymbolReader<Symbol> : SymbolReader<Symbol>+Sized {
    /// Maps symbols in this stream to symbols in a new stream
    fn map_symbols<'a, OutputSymbol, MapFunction>(&'a mut self, mapping_function: MapFunction) -> MappedStream<'a, MapFunction, Self>
    where MapFunction: FnMut(Symbol) -> OutputSymbol;
}

impl<Symbol, Reader: SymbolReader<Symbol>> MapSymbolReader<Symbol> for Reader {
    fn map_symbols<'a, OutputSymbol, MapFunction>(&'a mut self, mapping_function: MapFunction) -> MappedStream<'a, MapFunction, Self>
    where MapFunction: FnMut(Symbol) -> OutputSymbol {
        MappedStream {
            source_stream:    self,
            mapping_function: mapping_function,
        }
    }
}

impl<'a, InputSymbol, OutputSymbol, MapFunction, Reader: SymbolReader<InputSymbol>+'a> SymbolReader<OutputSymbol> for MappedStream<'a, MapFunction, Reader>
where MapFunction: FnMut(InputSymbol) -> OutputSymbol {
    fn next_symbol(&mut self) -> Option<OutputSymbol> {
        if let Some(input_symbol) = self.source_stream.next_symbol() {
            Some((self.mapping_function)(input_symbol))
        } else {
            None
        }
    }
}

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

    #[test]
    fn can_read_from_vec() {
        let source      = vec![1, 2, 3];
        let mut reader  = source.read_symbols();

        assert!(reader.next_symbol() == Some(1));
        assert!(reader.next_symbol() == Some(2));
        assert!(reader.next_symbol() == Some(3));
        assert!(reader.next_symbol() == None);
    }

    #[test]
    fn can_convert_to_vec() {
        let source      = vec![1, 2, 3];
        let mut reader  = source.read_symbols();
        let result      = reader.to_vec();

        assert!(result == vec![1, 2, 3]);
    }

    #[test]
    fn can_map_stream() {
        let source      = vec![1, 2, 3];
        let mut reader  = source.read_symbols();
        let result      = reader.map_symbols(|sym| sym+1).to_vec();

        assert!(result == vec![2, 3, 4]);
    }

    #[test]
    fn can_read_from_bytes_reader() {
        let array: [u8; 3] = [1, 2, 3];
        let slice = &array[..];
        let mut reader = ByteSymbolReader::from(slice);

        assert!(reader.next_symbol() == Some(1));
        assert!(reader.next_symbol() == Some(2));
        assert!(reader.next_symbol() == Some(3));
        assert!(reader.next_symbol() == None);
    }

    #[test]
    fn can_read_from_string_reader() {
        let mut reader = "abc".read_symbols();

        assert!(reader.next_symbol() == Some('a'));
        assert!(reader.next_symbol() == Some('b'));
        assert!(reader.next_symbol() == Some('c'));
        assert!(reader.next_symbol() == None);
    }
}
<commit_msg>Need to re-introduce phantom data for this, I think<commit_after>//
//   Copyright 2016 Andrew Hunter
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

//!
//! A symbol reader reads one symbol at a time from a source
//!

use std::slice::Iter;
use std::io::Read;
use std::io::Bytes;
use std::str::Chars;
use std::marker::PhantomData;

///
/// A symbol reader reads one symbol at a time from a source
///
pub trait SymbolReader<Symbol> {
    ///
    /// Reads the next symbol from this reader
    ///
    fn next_symbol(&mut self) -> Option<Symbol>;
}

///
/// Trait that can be implemented by things that can construct a symbol reader
///
pub trait SymbolSource<'a, Symbol> {
    /// The type of symbol reader that will be generated by this call
    type SymbolReader: SymbolReader<Symbol>+'a;

    /// Returns a new object that can read the symbols from this one
    fn read_symbols(self) -> Self::SymbolReader;
}

impl<'a, Symbol: Clone+'a> SymbolSource<'a, Symbol> for &'a Vec<Symbol> {
    type SymbolReader = Iter<'a, Symbol>;

    fn read_symbols(self) -> Self::SymbolReader {
        self.iter()
    }
}

impl<'a, Symbol: Clone+'a> SymbolReader<Symbol> for Iter<'a, Symbol> {
    fn next_symbol(&mut self) -> Option<Symbol> {
        if let Some(sym) = self.next() {
            Some(sym.clone())
        } else {
            None
        }
    }
}

// Can read from streams

//
// TODO: implementing SymbolSource for Read would be nice
// It's just a matter of calling bytes() on the appropriate type, but Rust's type system is simultaneously too clever and not clever enough
// to allow that in any sensible way. You can't do it an an arbitrary Read because the Bytes object is not sized without knowing its real
// type; I couldn't figure out a trick using the Self type to get around that, though those do work sometimes.
// `impl<X: Read> SymbolReader for X` sounds like a nice idea but Rust thinks X can be anything and won't allow any other implementations
// of the interface, which makes that useless too.
// Screwing around with lifetimes and references like below seems like it should work, but Rust doesn't seem to see the implementations on
// actual streams.
//

/*
impl<'a> SymbolSource<'a, u8> for &'a mut Read {
    type SymbolReader = ByteSymbolReader<Self>;

    fn read_symbols(self) -> Self::SymbolReader {
        ByteSymbolReader::new(self.bytes())
    }
}

impl<'a> SymbolSource<'a, u8> for &'a mut BufRead {
    type SymbolReader = ByteSymbolReader<Self>;

    fn read_symbols(self) -> Self::SymbolReader {
        ByteSymbolReader::new(self.bytes())
    }
}
*/

///
/// The ByteSymbolReader turns a `std::io::Bytes` object into a symbol reader
///
pub struct ByteSymbolReader<Reader: Read> {
    bytes: Bytes<Reader>
}

impl<Reader: Read> ByteSymbolReader<Reader> {
    pub fn new(bytes: Bytes<Reader>) -> ByteSymbolReader<Reader> {
        ByteSymbolReader { bytes: bytes }
    }

    pub fn from(reader: Reader) -> ByteSymbolReader<Reader> {
        Self::new(reader.bytes())
    }
}

impl<Reader: Read> SymbolReader<u8> for ByteSymbolReader<Reader> {
    fn next_symbol(&mut self) -> Option<u8> {
        if let Some(Ok(sym)) = self.bytes.next() {
            Some(sym.clone())
        } else {
            None
        }
    }
}

//
// Can read from strings
//
impl<'a> SymbolSource<'a, char> for &'a str {
    type SymbolReader = Chars<'a>;

    fn read_symbols(self) -> Self::SymbolReader {
        self.chars()
    }
}

impl<'a> SymbolReader<char> for Chars<'a> {
    fn next_symbol(&mut self) -> Option<char> {
        self.next()
    }
}

///
/// Converts symbol streams to vectors
///
pub trait ReaderToVector<Symbol> {
    /// Reads every symbol in this object and returns a vector
    fn to_vec(&mut self) -> Vec<Symbol>;
}

impl<Symbol, Reader: SymbolReader<Symbol>> ReaderToVector<Symbol> for Reader {
    #[inline]
    fn to_vec(&mut self) -> Vec<Symbol> {
        let mut result = vec![];

        while let Some(symbol) = self.next_symbol() {
            result.push(symbol);
        }

        result
    }
}

///
/// A symbol stream that performs mapping of symbols from a source stream
///
pub struct MappedStream<'a, InputSymbol, MapFunction, Reader>
where Reader: SymbolReader<InputSymbol>+'a {
    /// The source stream
    source_stream: &'a mut Reader,

    /// A function used to map a symbol from the source stream to a symbol in the output stream
    mapping_function: MapFunction,

    // Can't see how to eliminate the input symbol reference due to rust's type system
    #[allow(dead_code)]
    phantom: PhantomData<InputSymbol>
}

///
/// Provides a way to map symbol streams to streams of other types
///
pub trait MapSymbolReader<Symbol> : SymbolReader<Symbol>+Sized {
    /// Maps symbols in this stream to symbols in a new stream
    fn map_symbols<'a, OutputSymbol, MapFunction>(&'a mut self, mapping_function: MapFunction) -> MappedStream<'a, Symbol, MapFunction, Self>
    where MapFunction: FnMut(Symbol) -> OutputSymbol;
}

impl<Symbol, Reader: SymbolReader<Symbol>> MapSymbolReader<Symbol> for Reader {
    fn map_symbols<'a, OutputSymbol, MapFunction>(&'a mut self, mapping_function: MapFunction) -> MappedStream<'a, Symbol, MapFunction, Self>
    where MapFunction: FnMut(Symbol) -> OutputSymbol {
        MappedStream {
            source_stream:    self,
            mapping_function: mapping_function,
            phantom:          PhantomData
        }
    }
}

impl<'a, InputSymbol, OutputSymbol, MapFunction, Reader: SymbolReader<InputSymbol>+'a> SymbolReader<OutputSymbol> for MappedStream<'a, InputSymbol, MapFunction, Reader>
where MapFunction: FnMut(InputSymbol) -> OutputSymbol {
    fn next_symbol(&mut self) -> Option<OutputSymbol> {
        if let Some(input_symbol) = self.source_stream.next_symbol() {
            Some((self.mapping_function)(input_symbol))
        } else {
            None
        }
    }
}

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

    #[test]
    fn can_read_from_vec() {
        let source      = vec![1, 2, 3];
        let mut reader  = source.read_symbols();

        assert!(reader.next_symbol() == Some(1));
        assert!(reader.next_symbol() == Some(2));
        assert!(reader.next_symbol() == Some(3));
        assert!(reader.next_symbol() == None);
    }

    #[test]
    fn can_convert_to_vec() {
        let source      = vec![1, 2, 3];
        let mut reader  = source.read_symbols();
        let result      = reader.to_vec();

        assert!(result == vec![1, 2, 3]);
    }

    #[test]
    fn can_map_stream() {
        let source      = vec![1, 2, 3];
        let mut reader  = source.read_symbols();
        let result      = reader.map_symbols(|sym| sym+1).to_vec();

        assert!(result == vec![2, 3, 4]);
    }

    #[test]
    fn can_read_from_bytes_reader() {
        let array: [u8; 3] = [1, 2, 3];
        let slice = &array[..];
        let mut reader = ByteSymbolReader::from(slice);

        assert!(reader.next_symbol() == Some(1));
        assert!(reader.next_symbol() == Some(2));
        assert!(reader.next_symbol() == Some(3));
        assert!(reader.next_symbol() == None);
    }

    #[test]
    fn can_read_from_string_reader() {
        let mut reader = "abc".read_symbols();

        assert!(reader.next_symbol() == Some('a'));
        assert!(reader.next_symbol() == Some('b'));
        assert!(reader.next_symbol() == Some('c'));
        assert!(reader.next_symbol() == None);
    }
}
<|endoftext|>


<commit_before>#![allow(unused_variables, dead_code)]

use std::ffi::{OsStr, OsString};

pub type ShortArg = u8;
pub type LongArg = &'static str;

#[derive(PartialEq, Debug)]
pub enum Flag {
    Short(ShortArg),
    Long(LongArg),
}

#[derive(PartialEq, Debug)]
pub enum Strictness {
    ComplainAboutRedundantArguments,
    UseLastArguments,
}

#[derive(Copy, Clone, PartialEq, Debug)]
pub enum TakesValue {
    Necessary,
    Forbidden,
}

#[derive(PartialEq, Debug)]
pub struct Arg {
    short: Option<ShortArg>,
    long: LongArg,
    takes_value: TakesValue,
}

#[derive(PartialEq, Debug)]
pub struct Args(&'static [Arg]);

impl Args {
    fn lookup_short<'a>(&self, short: ShortArg) -> Result<&Arg, ParseError<'a>> {
        match self.0.into_iter().find(|arg| arg.short == Some(short)) {
            Some(arg)  => Ok(arg),
            None       => Err(ParseError::UnknownShortArgument { attempt: short })
        }
    }

    fn lookup_long<'a>(&self, long: &'a OsStr) -> Result<&Arg, ParseError<'a>> {
        match self.0.into_iter().find(|arg| arg.long == long) {
            Some(arg)  => Ok(arg),
            None       => Err(ParseError::UnknownArgument { attempt: long })
        }
    }
}

#[derive(PartialEq, Debug)]
pub struct Matches<'a> {
    /// Long and short arguments need to be kept in the same vector, because
    /// we usually want the one nearest the end to count.
    flags: Vec<(Flag, Option<&'a OsStr>)>,
    frees: Vec<&'a OsStr>,
}

#[derive(PartialEq, Debug)]
pub enum ParseError<'a> {
    NeedsValue { flag: Flag },
    ForbiddenValue { flag: Flag },
    UnknownShortArgument { attempt: ShortArg },
    UnknownArgument { attempt: &'a OsStr },
}

fn parse<'a>(args: Args, inputs: &'a [OsString]) -> Result<Matches<'a>, ParseError<'a>> {
    use std::os::unix::ffi::OsStrExt;
    use self::TakesValue::*;

    let mut parsing = true;

    let mut results = Matches {
        flags: Vec::new(),
        frees: Vec::new(),
    };

    let mut iter = inputs.iter();
    while let Some(arg) = iter.next() {
        let bytes = arg.as_bytes();

        if !parsing {
            results.frees.push(arg)
        }
        else if arg == "--" {
            parsing = false;
        }
        else if bytes.starts_with(b"--") {
            let long_arg_name = OsStr::from_bytes(&bytes[2..]);

            if let Some((before, after)) = split_on_equals(long_arg_name) {
                let arg = args.lookup_long(before)?;
                let flag = Flag::Long(arg.long);
                match arg.takes_value {
                    Necessary  => results.flags.push((flag, Some(after))),
                    Forbidden  => return Err(ParseError::ForbiddenValue { flag })
                }
            }
            else {
                let arg = args.lookup_long(long_arg_name)?;
                let flag = Flag::Long(arg.long);
                match arg.takes_value {
                    Forbidden  => results.flags.push((flag, None)),
                    Necessary  => {
                        if let Some(next_arg) = iter.next() {
                            results.flags.push((flag, Some(next_arg)));
                        }
                        else {
                            return Err(ParseError::NeedsValue { flag })
                        }
                    }
                }
            }
        }
        else if bytes.starts_with(b"-") && arg != "-" {
            let short_arg = OsStr::from_bytes(&bytes[1..]);
            if let Some((before, after)) = split_on_equals(short_arg) {
                let (arg_with_value, other_args) = before.as_bytes().split_last().unwrap();

                for byte in other_args {
                    let arg = args.lookup_short(*byte)?;
                    let flag = Flag::Short(*byte);
                    match arg.takes_value {
                        Forbidden  => results.flags.push((flag, None)),
                        Necessary  => return Err(ParseError::NeedsValue { flag })
                    }
                }

                let arg = args.lookup_short(*before.as_bytes().last().unwrap())?;
                let flag = Flag::Short(arg.short.unwrap());
                match arg.takes_value {
                    Necessary  => results.flags.push((flag, Some(after))),
                    Forbidden  => return Err(ParseError::ForbiddenValue { flag })
                }
            }
            else {
                for (index, byte) in bytes.into_iter().enumerate().skip(1) {
                    let arg = args.lookup_short(*byte)?;
                    let flag = Flag::Short(*byte);
                    match arg.takes_value {
                        Forbidden  => results.flags.push((flag, None)),
                        Necessary  => {
                            if index < bytes.len() - 1 {
                                let remnants = &bytes[index+1 ..];
                                results.flags.push((flag, Some(OsStr::from_bytes(remnants))));
                                break;
                            }
                            else if let Some(next_arg) = iter.next() {
                                results.flags.push((flag, Some(next_arg)));
                            }
                            else {
                                return Err(ParseError::NeedsValue { flag })
                            }
                        }
                    }
                }
            }
        }
        else {
            results.frees.push(arg)
        }
    }

    Ok(results)
}

/// Splits a string on its `=` character, returning the two substrings on
/// either side. Returns `None` if there’s no equals or a string is missing.
fn split_on_equals(input: &OsStr) -> Option<(&OsStr, &OsStr)> {
    use std::os::unix::ffi::OsStrExt;

    if let Some(index) = input.as_bytes().iter().position(|elem| *elem == b'=') {
        let (before, after) = input.as_bytes().split_at(index);

        // The after string contains the = that we need to remove.
        if before.len() >= 1 && after.len() >= 2 {
            return Some((OsStr::from_bytes(before),
                         OsStr::from_bytes(&after[1..])))
        }
    }

    None
}

/// Creates an `OSString` (used in tests)
#[cfg(test)]
fn os(input: &'static str) -> OsString {
    let mut os = OsString::new();
    os.push(input);
    os
}

#[cfg(test)]
mod split_test {
    use super::{split_on_equals, os};

    macro_rules! test_split {
        ($name:ident: $input:expr => None) => {
            #[test]
            fn $name() {
                assert_eq!(split_on_equals(&os($input)),
                           None);
            }
        };

        ($name:ident: $input:expr => $before:expr, $after:expr) => {
            #[test]
            fn $name() {
                assert_eq!(split_on_equals(&os($input)),
                           Some((&*os($before), &*os($after))));
            }
        };
    }

    test_split!(empty:   ""   => None);
    test_split!(letter:  "a"  => None);

    test_split!(just:      "="    => None);
    test_split!(intro:     "=bbb" => None);
    test_split!(denou:  "aaa="    => None);
    test_split!(equals: "aaa=bbb" => "aaa", "bbb");

    test_split!(sort: "--sort=size"     => "--sort", "size");
    test_split!(more: "this=that=other" => "this",   "that=other");
}

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

    static TEST_ARGS: &'static [Arg] = &[
        Arg { short: Some(b'l'), long: "long",     takes_value: TakesValue::Forbidden },
        Arg { short: Some(b'v'), long: "verbose",  takes_value: TakesValue::Forbidden },
        Arg { short: Some(b'c'), long: "count",    takes_value: TakesValue::Necessary }
    ];

    #[test]
    fn empty() {
        let bits = [ ];
        let results = parse(Args(TEST_ARGS), &bits);
        assert_eq!(results, Ok(Matches { frees: vec![], flags: vec![] }))
    }

    #[test]
    fn filename() {
        let bits = [ os("exa") ];
        let results = parse(Args(TEST_ARGS), &bits);
        assert_eq!(results, Ok(Matches { frees: vec![ os("exa").as_os_str() ], flags: vec![] }))
    }

    #[test]
    fn the_dashes_do_nothing() {
        let bits = [ os("--") ];
        let results = parse(Args(TEST_ARGS), &bits);
        assert_eq!(results, Ok(Matches { frees: vec![], flags: vec![] }))
    }

    #[test]
    fn but_just_one_does() {
        let bits = [ os("-") ];
        let results = parse(Args(TEST_ARGS), &bits);
        assert_eq!(results, Ok(Matches { frees: vec![ os("-").as_os_str() ], flags: vec![] }))
    }

    // ----- long args --------

    #[test]
    fn as_filename() {
        let bits = [ os("--"), os("--long") ];
        let results = parse(Args(TEST_ARGS), &bits);
        assert_eq!(results, Ok(Matches { frees: vec![os("--long").as_os_str() ], flags: vec![] }))
    }

    #[test]
    fn long() {
        let bits = [ os("--long") ];
        let results = parse(Args(TEST_ARGS), &bits);
        assert_eq!(results, Ok(Matches { frees: vec![], flags: vec![ (Flag::Long("long"), None) ] }))
    }

    #[test]
    fn long_equals() {
        let bits = [ os("--long=equals") ];
        let results = parse(Args(TEST_ARGS), &bits);
        assert_eq!(results, Err(ParseError::ForbiddenValue { flag: Flag::Long("long") }))
    }

    #[test]
    fn no_arg_separate() {
        let bits = [ os("--long"), os("4") ];
        let results = parse(Args(TEST_ARGS), &bits);
        assert_eq!(results, Ok(Matches { frees: vec![ os("4").as_os_str() ], flags: vec![ (Flag::Long("long"), None) ] }))
    }

    #[test]
    fn two_long_flags() {
        let bits = [ os("--long"), os("--verbose") ];
        let results = parse(Args(TEST_ARGS), &bits);
        assert_eq!(results, Ok(Matches { frees: vec![], flags: vec![ (Flag::Long("long"), None), (Flag::Long("verbose"), None) ] }))
    }

    #[test]
    fn no_arg_given() {
        let bits = [ os("--count") ];
        let results = parse(Args(TEST_ARGS), &bits);
        assert_eq!(results, Err(ParseError::NeedsValue { flag: Flag::Long("count") }))
    }

    #[test]
    fn arg_equals() {
        let bits = [ os("--count=4") ];
        let results = parse(Args(TEST_ARGS), &bits);
        assert_eq!(results, Ok(Matches { frees: vec![], flags: vec![ (Flag::Long("count"), Some(os("4").as_os_str())) ] }))
    }

    #[test]
    fn arg_separate() {
        let bits = [ os("--count"), os("4") ];
        let results = parse(Args(TEST_ARGS), &bits);
        assert_eq!(results, Ok(Matches { frees: vec![], flags: vec![ (Flag::Long("count"), Some(os("4").as_os_str())) ] }))
    }

    // ----- short args --------

    #[test]
    fn short_as_filename() {
        let bits = [ os("--"), os("-l") ];
        let results = parse(Args(TEST_ARGS), &bits);
        assert_eq!(results, Ok(Matches { frees: vec![os("-l").as_os_str() ], flags: vec![] }))
    }

    #[test]
    fn short_long() {
        let bits = [ os("-l") ];
        let results = parse(Args(TEST_ARGS), &bits);
        assert_eq!(results, Ok(Matches { frees: vec![], flags: vec![ (Flag::Short(b'l'), None) ] }))
    }

    #[test]
    fn short_long_equals() {
        let bits = [ os("-l=equals") ];
        let results = parse(Args(TEST_ARGS), &bits);
        assert_eq!(results, Err(ParseError::ForbiddenValue { flag: Flag::Short(b'l') }))
    }

    #[test]
    fn short_no_arg_separate() {
        let bits = [ os("-l"), os("4") ];
        let results = parse(Args(TEST_ARGS), &bits);
        assert_eq!(results, Ok(Matches { frees: vec![ os("4").as_os_str() ], flags: vec![ (Flag::Short(b'l'), None) ] }))
    }

    #[test]
    fn short_no_arg_given() {
        let bits = [ os("-c") ];
        let results = parse(Args(TEST_ARGS), &bits);
        assert_eq!(results, Err(ParseError::NeedsValue { flag: Flag::Short(b'c') }))
    }

    #[test]
    fn short_arg_equals() {
        let bits = [ os("-c=4") ];
        let results = parse(Args(TEST_ARGS), &bits);
        assert_eq!(results, Ok(Matches { frees: vec![], flags: vec![ (Flag::Short(b'c'), Some(os("4").as_os_str())) ] }))
    }

    #[test]
    fn short_arg_separate() {
        let bits = [ os("-c"), os("4") ];
        let results = parse(Args(TEST_ARGS), &bits);
        assert_eq!(results, Ok(Matches { frees: vec![], flags: vec![ (Flag::Short(b'c'), Some(os("4").as_os_str())) ] }))
    }

    #[test]
    fn two_short_flags() {
        let bits = [ os("-lv") ];
        let results = parse(Args(TEST_ARGS), &bits);
        assert_eq!(results, Ok(Matches { frees: vec![], flags: vec![ (Flag::Short(b'l'), None), (Flag::Short(b'v'), None) ] }))
    }

    #[test]
    fn two_short_flags_nothing() {
        let bits = [ os("-lctwo") ];
        let results = parse(Args(TEST_ARGS), &bits);
        assert_eq!(results, Ok(Matches { frees: vec![], flags: vec![ (Flag::Short(b'l'), None), (Flag::Short(b'c'), Some(os("two").as_os_str())) ] }))
    }

    #[test]
    fn two_short_flags_equals() {
        let bits = [ os("-lc=two") ];
        let results = parse(Args(TEST_ARGS), &bits);
        assert_eq!(results, Ok(Matches { frees: vec![], flags: vec![ (Flag::Short(b'l'), None), (Flag::Short(b'c'), Some(os("two").as_os_str())) ] }))
    }

    #[test]
    fn two_short_flags_next() {
        let bits = [ os("-lc"), os("two") ];
        let results = parse(Args(TEST_ARGS), &bits);
        assert_eq!(results, Ok(Matches { frees: vec![], flags: vec![ (Flag::Short(b'l'), None), (Flag::Short(b'c'), Some(os("two").as_os_str())) ] }))
    }
}
<commit_msg>Macroify parser tests<commit_after>#![allow(unused_variables, dead_code)]

use std::ffi::{OsStr, OsString};

pub type ShortArg = u8;
pub type LongArg = &'static str;

#[derive(PartialEq, Debug)]
pub enum Flag {
    Short(ShortArg),
    Long(LongArg),
}

#[derive(PartialEq, Debug)]
pub enum Strictness {
    ComplainAboutRedundantArguments,
    UseLastArguments,
}

#[derive(Copy, Clone, PartialEq, Debug)]
pub enum TakesValue {
    Necessary,
    Forbidden,
}

#[derive(PartialEq, Debug)]
pub struct Arg {
    short: Option<ShortArg>,
    long: LongArg,
    takes_value: TakesValue,
}

#[derive(PartialEq, Debug)]
pub struct Args(&'static [Arg]);

impl Args {
    fn lookup_short<'a>(&self, short: ShortArg) -> Result<&Arg, ParseError<'a>> {
        match self.0.into_iter().find(|arg| arg.short == Some(short)) {
            Some(arg)  => Ok(arg),
            None       => Err(ParseError::UnknownShortArgument { attempt: short })
        }
    }

    fn lookup_long<'a>(&self, long: &'a OsStr) -> Result<&Arg, ParseError<'a>> {
        match self.0.into_iter().find(|arg| arg.long == long) {
            Some(arg)  => Ok(arg),
            None       => Err(ParseError::UnknownArgument { attempt: long })
        }
    }
}

#[derive(PartialEq, Debug)]
pub struct Matches<'a> {
    /// Long and short arguments need to be kept in the same vector, because
    /// we usually want the one nearest the end to count.
    flags: Vec<(Flag, Option<&'a OsStr>)>,
    frees: Vec<&'a OsStr>,
}

#[derive(PartialEq, Debug)]
pub enum ParseError<'a> {
    NeedsValue { flag: Flag },
    ForbiddenValue { flag: Flag },
    UnknownShortArgument { attempt: ShortArg },
    UnknownArgument { attempt: &'a OsStr },
}

fn parse<'a>(args: Args, inputs: &'a [OsString]) -> Result<Matches<'a>, ParseError<'a>> {
    use std::os::unix::ffi::OsStrExt;
    use self::TakesValue::*;

    let mut parsing = true;

    let mut results = Matches {
        flags: Vec::new(),
        frees: Vec::new(),
    };

    let mut iter = inputs.iter();
    while let Some(arg) = iter.next() {
        let bytes = arg.as_bytes();

        if !parsing {
            results.frees.push(arg)
        }
        else if arg == "--" {
            parsing = false;
        }
        else if bytes.starts_with(b"--") {
            let long_arg_name = OsStr::from_bytes(&bytes[2..]);

            if let Some((before, after)) = split_on_equals(long_arg_name) {
                let arg = args.lookup_long(before)?;
                let flag = Flag::Long(arg.long);
                match arg.takes_value {
                    Necessary  => results.flags.push((flag, Some(after))),
                    Forbidden  => return Err(ParseError::ForbiddenValue { flag })
                }
            }
            else {
                let arg = args.lookup_long(long_arg_name)?;
                let flag = Flag::Long(arg.long);
                match arg.takes_value {
                    Forbidden  => results.flags.push((flag, None)),
                    Necessary  => {
                        if let Some(next_arg) = iter.next() {
                            results.flags.push((flag, Some(next_arg)));
                        }
                        else {
                            return Err(ParseError::NeedsValue { flag })
                        }
                    }
                }
            }
        }
        else if bytes.starts_with(b"-") && arg != "-" {
            let short_arg = OsStr::from_bytes(&bytes[1..]);
            if let Some((before, after)) = split_on_equals(short_arg) {
                let (arg_with_value, other_args) = before.as_bytes().split_last().unwrap();

                for byte in other_args {
                    let arg = args.lookup_short(*byte)?;
                    let flag = Flag::Short(*byte);
                    match arg.takes_value {
                        Forbidden  => results.flags.push((flag, None)),
                        Necessary  => return Err(ParseError::NeedsValue { flag })
                    }
                }

                let arg = args.lookup_short(*before.as_bytes().last().unwrap())?;
                let flag = Flag::Short(arg.short.unwrap());
                match arg.takes_value {
                    Necessary  => results.flags.push((flag, Some(after))),
                    Forbidden  => return Err(ParseError::ForbiddenValue { flag })
                }
            }
            else {
                for (index, byte) in bytes.into_iter().enumerate().skip(1) {
                    let arg = args.lookup_short(*byte)?;
                    let flag = Flag::Short(*byte);
                    match arg.takes_value {
                        Forbidden  => results.flags.push((flag, None)),
                        Necessary  => {
                            if index < bytes.len() - 1 {
                                let remnants = &bytes[index+1 ..];
                                results.flags.push((flag, Some(OsStr::from_bytes(remnants))));
                                break;
                            }
                            else if let Some(next_arg) = iter.next() {
                                results.flags.push((flag, Some(next_arg)));
                            }
                            else {
                                return Err(ParseError::NeedsValue { flag })
                            }
                        }
                    }
                }
            }
        }
        else {
            results.frees.push(arg)
        }
    }

    Ok(results)
}

/// Splits a string on its `=` character, returning the two substrings on
/// either side. Returns `None` if there’s no equals or a string is missing.
fn split_on_equals(input: &OsStr) -> Option<(&OsStr, &OsStr)> {
    use std::os::unix::ffi::OsStrExt;

    if let Some(index) = input.as_bytes().iter().position(|elem| *elem == b'=') {
        let (before, after) = input.as_bytes().split_at(index);

        // The after string contains the = that we need to remove.
        if before.len() >= 1 && after.len() >= 2 {
            return Some((OsStr::from_bytes(before),
                         OsStr::from_bytes(&after[1..])))
        }
    }

    None
}

/// Creates an `OSString` (used in tests)
#[cfg(test)]
fn os(input: &'static str) -> OsString {
    let mut os = OsString::new();
    os.push(input);
    os
}

#[cfg(test)]
mod split_test {
    use super::{split_on_equals, os};

    macro_rules! test_split {
        ($name:ident: $input:expr => None) => {
            #[test]
            fn $name() {
                assert_eq!(split_on_equals(&os($input)),
                           None);
            }
        };

        ($name:ident: $input:expr => $before:expr, $after:expr) => {
            #[test]
            fn $name() {
                assert_eq!(split_on_equals(&os($input)),
                           Some((&*os($before), &*os($after))));
            }
        };
    }

    test_split!(empty:   ""   => None);
    test_split!(letter:  "a"  => None);

    test_split!(just:      "="    => None);
    test_split!(intro:     "=bbb" => None);
    test_split!(denou:  "aaa="    => None);
    test_split!(equals: "aaa=bbb" => "aaa", "bbb");

    test_split!(sort: "--sort=size"     => "--sort", "size");
    test_split!(more: "this=that=other" => "this",   "that=other");
}

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

    static TEST_ARGS: &'static [Arg] = &[
        Arg { short: Some(b'l'), long: "long",     takes_value: TakesValue::Forbidden },
        Arg { short: Some(b'v'), long: "verbose",  takes_value: TakesValue::Forbidden },
        Arg { short: Some(b'c'), long: "count",    takes_value: TakesValue::Necessary }
    ];

    macro_rules! test {
        ($name:ident: $input:expr => $result:expr) => {
            #[test]
            fn $name() {
                let bits = $input;
                let results = parse(Args(TEST_ARGS), &bits);
                assert_eq!(results, $result);
            }
        };
    }

    // Just filenames
    test!(empty:       []           => Ok(Matches { frees: vec![], flags: vec![] }));
    test!(one_arg:     [os("exa")]  => Ok(Matches { frees: vec![ os("exa").as_os_str() ], flags: vec![] }));

    // Dashes and double dashes
    test!(one_dash:    [os("-")]                 => Ok(Matches { frees: vec![ os("-").as_os_str() ],      flags: vec![] }));
    test!(two_dashes:  [os("--")]                => Ok(Matches { frees: vec![],                           flags: vec![] }));
    test!(two_file:    [os("--"), os("file")]    => Ok(Matches { frees: vec![ os("file").as_os_str() ],   flags: vec![] }));
    test!(two_arg_l:   [os("--"), os("--long")]  => Ok(Matches { frees: vec![ os("--long").as_os_str() ], flags: vec![] }));
    test!(two_arg_s:   [os("--"), os("-l")]      => Ok(Matches { frees: vec![ os("-l").as_os_str() ],     flags: vec![] }));

    // Long args
    test!(long:        [os("--long")]                  => Ok(Matches { frees: vec![],                      flags: vec![ (Flag::Long("long"), None) ] }));
    test!(long_then:   [os("--long"), os("4")]         => Ok(Matches { frees: vec![ os("4").as_os_str() ], flags: vec![ (Flag::Long("long"), None) ] }));
    test!(long_two:    [os("--long"), os("--verbose")] => Ok(Matches { frees: vec![],                      flags: vec![ (Flag::Long("long"), None), (Flag::Long("verbose"), None) ] }));

    // Long args with values
    test!(bad_equals:  [os("--long=equals")]    => Err(ParseError::ForbiddenValue { flag: Flag::Long("long") }));
    test!(no_arg:      [os("--count")]          => Err(ParseError::NeedsValue     { flag: Flag::Long("count") }));
    test!(arg_equals:  [os("--count=4")]        => Ok(Matches { frees: vec![], flags: vec![ (Flag::Long("count"), Some(os("4").as_os_str())) ] }));
    test!(arg_then:    [os("--count"), os("4")] => Ok(Matches { frees: vec![], flags: vec![ (Flag::Long("count"), Some(os("4").as_os_str())) ] }));

    // Short args
    test!(short:       [os("-l")]                  => Ok(Matches { frees: vec![], flags: vec![ (Flag::Short(b'l'), None) ] }));
    test!(short_then:  [os("-l"), os("4")]         => Ok(Matches { frees: vec![ os("4").as_os_str() ], flags: vec![ (Flag::Short(b'l'), None) ] }));
    test!(short_two:   [os("-lv")]                 => Ok(Matches { frees: vec![], flags: vec![ (Flag::Short(b'l'), None), (Flag::Short(b'v'), None) ] }));
    test!(mixed:       [os("-v"), os("--long")]    => Ok(Matches { frees: vec![], flags: vec![ (Flag::Short(b'v'), None), (Flag::Long("long"), None) ] }));

    // Short args with values
    test!(bad_short:          [os("-l=equals")]       => Err(ParseError::ForbiddenValue { flag: Flag::Short(b'l') }));
    test!(short_none:         [os("-c")]              => Err(ParseError::NeedsValue     { flag: Flag::Short(b'c') }));
    test!(short_arg_eq:       [os("-c=4")]            => Ok(Matches { frees: vec![], flags: vec![ (Flag::Short(b'c'), Some(os("4").as_os_str())) ] }));
    test!(short_arg_then:     [os("-c"), os("4")]     => Ok(Matches { frees: vec![], flags: vec![ (Flag::Short(b'c'), Some(os("4").as_os_str())) ] }));
    test!(short_two_together: [os("-lctwo")]          => Ok(Matches { frees: vec![], flags: vec![ (Flag::Short(b'l'), None), (Flag::Short(b'c'), Some(os("two").as_os_str())) ] }));
    test!(short_two_equals:   [os("-lc=two")]         => Ok(Matches { frees: vec![], flags: vec![ (Flag::Short(b'l'), None), (Flag::Short(b'c'), Some(os("two").as_os_str())) ] }));
    test!(short_two_next:     [os("-lc"), os("two")]  => Ok(Matches { frees: vec![], flags: vec![ (Flag::Short(b'l'), None), (Flag::Short(b'c'), Some(os("two").as_os_str())) ] }));
}
<|endoftext|>