<commit_before>// run-pass

// edition:2018
// aux-build:arc_wake.rs

#![feature(async_await, async_closure)]

extern crate arc_wake;

use std::pin::Pin;
use std::future::Future;
use std::sync::{
    Arc,
    atomic::{self, AtomicUsize},
};
use std::task::{Context, Poll};
use arc_wake::ArcWake;

struct Counter {
    wakes: AtomicUsize,
}

impl ArcWake for Counter {
    fn wake(self: Arc<Self>) {
        Self::wake_by_ref(&self)
    }
    fn wake_by_ref(arc_self: &Arc<Self>) {
        arc_self.wakes.fetch_add(1, atomic::Ordering::SeqCst);
    }
}

struct WakeOnceThenComplete(bool);

fn wake_and_yield_once() -> WakeOnceThenComplete { WakeOnceThenComplete(false) }

impl Future for WakeOnceThenComplete {
    type Output = ();
    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
        if self.0 {
            Poll::Ready(())
        } else {
            cx.waker().wake_by_ref();
            self.0 = true;
            Poll::Pending
        }
    }
}

fn async_block(x: u8) -> impl Future<Output = u8> {
    async move {
        wake_and_yield_once().await;
        x
    }
}

fn async_block_with_borrow_named_lifetime<'a>(x: &'a u8) -> impl Future<Output = u8> + 'a {
    async move {
        wake_and_yield_once().await;
        *x
    }
}

fn async_nonmove_block(x: u8) -> impl Future<Output = u8> {
    async move {
        let future = async {
            wake_and_yield_once().await;
            x
        };
        future.await
    }
}

fn async_closure(x: u8) -> impl Future<Output = u8> {
    (async move |x: u8| -> u8 {
        wake_and_yield_once().await;
        x
    })(x)
}

async fn async_fn(x: u8) -> u8 {
    wake_and_yield_once().await;
    x
}

async fn generic_async_fn<T>(x: T) -> T {
    wake_and_yield_once().await;
    x
}

async fn async_fn_with_borrow(x: &u8) -> u8 {
    wake_and_yield_once().await;
    *x
}

async fn async_fn_with_borrow_named_lifetime<'a>(x: &'a u8) -> u8 {
    wake_and_yield_once().await;
    *x
}

fn async_fn_with_impl_future_named_lifetime<'a>(x: &'a u8) -> impl Future<Output = u8> + 'a {
    async move {
        wake_and_yield_once().await;
        *x
    }
}

/* FIXME(cramertj) support when `existential type T<'a, 'b>:;` works
async fn async_fn_multiple_args(x: &u8, _y: &u8) -> u8 {
    await!(wake_and_yield_once());
    *x
}
*/

async fn async_fn_multiple_args_named_lifetime<'a>(x: &'a u8, _y: &'a u8) -> u8 {
    wake_and_yield_once().await;
    *x
}

fn async_fn_with_internal_borrow(y: u8) -> impl Future<Output = u8> {
    async move {
        async_fn_with_borrow_named_lifetime(&y).await
    }
}

async unsafe fn unsafe_async_fn(x: u8) -> u8 {
    wake_and_yield_once().await;
    x
}

unsafe fn unsafe_fn(x: u8) -> u8 {
    x
}

fn async_block_in_unsafe_block(x: u8) -> impl Future<Output = u8> {
    unsafe {
        async move {
            unsafe_fn(unsafe_async_fn(x).await)
        }
    }
}

struct Foo;

trait Bar {
    fn foo() {}
}

impl Foo {
    async fn async_assoc_item(x: u8) -> u8 {
        unsafe {
            unsafe_async_fn(x).await
        }
    }

    async unsafe fn async_unsafe_assoc_item(x: u8) -> u8 {
        unsafe_async_fn(x).await
    }
}

fn test_future_yields_once_then_returns<F, Fut>(f: F)
where
    F: FnOnce(u8) -> Fut,
    Fut: Future<Output = u8>,
{
    let mut fut = Box::pin(f(9));
    let counter = Arc::new(Counter { wakes: AtomicUsize::new(0) });
    let waker = ArcWake::into_waker(counter.clone());
    let mut cx = Context::from_waker(&waker);
    assert_eq!(0, counter.wakes.load(atomic::Ordering::SeqCst));
    assert_eq!(Poll::Pending, fut.as_mut().poll(&mut cx));
    assert_eq!(1, counter.wakes.load(atomic::Ordering::SeqCst));
    assert_eq!(Poll::Ready(9), fut.as_mut().poll(&mut cx));
}

fn main() {
    macro_rules! test {
        ($($fn_name:expr,)*) => { $(
            test_future_yields_once_then_returns($fn_name);
        )* }
    }

    macro_rules! test_with_borrow {
        ($($fn_name:expr,)*) => { $(
            test_future_yields_once_then_returns(|x| {
                async move {
                    $fn_name(&x).await
                }
            });
        )* }
    }

    test! {
        async_block,
        async_nonmove_block,
        async_closure,
        async_fn,
        generic_async_fn,
        async_fn_with_internal_borrow,
        async_block_in_unsafe_block,
        Foo::async_assoc_item,
        |x| {
            async move {
                unsafe { unsafe_async_fn(x).await }
            }
        },
        |x| {
            async move {
                unsafe { Foo::async_unsafe_assoc_item(x).await }
            }
        },
    }
    test_with_borrow! {
        async_block_with_borrow_named_lifetime,
        async_fn_with_borrow,
        async_fn_with_borrow_named_lifetime,
        async_fn_with_impl_future_named_lifetime,
        |x| {
            async move {
                async_fn_multiple_args_named_lifetime(x, x).await
            }
        },
    }
}
<commit_msg>remove redundant async_closure test in async-await.rs<commit_after>// run-pass

// edition:2018
// aux-build:arc_wake.rs

#![feature(async_await, async_closure)]

extern crate arc_wake;

use std::pin::Pin;
use std::future::Future;
use std::sync::{
    Arc,
    atomic::{self, AtomicUsize},
};
use std::task::{Context, Poll};
use arc_wake::ArcWake;

struct Counter {
    wakes: AtomicUsize,
}

impl ArcWake for Counter {
    fn wake(self: Arc<Self>) {
        Self::wake_by_ref(&self)
    }
    fn wake_by_ref(arc_self: &Arc<Self>) {
        arc_self.wakes.fetch_add(1, atomic::Ordering::SeqCst);
    }
}

struct WakeOnceThenComplete(bool);

fn wake_and_yield_once() -> WakeOnceThenComplete { WakeOnceThenComplete(false) }

impl Future for WakeOnceThenComplete {
    type Output = ();
    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
        if self.0 {
            Poll::Ready(())
        } else {
            cx.waker().wake_by_ref();
            self.0 = true;
            Poll::Pending
        }
    }
}

fn async_block(x: u8) -> impl Future<Output = u8> {
    async move {
        wake_and_yield_once().await;
        x
    }
}

fn async_block_with_borrow_named_lifetime<'a>(x: &'a u8) -> impl Future<Output = u8> + 'a {
    async move {
        wake_and_yield_once().await;
        *x
    }
}

fn async_nonmove_block(x: u8) -> impl Future<Output = u8> {
    async move {
        let future = async {
            wake_and_yield_once().await;
            x
        };
        future.await
    }
}

// see async-closure.rs for async_closure

async fn async_fn(x: u8) -> u8 {
    wake_and_yield_once().await;
    x
}

async fn generic_async_fn<T>(x: T) -> T {
    wake_and_yield_once().await;
    x
}

async fn async_fn_with_borrow(x: &u8) -> u8 {
    wake_and_yield_once().await;
    *x
}

async fn async_fn_with_borrow_named_lifetime<'a>(x: &'a u8) -> u8 {
    wake_and_yield_once().await;
    *x
}

fn async_fn_with_impl_future_named_lifetime<'a>(x: &'a u8) -> impl Future<Output = u8> + 'a {
    async move {
        wake_and_yield_once().await;
        *x
    }
}

/* FIXME(cramertj) support when `existential type T<'a, 'b>:;` works
async fn async_fn_multiple_args(x: &u8, _y: &u8) -> u8 {
    await!(wake_and_yield_once());
    *x
}
*/

async fn async_fn_multiple_args_named_lifetime<'a>(x: &'a u8, _y: &'a u8) -> u8 {
    wake_and_yield_once().await;
    *x
}

fn async_fn_with_internal_borrow(y: u8) -> impl Future<Output = u8> {
    async move {
        async_fn_with_borrow_named_lifetime(&y).await
    }
}

async unsafe fn unsafe_async_fn(x: u8) -> u8 {
    wake_and_yield_once().await;
    x
}

unsafe fn unsafe_fn(x: u8) -> u8 {
    x
}

fn async_block_in_unsafe_block(x: u8) -> impl Future<Output = u8> {
    unsafe {
        async move {
            unsafe_fn(unsafe_async_fn(x).await)
        }
    }
}

struct Foo;

trait Bar {
    fn foo() {}
}

impl Foo {
    async fn async_assoc_item(x: u8) -> u8 {
        unsafe {
            unsafe_async_fn(x).await
        }
    }

    async unsafe fn async_unsafe_assoc_item(x: u8) -> u8 {
        unsafe_async_fn(x).await
    }
}

fn test_future_yields_once_then_returns<F, Fut>(f: F)
where
    F: FnOnce(u8) -> Fut,
    Fut: Future<Output = u8>,
{
    let mut fut = Box::pin(f(9));
    let counter = Arc::new(Counter { wakes: AtomicUsize::new(0) });
    let waker = ArcWake::into_waker(counter.clone());
    let mut cx = Context::from_waker(&waker);
    assert_eq!(0, counter.wakes.load(atomic::Ordering::SeqCst));
    assert_eq!(Poll::Pending, fut.as_mut().poll(&mut cx));
    assert_eq!(1, counter.wakes.load(atomic::Ordering::SeqCst));
    assert_eq!(Poll::Ready(9), fut.as_mut().poll(&mut cx));
}

fn main() {
    macro_rules! test {
        ($($fn_name:expr,)*) => { $(
            test_future_yields_once_then_returns($fn_name);
        )* }
    }

    macro_rules! test_with_borrow {
        ($($fn_name:expr,)*) => { $(
            test_future_yields_once_then_returns(|x| {
                async move {
                    $fn_name(&x).await
                }
            });
        )* }
    }

    test! {
        async_block,
        async_nonmove_block,
        async_fn,
        generic_async_fn,
        async_fn_with_internal_borrow,
        async_block_in_unsafe_block,
        Foo::async_assoc_item,
        |x| {
            async move {
                unsafe { unsafe_async_fn(x).await }
            }
        },
        |x| {
            async move {
                unsafe { Foo::async_unsafe_assoc_item(x).await }
            }
        },
    }
    test_with_borrow! {
        async_block_with_borrow_named_lifetime,
        async_fn_with_borrow,
        async_fn_with_borrow_named_lifetime,
        async_fn_with_impl_future_named_lifetime,
        |x| {
            async move {
                async_fn_multiple_args_named_lifetime(x, x).await
            }
        },
    }
}
<|endoftext|>


<commit_before>use super::attestation_parent_hashes::{attestation_parent_hashes, ParentHashesError};
use super::db::stores::{BeaconBlockAtSlotError, BeaconBlockStore, ValidatorStore};
use super::db::{ClientDB, DBError};
use super::message_generation::generate_signed_message;
use super::signature_verification::{
    verify_aggregate_signature_for_indices, SignatureVerificationError,
};
use super::types::Hash256;
use super::types::{AttestationRecord, AttesterMap};
use std::collections::HashSet;
use std::sync::Arc;

#[derive(Debug, PartialEq)]
pub enum AttestationValidationError {
    ParentSlotTooHigh,
    ParentSlotTooLow,
    BlockSlotTooHigh,
    BlockSlotTooLow,
    JustifiedSlotIncorrect,
    InvalidJustifiedBlockHash,
    TooManyObliqueHashes,
    BadCurrentHashes,
    BadObliqueHashes,
    BadAttesterMap,
    IntWrapping,
    PublicKeyCorrupt,
    NoPublicKeyForValidator,
    BadBitfieldLength,
    InvalidBitfield,
    InvalidBitfieldEndBits,
    NoSignatures,
    NonZeroTrailingBits,
    BadAggregateSignature,
    DBError(String),
    OutOfBoundsBitfieldIndex,
}

/// The context against which some attestation should be validated.
pub struct AttestationValidationContext<T>
where
    T: ClientDB + Sized,
{
    /// The slot as determined by the system time.
    pub block_slot: u64,
    /// The slot of the parent of the block that contained this attestation.
    pub parent_block_slot: u64,
    /// The cycle_length as determined by the chain configuration.
    pub cycle_length: u8,
    /// The last justified slot as per the client's view of the canonical chain.
    pub last_justified_slot: u64,
    /// A vec of the hashes of the blocks preceeding the present slot.
    pub recent_block_hashes: Arc<Vec<Hash256>>,
    /// The store containing block information.
    pub block_store: Arc<BeaconBlockStore<T>>,
    /// The store containing validator information.
    pub validator_store: Arc<ValidatorStore<T>>,
    /// A map of (slot, shard_id) to the attestation set of validation indices.
    pub attester_map: Arc<AttesterMap>,
}

impl<T> AttestationValidationContext<T>
where
    T: ClientDB,
{
    /// Validate a (fully deserialized) AttestationRecord against this context.
    ///
    /// The function will return a HashSet of validator indices (canonical validator indices not
    /// attestation indices) if the validation passed successfully, or an error otherwise.
    ///
    /// The attestation's aggregate signature will be verified, therefore the function must able to
    /// access all required validation public keys via the `validator_store`.
    pub fn validate_attestation(
        &self,
        a: &AttestationRecord,
    ) -> Result<HashSet<usize>, AttestationValidationError> {
        /*
         * The attesation slot must be less than or equal to the parent of the slot of the block
         * that contained the attestation.
         */
        if a.slot > self.parent_block_slot {
            return Err(AttestationValidationError::ParentSlotTooHigh);
        }

        /*
         * The slot of this attestation must not be more than cycle_length + 1 distance
         * from the parent_slot of block that contained it.
         */
        if a.slot
            < self
                .parent_block_slot
                .saturating_sub(u64::from(self.cycle_length).saturating_add(1))
        {
            return Err(AttestationValidationError::ParentSlotTooLow);
        }

        /*
         * The attestation justified slot must not be higher than the last_justified_slot of the
         * context.
         */
        if a.justified_slot > self.last_justified_slot {
            return Err(AttestationValidationError::JustifiedSlotIncorrect);
        }

        /*
         * There is no need to include more oblique parents hashes than there are blocks
         * in a cycle.
         */
        if a.oblique_parent_hashes.len() > usize::from(self.cycle_length) {
            return Err(AttestationValidationError::TooManyObliqueHashes);
        }

        /*
         * Retrieve the set of attestation indices for this slot and shard id.
         *
         * This is an array mapping the order that validators will appear in the bitfield to the
         * canonincal index of a validator.
         */
        let attestation_indices = self
            .attester_map
            .get(&(a.slot, a.shard_id))
            .ok_or(AttestationValidationError::BadAttesterMap)?;

        /*
         * The bitfield must be no longer than the minimum required to represent each validator in the
         * attestation indices for this slot and shard id.
         */
        if a.attester_bitfield.num_bytes() != bytes_for_bits(attestation_indices.len()) {
            return Err(AttestationValidationError::BadBitfieldLength);
        }

        /*
         * If there are excess bits in the bitfield because the number of a validators in not a
         * multiple of 8, reject this attestation record.
         *
         * Allow extra set bits would permit mutliple different byte layouts (and therefore hashes) to
         * refer to the same AttesationRecord.
         */
        if a.attester_bitfield.len() > attestation_indices.len() {
            return Err(AttestationValidationError::InvalidBitfieldEndBits);
        }

        /*
         * Generate the parent hashes for this attestation
         */
        let parent_hashes = attestation_parent_hashes(
            self.cycle_length,
            self.block_slot,
            a.slot,
            &self.recent_block_hashes,
            &a.oblique_parent_hashes,
        )?;

        /*
         * The specified justified block hash supplied in the attestation must be in the chain at
         * the given slot number.
         *
         * First, we find the latest parent hash from the parent_hashes array. Then, using the
         * block store (database) we iterate back through the blocks until we find (or fail to
         * find) the justified block hash referenced in the attestation record.
         */
        let latest_parent_hash = parent_hashes
            .last()
            .ok_or(AttestationValidationError::BadCurrentHashes)?;
        match self
            .block_store
            .block_at_slot(&latest_parent_hash, a.justified_slot)?
        {
            Some((ref hash, _)) if *hash == a.justified_block_hash.to_vec() => (),
            _ => return Err(AttestationValidationError::InvalidJustifiedBlockHash),
        };

        /*
         * Generate the message that this attestation aggregate signature must sign across.
         */
        let signed_message = {
            generate_signed_message(
                a.slot,
                &parent_hashes,
                a.shard_id,
                &a.shard_block_hash,
                a.justified_slot,
            )
        };

        let voted_hashset = verify_aggregate_signature_for_indices(
            &signed_message,
            &a.aggregate_sig,
            &attestation_indices,
            &a.attester_bitfield,
            &self.validator_store,
        )?;

        /*
         * If the hashset of voters is None, the signature verification failed.
         */
        match voted_hashset {
            None => Err(AttestationValidationError::BadAggregateSignature),
            Some(hashset) => Ok(hashset),
        }
    }
}

fn bytes_for_bits(bits: usize) -> usize {
    (bits.saturating_sub(1) / 8) + 1
}

impl From<ParentHashesError> for AttestationValidationError {
    fn from(e: ParentHashesError) -> Self {
        match e {
            ParentHashesError::BadCurrentHashes => AttestationValidationError::BadCurrentHashes,
            ParentHashesError::BadObliqueHashes => AttestationValidationError::BadObliqueHashes,
            ParentHashesError::SlotTooLow => AttestationValidationError::BlockSlotTooLow,
            ParentHashesError::SlotTooHigh => AttestationValidationError::BlockSlotTooHigh,
            ParentHashesError::IntWrapping => AttestationValidationError::IntWrapping,
        }
    }
}

impl From<BeaconBlockAtSlotError> for AttestationValidationError {
    fn from(e: BeaconBlockAtSlotError) -> Self {
        match e {
            BeaconBlockAtSlotError::DBError(s) => AttestationValidationError::DBError(s),
            _ => AttestationValidationError::InvalidJustifiedBlockHash,
        }
    }
}

impl From<DBError> for AttestationValidationError {
    fn from(e: DBError) -> Self {
        AttestationValidationError::DBError(e.message)
    }
}

impl From<SignatureVerificationError> for AttestationValidationError {
    fn from(e: SignatureVerificationError) -> Self {
        match e {
            SignatureVerificationError::BadValidatorIndex => {
                AttestationValidationError::BadAttesterMap
            }
            SignatureVerificationError::PublicKeyCorrupt => {
                AttestationValidationError::PublicKeyCorrupt
            }
            SignatureVerificationError::NoPublicKeyForValidator => {
                AttestationValidationError::NoPublicKeyForValidator
            }
            SignatureVerificationError::DBError(s) => AttestationValidationError::DBError(s),
            SignatureVerificationError::OutOfBoundsBitfieldIndex => {
                AttestationValidationError::OutOfBoundsBitfieldIndex
            }
        }
    }
}
<commit_msg>Fixes bug with attestation validation that arose from change to API<commit_after>use super::attestation_parent_hashes::{attestation_parent_hashes, ParentHashesError};
use super::db::stores::{BeaconBlockAtSlotError, BeaconBlockStore, ValidatorStore};
use super::db::{ClientDB, DBError};
use super::message_generation::generate_signed_message;
use super::signature_verification::{
    verify_aggregate_signature_for_indices, SignatureVerificationError,
};
use super::types::Hash256;
use super::types::{AttestationRecord, AttesterMap};
use std::collections::HashSet;
use std::sync::Arc;

#[derive(Debug, PartialEq)]
pub enum AttestationValidationError {
    ParentSlotTooHigh,
    ParentSlotTooLow,
    BlockSlotTooHigh,
    BlockSlotTooLow,
    JustifiedSlotIncorrect,
    InvalidJustifiedBlockHash,
    TooManyObliqueHashes,
    BadCurrentHashes,
    BadObliqueHashes,
    BadAttesterMap,
    IntWrapping,
    PublicKeyCorrupt,
    NoPublicKeyForValidator,
    BadBitfieldLength,
    InvalidBitfield,
    InvalidBitfieldEndBits,
    NoSignatures,
    NonZeroTrailingBits,
    BadAggregateSignature,
    DBError(String),
    OutOfBoundsBitfieldIndex,
}

/// The context against which some attestation should be validated.
pub struct AttestationValidationContext<T>
where
    T: ClientDB + Sized,
{
    /// The slot as determined by the system time.
    pub block_slot: u64,
    /// The slot of the parent of the block that contained this attestation.
    pub parent_block_slot: u64,
    /// The cycle_length as determined by the chain configuration.
    pub cycle_length: u8,
    /// The last justified slot as per the client's view of the canonical chain.
    pub last_justified_slot: u64,
    /// A vec of the hashes of the blocks preceeding the present slot.
    pub recent_block_hashes: Arc<Vec<Hash256>>,
    /// The store containing block information.
    pub block_store: Arc<BeaconBlockStore<T>>,
    /// The store containing validator information.
    pub validator_store: Arc<ValidatorStore<T>>,
    /// A map of (slot, shard_id) to the attestation set of validation indices.
    pub attester_map: Arc<AttesterMap>,
}

impl<T> AttestationValidationContext<T>
where
    T: ClientDB,
{
    /// Validate a (fully deserialized) AttestationRecord against this context.
    ///
    /// The function will return a HashSet of validator indices (canonical validator indices not
    /// attestation indices) if the validation passed successfully, or an error otherwise.
    ///
    /// The attestation's aggregate signature will be verified, therefore the function must able to
    /// access all required validation public keys via the `validator_store`.
    pub fn validate_attestation(
        &self,
        a: &AttestationRecord,
    ) -> Result<HashSet<usize>, AttestationValidationError> {
        /*
         * The attesation slot must be less than or equal to the parent of the slot of the block
         * that contained the attestation.
         */
        if a.slot > self.parent_block_slot {
            return Err(AttestationValidationError::ParentSlotTooHigh);
        }

        /*
         * The slot of this attestation must not be more than cycle_length + 1 distance
         * from the parent_slot of block that contained it.
         */
        if a.slot
            < self
                .parent_block_slot
                .saturating_sub(u64::from(self.cycle_length).saturating_add(1))
        {
            return Err(AttestationValidationError::ParentSlotTooLow);
        }

        /*
         * The attestation justified slot must not be higher than the last_justified_slot of the
         * context.
         */
        if a.justified_slot > self.last_justified_slot {
            return Err(AttestationValidationError::JustifiedSlotIncorrect);
        }

        /*
         * There is no need to include more oblique parents hashes than there are blocks
         * in a cycle.
         */
        if a.oblique_parent_hashes.len() > usize::from(self.cycle_length) {
            return Err(AttestationValidationError::TooManyObliqueHashes);
        }

        /*
         * Retrieve the set of attestation indices for this slot and shard id.
         *
         * This is an array mapping the order that validators will appear in the bitfield to the
         * canonincal index of a validator.
         */
        let attestation_indices = self
            .attester_map
            .get(&(a.slot, a.shard_id))
            .ok_or(AttestationValidationError::BadAttesterMap)?;

        /*
         * The bitfield must be no longer than the minimum required to represent each validator in the
         * attestation indices for this slot and shard id.
         */
        if a.attester_bitfield.num_bytes() != bytes_for_bits(attestation_indices.len()) {
            return Err(AttestationValidationError::BadBitfieldLength);
        }

        /*
         * If there are excess bits in the bitfield because the number of a validators in not a
         * multiple of 8, reject this attestation record.
         *
         * Allow extra set bits would permit mutliple different byte layouts (and therefore hashes) to
         * refer to the same AttesationRecord.
         */
        if a.attester_bitfield.num_set_bits() > attestation_indices.len() {
            return Err(AttestationValidationError::InvalidBitfieldEndBits);
        }

        /*
         * Generate the parent hashes for this attestation
         */
        let parent_hashes = attestation_parent_hashes(
            self.cycle_length,
            self.block_slot,
            a.slot,
            &self.recent_block_hashes,
            &a.oblique_parent_hashes,
        )?;

        /*
         * The specified justified block hash supplied in the attestation must be in the chain at
         * the given slot number.
         *
         * First, we find the latest parent hash from the parent_hashes array. Then, using the
         * block store (database) we iterate back through the blocks until we find (or fail to
         * find) the justified block hash referenced in the attestation record.
         */
        let latest_parent_hash = parent_hashes
            .last()
            .ok_or(AttestationValidationError::BadCurrentHashes)?;
        match self
            .block_store
            .block_at_slot(&latest_parent_hash, a.justified_slot)?
        {
            Some((ref hash, _)) if *hash == a.justified_block_hash.to_vec() => (),
            _ => return Err(AttestationValidationError::InvalidJustifiedBlockHash),
        };

        /*
         * Generate the message that this attestation aggregate signature must sign across.
         */
        let signed_message = {
            generate_signed_message(
                a.slot,
                &parent_hashes,
                a.shard_id,
                &a.shard_block_hash,
                a.justified_slot,
            )
        };

        let voted_hashset = verify_aggregate_signature_for_indices(
            &signed_message,
            &a.aggregate_sig,
            &attestation_indices,
            &a.attester_bitfield,
            &self.validator_store,
        )?;

        /*
         * If the hashset of voters is None, the signature verification failed.
         */
        match voted_hashset {
            None => Err(AttestationValidationError::BadAggregateSignature),
            Some(hashset) => Ok(hashset),
        }
    }
}

fn bytes_for_bits(bits: usize) -> usize {
    (bits.saturating_sub(1) / 8) + 1
}

impl From<ParentHashesError> for AttestationValidationError {
    fn from(e: ParentHashesError) -> Self {
        match e {
            ParentHashesError::BadCurrentHashes => AttestationValidationError::BadCurrentHashes,
            ParentHashesError::BadObliqueHashes => AttestationValidationError::BadObliqueHashes,
            ParentHashesError::SlotTooLow => AttestationValidationError::BlockSlotTooLow,
            ParentHashesError::SlotTooHigh => AttestationValidationError::BlockSlotTooHigh,
            ParentHashesError::IntWrapping => AttestationValidationError::IntWrapping,
        }
    }
}

impl From<BeaconBlockAtSlotError> for AttestationValidationError {
    fn from(e: BeaconBlockAtSlotError) -> Self {
        match e {
            BeaconBlockAtSlotError::DBError(s) => AttestationValidationError::DBError(s),
            _ => AttestationValidationError::InvalidJustifiedBlockHash,
        }
    }
}

impl From<DBError> for AttestationValidationError {
    fn from(e: DBError) -> Self {
        AttestationValidationError::DBError(e.message)
    }
}

impl From<SignatureVerificationError> for AttestationValidationError {
    fn from(e: SignatureVerificationError) -> Self {
        match e {
            SignatureVerificationError::BadValidatorIndex => {
                AttestationValidationError::BadAttesterMap
            }
            SignatureVerificationError::PublicKeyCorrupt => {
                AttestationValidationError::PublicKeyCorrupt
            }
            SignatureVerificationError::NoPublicKeyForValidator => {
                AttestationValidationError::NoPublicKeyForValidator
            }
            SignatureVerificationError::DBError(s) => AttestationValidationError::DBError(s),
            SignatureVerificationError::OutOfBoundsBitfieldIndex => {
                AttestationValidationError::OutOfBoundsBitfieldIndex
            }
        }
    }
}
<|endoftext|>


<commit_before>use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then};
use rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass};
use rustc::{declare_lint_pass, declare_tool_lint};
use rustc_errors::Applicability;
use std::borrow::Cow;
use syntax::ast::*;
use syntax::parse::{parser, token};
use syntax::tokenstream::TokenStream;
use syntax_pos::{BytePos, Span};

declare_clippy_lint! {
    /// **What it does:** This lint warns when you use `println!("")` to
    /// print a newline.
    ///
    /// **Why is this bad?** You should use `println!()`, which is simpler.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// println!("");
    /// ```
    pub PRINTLN_EMPTY_STRING,
    style,
    "using `println!(\"\")` with an empty string"
}

declare_clippy_lint! {
    /// **What it does:** This lint warns when you use `print!()` with a format
    /// string that
    /// ends in a newline.
    ///
    /// **Why is this bad?** You should use `println!()` instead, which appends the
    /// newline.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// # let name = "World";
    /// print!("Hello {}!\n", name);
    /// ```
    /// use println!() instead
    /// ```rust
    /// # let name = "World";
    /// println!("Hello {}!", name);
    /// ```
    pub PRINT_WITH_NEWLINE,
    style,
    "using `print!()` with a format string that ends in a single newline"
}

declare_clippy_lint! {
    /// **What it does:** Checks for printing on *stdout*. The purpose of this lint
    /// is to catch debugging remnants.
    ///
    /// **Why is this bad?** People often print on *stdout* while debugging an
    /// application and might forget to remove those prints afterward.
    ///
    /// **Known problems:** Only catches `print!` and `println!` calls.
    ///
    /// **Example:**
    /// ```rust
    /// println!("Hello world!");
    /// ```
    pub PRINT_STDOUT,
    restriction,
    "printing on stdout"
}

declare_clippy_lint! {
    /// **What it does:** Checks for use of `Debug` formatting. The purpose of this
    /// lint is to catch debugging remnants.
    ///
    /// **Why is this bad?** The purpose of the `Debug` trait is to facilitate
    /// debugging Rust code. It should not be used in in user-facing output.
    ///
    /// **Example:**
    /// ```rust
    /// # let foo = "bar";
    /// println!("{:?}", foo);
    /// ```
    pub USE_DEBUG,
    restriction,
    "use of `Debug`-based formatting"
}

declare_clippy_lint! {
    /// **What it does:** This lint warns about the use of literals as `print!`/`println!` args.
    ///
    /// **Why is this bad?** Using literals as `println!` args is inefficient
    /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
    /// (i.e., just put the literal in the format string)
    ///
    /// **Known problems:** Will also warn with macro calls as arguments that expand to literals
    /// -- e.g., `println!("{}", env!("FOO"))`.
    ///
    /// **Example:**
    /// ```rust
    /// println!("{}", "foo");
    /// ```
    /// use the literal without formatting:
    /// ```rust
    /// println!("foo");
    /// ```
    pub PRINT_LITERAL,
    style,
    "printing a literal with a format string"
}

declare_clippy_lint! {
    /// **What it does:** This lint warns when you use `writeln!(buf, "")` to
    /// print a newline.
    ///
    /// **Why is this bad?** You should use `writeln!(buf)`, which is simpler.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// # use std::fmt::Write;
    /// # let mut buf = String::new();
    /// writeln!(buf, "");
    /// ```
    pub WRITELN_EMPTY_STRING,
    style,
    "using `writeln!(buf, \"\")` with an empty string"
}

declare_clippy_lint! {
    /// **What it does:** This lint warns when you use `write!()` with a format
    /// string that
    /// ends in a newline.
    ///
    /// **Why is this bad?** You should use `writeln!()` instead, which appends the
    /// newline.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// # use std::fmt::Write;
    /// # let mut buf = String::new();
    /// # let name = "World";
    /// write!(buf, "Hello {}!\n", name);
    /// ```
    pub WRITE_WITH_NEWLINE,
    style,
    "using `write!()` with a format string that ends in a single newline"
}

declare_clippy_lint! {
    /// **What it does:** This lint warns about the use of literals as `write!`/`writeln!` args.
    ///
    /// **Why is this bad?** Using literals as `writeln!` args is inefficient
    /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
    /// (i.e., just put the literal in the format string)
    ///
    /// **Known problems:** Will also warn with macro calls as arguments that expand to literals
    /// -- e.g., `writeln!(buf, "{}", env!("FOO"))`.
    ///
    /// **Example:**
    /// ```rust
    /// # use std::fmt::Write;
    /// # let mut buf = String::new();
    /// writeln!(buf, "{}", "foo");
    /// ```
    pub WRITE_LITERAL,
    style,
    "writing a literal with a format string"
}

declare_lint_pass!(Write => [
    PRINT_WITH_NEWLINE,
    PRINTLN_EMPTY_STRING,
    PRINT_STDOUT,
    USE_DEBUG,
    PRINT_LITERAL,
    WRITE_WITH_NEWLINE,
    WRITELN_EMPTY_STRING,
    WRITE_LITERAL
]);

impl EarlyLintPass for Write {
    fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &Mac) {
        if mac.node.path == sym!(println) {
            span_lint(cx, PRINT_STDOUT, mac.span, "use of `println!`");
            if let (Some(fmt_str), _) = check_tts(cx, &mac.node.tts, false) {
                if fmt_str.contents.is_empty() {
                    span_lint_and_sugg(
                        cx,
                        PRINTLN_EMPTY_STRING,
                        mac.span,
                        "using `println!(\"\")`",
                        "replace it with",
                        "println!()".to_string(),
                        Applicability::MachineApplicable,
                    );
                }
            }
        } else if mac.node.path == sym!(print) {
            span_lint(cx, PRINT_STDOUT, mac.span, "use of `print!`");
            if let (Some(fmt_str), _) = check_tts(cx, &mac.node.tts, false) {
                if check_newlines(&fmt_str) {
                    span_lint_and_then(
                        cx,
                        PRINT_WITH_NEWLINE,
                        mac.span,
                        "using `print!()` with a format string that ends in a single newline",
                        |err| {
                            err.multipart_suggestion(
                                "use `println!` instead",
                                vec![
                                    (mac.node.path.span, String::from("println")),
                                    (fmt_str.newline_span(), String::new()),
                                ],
                                Applicability::MachineApplicable,
                            );
                        },
                    );
                }
            }
        } else if mac.node.path == sym!(write) {
            if let (Some(fmt_str), _) = check_tts(cx, &mac.node.tts, true) {
                if check_newlines(&fmt_str) {
                    span_lint_and_then(
                        cx,
                        WRITE_WITH_NEWLINE,
                        mac.span,
                        "using `write!()` with a format string that ends in a single newline",
                        |err| {
                            err.multipart_suggestion(
                                "use `writeln!()` instead",
                                vec![
                                    (mac.node.path.span, String::from("writeln")),
                                    (fmt_str.newline_span(), String::new()),
                                ],
                                Applicability::MachineApplicable,
                            );
                        },
                    )
                }
            }
        } else if mac.node.path == sym!(writeln) {
            if let (Some(fmt_str), expr) = check_tts(cx, &mac.node.tts, true) {
                if fmt_str.contents.is_empty() {
                    let mut applicability = Applicability::MachineApplicable;
                    let suggestion = expr.map_or_else(
                        move || {
                            applicability = Applicability::HasPlaceholders;
                            Cow::Borrowed("v")
                        },
                        move |expr| snippet_with_applicability(cx, expr.span, "v", &mut applicability),
                    );

                    span_lint_and_sugg(
                        cx,
                        WRITELN_EMPTY_STRING,
                        mac.span,
                        format!("using `writeln!({}, \"\")`", suggestion).as_str(),
                        "replace it with",
                        format!("writeln!({})", suggestion),
                        applicability,
                    );
                }
            }
        }
    }
}

/// The arguments of a `print[ln]!` or `write[ln]!` invocation.
struct FmtStr {
    /// The contents of the format string (inside the quotes).
    contents: String,
    style: StrStyle,
    /// The span of the format string, including quotes, the raw marker, and any raw hashes.
    span: Span,
}

impl FmtStr {
    /// Given a format string that ends in a newline and its span, calculates the span of the
    /// newline.
    fn newline_span(&self) -> Span {
        let sp = self.span;

        let newline_sp_hi = sp.hi()
            - match self.style {
                StrStyle::Cooked => BytePos(1),
                StrStyle::Raw(hashes) => BytePos((1 + hashes).into()),
            };

        let newline_sp_len = if self.contents.ends_with('\n') {
            BytePos(1)
        } else if self.contents.ends_with(r"\n") {
            BytePos(2)
        } else {
            panic!("expected format string to contain a newline");
        };

        sp.with_lo(newline_sp_hi - newline_sp_len).with_hi(newline_sp_hi)
    }
}

/// Checks the arguments of `print[ln]!` and `write[ln]!` calls. It will return a tuple of two
/// `Option`s. The first `Option` of the tuple is the macro's format string. It includes
/// the contents of the string, whether it's a raw string, and the span of the literal in the
/// source. The second `Option` in the tuple is, in the `write[ln]!` case, the expression the
/// `format_str` should be written to.
///
/// Example:
///
/// Calling this function on
/// ```rust
/// # use std::fmt::Write;
/// # let mut buf = String::new();
/// # let something = "something";
/// writeln!(buf, "string to write: {}", something);
/// ```
/// will return
/// ```rust,ignore
/// (Some("string to write: {}"), Some(buf))
/// ```
fn check_tts<'a>(cx: &EarlyContext<'a>, tts: &TokenStream, is_write: bool) -> (Option<FmtStr>, Option<Expr>) {
    use fmt_macros::*;
    let tts = tts.clone();

    let mut parser = parser::Parser::new(&cx.sess.parse_sess, tts, None, false, false, None);
    let mut expr: Option<Expr> = None;
    if is_write {
        expr = match parser.parse_expr().map_err(|mut err| err.cancel()) {
            Ok(p) => Some(p.into_inner()),
            Err(_) => return (None, None),
        };
        // might be `writeln!(foo)`
        if parser.expect(&token::Comma).map_err(|mut err| err.cancel()).is_err() {
            return (None, expr);
        }
    }

    let (fmtstr, fmtstyle) = match parser.parse_str().map_err(|mut err| err.cancel()) {
        Ok((fmtstr, fmtstyle)) => (fmtstr.to_string(), fmtstyle),
        Err(_) => return (None, expr),
    };
    let fmtspan = parser.prev_span;
    let tmp = fmtstr.clone();
    let mut args = vec![];
    let mut fmt_parser = Parser::new(&tmp, None, Vec::new(), false);
    while let Some(piece) = fmt_parser.next() {
        if !fmt_parser.errors.is_empty() {
            return (None, expr);
        }
        if let Piece::NextArgument(arg) = piece {
            if arg.format.ty == "?" {
                // FIXME: modify rustc's fmt string parser to give us the current span
                span_lint(cx, USE_DEBUG, parser.prev_span, "use of `Debug`-based formatting");
            }
            args.push(arg);
        }
    }
    let lint = if is_write { WRITE_LITERAL } else { PRINT_LITERAL };
    let mut idx = 0;
    loop {
        const SIMPLE: FormatSpec<'_> = FormatSpec {
            fill: None,
            align: AlignUnknown,
            flags: 0,
            precision: CountImplied,
            precision_span: None,
            width: CountImplied,
            width_span: None,
            ty: "",
        };
        if !parser.eat(&token::Comma) {
            return (
                Some(FmtStr {
                    contents: fmtstr,
                    style: fmtstyle,
                    span: fmtspan,
                }),
                expr,
            );
        }
        let token_expr = if let Ok(expr) = parser.parse_expr().map_err(|mut err| err.cancel()) {
            expr
        } else {
            return (
                Some(FmtStr {
                    contents: fmtstr,
                    style: fmtstyle,
                    span: fmtspan,
                }),
                None,
            );
        };
        match &token_expr.node {
            ExprKind::Lit(_) => {
                let mut all_simple = true;
                let mut seen = false;
                for arg in &args {
                    match arg.position {
                        ArgumentImplicitlyIs(n) | ArgumentIs(n) => {
                            if n == idx {
                                all_simple &= arg.format == SIMPLE;
                                seen = true;
                            }
                        },
                        ArgumentNamed(_) => {},
                    }
                }
                if all_simple && seen {
                    span_lint(cx, lint, token_expr.span, "literal with an empty format string");
                }
                idx += 1;
            },
            ExprKind::Assign(lhs, rhs) => {
                if let ExprKind::Lit(_) = rhs.node {
                    if let ExprKind::Path(_, p) = &lhs.node {
                        let mut all_simple = true;
                        let mut seen = false;
                        for arg in &args {
                            match arg.position {
                                ArgumentImplicitlyIs(_) | ArgumentIs(_) => {},
                                ArgumentNamed(name) => {
                                    if *p == name {
                                        seen = true;
                                        all_simple &= arg.format == SIMPLE;
                                    }
                                },
                            }
                        }
                        if all_simple && seen {
                            span_lint(cx, lint, rhs.span, "literal with an empty format string");
                        }
                    }
                }
            },
            _ => idx += 1,
        }
    }
}

/// Checks if the format string constains a single newline that terminates it.
///
/// Literal and escaped newlines are both checked (only literal for raw strings).
fn check_newlines(fmt_str: &FmtStr) -> bool {
    let s = &fmt_str.contents;

    if s.ends_with('\n') {
        return true;
    } else if let StrStyle::Raw(_) = fmt_str.style {
        return false;
    }

    if s.len() < 2 {
        return false;
    }

    let bytes = s.as_bytes();
    if bytes[bytes.len() - 2] != b'\\' || bytes[bytes.len() - 1] != b'n' {
        return false;
    }

    let mut escaping = false;
    for (index, &byte) in bytes.iter().enumerate() {
        if escaping {
            if byte == b'n' {
                return index == bytes.len() - 1;
            }
            escaping = false;
        } else if byte == b'\\' {
            escaping = true;
        }
    }

    false
}
<commit_msg>Auto merge of #4370 - vallentin:master, r=flip1995<commit_after>use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then};
use rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass};
use rustc::{declare_lint_pass, declare_tool_lint};
use rustc_errors::Applicability;
use std::borrow::Cow;
use syntax::ast::*;
use syntax::parse::{parser, token};
use syntax::tokenstream::TokenStream;
use syntax_pos::{BytePos, Span};

declare_clippy_lint! {
    /// **What it does:** This lint warns when you use `println!("")` to
    /// print a newline.
    ///
    /// **Why is this bad?** You should use `println!()`, which is simpler.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// println!("");
    /// ```
    pub PRINTLN_EMPTY_STRING,
    style,
    "using `println!(\"\")` with an empty string"
}

declare_clippy_lint! {
    /// **What it does:** This lint warns when you use `print!()` with a format
    /// string that
    /// ends in a newline.
    ///
    /// **Why is this bad?** You should use `println!()` instead, which appends the
    /// newline.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// # let name = "World";
    /// print!("Hello {}!\n", name);
    /// ```
    /// use println!() instead
    /// ```rust
    /// # let name = "World";
    /// println!("Hello {}!", name);
    /// ```
    pub PRINT_WITH_NEWLINE,
    style,
    "using `print!()` with a format string that ends in a single newline"
}

declare_clippy_lint! {
    /// **What it does:** Checks for printing on *stdout*. The purpose of this lint
    /// is to catch debugging remnants.
    ///
    /// **Why is this bad?** People often print on *stdout* while debugging an
    /// application and might forget to remove those prints afterward.
    ///
    /// **Known problems:** Only catches `print!` and `println!` calls.
    ///
    /// **Example:**
    /// ```rust
    /// println!("Hello world!");
    /// ```
    pub PRINT_STDOUT,
    restriction,
    "printing on stdout"
}

declare_clippy_lint! {
    /// **What it does:** Checks for use of `Debug` formatting. The purpose of this
    /// lint is to catch debugging remnants.
    ///
    /// **Why is this bad?** The purpose of the `Debug` trait is to facilitate
    /// debugging Rust code. It should not be used in user-facing output.
    ///
    /// **Example:**
    /// ```rust
    /// # let foo = "bar";
    /// println!("{:?}", foo);
    /// ```
    pub USE_DEBUG,
    restriction,
    "use of `Debug`-based formatting"
}

declare_clippy_lint! {
    /// **What it does:** This lint warns about the use of literals as `print!`/`println!` args.
    ///
    /// **Why is this bad?** Using literals as `println!` args is inefficient
    /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
    /// (i.e., just put the literal in the format string)
    ///
    /// **Known problems:** Will also warn with macro calls as arguments that expand to literals
    /// -- e.g., `println!("{}", env!("FOO"))`.
    ///
    /// **Example:**
    /// ```rust
    /// println!("{}", "foo");
    /// ```
    /// use the literal without formatting:
    /// ```rust
    /// println!("foo");
    /// ```
    pub PRINT_LITERAL,
    style,
    "printing a literal with a format string"
}

declare_clippy_lint! {
    /// **What it does:** This lint warns when you use `writeln!(buf, "")` to
    /// print a newline.
    ///
    /// **Why is this bad?** You should use `writeln!(buf)`, which is simpler.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// # use std::fmt::Write;
    /// # let mut buf = String::new();
    /// writeln!(buf, "");
    /// ```
    pub WRITELN_EMPTY_STRING,
    style,
    "using `writeln!(buf, \"\")` with an empty string"
}

declare_clippy_lint! {
    /// **What it does:** This lint warns when you use `write!()` with a format
    /// string that
    /// ends in a newline.
    ///
    /// **Why is this bad?** You should use `writeln!()` instead, which appends the
    /// newline.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// # use std::fmt::Write;
    /// # let mut buf = String::new();
    /// # let name = "World";
    /// write!(buf, "Hello {}!\n", name);
    /// ```
    pub WRITE_WITH_NEWLINE,
    style,
    "using `write!()` with a format string that ends in a single newline"
}

declare_clippy_lint! {
    /// **What it does:** This lint warns about the use of literals as `write!`/`writeln!` args.
    ///
    /// **Why is this bad?** Using literals as `writeln!` args is inefficient
    /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
    /// (i.e., just put the literal in the format string)
    ///
    /// **Known problems:** Will also warn with macro calls as arguments that expand to literals
    /// -- e.g., `writeln!(buf, "{}", env!("FOO"))`.
    ///
    /// **Example:**
    /// ```rust
    /// # use std::fmt::Write;
    /// # let mut buf = String::new();
    /// writeln!(buf, "{}", "foo");
    /// ```
    pub WRITE_LITERAL,
    style,
    "writing a literal with a format string"
}

declare_lint_pass!(Write => [
    PRINT_WITH_NEWLINE,
    PRINTLN_EMPTY_STRING,
    PRINT_STDOUT,
    USE_DEBUG,
    PRINT_LITERAL,
    WRITE_WITH_NEWLINE,
    WRITELN_EMPTY_STRING,
    WRITE_LITERAL
]);

impl EarlyLintPass for Write {
    fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &Mac) {
        if mac.node.path == sym!(println) {
            span_lint(cx, PRINT_STDOUT, mac.span, "use of `println!`");
            if let (Some(fmt_str), _) = check_tts(cx, &mac.node.tts, false) {
                if fmt_str.contents.is_empty() {
                    span_lint_and_sugg(
                        cx,
                        PRINTLN_EMPTY_STRING,
                        mac.span,
                        "using `println!(\"\")`",
                        "replace it with",
                        "println!()".to_string(),
                        Applicability::MachineApplicable,
                    );
                }
            }
        } else if mac.node.path == sym!(print) {
            span_lint(cx, PRINT_STDOUT, mac.span, "use of `print!`");
            if let (Some(fmt_str), _) = check_tts(cx, &mac.node.tts, false) {
                if check_newlines(&fmt_str) {
                    span_lint_and_then(
                        cx,
                        PRINT_WITH_NEWLINE,
                        mac.span,
                        "using `print!()` with a format string that ends in a single newline",
                        |err| {
                            err.multipart_suggestion(
                                "use `println!` instead",
                                vec![
                                    (mac.node.path.span, String::from("println")),
                                    (fmt_str.newline_span(), String::new()),
                                ],
                                Applicability::MachineApplicable,
                            );
                        },
                    );
                }
            }
        } else if mac.node.path == sym!(write) {
            if let (Some(fmt_str), _) = check_tts(cx, &mac.node.tts, true) {
                if check_newlines(&fmt_str) {
                    span_lint_and_then(
                        cx,
                        WRITE_WITH_NEWLINE,
                        mac.span,
                        "using `write!()` with a format string that ends in a single newline",
                        |err| {
                            err.multipart_suggestion(
                                "use `writeln!()` instead",
                                vec![
                                    (mac.node.path.span, String::from("writeln")),
                                    (fmt_str.newline_span(), String::new()),
                                ],
                                Applicability::MachineApplicable,
                            );
                        },
                    )
                }
            }
        } else if mac.node.path == sym!(writeln) {
            if let (Some(fmt_str), expr) = check_tts(cx, &mac.node.tts, true) {
                if fmt_str.contents.is_empty() {
                    let mut applicability = Applicability::MachineApplicable;
                    let suggestion = expr.map_or_else(
                        move || {
                            applicability = Applicability::HasPlaceholders;
                            Cow::Borrowed("v")
                        },
                        move |expr| snippet_with_applicability(cx, expr.span, "v", &mut applicability),
                    );

                    span_lint_and_sugg(
                        cx,
                        WRITELN_EMPTY_STRING,
                        mac.span,
                        format!("using `writeln!({}, \"\")`", suggestion).as_str(),
                        "replace it with",
                        format!("writeln!({})", suggestion),
                        applicability,
                    );
                }
            }
        }
    }
}

/// The arguments of a `print[ln]!` or `write[ln]!` invocation.
struct FmtStr {
    /// The contents of the format string (inside the quotes).
    contents: String,
    style: StrStyle,
    /// The span of the format string, including quotes, the raw marker, and any raw hashes.
    span: Span,
}

impl FmtStr {
    /// Given a format string that ends in a newline and its span, calculates the span of the
    /// newline.
    fn newline_span(&self) -> Span {
        let sp = self.span;

        let newline_sp_hi = sp.hi()
            - match self.style {
                StrStyle::Cooked => BytePos(1),
                StrStyle::Raw(hashes) => BytePos((1 + hashes).into()),
            };

        let newline_sp_len = if self.contents.ends_with('\n') {
            BytePos(1)
        } else if self.contents.ends_with(r"\n") {
            BytePos(2)
        } else {
            panic!("expected format string to contain a newline");
        };

        sp.with_lo(newline_sp_hi - newline_sp_len).with_hi(newline_sp_hi)
    }
}

/// Checks the arguments of `print[ln]!` and `write[ln]!` calls. It will return a tuple of two
/// `Option`s. The first `Option` of the tuple is the macro's format string. It includes
/// the contents of the string, whether it's a raw string, and the span of the literal in the
/// source. The second `Option` in the tuple is, in the `write[ln]!` case, the expression the
/// `format_str` should be written to.
///
/// Example:
///
/// Calling this function on
/// ```rust
/// # use std::fmt::Write;
/// # let mut buf = String::new();
/// # let something = "something";
/// writeln!(buf, "string to write: {}", something);
/// ```
/// will return
/// ```rust,ignore
/// (Some("string to write: {}"), Some(buf))
/// ```
fn check_tts<'a>(cx: &EarlyContext<'a>, tts: &TokenStream, is_write: bool) -> (Option<FmtStr>, Option<Expr>) {
    use fmt_macros::*;
    let tts = tts.clone();

    let mut parser = parser::Parser::new(&cx.sess.parse_sess, tts, None, false, false, None);
    let mut expr: Option<Expr> = None;
    if is_write {
        expr = match parser.parse_expr().map_err(|mut err| err.cancel()) {
            Ok(p) => Some(p.into_inner()),
            Err(_) => return (None, None),
        };
        // might be `writeln!(foo)`
        if parser.expect(&token::Comma).map_err(|mut err| err.cancel()).is_err() {
            return (None, expr);
        }
    }

    let (fmtstr, fmtstyle) = match parser.parse_str().map_err(|mut err| err.cancel()) {
        Ok((fmtstr, fmtstyle)) => (fmtstr.to_string(), fmtstyle),
        Err(_) => return (None, expr),
    };
    let fmtspan = parser.prev_span;
    let tmp = fmtstr.clone();
    let mut args = vec![];
    let mut fmt_parser = Parser::new(&tmp, None, Vec::new(), false);
    while let Some(piece) = fmt_parser.next() {
        if !fmt_parser.errors.is_empty() {
            return (None, expr);
        }
        if let Piece::NextArgument(arg) = piece {
            if arg.format.ty == "?" {
                // FIXME: modify rustc's fmt string parser to give us the current span
                span_lint(cx, USE_DEBUG, parser.prev_span, "use of `Debug`-based formatting");
            }
            args.push(arg);
        }
    }
    let lint = if is_write { WRITE_LITERAL } else { PRINT_LITERAL };
    let mut idx = 0;
    loop {
        const SIMPLE: FormatSpec<'_> = FormatSpec {
            fill: None,
            align: AlignUnknown,
            flags: 0,
            precision: CountImplied,
            precision_span: None,
            width: CountImplied,
            width_span: None,
            ty: "",
        };
        if !parser.eat(&token::Comma) {
            return (
                Some(FmtStr {
                    contents: fmtstr,
                    style: fmtstyle,
                    span: fmtspan,
                }),
                expr,
            );
        }
        let token_expr = if let Ok(expr) = parser.parse_expr().map_err(|mut err| err.cancel()) {
            expr
        } else {
            return (
                Some(FmtStr {
                    contents: fmtstr,
                    style: fmtstyle,
                    span: fmtspan,
                }),
                None,
            );
        };
        match &token_expr.node {
            ExprKind::Lit(_) => {
                let mut all_simple = true;
                let mut seen = false;
                for arg in &args {
                    match arg.position {
                        ArgumentImplicitlyIs(n) | ArgumentIs(n) => {
                            if n == idx {
                                all_simple &= arg.format == SIMPLE;
                                seen = true;
                            }
                        },
                        ArgumentNamed(_) => {},
                    }
                }
                if all_simple && seen {
                    span_lint(cx, lint, token_expr.span, "literal with an empty format string");
                }
                idx += 1;
            },
            ExprKind::Assign(lhs, rhs) => {
                if let ExprKind::Lit(_) = rhs.node {
                    if let ExprKind::Path(_, p) = &lhs.node {
                        let mut all_simple = true;
                        let mut seen = false;
                        for arg in &args {
                            match arg.position {
                                ArgumentImplicitlyIs(_) | ArgumentIs(_) => {},
                                ArgumentNamed(name) => {
                                    if *p == name {
                                        seen = true;
                                        all_simple &= arg.format == SIMPLE;
                                    }
                                },
                            }
                        }
                        if all_simple && seen {
                            span_lint(cx, lint, rhs.span, "literal with an empty format string");
                        }
                    }
                }
            },
            _ => idx += 1,
        }
    }
}

/// Checks if the format string constains a single newline that terminates it.
///
/// Literal and escaped newlines are both checked (only literal for raw strings).
fn check_newlines(fmt_str: &FmtStr) -> bool {
    let s = &fmt_str.contents;

    if s.ends_with('\n') {
        return true;
    } else if let StrStyle::Raw(_) = fmt_str.style {
        return false;
    }

    if s.len() < 2 {
        return false;
    }

    let bytes = s.as_bytes();
    if bytes[bytes.len() - 2] != b'\\' || bytes[bytes.len() - 1] != b'n' {
        return false;
    }

    let mut escaping = false;
    for (index, &byte) in bytes.iter().enumerate() {
        if escaping {
            if byte == b'n' {
                return index == bytes.len() - 1;
            }
            escaping = false;
        } else if byte == b'\\' {
            escaping = true;
        }
    }

    false
}
<|endoftext|>


<commit_before>use board::*;
use std::io::{self, BufRead};

/* Modify board state */

//Allow something to draw a card
pub fn draw_card<'a>(player: &'a mut Player, max_id: &'a mut i32) {
    if player.deck.cards.len() > 0 {
        let mut topcard: Card = player.deck.cards.pop().unwrap();
        *max_id += 1;
        topcard.id = max_id.clone();
        println!("{}", topcard);
        player.hand.push(topcard);
    }
}

//Move a card from one location to another
pub fn move_card<'a>(id: &'a i32, curr_loc: &'a mut Vec<Card>, destination: &'a mut Vec<Card>) {
    //find the index
    let mut card: i32 = -1;
    for i in 0..curr_loc.len() {
        if curr_loc[i].id == id.clone() {
            card = i as i32;
            break;
        }
    }
    if card == -1{
        println!("Card doesnt exist");
    }
    else {
        //actually move the card
        destination.push(curr_loc.remove(card as usize));

        //Check for effects of card
        //for i in current_player.field.last().unwrap().clone().abilities {
        //    //TODO:
        //}
    }
}

// Attack a player right in the face with one of your cards
pub fn attack_face<'a>(attacker: &'a mut i32, mut target: &'a mut Player, mut you: &'a mut Player) {
    let mut index: i32 = -1;
    for i in 0..you.field.len() {
        if you.field[i].id == attacker.clone() { index = i as i32; }

    }
    if index < 0 {
        println!("Monster doesnt exist");}
    else {
        //trigger_abilities("on_player_attacked".to_owned(), &mut you.field[index as usize].clone(), &mut you, &mut target);

        target.health -= you.field[index as usize].attack;
    }
}

//Force two creatures to fight
pub fn attack<'a>(attacker: &'a i32, target: &'a i32, mut you: &'a mut Player, mut opponent: &'a mut Player) {
    //For the fighting we shouldnt need the players, but if someone has an ability that when it
    //dies do something to the rest of the field then we need it

    //turn the ids into an index. we can assume that the cards are all on the field
    let mut a_index: i32 = -1;
    for i in 0..you.field.len() {
        if you.field[i].id == attacker.clone() { a_index = i as i32; }
    }
    let mut t_index: i32 = -1;
    for i in 0..opponent.field.len() {
        if opponent.field[i].id == target.clone() { t_index = i as i32; }
    }

    //Check that this is valid
    if a_index > -1 && t_index > -1 {
        let a = a_index as usize;
        let t = t_index as usize;

        if !you.field[a].fatigued {

            //Check for on-combat abilities
            //trigger_abilities("on-combat".to_owned(), &mut you.field[a].clone(), &mut you, &mut opponent);
            //trigger_abilities("on-combat".to_owned(), &mut opponent.field[t].clone(), &mut opponent, &mut you);

            //actually do the dmg
            you.field[a].health -= opponent.field[t].attack;
            opponent.field[t].health -= you.field[a].attack;

            //Trigger on death
            //trigger_abilities("on-combat".to_owned(), &mut you.field[a].clone(), &mut you, &mut opponent);
            //trigger_abilities("on-combat".to_owned(), &mut opponent.field[t].clone(), &mut opponent, &mut you);

            //move bodies to the graveyard
            if you.field[a].health < 1 { move_card(&attacker, &mut you.field, &mut you.graveyard); }
            if opponent.field[t].health < 1 { move_card(&target, &mut opponent.field, &mut opponent.graveyard); }
        }
    }
}

fn get_index<'a>(id: &'a i32, location: &'a Vec<Card>) -> Option<i32> {
    let mut index = None;
    for i in 0..location.len() {
        if location[i].id == id.clone() {
            index = Some(i as i32)

        }
    }
    return index;
}

fn ask(message: String) -> String {
    //This will be sent to the current player
    println!("{}", message);
    let mut line = String::new();
    let stdin = io::stdin();
    stdin.lock().read_line(&mut line).expect("could not read line");
    return line.trim_right_matches("\r\n").to_string();
}
/* Abilities */

pub fn trigger_single<'a>(trigger: String, id: &i32, caster: &'a mut Player, target_owner: &'a mut Player) {

    //Get a reference to the card
    let mut card: Card = Card::default();
    let index_o = get_index(&id, &caster.field);
    let index_c = get_index(&id, &target_owner.field);
    if index_o.is_some() { card = caster.field[index_o.unwrap() as usize].clone();  }
    if index_c.is_some() { card = target_owner.field[index_c.unwrap()as usize].clone(); }

    //Check abilities
    for thing in card.clone().abilities {

        println!("activating {}", thing.name);
        for ability in thing.ability_raws {
            //If its the correct trigger type
            if ability.trigger == trigger {

                //We know that the card is valid, and we have its index.
                //Things can only ever use thier effects when they are on the feild,
                //so we dont need to try and figure out where that is

                let effect:Vec<&str> = ability.effect.split_whitespace().collect();
                //attack's only valid target is another monster

                if effect[0].to_string() == "destroy" {
                    //Figure out what card will be destroyed
                    let t = ask("what monster do you want destroyed".to_owned());

                    //Check both fields
                    let index_c = get_index(&t.trim().parse::<i32>().unwrap(), &caster.field);
                    let index_t = get_index(&t.trim().parse::<i32>().unwrap(), &target_owner.field);

                    //if its your side
                    if index_c.is_some() {
                        if ability.target == "target creature".to_owned() || ability.target == "ally creature".to_owned() {
                            move_card(&t.trim().parse::<i32>().unwrap(), &mut caster.field, &mut caster.graveyard);
                            //trigger_single("on_death".to_owned(), &id, &'a );
                        }
                    }

                    //If its on the opponents side
                    else if index_t.is_some() {
                        if ability.target == "target enemy creature".to_owned() {
                            move_card(&t.trim().parse::<i32>().unwrap(), &mut target_owner.field, &mut target_owner.graveyard);
                            //trigger_single("on_death".to_owned());
                        }
                    }
                    else {
                        println!("invalid target");
                        break;
                    }
                }
                //Modify ability
                else if effect[0].to_string() == "modify" {
                    //effect[1] is the stat we are looking to change,
                    //effect[2] is by how much we will change it
                    if ability.target == "self" {
                        if index_o.is_some() {
                            modify_stat(&id, effect[1].to_owned(), effect[2].parse().unwrap_or(0), &mut caster.field);
                        }
                    }
                    else if ability.target == "target_creature" {
                        let tmp_id: String = ask("What id do you want to target".to_owned());
                        let target_id = tmp_id.parse::<i32>();

                        if target_id.is_ok() {
                            let is_ally = get_index(&target_id.clone().unwrap(), &caster.field);
                            if is_ally.is_some() {
                                modify_stat(&id, effect[1].to_owned(), effect[2].parse().unwrap_or(0), &mut caster.field);
                            }
                            else {
                                let is_ally = get_index(&target_id.unwrap(), &caster.field);
                                if is_ally.is_some() {
                                    modify_stat(&id, effect[1].to_owned(), effect[2].parse().unwrap_or(0), &mut target_owner.field);
                                }
                            }
                        }
                    }
                    else if ability.target == "target_ally_creature" {
                        let tmp_id: String = ask("What id do you want to target".to_owned());
                        let target_id = tmp_id.parse::<i32>();

                        if target_id.is_ok() {
                            let is_ally = get_index(&target_id.clone().unwrap(), &caster.field);
                            if is_ally.is_some() {
                                modify_stat(&id, effect[1].to_owned(), effect[2].parse().unwrap_or(0), &mut caster.field);
                            }
                        }
                    }
                    else if ability.target == "target_enemy_creature" {
                        let tmp_id: String = ask("What id do you want to target".to_owned());
                        let target_id = tmp_id.parse::<i32>();

                        if target_id.is_ok() {
                            let is_ally = get_index(&target_id.unwrap(), &caster.field);
                            if is_ally.is_some() {
                                modify_stat(&id, effect[1].to_owned(), effect[2].parse().unwrap_or(0), &mut target_owner.field);
                            }
                        }
                    }
                    else if ability.target == "both_fields" {
                        for i  in 0..caster.field.len() {
                            modify_stat(&&caster.field[i].id.clone(), effect[1].to_owned(), effect[2].parse().unwrap_or(0), &mut caster.field);
                        }
                        for i in 0..target_owner.field.len() {
                            modify_stat(&&target_owner.field[i].id.clone(), effect[1].to_owned(), effect[2].parse().unwrap_or(0), &mut target_owner.field);
                        }
                    }
                    else if ability.target == "enemy_field" {
                        for i in 0..target_owner.field.len() {
                            modify_stat(&&target_owner.field[i].id.clone(), effect[1].to_owned(), effect[2].parse().unwrap_or(0), &mut target_owner.field);
                        }

                    }
                    else if ability.target == "ally_field" {
                        for i in 0..caster.field.len() {
                            modify_stat(&&caster.field[i].id.clone(), effect[1].to_owned(), effect[2].parse::<i32>().unwrap(), &mut caster.field);
                        }
                    }
                }
            }
        }
    }
}

pub fn modify_stat<'a>(id: &'a &i32, stat: String, amount: i32, location: &'a mut Vec<Card>) {
    let index = get_index(id, location);
    if index.is_some() {
        if stat == "health" { location[index.unwrap() as usize].health += amount; }
        else if stat == "attack" { location[index.unwrap() as usize].attack += amount; }
        else if stat == "durability" { location[index.unwrap() as usize].durability += amount; }
    }
    else {
        println!("Could not find creature");
    }
}

//Checks all creatures on a field
pub fn trigger_player<'a>(trigger: String, you: &'a mut Player, opponent: &'a mut Player) {}
//This will have the following triggers,
//on player_attacked
//on turn_start
//on turn_end

/*
   pub fn untill_turn_start<'a>(mut player: &'a mut Player) {

   player.field[0].abilities.remove(0);

   for i in &mut player.field {
   for j in 0..i.abilities.len() {
   for q in 0..j.ability_raws.len() {
   if j.abilities[q].trigger == "untill_turn_start".to_owned() {
   j.abilities.remove(j);
   }
   }
   }
   }
   }
   pub fn untill_turn_end<'a>(mut player: &'a mut Player) {

   player.field[0].abilities.remove(0);

   for i in &mut player.field {
   for j in 0..i.abilities.len() {
   if i.abilities[j].trigger == "untill_turn_end".to_owned() {
   i.abilities.remove(j);
   }
   }
   }
   }

*/

#[cfg(test)]
mod tests {
    use action::*;
    use board::*;

    #[test]
    fn test_move_card() {
        let card: Card = Card{name: "Test_card".to_owned(),  ..Card::default()};
        let mut hand: Vec<Card> = Vec::new();
        let mut field: Vec<Card> = Vec::new();
        hand.push(card.clone());
        move_card(&card.id, &mut hand, &mut field);
        assert!(field.last().unwrap().name == "Test_card");
    }

    #[test]
    fn test_ability_modify(){
        let a: AbilityRaw = AbilityRaw{
            name: "buff 5 attack".to_owned(),
            level_requirement: 0,
            target: "enemy_field".to_owned(),
            trigger: "on_play".to_owned(),
            effect: "modify attack 5".to_owned()};
        let b: AbilityRaw = AbilityRaw{
            name: "buff 5 health".to_owned(),
            level_requirement: 0,
            target: "enemy_field".to_owned(),
            trigger: "on_play".to_owned(),
            effect: "modify health 5".to_owned()};

        let mut buff: Ability = Ability::default();
        buff.ability_raws.push(b);
        buff.ability_raws.push(a);

        let mut card: Card = Card{name: "Test_card".to_owned(), id: 1,  ..Card::default()};

        let card1 = Card{ attack: 0, id: 2, ..Card::default()};
        let mut card2: Card = card1.clone();
        card2.id = 3;

        card.abilities.push(buff);

        let mut d: Deck = Deck{cards: Vec::new(), name_of_deck: "deck".to_owned()};
        let mut p1: Player = create_player("p1".to_owned(), d.clone());
        let mut p2: Player = create_player("p2".to_owned(), d.clone());

        p1.field.push(card.clone());
        p2.field.push(card1);
        p2.field.push(card2);

        trigger_single("on_play".to_owned(), &card.id, &mut p1, &mut p2 );
        assert!(p2.field[0].attack == 5);
        assert!(p2.field[1].attack == 5);
        assert!(p2.field[0].health == 5);
        assert!(p2.field[1].health == 5);
    }
}
<commit_msg>forgot to update tests to new action spec<commit_after>use board::*;
use std::io::{self, BufRead};

/* Modify board state */

//Allow something to draw a card
pub fn draw_card<'a>(player: &'a mut Player, max_id: &'a mut i32) {
    if player.deck.cards.len() > 0 {
        let mut topcard: Card = player.deck.cards.pop().unwrap();
        *max_id += 1;
        topcard.id = max_id.clone();
        println!("{}", topcard);
        player.hand.push(topcard);
    }
}

//Move a card from one location to another
pub fn move_card<'a>(id: &'a i32, curr_loc: &'a mut Vec<Card>, destination: &'a mut Vec<Card>) {
    //find the index
    let mut card: i32 = -1;
    for i in 0..curr_loc.len() {
        if curr_loc[i].id == id.clone() {
            card = i as i32;
            break;
        }
    }
    if card == -1{
        println!("Card doesnt exist");
    }
    else {
        //actually move the card
        destination.push(curr_loc.remove(card as usize));

        //Check for effects of card
        //for i in current_player.field.last().unwrap().clone().abilities {
        //    //TODO:
        //}
    }
}

// Attack a player right in the face with one of your cards
pub fn attack_face<'a>(attacker: &'a mut i32, mut target: &'a mut Player, mut you: &'a mut Player) {
    let mut index: i32 = -1;
    for i in 0..you.field.len() {
        if you.field[i].id == attacker.clone() { index = i as i32; }

    }
    if index < 0 {
        println!("Monster doesnt exist");}
    else {
        //trigger_abilities("on_player_attacked".to_owned(), &mut you.field[index as usize].clone(), &mut you, &mut target);

        target.health -= you.field[index as usize].attack;
    }
}

//Force two creatures to fight
pub fn attack<'a>(attacker: &'a i32, target: &'a i32, mut you: &'a mut Player, mut opponent: &'a mut Player) {
    //For the fighting we shouldnt need the players, but if someone has an ability that when it
    //dies do something to the rest of the field then we need it

    //turn the ids into an index. we can assume that the cards are all on the field
    let mut a_index: i32 = -1;
    for i in 0..you.field.len() {
        if you.field[i].id == attacker.clone() { a_index = i as i32; }
    }
    let mut t_index: i32 = -1;
    for i in 0..opponent.field.len() {
        if opponent.field[i].id == target.clone() { t_index = i as i32; }
    }

    //Check that this is valid
    if a_index > -1 && t_index > -1 {
        let a = a_index as usize;
        let t = t_index as usize;

        if !you.field[a].fatigued {

            //Check for on-combat abilities
            //trigger_abilities("on-combat".to_owned(), &mut you.field[a].clone(), &mut you, &mut opponent);
            //trigger_abilities("on-combat".to_owned(), &mut opponent.field[t].clone(), &mut opponent, &mut you);

            //actually do the dmg
            you.field[a].health -= opponent.field[t].attack;
            opponent.field[t].health -= you.field[a].attack;

            //Trigger on death
            //trigger_abilities("on-combat".to_owned(), &mut you.field[a].clone(), &mut you, &mut opponent);
            //trigger_abilities("on-combat".to_owned(), &mut opponent.field[t].clone(), &mut opponent, &mut you);

            //move bodies to the graveyard
            if you.field[a].health < 1 { move_card(&attacker, &mut you.field, &mut you.graveyard); }
            if opponent.field[t].health < 1 { move_card(&target, &mut opponent.field, &mut opponent.graveyard); }
        }
    }
}

fn get_index<'a>(id: &'a i32, location: &'a Vec<Card>) -> Option<i32> {
    let mut index = None;
    for i in 0..location.len() {
        if location[i].id == id.clone() {
            index = Some(i as i32)

        }
    }
    return index;
}

fn ask(message: String) -> String {
    //This will be sent to the current player
    println!("{}", message);
    let mut line = String::new();
    let stdin = io::stdin();
    stdin.lock().read_line(&mut line).expect("could not read line");
    return line.trim_right_matches("\r\n").to_string();
}
/* Abilities */

pub fn trigger_single<'a>(trigger: String, id: &i32, caster: &'a mut Player, target_owner: &'a mut Player) {

    //Get a reference to the card
    let mut card: Card = Card::default();
    let index_o = get_index(&id, &caster.field);
    let index_c = get_index(&id, &target_owner.field);
    if index_o.is_some() { card = caster.field[index_o.unwrap() as usize].clone();  }
    if index_c.is_some() { card = target_owner.field[index_c.unwrap()as usize].clone(); }

    //Check abilities
    for thing in card.clone().abilities {

        println!("activating {}", thing.name);
        for ability in thing.ability_raws {
            //If its the correct trigger type
            if ability.trigger == trigger {

                //We know that the card is valid, and we have its index.
                //Things can only ever use thier effects when they are on the feild,
                //so we dont need to try and figure out where that is

                let effect:Vec<&str> = ability.effect.split_whitespace().collect();
                //attack's only valid target is another monster

                if effect[0].to_string() == "destroy" {
                    //Figure out what card will be destroyed
                    let t = ask("what monster do you want destroyed".to_owned());

                    //Check both fields
                    let index_c = get_index(&t.trim().parse::<i32>().unwrap(), &caster.field);
                    let index_t = get_index(&t.trim().parse::<i32>().unwrap(), &target_owner.field);

                    //if its your side
                    if index_c.is_some() {
                        if ability.target == "target creature".to_owned() || ability.target == "ally creature".to_owned() {
                            move_card(&t.trim().parse::<i32>().unwrap(), &mut caster.field, &mut caster.graveyard);
                            //trigger_single("on_death".to_owned(), &id, &'a );
                        }
                    }

                    //If its on the opponents side
                    else if index_t.is_some() {
                        if ability.target == "target enemy creature".to_owned() {
                            move_card(&t.trim().parse::<i32>().unwrap(), &mut target_owner.field, &mut target_owner.graveyard);
                            //trigger_single("on_death".to_owned());
                        }
                    }
                    else {
                        println!("invalid target");
                        break;
                    }
                }
                //Modify ability
                else if effect[0].to_string() == "modify" {
                    //effect[1] is the stat we are looking to change,
                    //effect[2] is by how much we will change it
                    if ability.target == "self" {
                        if index_o.is_some() {
                            modify_stat(&id, effect[1].to_owned(), effect[2].parse().unwrap_or(0), &mut caster.field);
                        }
                    }
                    else if ability.target == "target_creature" {
                        let tmp_id: String = ask("What id do you want to target".to_owned());
                        let target_id = tmp_id.parse::<i32>();

                        if target_id.is_ok() {
                            let is_ally = get_index(&target_id.clone().unwrap(), &caster.field);
                            if is_ally.is_some() {
                                modify_stat(&id, effect[1].to_owned(), effect[2].parse().unwrap_or(0), &mut caster.field);
                            }
                            else {
                                let is_ally = get_index(&target_id.unwrap(), &caster.field);
                                if is_ally.is_some() {
                                    modify_stat(&id, effect[1].to_owned(), effect[2].parse().unwrap_or(0), &mut target_owner.field);
                                }
                            }
                        }
                    }
                    else if ability.target == "target_ally_creature" {
                        let tmp_id: String = ask("What id do you want to target".to_owned());
                        let target_id = tmp_id.parse::<i32>();

                        if target_id.is_ok() {
                            let is_ally = get_index(&target_id.clone().unwrap(), &caster.field);
                            if is_ally.is_some() {
                                modify_stat(&id, effect[1].to_owned(), effect[2].parse().unwrap_or(0), &mut caster.field);
                            }
                        }
                    }
                    else if ability.target == "target_enemy_creature" {
                        let tmp_id: String = ask("What id do you want to target".to_owned());
                        let target_id = tmp_id.parse::<i32>();

                        if target_id.is_ok() {
                            let is_ally = get_index(&target_id.unwrap(), &caster.field);
                            if is_ally.is_some() {
                                modify_stat(&id, effect[1].to_owned(), effect[2].parse().unwrap_or(0), &mut target_owner.field);
                            }
                        }
                    }
                    else if ability.target == "both_fields" {
                        for i  in 0..caster.field.len() {
                            modify_stat(&&caster.field[i].id.clone(), effect[1].to_owned(), effect[2].parse().unwrap_or(0), &mut caster.field);
                        }
                        for i in 0..target_owner.field.len() {
                            modify_stat(&&target_owner.field[i].id.clone(), effect[1].to_owned(), effect[2].parse().unwrap_or(0), &mut target_owner.field);
                        }
                    }
                    else if ability.target == "enemy_field" {
                        for i in 0..target_owner.field.len() {
                            modify_stat(&&target_owner.field[i].id.clone(), effect[1].to_owned(), effect[2].parse().unwrap_or(0), &mut target_owner.field);
                        }

                    }
                    else if ability.target == "ally_field" {
                        for i in 0..caster.field.len() {
                            modify_stat(&&caster.field[i].id.clone(), effect[1].to_owned(), effect[2].parse::<i32>().unwrap(), &mut caster.field);
                        }
                    }
                }
            }
        }
    }
}

pub fn modify_stat<'a>(id: &'a &i32, stat: String, amount: i32, location: &'a mut Vec<Card>) {
    let index = get_index(id, location);
    if index.is_some() {
        if stat == "health" { location[index.unwrap() as usize].health += amount; }
        else if stat == "attack" { location[index.unwrap() as usize].attack += amount; }
        else if stat == "durability" { location[index.unwrap() as usize].durability += amount; }
    }
    else {
        println!("Could not find creature");
    }
}

//Checks all creatures on a field
pub fn trigger_player<'a>(trigger: String, you: &'a mut Player, opponent: &'a mut Player) {}
//This will have the following triggers,
//on player_attacked
//on turn_start
//on turn_end

/*
   pub fn untill_turn_start<'a>(mut player: &'a mut Player) {

   player.field[0].abilities.remove(0);

   for i in &mut player.field {
   for j in 0..i.abilities.len() {
   for q in 0..j.ability_raws.len() {
   if j.abilities[q].trigger == "untill_turn_start".to_owned() {
   j.abilities.remove(j);
   }
   }
   }
   }
   }
   pub fn untill_turn_end<'a>(mut player: &'a mut Player) {

   player.field[0].abilities.remove(0);

   for i in &mut player.field {
   for j in 0..i.abilities.len() {
   if i.abilities[j].trigger == "untill_turn_end".to_owned() {
   i.abilities.remove(j);
   }
   }
   }
   }

*/

#[cfg(test)]
mod tests {
    use action::*;
    use board::*;

    #[test]
    fn test_move_card() {
        let card: Card = Card{name: "Test_card".to_owned(),  ..Card::default()};
        let mut hand: Vec<Card> = Vec::new();
        let mut field: Vec<Card> = Vec::new();
        hand.push(card.clone());
        move_card(&card.id, &mut hand, &mut field);
        assert!(field.last().unwrap().name == "Test_card");
    }

    #[test]
    fn test_ability_modify(){
        let a: AbilityRaw = AbilityRaw{
            target: "enemy_field".to_owned(),
            trigger: "on_play".to_owned(),
            effect: "modify attack 5".to_owned()};
        let b: AbilityRaw = AbilityRaw{
            target: "enemy_field".to_owned(),
            trigger: "on_play".to_owned(),
            effect: "modify health 5".to_owned()};

        let mut buff: Ability = Ability::default();
        buff.ability_raws.push(b);
        buff.ability_raws.push(a);

        let mut card: Card = Card{name: "Test_card".to_owned(), id: 1,  ..Card::default()};

        let card1 = Card{ attack: 0, id: 2, ..Card::default()};
        let mut card2: Card = card1.clone();
        card2.id = 3;

        card.abilities.push(buff);

        let mut d: Deck = Deck{cards: Vec::new(), name_of_deck: "deck".to_owned()};
        let mut p1: Player = create_player("p1".to_owned(), d.clone());
        let mut p2: Player = create_player("p2".to_owned(), d.clone());

        p1.field.push(card.clone());
        p2.field.push(card1);
        p2.field.push(card2);

        trigger_single("on_play".to_owned(), &card.id, &mut p1, &mut p2 );
        assert!(p2.field[0].attack == 5);
        assert!(p2.field[1].attack == 5);
        assert!(p2.field[0].health == 5);
        assert!(p2.field[1].health == 5);
    }
}
<|endoftext|>


<commit_before>use std::time::Duration;

pub fn short_timeout() -> Duration {
    Duration::from_secs(5)
}

pub fn medium_timeout() -> Duration {
    Duration::from_secs(20)
}

pub fn long_timeout() -> Duration {
    Duration::from_secs(200)
}
<commit_msg>Increase timeouts for android.<commit_after>use std::time::Duration;

pub fn short_timeout() -> Duration {
    Duration::from_secs(300)
}

pub fn medium_timeout() -> Duration {
    Duration::from_secs(300)
}

pub fn long_timeout() -> Duration {
    Duration::from_secs(500)
}
<|endoftext|>


<commit_before>extern crate env_logger;
extern crate log_panics;
extern crate log;
#[cfg(target_os = "android")]
extern crate android_logger;
extern crate libc;

#[cfg(target_os = "android")]
use self::android_logger::Filter;
use self::log::LevelFilter;

pub fn init_nullpay_logger(log: &'static log::Log) -> Result<(), log::SetLoggerError> {
    log::set_logger(log)?;
    log::set_max_level(LevelFilter::Trace);
    Ok(())
}

#[macro_export]
macro_rules! try_log {
    ($expr:expr) => (match $expr {
        Ok(val) => val,
        Err(err) => {
            error!("try_log! | {}", err);
            return Err(From::from(err))
        }
    })
}

macro_rules! _map_err {
    ($lvl:expr, $expr:expr) => (
        |err| {
            log!($lvl, "{} - {}", $expr, err);
            err
        }
    );
    ($lvl:expr) => (
        |err| {
            log!($lvl, "{}", err);
            err
        }
    )
}

#[macro_export]
macro_rules! map_err_err {
    () => ( _map_err!(::log::Level::Error) );
    ($($arg:tt)*) => ( _map_err!(::log::Level::Error, $($arg)*) )
}

#[macro_export]
macro_rules! map_err_trace {
    () => ( _map_err!(::log::Level::Trace) );
    ($($arg:tt)*) => ( _map_err!(::log::Level::Trace, $($arg)*) )
}

#[macro_export]
macro_rules! map_err_info {
    () => ( _map_err!(::log::Level::Info) );
    ($($arg:tt)*) => ( _map_err!(::log::Level::Info, $($arg)*) )
}

#[cfg(debug_assertions)]
#[macro_export]
macro_rules! secret {
    ($val:expr) => {{ $val }};
}

#[cfg(not(debug_assertions))]
#[macro_export]
macro_rules! secret {
    ($val:expr) => {{ "_" }};
}<commit_msg>Removed unused imports<commit_after>extern crate log;

use self::log::LevelFilter;

pub fn init_nullpay_logger(log: &'static log::Log) -> Result<(), log::SetLoggerError> {
    log::set_logger(log)?;
    log::set_max_level(LevelFilter::Trace);
    Ok(())
}

#[macro_export]
macro_rules! try_log {
    ($expr:expr) => (match $expr {
        Ok(val) => val,
        Err(err) => {
            error!("try_log! | {}", err);
            return Err(From::from(err))
        }
    })
}

macro_rules! _map_err {
    ($lvl:expr, $expr:expr) => (
        |err| {
            log!($lvl, "{} - {}", $expr, err);
            err
        }
    );
    ($lvl:expr) => (
        |err| {
            log!($lvl, "{}", err);
            err
        }
    )
}

#[macro_export]
macro_rules! map_err_err {
    () => ( _map_err!(::log::Level::Error) );
    ($($arg:tt)*) => ( _map_err!(::log::Level::Error, $($arg)*) )
}

#[macro_export]
macro_rules! map_err_trace {
    () => ( _map_err!(::log::Level::Trace) );
    ($($arg:tt)*) => ( _map_err!(::log::Level::Trace, $($arg)*) )
}

#[macro_export]
macro_rules! map_err_info {
    () => ( _map_err!(::log::Level::Info) );
    ($($arg:tt)*) => ( _map_err!(::log::Level::Info, $($arg)*) )
}

#[cfg(debug_assertions)]
#[macro_export]
macro_rules! secret {
    ($val:expr) => {{ $val }};
}

#[cfg(not(debug_assertions))]
#[macro_export]
macro_rules! secret {
    ($val:expr) => {{ "_" }};
}<|endoftext|>


<commit_before>use clap::App;

use hab::cli::sup_commands;

pub fn cli<'a, 'b>() -> App<'a, 'b> { sup_commands() }

#[cfg(test)]
mod test {
    use super::cli;
    use clap::ErrorKind;

    macro_rules! assert_cli_cmd {
        ($test:ident, $cmd:expr, $( $key:expr => $value:tt ),+) => {
            #[test]
            fn $test() {
                assert_cmd!(cli(), $cmd, $( $key => $value ),+ );
            }
        }
    }

    #[test]
    fn sup_help_on_run_subcommand() {
        let r = cli().get_matches_from_safe(vec!["hab-sup", "run", "--help"]);
        assert!(r.is_err());
        // not `ErrorKind::InvalidSubcommand`
        assert_eq!(r.unwrap_err().kind, ErrorKind::HelpDisplayed);
    }

    mod sup_run {
        use super::*;

        assert_cli_cmd!(should_handle_multiple_peer_flags,
                        "hab-sup run --peer 1.1.1.1 --peer 2.2.2.2",
                        "PEER" => ["1.1.1.1", "2.2.2.2"]);

        assert_cli_cmd!(should_handle_single_peer_flag_with_multiple_values,
                        "hab-sup run --peer 1.1.1.1 2.2.2.2",
                        "PEER" => ["1.1.1.1", "2.2.2.2"]);

        assert_cli_cmd!(should_handle_peer_flag_with_arguments,
                        "hab-sup run --peer 1.1.1.1 2.2.2.2 -- core/redis",
                        "PEER" => ["1.1.1.1", "2.2.2.2"],
                        "PKG_IDENT_OR_ARTIFACT" => "core/redis");

        assert_cli_cmd!(should_handle_multiple_bind_flags,
                        "hab-sup run --bind service.group1 --bind service.group2",
                        "BIND" => ["service.group1", "service.group2"]);

        assert_cli_cmd!(should_handle_single_bind_flag_with_multiple_values,
                        "hab-sup run --bind service.group1 service.group2",
                        "BIND" => ["service.group1", "service.group2"]);

        assert_cli_cmd!(should_handle_bind_flag_with_arguments,
                        "hab-sup run --bind service.group1 service.group2 -- core/redis",
                        "BIND" => ["service.group1", "service.group2"],
                        "PKG_IDENT_OR_ARTIFACT" => "core/redis");

    }

}
<commit_msg>add tests for local-gossip-mode exclusivity<commit_after>use clap::App;

use hab::cli::sup_commands;

pub fn cli<'a, 'b>() -> App<'a, 'b> { sup_commands() }

#[cfg(test)]
mod test {
    use super::cli;
    use clap::ErrorKind;
    use std::iter::FromIterator;

    macro_rules! assert_cli_cmd {
        ($test:ident, $cmd:expr, $( $key:expr => $value:tt ),+) => {
            #[test]
            fn $test() {
                assert_cmd!(cli(), $cmd, $( $key => $value ),+ );
            }
        }
    }

    #[test]
    fn sup_help_on_run_subcommand() {
        let r = cli().get_matches_from_safe(vec!["hab-sup", "run", "--help"]);
        assert!(r.is_err());
        // not `ErrorKind::InvalidSubcommand`
        assert_eq!(r.unwrap_err().kind, ErrorKind::HelpDisplayed);
    }

    mod sup_run {
        use super::*;

        assert_cli_cmd!(should_handle_multiple_peer_flags,
                        "hab-sup run --peer 1.1.1.1 --peer 2.2.2.2",
                        "PEER" => ["1.1.1.1", "2.2.2.2"]);

        assert_cli_cmd!(should_handle_single_peer_flag_with_multiple_values,
                        "hab-sup run --peer 1.1.1.1 2.2.2.2",
                        "PEER" => ["1.1.1.1", "2.2.2.2"]);

        assert_cli_cmd!(should_handle_peer_flag_with_arguments,
                        "hab-sup run --peer 1.1.1.1 2.2.2.2 -- core/redis",
                        "PEER" => ["1.1.1.1", "2.2.2.2"],
                        "PKG_IDENT_OR_ARTIFACT" => "core/redis");

        assert_cli_cmd!(should_handle_multiple_bind_flags,
                        "hab-sup run --bind service.group1 --bind service.group2",
                        "BIND" => ["service.group1", "service.group2"]);

        assert_cli_cmd!(should_handle_single_bind_flag_with_multiple_values,
                        "hab-sup run --bind service.group1 service.group2",
                        "BIND" => ["service.group1", "service.group2"]);

        assert_cli_cmd!(should_handle_bind_flag_with_arguments,
                        "hab-sup run --bind service.group1 service.group2 -- core/redis",
                        "BIND" => ["service.group1", "service.group2"],
                        "PKG_IDENT_OR_ARTIFACT" => "core/redis");

        #[test]
        fn local_gossip_mode_and_listen_gossip_are_mutually_exclusive() {
            let cmd_vec = Vec::from_iter(
                "hab-sup run --listen-gossip 1.1.1.1:1111 --local-gossip-mode".split_whitespace(),
            );
            assert!(cli().get_matches_from_safe(cmd_vec).is_err());
        }

        #[test]
        fn local_gossip_mode_and_peer_are_mutually_exclusive() {
            let cmd_vec = Vec::from_iter(
                "hab-sup run --peer 1.1.1.1:1111 --local-gossip-mode".split_whitespace(),
            );
            assert!(cli().get_matches_from_safe(cmd_vec).is_err());
        }

    }

}
<|endoftext|>