<commit_before>use actix::prelude::*;
use godcoin::prelude::*;

mod common;
pub use common::*;

#[test]
fn transfer_from_minter() {
    System::run(|| {
        let minter = TestMinter::new();

        let from_addr = ScriptHash::from(&minter.genesis_info().script);
        let from_bal = minter.chain().get_balance(&from_addr, &[]).unwrap();
        let to_addr = KeyPair::gen();
        let amount = get_asset("1.0000 GRAEL");

        let tx = {
            let mut tx = TransferTx {
                base: create_tx_header(TxType::TRANSFER, "1.0000 GRAEL"),
                from: from_addr.clone(),
                to: (&to_addr.0).into(),
                amount,
                memo: vec![],
                script: minter.genesis_info().script.clone(),
            };
            tx.append_sign(&minter.genesis_info().wallet_keys[3]);
            tx.append_sign(&minter.genesis_info().wallet_keys[0]);
            TxVariant::TransferTx(tx)
        };
        let fut = minter.request(MsgRequest::Broadcast(tx));
        Arbiter::spawn(
            fut.and_then(move |res| {
                assert_eq!(res, MsgResponse::Broadcast);
                minter.produce_block().map(|_| minter)
            })
            .and_then(move |minter| {
                let chain = minter.chain();
                let cur_bal = chain.get_balance(&to_addr.0.into(), &[]);
                assert_eq!(cur_bal, Some(amount));

                // The fee transfers back to the minter wallet in the form of a reward tx so it
                // must not be subtracted during the assertion
                let cur_bal = chain.get_balance(&from_addr, &[]);
                assert_eq!(cur_bal, from_bal.sub(amount));

                System::current().stop();
                Ok(())
            }),
        );
    })
    .unwrap();
}

#[test]
fn transfer_from_user() {
    System::run(|| {
        let minter = TestMinter::new();

        let user_1_addr = KeyPair::gen();
        let user_2_addr = KeyPair::gen();

        let fut = {
            let tx = {
                let mut tx = TransferTx {
                    base: create_tx_header(TxType::TRANSFER, "1.0000 GRAEL"),
                    from: ScriptHash::from(&minter.genesis_info().script),
                    to: (&user_1_addr.0).into(),
                    amount: get_asset("100.0000 GRAEL"),
                    memo: vec![],
                    script: minter.genesis_info().script.clone(),
                };
                tx.append_sign(&minter.genesis_info().wallet_keys[3]);
                tx.append_sign(&minter.genesis_info().wallet_keys[0]);
                TxVariant::TransferTx(tx)
            };
            minter.request(MsgRequest::Broadcast(tx))
        };

        Arbiter::spawn(
            fut.and_then({
                let user_1_addr = user_1_addr.clone();
                let user_2_addr = user_2_addr.clone();
                move |res| {
                    assert_eq!(res, MsgResponse::Broadcast);
                    let tx = {
                        let mut tx = TransferTx {
                            base: create_tx_header(TxType::TRANSFER, "1.0000 GRAEL"),
                            from: (&user_1_addr.0).into(),
                            to: (&user_2_addr.0).into(),
                            amount: get_asset("99.0000 GRAEL"),
                            memo: vec![],
                            script: user_1_addr.0.clone().into(),
                        };
                        tx.append_sign(&user_1_addr);
                        TxVariant::TransferTx(tx)
                    };
                    minter
                        .request(MsgRequest::Broadcast(tx))
                        .map(|res| (res, minter))
                }
            })
            .and_then(|(res, minter)| {
                assert_eq!(res, MsgResponse::Broadcast);
                minter.produce_block().map(|_| minter)
            })
            .and_then(move |minter| {
                let user_1_bal = minter.chain().get_balance(&user_1_addr.0.into(), &[]);
                assert_eq!(user_1_bal, Some(get_asset("0.0000 GRAEL")));

                let user_2_bal = minter.chain().get_balance(&user_2_addr.0.into(), &[]);
                assert_eq!(user_2_bal, Some(get_asset("99.0000 GRAEL")));

                let minter_addr = ScriptHash::from(&minter.genesis_info().script);
                let minter_bal = minter.chain().get_balance(&minter_addr, &[]);
                // The fee loops back to the minter in a reward transaction generated by the
                // minter block production
                assert_eq!(minter_bal, Some(get_asset("901.0000 GRAEL")));

                System::current().stop();
                Ok(())
            }),
        );
    })
    .unwrap();
}

#[test]
fn insufficient_balance_caused_by_fee() {
    System::run(|| {
        let minter = TestMinter::new();

        let from_addr = ScriptHash::from(&minter.genesis_info().script);
        let to_addr = KeyPair::gen();
        let tx = {
            let mut tx = TransferTx {
                base: create_tx_header(TxType::TRANSFER, "1001.0000 GRAEL"),
                from: from_addr.clone(),
                to: (&to_addr.0).into(),
                amount: get_asset("0.0000 GRAEL"),
                memo: vec![],
                script: minter.genesis_info().script.clone(),
            };
            tx.append_sign(&minter.genesis_info().wallet_keys[3]);
            tx.append_sign(&minter.genesis_info().wallet_keys[0]);
            TxVariant::TransferTx(tx)
        };
        let fut = minter.request(MsgRequest::Broadcast(tx));
        Arbiter::spawn(
            fut.and_then(move |res| {
                assert_eq!(
                    res,
                    MsgResponse::Error(net::ErrorKind::TxValidation(
                        verify::TxErr::InsufficientBalance
                    ))
                );
                minter.produce_block().map(|_| minter)
            })
            .and_then(move |minter| {
                let chain = minter.chain();
                let cur_bal = chain.get_balance(&to_addr.0.into(), &[]);
                assert_eq!(cur_bal, Some(get_asset("0.0000 GRAEL")));

                let cur_bal = chain.get_balance(&from_addr, &[]);
                assert_eq!(cur_bal, Some(get_asset("1000.0000 GRAEL")));

                System::current().stop();
                Ok(())
            }),
        );
    })
    .unwrap();
}

#[test]
fn insufficient_balance_caused_by_amt() {
    System::run(|| {
        let minter = TestMinter::new();

        let from_addr = ScriptHash::from(&minter.genesis_info().script);
        let to_addr = KeyPair::gen();
        let tx = {
            let mut tx = TransferTx {
                base: create_tx_header(TxType::TRANSFER, "1.0000 GRAEL"),
                from: from_addr.clone(),
                to: (&to_addr.0).into(),
                amount: get_asset("500000.0000 GRAEL"),
                memo: vec![],
                script: minter.genesis_info().script.clone(),
            };
            tx.append_sign(&minter.genesis_info().wallet_keys[3]);
            tx.append_sign(&minter.genesis_info().wallet_keys[0]);
            TxVariant::TransferTx(tx)
        };
        let fut = minter.request(MsgRequest::Broadcast(tx));
        Arbiter::spawn(
            fut.and_then(move |res| {
                assert_eq!(
                    res,
                    MsgResponse::Error(net::ErrorKind::TxValidation(
                        verify::TxErr::InsufficientBalance
                    ))
                );
                minter.produce_block().map(|_| minter)
            })
            .and_then(move |minter| {
                let chain = minter.chain();
                let cur_bal = chain.get_balance(&to_addr.0.into(), &[]);
                assert_eq!(cur_bal, Some(get_asset("0.0000 GRAEL")));

                let cur_bal = chain.get_balance(&from_addr, &[]);
                assert_eq!(cur_bal, Some(get_asset("1000.0000 GRAEL")));

                System::current().stop();
                Ok(())
            }),
        );
    })
    .unwrap();
}

#[test]
fn memo_too_large() {
    System::run(|| {
        let minter = TestMinter::new();

        let from_addr = ScriptHash::from(&minter.genesis_info().script);
        let to_addr = KeyPair::gen();
        let tx = {
            let mut tx = TransferTx {
                base: create_tx_header(TxType::TRANSFER, "1.0000 GRAEL"),
                from: from_addr.clone(),
                to: (&to_addr.0).into(),
                amount: get_asset("1.0000 GRAEL"),
                memo: (0..=godcoin::constants::MAX_MEMO_BYTE_SIZE)
                    .map(|_| 0)
                    .collect(),
                script: minter.genesis_info().script.clone(),
            };
            tx.append_sign(&minter.genesis_info().wallet_keys[3]);
            tx.append_sign(&minter.genesis_info().wallet_keys[0]);
            TxVariant::TransferTx(tx)
        };
        let fut = minter.request(MsgRequest::Broadcast(tx));
        Arbiter::spawn(
            fut.and_then(move |res| {
                assert_eq!(
                    res,
                    MsgResponse::Error(net::ErrorKind::TxValidation(verify::TxErr::TxTooLarge))
                );
                minter.produce_block().map(|_| minter)
            })
            .and_then(move |minter| {
                let chain = minter.chain();
                let cur_bal = chain.get_balance(&to_addr.0.into(), &[]);
                assert_eq!(cur_bal, Some(get_asset("0.0000 GRAEL")));

                let cur_bal = chain.get_balance(&from_addr, &[]);
                assert_eq!(cur_bal, Some(get_asset("1000.0000 GRAEL")));

                System::current().stop();
                Ok(())
            }),
        );
    })
    .unwrap();
}

#[test]
fn script_too_large() {
    System::run(|| {
        let minter = TestMinter::new();

        let from_script = Script::new(
            (0..=godcoin::constants::MAX_SCRIPT_BYTE_SIZE)
                .map(|_| 0)
                .collect(),
        );
        let from_addr = ScriptHash::from(&from_script);
        let to_addr = KeyPair::gen();
        let tx = {
            let mut tx = TransferTx {
                base: create_tx_header(TxType::TRANSFER, "1.0000 GRAEL"),
                from: from_addr,
                to: (&to_addr.0).into(),
                amount: get_asset("1.0000 GRAEL"),
                memo: vec![],
                script: from_script,
            };
            tx.append_sign(&minter.genesis_info().wallet_keys[3]);
            tx.append_sign(&minter.genesis_info().wallet_keys[0]);
            TxVariant::TransferTx(tx)
        };
        let fut = minter.request(MsgRequest::Broadcast(tx));
        Arbiter::spawn(fut.and_then(move |res| {
            assert_eq!(
                res,
                MsgResponse::Error(net::ErrorKind::TxValidation(verify::TxErr::TxTooLarge))
            );
            System::current().stop();
            Ok(())
        }));
    })
    .unwrap();
}
<commit_msg>Test when the fee is less than the required total<commit_after>use actix::prelude::*;
use godcoin::prelude::*;

mod common;
pub use common::*;

#[test]
fn transfer_from_minter() {
    System::run(|| {
        let minter = TestMinter::new();

        let from_addr = ScriptHash::from(&minter.genesis_info().script);
        let from_bal = minter.chain().get_balance(&from_addr, &[]).unwrap();
        let to_addr = KeyPair::gen();
        let amount = get_asset("1.0000 GRAEL");

        let tx = {
            let mut tx = TransferTx {
                base: create_tx_header(TxType::TRANSFER, "1.0000 GRAEL"),
                from: from_addr.clone(),
                to: (&to_addr.0).into(),
                amount,
                memo: vec![],
                script: minter.genesis_info().script.clone(),
            };
            tx.append_sign(&minter.genesis_info().wallet_keys[3]);
            tx.append_sign(&minter.genesis_info().wallet_keys[0]);
            TxVariant::TransferTx(tx)
        };
        let fut = minter.request(MsgRequest::Broadcast(tx));
        Arbiter::spawn(
            fut.and_then(move |res| {
                assert_eq!(res, MsgResponse::Broadcast);
                minter.produce_block().map(|_| minter)
            })
            .and_then(move |minter| {
                let chain = minter.chain();
                let cur_bal = chain.get_balance(&to_addr.0.into(), &[]);
                assert_eq!(cur_bal, Some(amount));

                // The fee transfers back to the minter wallet in the form of a reward tx so it
                // must not be subtracted during the assertion
                let cur_bal = chain.get_balance(&from_addr, &[]);
                assert_eq!(cur_bal, from_bal.sub(amount));

                System::current().stop();
                Ok(())
            }),
        );
    })
    .unwrap();
}

#[test]
fn transfer_from_user() {
    System::run(|| {
        let minter = TestMinter::new();

        let user_1_addr = KeyPair::gen();
        let user_2_addr = KeyPair::gen();

        let fut = {
            let tx = {
                let mut tx = TransferTx {
                    base: create_tx_header(TxType::TRANSFER, "1.0000 GRAEL"),
                    from: ScriptHash::from(&minter.genesis_info().script),
                    to: (&user_1_addr.0).into(),
                    amount: get_asset("100.0000 GRAEL"),
                    memo: vec![],
                    script: minter.genesis_info().script.clone(),
                };
                tx.append_sign(&minter.genesis_info().wallet_keys[3]);
                tx.append_sign(&minter.genesis_info().wallet_keys[0]);
                TxVariant::TransferTx(tx)
            };
            minter.request(MsgRequest::Broadcast(tx))
        };

        Arbiter::spawn(
            fut.and_then({
                let user_1_addr = user_1_addr.clone();
                let user_2_addr = user_2_addr.clone();
                move |res| {
                    assert_eq!(res, MsgResponse::Broadcast);
                    let tx = {
                        let mut tx = TransferTx {
                            base: create_tx_header(TxType::TRANSFER, "1.0000 GRAEL"),
                            from: (&user_1_addr.0).into(),
                            to: (&user_2_addr.0).into(),
                            amount: get_asset("99.0000 GRAEL"),
                            memo: vec![],
                            script: user_1_addr.0.clone().into(),
                        };
                        tx.append_sign(&user_1_addr);
                        TxVariant::TransferTx(tx)
                    };
                    minter
                        .request(MsgRequest::Broadcast(tx))
                        .map(|res| (res, minter))
                }
            })
            .and_then(|(res, minter)| {
                assert_eq!(res, MsgResponse::Broadcast);
                minter.produce_block().map(|_| minter)
            })
            .and_then(move |minter| {
                let user_1_bal = minter.chain().get_balance(&user_1_addr.0.into(), &[]);
                assert_eq!(user_1_bal, Some(get_asset("0.0000 GRAEL")));

                let user_2_bal = minter.chain().get_balance(&user_2_addr.0.into(), &[]);
                assert_eq!(user_2_bal, Some(get_asset("99.0000 GRAEL")));

                let minter_addr = ScriptHash::from(&minter.genesis_info().script);
                let minter_bal = minter.chain().get_balance(&minter_addr, &[]);
                // The fee loops back to the minter in a reward transaction generated by the
                // minter block production
                assert_eq!(minter_bal, Some(get_asset("901.0000 GRAEL")));

                System::current().stop();
                Ok(())
            }),
        );
    })
    .unwrap();
}

#[test]
fn insufficient_balance_caused_by_fee() {
    System::run(|| {
        let minter = TestMinter::new();

        let from_addr = ScriptHash::from(&minter.genesis_info().script);
        let to_addr = KeyPair::gen();
        let tx = {
            let mut tx = TransferTx {
                base: create_tx_header(TxType::TRANSFER, "1001.0000 GRAEL"),
                from: from_addr.clone(),
                to: (&to_addr.0).into(),
                amount: get_asset("0.0000 GRAEL"),
                memo: vec![],
                script: minter.genesis_info().script.clone(),
            };
            tx.append_sign(&minter.genesis_info().wallet_keys[3]);
            tx.append_sign(&minter.genesis_info().wallet_keys[0]);
            TxVariant::TransferTx(tx)
        };
        let fut = minter.request(MsgRequest::Broadcast(tx));
        Arbiter::spawn(
            fut.and_then(move |res| {
                assert_eq!(
                    res,
                    MsgResponse::Error(net::ErrorKind::TxValidation(
                        verify::TxErr::InsufficientBalance
                    ))
                );
                minter.produce_block().map(|_| minter)
            })
            .and_then(move |minter| {
                let chain = minter.chain();
                let cur_bal = chain.get_balance(&to_addr.0.into(), &[]);
                assert_eq!(cur_bal, Some(get_asset("0.0000 GRAEL")));

                let cur_bal = chain.get_balance(&from_addr, &[]);
                assert_eq!(cur_bal, Some(get_asset("1000.0000 GRAEL")));

                System::current().stop();
                Ok(())
            }),
        );
    })
    .unwrap();
}

#[test]
fn insufficient_fee() {
    System::run(|| {
        let minter = TestMinter::new();

        let from_addr = ScriptHash::from(&minter.genesis_info().script);
        let info = minter.chain().get_address_info(&from_addr, &[]).unwrap();
        let bad_fee = info.net_fee.add(info.addr_fee).unwrap().sub(get_asset("0.0001 GRAEL")).unwrap();
        let tx = {
            let mut tx = TransferTx {
                base: create_tx_header(TxType::TRANSFER, &bad_fee.to_string()),
                from: from_addr.clone(),
                to: KeyPair::gen().0.into(),
                amount: get_asset("0.0000 GRAEL"),
                memo: vec![],
                script: minter.genesis_info().script.clone(),
            };
            tx.append_sign(&minter.genesis_info().wallet_keys[3]);
            tx.append_sign(&minter.genesis_info().wallet_keys[0]);
            TxVariant::TransferTx(tx)
        };
        let fut = minter.request(MsgRequest::Broadcast(tx));
        Arbiter::spawn(
            fut.and_then(move |res| {
                assert_eq!(
                    res,
                    MsgResponse::Error(net::ErrorKind::TxValidation(
                        verify::TxErr::InvalidFeeAmount
                    ))
                );
                System::current().stop();
                Ok(())
            }),
        );
    })
    .unwrap();
}

#[test]
fn insufficient_balance_caused_by_amt() {
    System::run(|| {
        let minter = TestMinter::new();

        let from_addr = ScriptHash::from(&minter.genesis_info().script);
        let to_addr = KeyPair::gen();
        let tx = {
            let mut tx = TransferTx {
                base: create_tx_header(TxType::TRANSFER, "1.0000 GRAEL"),
                from: from_addr.clone(),
                to: (&to_addr.0).into(),
                amount: get_asset("500000.0000 GRAEL"),
                memo: vec![],
                script: minter.genesis_info().script.clone(),
            };
            tx.append_sign(&minter.genesis_info().wallet_keys[3]);
            tx.append_sign(&minter.genesis_info().wallet_keys[0]);
            TxVariant::TransferTx(tx)
        };
        let fut = minter.request(MsgRequest::Broadcast(tx));
        Arbiter::spawn(
            fut.and_then(move |res| {
                assert_eq!(
                    res,
                    MsgResponse::Error(net::ErrorKind::TxValidation(
                        verify::TxErr::InsufficientBalance
                    ))
                );
                minter.produce_block().map(|_| minter)
            })
            .and_then(move |minter| {
                let chain = minter.chain();
                let cur_bal = chain.get_balance(&to_addr.0.into(), &[]);
                assert_eq!(cur_bal, Some(get_asset("0.0000 GRAEL")));

                let cur_bal = chain.get_balance(&from_addr, &[]);
                assert_eq!(cur_bal, Some(get_asset("1000.0000 GRAEL")));

                System::current().stop();
                Ok(())
            }),
        );
    })
    .unwrap();
}

#[test]
fn memo_too_large() {
    System::run(|| {
        let minter = TestMinter::new();

        let from_addr = ScriptHash::from(&minter.genesis_info().script);
        let to_addr = KeyPair::gen();
        let tx = {
            let mut tx = TransferTx {
                base: create_tx_header(TxType::TRANSFER, "1.0000 GRAEL"),
                from: from_addr.clone(),
                to: (&to_addr.0).into(),
                amount: get_asset("1.0000 GRAEL"),
                memo: (0..=godcoin::constants::MAX_MEMO_BYTE_SIZE)
                    .map(|_| 0)
                    .collect(),
                script: minter.genesis_info().script.clone(),
            };
            tx.append_sign(&minter.genesis_info().wallet_keys[3]);
            tx.append_sign(&minter.genesis_info().wallet_keys[0]);
            TxVariant::TransferTx(tx)
        };
        let fut = minter.request(MsgRequest::Broadcast(tx));
        Arbiter::spawn(
            fut.and_then(move |res| {
                assert_eq!(
                    res,
                    MsgResponse::Error(net::ErrorKind::TxValidation(verify::TxErr::TxTooLarge))
                );
                minter.produce_block().map(|_| minter)
            })
            .and_then(move |minter| {
                let chain = minter.chain();
                let cur_bal = chain.get_balance(&to_addr.0.into(), &[]);
                assert_eq!(cur_bal, Some(get_asset("0.0000 GRAEL")));

                let cur_bal = chain.get_balance(&from_addr, &[]);
                assert_eq!(cur_bal, Some(get_asset("1000.0000 GRAEL")));

                System::current().stop();
                Ok(())
            }),
        );
    })
    .unwrap();
}

#[test]
fn script_too_large() {
    System::run(|| {
        let minter = TestMinter::new();

        let from_script = Script::new(
            (0..=godcoin::constants::MAX_SCRIPT_BYTE_SIZE)
                .map(|_| 0)
                .collect(),
        );
        let from_addr = ScriptHash::from(&from_script);
        let to_addr = KeyPair::gen();
        let tx = {
            let mut tx = TransferTx {
                base: create_tx_header(TxType::TRANSFER, "1.0000 GRAEL"),
                from: from_addr,
                to: (&to_addr.0).into(),
                amount: get_asset("1.0000 GRAEL"),
                memo: vec![],
                script: from_script,
            };
            tx.append_sign(&minter.genesis_info().wallet_keys[3]);
            tx.append_sign(&minter.genesis_info().wallet_keys[0]);
            TxVariant::TransferTx(tx)
        };
        let fut = minter.request(MsgRequest::Broadcast(tx));
        Arbiter::spawn(fut.and_then(move |res| {
            assert_eq!(
                res,
                MsgResponse::Error(net::ErrorKind::TxValidation(verify::TxErr::TxTooLarge))
            );
            System::current().stop();
            Ok(())
        }));
    })
    .unwrap();
}
<|endoftext|>


<commit_before>use super::utils::types::{ Sha256Digest };
use super::blake2::{ Blake2s, Digest };
use super::bytes::{ BytesMut, BufMut };
use super::crystallized_state::CrystallizedState;
use super::aggregate_vote::AggregateVote;
use super::config::Config;

const AGG_VOTE_MSG_SIZE: i32 = 2 + 32 + 32 + 8 + 8;

// Interprets a 3-byte slice from a [u8] as an integer.
fn get_shift_from_source(source: &[u8], offset: usize) -> usize {
    (source[offset + 2] as usize) |
        ((source[offset + 1] as usize) << 8) |
        ((source[offset    ] as usize) << 16)
}

// Given entropy in the form of `seed`, return a shuffled list of validators
// indicies of size `validator_count` or `sample`.
pub fn get_shuffling(
    seed: &Sha256Digest,
    validator_count: &usize,
    config: &Config)
    -> Vec<usize>
{
    assert!(*validator_count > 0, "cannot shuffle 0 validators");
    let mut output: Vec<usize> = (0..*validator_count).collect();
    assert!(*validator_count <= (config.max_validators as usize),
        "validator_count exceeds max_validators");

    // Do the first blake hash round
    let mut source = Blake2s::new();
    source.input(&seed);

    let mut v = 0;
    while v < *validator_count {
        let current_source = source.result();
        let mut source_offset = 0;
        while source_offset < 30 {
            let m = get_shift_from_source(&current_source, source_offset);
            let shuffled_position: usize = (m % (validator_count - v)) + v;
            output.swap(v as usize, shuffled_position as usize);
            v += 1;
            if v >= *validator_count { break; }
            source_offset += 3;
        }
        // Re-hash the source (TODO: this does one extra hash, can be optimised)
        source = Blake2s::new();
        source.input(&current_source);
    }
    output
}

// Given an aggregate_vote and a crystallized_state,
// return a byte array for signing or verification.
pub fn get_crosslink_aggvote_msg(
    agg_vote: &AggregateVote,
    cry_state: &CrystallizedState)
    ->  Vec<u8>
{
    let mut buf = BytesMut::with_capacity(AGG_VOTE_MSG_SIZE as usize);
    buf.put_u16_be(agg_vote.shard_id);
    buf.extend_from_slice(&agg_vote.shard_block_hash.to_vec());
    buf.extend_from_slice(&cry_state.current_checkpoint.to_vec());
    buf.put_u64_be(cry_state.current_epoch);
    buf.put_u64_be(cry_state.last_justified_epoch);
    buf.to_vec()
}

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

    #[test]
    fn test_shuffling_shift_fn() {
        let mut x = get_shift_from_source(
            &vec![0_u8, 0, 1],
            0);
        assert_eq!((x as u32), 1);

        x = get_shift_from_source(
            &vec![0_u8, 1, 1],
            0);
        assert_eq!(x, 257);

        x = get_shift_from_source(
            &vec![1_u8, 1, 1],
            0);
        assert_eq!(x, 65793);

        x = get_shift_from_source(
            &vec![255_u8, 1, 1],
            0);
        assert_eq!(x, 16711937);
    }

    #[test]
    fn test_shuffling() {
        let s = get_shuffling(
            &Sha256Digest::zero(),
            &10,
            &Config::standard());
        assert_eq!(s,
                   vec!(0, 9, 7, 6, 4, 1, 8, 5, 2, 3),
                   "10 validator shuffle was not as expected");
    }

    #[test]
    fn test_shuffling_with_gt_half_max_validators() {
        let mut config = Config::standard();
        config.max_validators = 19;
        let s = get_shuffling(
            &Sha256Digest::zero(),
            &10,
            &Config::standard());
        assert_eq!(s,
                   vec!(0, 9, 7, 6, 4, 1, 8, 5, 2, 3),
                   "10 validator shuffle was not as expected");
    }

    #[test]
    fn test_crosslink_aggvote_msg() {
        let mut cs_state = CrystallizedState::zero();
        let mut agg_vote = AggregateVote::zero();
        // All zeros
        let m1 = get_crosslink_aggvote_msg(&agg_vote, &cs_state);
        assert_eq!(m1,
                   vec![0_u8; AGG_VOTE_MSG_SIZE as usize],
                   "failed all zeros test");
        // With some values
        agg_vote.shard_id = 42;
        cs_state.current_epoch = 99;
        cs_state.last_justified_epoch = 123;
        let m2 = get_crosslink_aggvote_msg(&agg_vote, &cs_state);
        assert_eq!(m2[0..2], [0, 42]);
        assert_eq!(m2[2..34], [0; 32]);     // TODO: test with non-zero hash
        assert_eq!(m2[34..66], [0; 32]);    // TODO: test with non-zero hash
        assert_eq!(m2[66..74], [0, 0, 0, 0, 0, 0, 0, 99]);
        assert_eq!(m2[74..82], [0, 0, 0, 0, 0, 0, 0, 123]);
    }

    #[test]
    fn test_attester_and_proposer_selection() {
        let mut cry_state = CrystallizedState::zero();
        for _ in 0..10 {
            cry_state.active_validators.push(ValidatorRecord {
                pubkey: get_dangerous_test_keypair().public,
                withdrawal_shard: 0,
                withdrawal_address: Address::zero(),
                randao_commitment: Sha256Digest::zero(),
                balance: 0,
                switch_dynasty: 0
            });
        }
        let act_state = ActiveState::zero();
        let (attestors, proposer) = get_attesters_and_proposer(
            &cry_state,
            &act_state,
            &0,
            &Config::standard());
        assert_eq!(attestors, [0, 9, 7, 6, 4, 1, 8, 5, 2]);
        assert_eq!(proposer, 3);
    }

    #[test]
    #[should_panic(expected = "must be >=2 active validators")]
    fn test_attester_and_proposer_selection_with_zero_active_validators() {
        let mut cry_state = CrystallizedState::zero();
        cry_state.active_validators = Vec::new();
        let act_state = ActiveState::zero();
        let (_attestors, _proposer) = get_attesters_and_proposer(
            &cry_state,
            &act_state,
            &0,
            &Config::standard());
    }
}
<commit_msg>Implement get_attesters_and_proposer<commit_after>use super::utils::types::{ Sha256Digest };
use super::blake2::{ Blake2s, Digest };
use super::bytes::{ BytesMut, BufMut };
use super::crystallized_state::CrystallizedState;
use super::active_state::ActiveState;
use super::aggregate_vote::AggregateVote;
use super::config::Config;

const AGG_VOTE_MSG_SIZE: i32 = 2 + 32 + 32 + 8 + 8;

// Interprets a 3-byte slice from a [u8] as an integer.
fn get_shift_from_source(source: &[u8], offset: usize) -> usize {
    (source[offset + 2] as usize) |
        ((source[offset + 1] as usize) << 8) |
        ((source[offset    ] as usize) << 16)
}

// Given entropy in the form of `seed`, return a shuffled list of validators
// indicies of size `validator_count` or `sample`.
pub fn get_shuffling(
    seed: &Sha256Digest,
    validator_count: &usize,
    config: &Config)
    -> Vec<usize>
{
    assert!(*validator_count > 0, "cannot shuffle 0 validators");
    let mut output: Vec<usize> = (0..*validator_count).collect();
    assert!(*validator_count <= (config.max_validators as usize),
        "validator_count exceeds max_validators");

    // Do the first blake hash round
    let mut source = Blake2s::new();
    source.input(&seed);

    let mut v = 0;
    while v < *validator_count {
        let current_source = source.result();
        let mut source_offset = 0;
        while source_offset < 30 {
            let m = get_shift_from_source(&current_source, source_offset);
            let shuffled_position: usize = (m % (validator_count - v)) + v;
            output.swap(v as usize, shuffled_position as usize);
            v += 1;
            if v >= *validator_count { break; }
            source_offset += 3;
        }
        // Re-hash the source (TODO: this does one extra hash, can be optimised)
        source = Blake2s::new();
        source.input(&current_source);
    }
    output
}

// Given an aggregate_vote and a crystallized_state,
// return a byte array for signing or verification.
pub fn get_crosslink_aggvote_msg(
    agg_vote: &AggregateVote,
    cry_state: &CrystallizedState)
    ->  Vec<u8>
{
    let mut buf = BytesMut::with_capacity(AGG_VOTE_MSG_SIZE as usize);
    buf.put_u16_be(agg_vote.shard_id);
    buf.extend_from_slice(&agg_vote.shard_block_hash.to_vec());
    buf.extend_from_slice(&cry_state.current_checkpoint.to_vec());
    buf.put_u64_be(cry_state.current_epoch);
    buf.put_u64_be(cry_state.last_justified_epoch);
    buf.to_vec()
}

// For a given state set and skip_count, return a proposer and set
// of attestors.
pub fn get_attesters_and_proposer(
    cry_state: &CrystallizedState,
    act_state: &ActiveState,
    skip_count: &u64,
    config: &Config)
    -> (Vec<usize>, usize)
{
    let active_validator_count = cry_state.num_active_validators();
    assert!(active_validator_count >= 2, "must be >=2 active validators");
    let shuffled_validator_indicies = get_shuffling(
        &act_state.randao,
        &active_validator_count,
        config);
    let proposer_count: usize = 1;
    let ideal_validator_count: usize = (config.attester_count as usize)
        + (*skip_count as usize) + proposer_count;
    if ideal_validator_count > active_validator_count {
        return (
            shuffled_validator_indicies[0..active_validator_count - 1].to_vec(),
            shuffled_validator_indicies[active_validator_count - 1]);
    } else {
        return (
            shuffled_validator_indicies[0..ideal_validator_count - 1].to_vec(),
            shuffled_validator_indicies[ideal_validator_count - 1]);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use super::super::validator_record::ValidatorRecord;
    use super::super::utils::types::Address;
    use super::super::
        utils::test_helpers::get_dangerous_test_keypair;

    #[test]
    fn test_shuffling_shift_fn() {
        let mut x = get_shift_from_source(
            &vec![0_u8, 0, 1],
            0);
        assert_eq!((x as u32), 1);

        x = get_shift_from_source(
            &vec![0_u8, 1, 1],
            0);
        assert_eq!(x, 257);

        x = get_shift_from_source(
            &vec![1_u8, 1, 1],
            0);
        assert_eq!(x, 65793);

        x = get_shift_from_source(
            &vec![255_u8, 1, 1],
            0);
        assert_eq!(x, 16711937);
    }

    #[test]
    fn test_shuffling() {
        let s = get_shuffling(
            &Sha256Digest::zero(),
            &10,
            &Config::standard());
        assert_eq!(s,
                   vec!(0, 9, 7, 6, 4, 1, 8, 5, 2, 3),
                   "10 validator shuffle was not as expected");
    }

    #[test]
    fn test_shuffling_with_gt_half_max_validators() {
        let mut config = Config::standard();
        config.max_validators = 19;
        let s = get_shuffling(
            &Sha256Digest::zero(),
            &10,
            &Config::standard());
        assert_eq!(s,
                   vec!(0, 9, 7, 6, 4, 1, 8, 5, 2, 3),
                   "10 validator shuffle was not as expected");
    }

    #[test]
    fn test_crosslink_aggvote_msg() {
        let mut cs_state = CrystallizedState::zero();
        let mut agg_vote = AggregateVote::zero();
        // All zeros
        let m1 = get_crosslink_aggvote_msg(&agg_vote, &cs_state);
        assert_eq!(m1,
                   vec![0_u8; AGG_VOTE_MSG_SIZE as usize],
                   "failed all zeros test");
        // With some values
        agg_vote.shard_id = 42;
        cs_state.current_epoch = 99;
        cs_state.last_justified_epoch = 123;
        let m2 = get_crosslink_aggvote_msg(&agg_vote, &cs_state);
        assert_eq!(m2[0..2], [0, 42]);
        assert_eq!(m2[2..34], [0; 32]);     // TODO: test with non-zero hash
        assert_eq!(m2[34..66], [0; 32]);    // TODO: test with non-zero hash
        assert_eq!(m2[66..74], [0, 0, 0, 0, 0, 0, 0, 99]);
        assert_eq!(m2[74..82], [0, 0, 0, 0, 0, 0, 0, 123]);
    }

    #[test]
    fn test_attester_and_proposer_selection() {
        let mut cry_state = CrystallizedState::zero();
        for _ in 0..10 {
            cry_state.active_validators.push(ValidatorRecord {
                pubkey: get_dangerous_test_keypair().public,
                withdrawal_shard: 0,
                withdrawal_address: Address::zero(),
                randao_commitment: Sha256Digest::zero(),
                balance: 0,
                switch_dynasty: 0
            });
        }
        let act_state = ActiveState::zero();
        let (attestors, proposer) = get_attesters_and_proposer(
            &cry_state,
            &act_state,
            &0,
            &Config::standard());
        assert_eq!(attestors, [0, 9, 7, 6, 4, 1, 8, 5, 2]);
        assert_eq!(proposer, 3);
    }

    #[test]
    #[should_panic(expected = "must be >=2 active validators")]
    fn test_attester_and_proposer_selection_with_zero_active_validators() {
        let mut cry_state = CrystallizedState::zero();
        cry_state.active_validators = Vec::new();
        let act_state = ActiveState::zero();
        let (_attestors, _proposer) = get_attesters_and_proposer(
            &cry_state,
            &act_state,
            &0,
            &Config::standard());
    }
}
<|endoftext|>


<commit_before>use clippy_utils::consts::{constant_context, Constant};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::in_macro;
use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_diagnostic_item;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;

declare_clippy_lint! {
    /// **What it does:** Checks for usage of `.repeat(1)` and suggest the following method for each types.
    /// - `.to_string()` for `str`
    /// - `.clone()` for `String`
    /// - `.to_vec()` for `slice`
    ///
    /// **Why is this bad?** For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning the string is the intention behind this, `clone()` should be used.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```rust
    /// fn main() {
    ///     let x = String::from("hello world").repeat(1);
    /// }
    /// ```
    /// Use instead:
    /// ```rust
    /// fn main() {
    ///     let x = String::from("hello world").clone();
    /// }
    /// ```
    pub REPEAT_ONCE,
    complexity,
    "using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` "
}

declare_lint_pass!(RepeatOnce => [REPEAT_ONCE]);

impl<'tcx> LateLintPass<'tcx> for RepeatOnce {
    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
        if_chain! {
            if let ExprKind::MethodCall(path, _, [receiver, count], _) = &expr.kind;
            if path.ident.name == sym!(repeat);
            if let Some(Constant::Int(1)) = constant_context(cx, cx.typeck_results()).expr(count);
            if !in_macro(receiver.span);
            then {
                let ty = cx.typeck_results().expr_ty(receiver).peel_refs();
                if ty.is_str() {
                    span_lint_and_sugg(
                        cx,
                        REPEAT_ONCE,
                        expr.span,
                        "calling `repeat(1)` on str",
                        "consider using `.to_string()` instead",
                        format!("{}.to_string()", snippet(cx, receiver.span, r#""...""#)),
                        Applicability::MachineApplicable,
                    );
                } else if ty.builtin_index().is_some() {
                    span_lint_and_sugg(
                        cx,
                        REPEAT_ONCE,
                        expr.span,
                        "calling `repeat(1)` on slice",
                        "consider using `.to_vec()` instead",
                        format!("{}.to_vec()", snippet(cx, receiver.span, r#""...""#)),
                        Applicability::MachineApplicable,
                    );
                } else if is_type_diagnostic_item(cx, ty, sym::string_type) {
                    span_lint_and_sugg(
                        cx,
                        REPEAT_ONCE,
                        expr.span,
                        "calling `repeat(1)` on a string literal",
                        "consider using `.clone()` instead",
                        format!("{}.clone()", snippet(cx, receiver.span, r#""...""#)),
                        Applicability::MachineApplicable,
                    );
                }
            }
        }
    }
}
<commit_msg>Documented constant expression evaluation for `repeat_once`<commit_after>use clippy_utils::consts::{constant_context, Constant};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::in_macro;
use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_diagnostic_item;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;

declare_clippy_lint! {
    /// **What it does:** Checks for usage of `.repeat(1)` and suggest the following method for each types.
    /// - `.to_string()` for `str`
    /// - `.clone()` for `String`
    /// - `.to_vec()` for `slice`
    ///
    /// The lint will evaluate constant expressions and values as arguments of `.repeat(..)` and emit a message if
    /// they are equivalent to `1`. (Related discussion in [rust-clippy#7306](https://github.com/rust-lang/rust-clippy/issues/7306))
    ///
    /// **Why is this bad?** For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning
    /// the string is the intention behind this, `clone()` should be used.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```rust
    /// fn main() {
    ///     let x = String::from("hello world").repeat(1);
    /// }
    /// ```
    /// Use instead:
    /// ```rust
    /// fn main() {
    ///     let x = String::from("hello world").clone();
    /// }
    /// ```
    pub REPEAT_ONCE,
    complexity,
    "using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` "
}

declare_lint_pass!(RepeatOnce => [REPEAT_ONCE]);

impl<'tcx> LateLintPass<'tcx> for RepeatOnce {
    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
        if_chain! {
            if let ExprKind::MethodCall(path, _, [receiver, count], _) = &expr.kind;
            if path.ident.name == sym!(repeat);
            if let Some(Constant::Int(1)) = constant_context(cx, cx.typeck_results()).expr(count);
            if !in_macro(receiver.span);
            then {
                let ty = cx.typeck_results().expr_ty(receiver).peel_refs();
                if ty.is_str() {
                    span_lint_and_sugg(
                        cx,
                        REPEAT_ONCE,
                        expr.span,
                        "calling `repeat(1)` on str",
                        "consider using `.to_string()` instead",
                        format!("{}.to_string()", snippet(cx, receiver.span, r#""...""#)),
                        Applicability::MachineApplicable,
                    );
                } else if ty.builtin_index().is_some() {
                    span_lint_and_sugg(
                        cx,
                        REPEAT_ONCE,
                        expr.span,
                        "calling `repeat(1)` on slice",
                        "consider using `.to_vec()` instead",
                        format!("{}.to_vec()", snippet(cx, receiver.span, r#""...""#)),
                        Applicability::MachineApplicable,
                    );
                } else if is_type_diagnostic_item(cx, ty, sym::string_type) {
                    span_lint_and_sugg(
                        cx,
                        REPEAT_ONCE,
                        expr.span,
                        "calling `repeat(1)` on a string literal",
                        "consider using `.clone()` instead",
                        format!("{}.clone()", snippet(cx, receiver.span, r#""...""#)),
                        Applicability::MachineApplicable,
                    );
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use reqwest;
pub use reqwest::header::ContentType;
use reqwest::Response;
pub use reqwest::{Method, StatusCode};
use result::{MwsError, MwsResult};
use sign::SignatureV2;
use std::io::Read;
use xmlhelper::decode::{FromXmlStream, Stream};
use SerializeMwsParams;

#[derive(Debug)]
pub struct ErrorResponse {
  pub status: StatusCode,
  pub info: Option<ErrorResponseInfo>,
  pub raw: String,
}

#[derive(Debug, Default)]
pub struct ErrorResponseInfo {
  pub errors: Vec<ErrorResponseError>,
  pub request_id: String,
}

#[derive(Debug, Default, PartialEq)]
pub struct ErrorResponseError {
  pub error_type: String,
  pub code: String,
  pub message: String,
  pub detail: String,
}

impl ErrorResponseInfo {
  fn from_xml_stream<R: ::std::io::Read>(s: &mut Stream<R>) -> MwsResult<ErrorResponseInfo> {
    use xmlhelper::decode::{characters, element, fold_elements, start_document};
    start_document(s)?;
    element(s, "ErrorResponse", |s| {
      fold_elements(s, ErrorResponseInfo::default(), |s, resp| {
        match s.local_name() {
          "Error" => {
            let err = fold_elements(s, ErrorResponseError::default(), |s, err| {
              match s.local_name() {
                "Type" => {
                  err.error_type = characters(s)?;
                }
                "Code" => {
                  err.code = characters(s)?;
                }
                "Message" => {
                  err.message = characters(s)?;
                }
                "Detail" => {
                  err.detail = characters(s)?;
                }
                _ => {}
              }
              Ok(())
            })?;
            resp.errors.push(err);
          }
          "RequestID" => {
            resp.request_id = characters(s)?;
          }
          _ => {}
        }
        Ok(())
      })
    })
    .into()
  }
}

impl FromXmlStream<Stream<reqwest::Response>> for ErrorResponseInfo {
  fn from_xml(s: &mut Stream<reqwest::Response>) -> MwsResult<ErrorResponseInfo> {
    ErrorResponseInfo::from_xml_stream(s)
  }
}

impl FromXmlStream<Stream<::std::io::Cursor<String>>> for ErrorResponseInfo {
  fn from_xml(s: &mut Stream<::std::io::Cursor<String>>) -> MwsResult<ErrorResponseInfo> {
    ErrorResponseInfo::from_xml_stream(s)
  }
}

/// [Reference](http://docs.developer.amazonservices.com/en_CA/dev_guide/DG_Endpoints.html)
pub struct ClientOptions {
  /// Your software can access Amazon Marketplace Web Service (Amazon MWS) using region-specific endpoints.
  pub endpoint: String,

  /// Your seller or merchant identifier.
  pub seller_id: String,

  /// Represents the authorization of a specific developer of a web application by a specific Amazon seller.
  pub mws_auth_token: Option<String>,

  /// Your Amazon MWS account is identified by your access key Id, which Amazon MWS uses to look up your Secret Access Key.
  pub aws_access_key_id: String,
  pub secret_key: String,
}

pub struct Client {
  options: ClientOptions,
  http_client: reqwest::Client,
}

impl Client {
  pub fn new(options: ClientOptions) -> MwsResult<Client> {
    Ok(Client {
      options: options,
      http_client: reqwest::Client::new(),
    })
  }

  pub fn with_http_client(options: ClientOptions, http_client: reqwest::Client) -> Client {
    Client {
      options: options,
      http_client: http_client,
    }
  }

  pub fn request<P>(
    &self,
    method: Method,
    path: &str,
    version: &str,
    action: &str,
    parameters: P,
  ) -> MwsResult<reqwest::Response>
  where
    P: SerializeMwsParams,
  {
    let mut sign = SignatureV2::new(
      &self.options.endpoint,
      &self.options.aws_access_key_id,
      &self.options.secret_key,
      self.options.mws_auth_token.as_ref().map(AsRef::as_ref),
    );
    for (k, v) in parameters.into_mws_params() {
      sign.add(&k, v);
    }
    sign.add("SellerId", &self.options.seller_id);
    //sign.add("Merchant", self.options.seller_id.as_ref());
    let url = sign
      .generate_url(method.clone(), path, version, action)?
      .to_string();
    // println!("request: {}", url);
    self
      .http_client
      .request(method, &url)
      .send()
      .map_err(MwsError::from)
      .and_then(handle_error_status)
  }

  pub fn request_with_body<P, R>(
    &self,
    method: Method,
    path: &str,
    version: &str,
    action: &str,
    parameters: P,
    body: R,
    content_md5: String,
    content_type: ContentType,
  ) -> MwsResult<reqwest::Response>
  where
    P: SerializeMwsParams,
    R: Read + Send + 'static,
  {
    let mut sign = SignatureV2::new(
      &self.options.endpoint,
      &self.options.aws_access_key_id,
      &self.options.secret_key,
      self.options.mws_auth_token.as_ref().map(AsRef::as_ref),
    );
    for (k, v) in parameters.into_mws_params() {
      sign.add(&k, v);
    }
    sign.add("SellerId", &self.options.seller_id);
    sign.add("ContentMD5Value", content_md5);
    //sign.add("Merchant", self.options.seller_id.as_ref());
    let url = sign
      .generate_url(method.clone(), path, version, action)?
      .to_string();
    //println!("request: {}", url);

    self
      .http_client
      .request(method, &url)
      .header(content_type)
      .body(reqwest::Body::new(body))
      .send()
      .map_err(MwsError::from)
      .and_then(handle_error_status)
  }

  pub fn request_with_form<P>(
    &self,
    method: Method,
    path: &str,
    version: &str,
    action: &str,
    parameters: P,
  ) -> MwsResult<reqwest::Response>
  where
    P: SerializeMwsParams,
  {
    use std::collections::HashMap;

    let mut sign = SignatureV2::new(
      &self.options.endpoint,
      &self.options.aws_access_key_id,
      &self.options.secret_key,
      self.options.mws_auth_token.as_ref().map(AsRef::as_ref),
    );
    for (k, v) in parameters.into_mws_params() {
      sign.add(&k, v);
    }
    sign.add("SellerId", &self.options.seller_id);
    //sign.add("Merchant", self.options.seller_id.as_ref());
    let url = sign.generate_url(method.clone(), path, version, action)?;
    let post_url = url.get_url_without_query();

    let mut form: HashMap<String, String> = url.pairs.into_iter().collect();
    form.insert("Signature".to_string(), url.signature);

    // println!("request url: {}", post_url);
    // println!("request form: {:#?}", form);

    self
      .http_client
      .request(method, &post_url)
      .form(&form)
      .send()
      .map_err(MwsError::from)
      .and_then(handle_error_status)
  }

  pub fn request_xml<P, T>(
    &self,
    method: Method,
    path: &str,
    version: &str,
    action: &str,
    parameters: P,
  ) -> MwsResult<T>
  where
    P: SerializeMwsParams,
    T: FromXmlStream<Stream<reqwest::Response>>,
  {
    let resp = self.request(method, path, version, action, parameters)?;
    let mut stream = Stream::new(resp);
    let v = T::from_xml(&mut stream)?;
    Ok(v)
  }

  pub fn request_xml_with_form<P, T>(
    &self,
    method: Method,
    path: &str,
    version: &str,
    action: &str,
    parameters: P,
  ) -> MwsResult<T>
  where
    P: SerializeMwsParams,
    T: FromXmlStream<Stream<reqwest::Response>>,
  {
    let resp = self.request_with_form(method, path, version, action, parameters)?;
    let mut stream = Stream::new(resp);
    let v = T::from_xml(&mut stream)?;
    Ok(v)
  }

  pub fn request_xml_with_body<P, R, T>(
    &self,
    method: Method,
    path: &str,
    version: &str,
    action: &str,
    parameters: P,
    body: R,
    content_md5: String,
    content_type: ContentType,
  ) -> MwsResult<T>
  where
    P: SerializeMwsParams,
    T: FromXmlStream<Stream<reqwest::Response>>,
    R: Read + Send + 'static,
  {
    let resp = self.request_with_body(
      method,
      path,
      version,
      action,
      parameters,
      body,
      content_md5,
      content_type,
    )?;
    let mut stream = Stream::new(resp);
    let v = T::from_xml(&mut stream)?;
    Ok(v)
  }

  pub fn request_raw<P>(
    &self,
    method: Method,
    path: &str,
    version: &str,
    action: &str,
    parameters: P,
  ) -> MwsResult<(StatusCode, Vec<(String, String)>, Vec<u8>)>
  where
    P: SerializeMwsParams,
  {
    let mut sign = SignatureV2::new(
      &self.options.endpoint,
      &self.options.aws_access_key_id,
      &self.options.secret_key,
      self.options.mws_auth_token.as_ref().map(AsRef::as_ref),
    );
    for (k, v) in parameters.into_mws_params() {
      sign.add(&k, v);
    }
    sign.add("SellerId", &self.options.seller_id);
    let url = sign
      .generate_url(method.clone(), path, version, action)?
      .to_string();
    let mut resp = self
      .http_client
      .request(method, &url)
      .send()
      .map_err(MwsError::from)
      .and_then(handle_error_status)?;

    let headers = resp
      .headers()
      .iter()
      .map(|view| (view.name().to_string(), view.value_string()))
      .collect();

    let mut body = vec![];
    resp.read_to_end(&mut body)?;
    Ok((resp.status().clone(), headers, body))
  }
}

fn handle_error_status(resp: Response) -> MwsResult<Response> {
  if resp.status().is_success() {
    Ok(resp)
  } else {
    use std::io::Cursor;

    let mut resp = resp;
    let mut body = String::new();
    resp.read_to_string(&mut body)?;
    let mut s = Stream::new(Cursor::new(body.clone()));
    match ErrorResponseInfo::from_xml(&mut s) {
      Ok(info) => Err(MwsError::ErrorResponse(ErrorResponse {
        status: resp.status().clone(),
        raw: body,
        info: Some(info),
      })),
      Err(_) => Err(MwsError::ErrorResponse(ErrorResponse {
        status: resp.status().clone(),
        raw: body,
        info: None,
      })),
    }
  }
}

#[cfg(test)]
pub fn get_test_client() -> Client {
  use std::env;
  Client::new(ClientOptions {
    endpoint: env::var("Endpoint").expect("get Endpoint"),
    seller_id: env::var("SellerId").expect("get SellerId"),
    mws_auth_token: None,
    aws_access_key_id: env::var("AWSAccessKeyId").expect("get AWSAccessKeyId"),
    secret_key: env::var("SecretKey").expect("get SecretKey"),
  })
  .expect("create client")
}

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

  #[test]
  #[ignore]
  fn it_works() {
    dotenv().ok();
    let client = get_test_client();
    let (status, _, body) = client
      .request_raw(
        Method::Post,
        "/Orders/2013-09-01",
        "2013-09-01",
        "GetServiceStatus",
        (),
      )
      .expect("send request");
    let body = String::from_utf8(body).unwrap();
    assert!(status.is_success());
    assert!(body.starts_with("<?xml"));

    use std::io::Cursor;
    let (status, _, body) = client
      .request_raw(
        Method::Post,
        "/Fake/2013-09-01",
        "2013-09-01",
        "GetServiceStatus",
        (),
      )
      .expect("send request");
    let body = String::from_utf8(body).unwrap();
    assert!(!status.is_success());
    let source = Cursor::new(body);
    let mut s = Stream::new(source);
    let err_info = ErrorResponseInfo::from_xml(&mut s).expect("decode error response");
    assert_eq!(err_info.errors.len(), 1);
    assert_eq!(
      err_info.errors[0],
      ErrorResponseError {
        error_type: "Sender".to_string(),
        code: "InvalidAddress".to_string(),
        message: "Section Fake/2013-09-01 is invalid".to_string(),
        detail: "".to_string(),
      }
    );
  }
}
<commit_msg>fix: ErrorRepose RequestId not captured.<commit_after>use reqwest;
pub use reqwest::header::ContentType;
use reqwest::Response;
pub use reqwest::{Method, StatusCode};
use result::{MwsError, MwsResult};
use sign::SignatureV2;
use std::io::Read;
use xmlhelper::decode::{FromXmlStream, Stream};
use SerializeMwsParams;

#[derive(Debug)]
pub struct ErrorResponse {
  pub status: StatusCode,
  pub info: Option<ErrorResponseInfo>,
  pub raw: String,
}

#[derive(Debug, Default)]
pub struct ErrorResponseInfo {
  pub errors: Vec<ErrorResponseError>,
  pub request_id: String,
}

#[derive(Debug, Default, PartialEq)]
pub struct ErrorResponseError {
  pub error_type: String,
  pub code: String,
  pub message: String,
  pub detail: String,
}

impl ErrorResponseInfo {
  fn from_xml_stream<R: ::std::io::Read>(s: &mut Stream<R>) -> MwsResult<ErrorResponseInfo> {
    use xmlhelper::decode::{characters, element, fold_elements, start_document};
    start_document(s)?;
    element(s, "ErrorResponse", |s| {
      fold_elements(s, ErrorResponseInfo::default(), |s, resp| {
        match s.local_name() {
          "Error" => {
            let err = fold_elements(s, ErrorResponseError::default(), |s, err| {
              match s.local_name() {
                "Type" => {
                  err.error_type = characters(s)?;
                }
                "Code" => {
                  err.code = characters(s)?;
                }
                "Message" => {
                  err.message = characters(s)?;
                }
                "Detail" => {
                  err.detail = characters(s)?;
                }
                _ => {}
              }
              Ok(())
            })?;
            resp.errors.push(err);
          }
          "RequestId" => {
            resp.request_id = characters(s)?;
          }
          _ => {}
        }
        Ok(())
      })
    })
    .into()
  }
}

impl FromXmlStream<Stream<reqwest::Response>> for ErrorResponseInfo {
  fn from_xml(s: &mut Stream<reqwest::Response>) -> MwsResult<ErrorResponseInfo> {
    ErrorResponseInfo::from_xml_stream(s)
  }
}

impl FromXmlStream<Stream<::std::io::Cursor<String>>> for ErrorResponseInfo {
  fn from_xml(s: &mut Stream<::std::io::Cursor<String>>) -> MwsResult<ErrorResponseInfo> {
    ErrorResponseInfo::from_xml_stream(s)
  }
}

/// [Reference](http://docs.developer.amazonservices.com/en_CA/dev_guide/DG_Endpoints.html)
pub struct ClientOptions {
  /// Your software can access Amazon Marketplace Web Service (Amazon MWS) using region-specific endpoints.
  pub endpoint: String,

  /// Your seller or merchant identifier.
  pub seller_id: String,

  /// Represents the authorization of a specific developer of a web application by a specific Amazon seller.
  pub mws_auth_token: Option<String>,

  /// Your Amazon MWS account is identified by your access key Id, which Amazon MWS uses to look up your Secret Access Key.
  pub aws_access_key_id: String,
  pub secret_key: String,
}

pub struct Client {
  options: ClientOptions,
  http_client: reqwest::Client,
}

impl Client {
  pub fn new(options: ClientOptions) -> MwsResult<Client> {
    Ok(Client {
      options: options,
      http_client: reqwest::Client::new(),
    })
  }

  pub fn with_http_client(options: ClientOptions, http_client: reqwest::Client) -> Client {
    Client {
      options: options,
      http_client: http_client,
    }
  }

  pub fn request<P>(
    &self,
    method: Method,
    path: &str,
    version: &str,
    action: &str,
    parameters: P,
  ) -> MwsResult<reqwest::Response>
  where
    P: SerializeMwsParams,
  {
    let mut sign = SignatureV2::new(
      &self.options.endpoint,
      &self.options.aws_access_key_id,
      &self.options.secret_key,
      self.options.mws_auth_token.as_ref().map(AsRef::as_ref),
    );
    for (k, v) in parameters.into_mws_params() {
      sign.add(&k, v);
    }
    sign.add("SellerId", &self.options.seller_id);
    //sign.add("Merchant", self.options.seller_id.as_ref());
    let url = sign
      .generate_url(method.clone(), path, version, action)?
      .to_string();
    // println!("request: {}", url);
    self
      .http_client
      .request(method, &url)
      .send()
      .map_err(MwsError::from)
      .and_then(handle_error_status)
  }

  pub fn request_with_body<P, R>(
    &self,
    method: Method,
    path: &str,
    version: &str,
    action: &str,
    parameters: P,
    body: R,
    content_md5: String,
    content_type: ContentType,
  ) -> MwsResult<reqwest::Response>
  where
    P: SerializeMwsParams,
    R: Read + Send + 'static,
  {
    let mut sign = SignatureV2::new(
      &self.options.endpoint,
      &self.options.aws_access_key_id,
      &self.options.secret_key,
      self.options.mws_auth_token.as_ref().map(AsRef::as_ref),
    );
    for (k, v) in parameters.into_mws_params() {
      sign.add(&k, v);
    }
    sign.add("SellerId", &self.options.seller_id);
    sign.add("ContentMD5Value", content_md5);
    //sign.add("Merchant", self.options.seller_id.as_ref());
    let url = sign
      .generate_url(method.clone(), path, version, action)?
      .to_string();
    //println!("request: {}", url);

    self
      .http_client
      .request(method, &url)
      .header(content_type)
      .body(reqwest::Body::new(body))
      .send()
      .map_err(MwsError::from)
      .and_then(handle_error_status)
  }

  pub fn request_with_form<P>(
    &self,
    method: Method,
    path: &str,
    version: &str,
    action: &str,
    parameters: P,
  ) -> MwsResult<reqwest::Response>
  where
    P: SerializeMwsParams,
  {
    use std::collections::HashMap;

    let mut sign = SignatureV2::new(
      &self.options.endpoint,
      &self.options.aws_access_key_id,
      &self.options.secret_key,
      self.options.mws_auth_token.as_ref().map(AsRef::as_ref),
    );
    for (k, v) in parameters.into_mws_params() {
      sign.add(&k, v);
    }
    sign.add("SellerId", &self.options.seller_id);
    //sign.add("Merchant", self.options.seller_id.as_ref());
    let url = sign.generate_url(method.clone(), path, version, action)?;
    let post_url = url.get_url_without_query();

    let mut form: HashMap<String, String> = url.pairs.into_iter().collect();
    form.insert("Signature".to_string(), url.signature);

    // println!("request url: {}", post_url);
    // println!("request form: {:#?}", form);

    self
      .http_client
      .request(method, &post_url)
      .form(&form)
      .send()
      .map_err(MwsError::from)
      .and_then(handle_error_status)
  }

  pub fn request_xml<P, T>(
    &self,
    method: Method,
    path: &str,
    version: &str,
    action: &str,
    parameters: P,
  ) -> MwsResult<T>
  where
    P: SerializeMwsParams,
    T: FromXmlStream<Stream<reqwest::Response>>,
  {
    let resp = self.request(method, path, version, action, parameters)?;
    let mut stream = Stream::new(resp);
    let v = T::from_xml(&mut stream)?;
    Ok(v)
  }

  pub fn request_xml_with_form<P, T>(
    &self,
    method: Method,
    path: &str,
    version: &str,
    action: &str,
    parameters: P,
  ) -> MwsResult<T>
  where
    P: SerializeMwsParams,
    T: FromXmlStream<Stream<reqwest::Response>>,
  {
    let resp = self.request_with_form(method, path, version, action, parameters)?;
    let mut stream = Stream::new(resp);
    let v = T::from_xml(&mut stream)?;
    Ok(v)
  }

  pub fn request_xml_with_body<P, R, T>(
    &self,
    method: Method,
    path: &str,
    version: &str,
    action: &str,
    parameters: P,
    body: R,
    content_md5: String,
    content_type: ContentType,
  ) -> MwsResult<T>
  where
    P: SerializeMwsParams,
    T: FromXmlStream<Stream<reqwest::Response>>,
    R: Read + Send + 'static,
  {
    let resp = self.request_with_body(
      method,
      path,
      version,
      action,
      parameters,
      body,
      content_md5,
      content_type,
    )?;
    let mut stream = Stream::new(resp);
    let v = T::from_xml(&mut stream)?;
    Ok(v)
  }

  pub fn request_raw<P>(
    &self,
    method: Method,
    path: &str,
    version: &str,
    action: &str,
    parameters: P,
  ) -> MwsResult<(StatusCode, Vec<(String, String)>, Vec<u8>)>
  where
    P: SerializeMwsParams,
  {
    let mut sign = SignatureV2::new(
      &self.options.endpoint,
      &self.options.aws_access_key_id,
      &self.options.secret_key,
      self.options.mws_auth_token.as_ref().map(AsRef::as_ref),
    );
    for (k, v) in parameters.into_mws_params() {
      sign.add(&k, v);
    }
    sign.add("SellerId", &self.options.seller_id);
    let url = sign
      .generate_url(method.clone(), path, version, action)?
      .to_string();
    let mut resp = self
      .http_client
      .request(method, &url)
      .send()
      .map_err(MwsError::from)
      .and_then(handle_error_status)?;

    let headers = resp
      .headers()
      .iter()
      .map(|view| (view.name().to_string(), view.value_string()))
      .collect();

    let mut body = vec![];
    resp.read_to_end(&mut body)?;
    Ok((resp.status().clone(), headers, body))
  }
}

fn handle_error_status(resp: Response) -> MwsResult<Response> {
  if resp.status().is_success() {
    Ok(resp)
  } else {
    use std::io::Cursor;

    let mut resp = resp;
    let mut body = String::new();
    resp.read_to_string(&mut body)?;
    let mut s = Stream::new(Cursor::new(body.clone()));
    match ErrorResponseInfo::from_xml(&mut s) {
      Ok(info) => Err(MwsError::ErrorResponse(ErrorResponse {
        status: resp.status().clone(),
        raw: body,
        info: Some(info),
      })),
      Err(_) => Err(MwsError::ErrorResponse(ErrorResponse {
        status: resp.status().clone(),
        raw: body,
        info: None,
      })),
    }
  }
}

#[cfg(test)]
pub fn get_test_client() -> Client {
  use std::env;
  Client::new(ClientOptions {
    endpoint: env::var("Endpoint").expect("get Endpoint"),
    seller_id: env::var("SellerId").expect("get SellerId"),
    mws_auth_token: None,
    aws_access_key_id: env::var("AWSAccessKeyId").expect("get AWSAccessKeyId"),
    secret_key: env::var("SecretKey").expect("get SecretKey"),
  })
  .expect("create client")
}

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

  #[test]
  #[ignore]
  fn it_works() {
    dotenv().ok();
    let client = get_test_client();
    let (status, _, body) = client
      .request_raw(
        Method::Post,
        "/Orders/2013-09-01",
        "2013-09-01",
        "GetServiceStatus",
        (),
      )
      .expect("send request");
    let body = String::from_utf8(body).unwrap();
    assert!(status.is_success());
    assert!(body.starts_with("<?xml"));

    use std::io::Cursor;
    let (status, _, body) = client
      .request_raw(
        Method::Post,
        "/Fake/2013-09-01",
        "2013-09-01",
        "GetServiceStatus",
        (),
      )
      .expect("send request");
    let body = String::from_utf8(body).unwrap();
    assert!(!status.is_success());
    let source = Cursor::new(body);
    let mut s = Stream::new(source);
    let err_info = ErrorResponseInfo::from_xml(&mut s).expect("decode error response");
    assert_eq!(err_info.errors.len(), 1);
    assert_eq!(
      err_info.errors[0],
      ErrorResponseError {
        error_type: "Sender".to_string(),
        code: "InvalidAddress".to_string(),
        message: "Section Fake/2013-09-01 is invalid".to_string(),
        detail: "".to_string(),
      }
    );
  }
}
<|endoftext|>


<commit_before>use reqwest;
pub use reqwest::header::ContentType;
use reqwest::Response;
pub use reqwest::{Method, StatusCode};
use result::{MwsError, MwsResult};
use sign::SignatureV2;
use std::io::Read;
use xmlhelper::decode::{FromXmlStream, Stream};
use SerializeMwsParams;

#[derive(Debug)]
pub struct ErrorResponse {
  pub status: StatusCode,
  pub info: Option<ErrorResponseInfo>,
  pub raw: String,
}

#[derive(Debug, Default)]
pub struct ErrorResponseInfo {
  pub errors: Vec<ErrorResponseError>,
  pub request_id: String,
}

#[derive(Debug, Default, PartialEq)]
pub struct ErrorResponseError {
  pub error_type: String,
  pub code: String,
  pub message: String,
  pub detail: String,
}

impl ErrorResponseInfo {
  fn from_xml_stream<R: ::std::io::Read>(s: &mut Stream<R>) -> MwsResult<ErrorResponseInfo> {
    use xmlhelper::decode::{characters, element, fold_elements, start_document};
    start_document(s)?;
    element(s, "ErrorResponse", |s| {
      fold_elements(s, ErrorResponseInfo::default(), |s, resp| {
        match s.local_name() {
          "Error" => {
            let err = fold_elements(s, ErrorResponseError::default(), |s, err| {
              match s.local_name() {
                "Type" => {
                  err.error_type = characters(s)?;
                }
                "Code" => {
                  err.code = characters(s)?;
                }
                "Message" => {
                  err.message = characters(s)?;
                }
                "Detail" => {
                  err.detail = characters(s)?;
                }
                _ => {}
              }
              Ok(())
            })?;
            resp.errors.push(err);
          }
          "RequestID" => {
            resp.request_id = characters(s)?;
          }
          _ => {}
        }
        Ok(())
      })
    })
    .into()
  }
}

impl FromXmlStream<Stream<reqwest::Response>> for ErrorResponseInfo {
  fn from_xml(s: &mut Stream<reqwest::Response>) -> MwsResult<ErrorResponseInfo> {
    ErrorResponseInfo::from_xml_stream(s)
  }
}

impl FromXmlStream<Stream<::std::io::Cursor<String>>> for ErrorResponseInfo {
  fn from_xml(s: &mut Stream<::std::io::Cursor<String>>) -> MwsResult<ErrorResponseInfo> {
    ErrorResponseInfo::from_xml_stream(s)
  }
}

/// [Reference](http://docs.developer.amazonservices.com/en_CA/dev_guide/DG_Endpoints.html)
pub struct ClientOptions {
  /// Your software can access Amazon Marketplace Web Service (Amazon MWS) using region-specific endpoints.
  pub endpoint: String,

  /// Your seller or merchant identifier.
  pub seller_id: String,

  /// Represents the authorization of a specific developer of a web application by a specific Amazon seller.
  pub mws_auth_token: Option<String>,

  /// Your Amazon MWS account is identified by your access key Id, which Amazon MWS uses to look up your Secret Access Key.
  pub aws_access_key_id: String,
  pub secret_key: String,
}

pub struct Client {
  options: ClientOptions,
  http_client: reqwest::Client,
}

impl Client {
  pub fn new(options: ClientOptions) -> MwsResult<Client> {
    Ok(Client {
      options: options,
      http_client: reqwest::Client::new(),
    })
  }

  pub fn with_http_client(options: ClientOptions, http_client: reqwest::Client) -> Client {
    Client {
      options: options,
      http_client: http_client,
    }
  }

  pub fn request<P>(
    &self,
    method: Method,
    path: &str,
    version: &str,
    action: &str,
    parameters: P,
  ) -> MwsResult<reqwest::Response>
  where
    P: SerializeMwsParams,
  {
    let mut sign = SignatureV2::new(
      &self.options.endpoint,
      &self.options.aws_access_key_id,
      &self.options.secret_key,
      self.options.mws_auth_token.as_ref().map(AsRef::as_ref),
    );
    for (k, v) in parameters.into_mws_params() {
      sign.add(&k, v);
    }
    sign.add("SellerId", &self.options.seller_id);
    //sign.add("Merchant", self.options.seller_id.as_ref());
    let url = sign
      .generate_url(method.clone(), path, version, action)?
      .to_string();
    //println!("request: {}", url);
    self
      .http_client
      .request(method, &url)
      .send()
      .map_err(MwsError::from)
      .and_then(handle_error_status)
  }

  pub fn request_with_body<P, R>(
    &self,
    method: Method,
    path: &str,
    version: &str,
    action: &str,
    parameters: P,
    body: R,
    content_md5: String,
    content_type: ContentType,
  ) -> MwsResult<reqwest::Response>
  where
    P: SerializeMwsParams,
    R: Read + Send + 'static,
  {
    let mut sign = SignatureV2::new(
      &self.options.endpoint,
      &self.options.aws_access_key_id,
      &self.options.secret_key,
      self.options.mws_auth_token.as_ref().map(AsRef::as_ref),
    );
    for (k, v) in parameters.into_mws_params() {
      sign.add(&k, v);
    }
    sign.add("SellerId", &self.options.seller_id);
    sign.add("ContentMD5Value", content_md5);
    //sign.add("Merchant", self.options.seller_id.as_ref());
    let url = sign
      .generate_url(method.clone(), path, version, action)?
      .to_string();
    //println!("request: {}", url);

    self
      .http_client
      .request(method, &url)
      .header(content_type)
      .body(reqwest::Body::new(body))
      .send()
      .map_err(MwsError::from)
      .and_then(handle_error_status)
  }

  pub fn request_xml<P, T>(
    &self,
    method: Method,
    path: &str,
    version: &str,
    action: &str,
    parameters: P,
  ) -> MwsResult<T>
  where
    P: SerializeMwsParams,
    T: FromXmlStream<Stream<reqwest::Response>>,
  {
    let resp = self.request(method, path, version, action, parameters)?;
    let mut stream = Stream::new(resp);
    let v = T::from_xml(&mut stream)?;
    Ok(v)
  }

  pub fn request_xml_with_body<P, R, T>(
    &self,
    method: Method,
    path: &str,
    version: &str,
    action: &str,
    parameters: P,
    body: R,
    content_md5: String,
    content_type: ContentType,
  ) -> MwsResult<T>
  where
    P: SerializeMwsParams,
    T: FromXmlStream<Stream<reqwest::Response>>,
    R: Read + Send + 'static,
  {
    let resp = self.request_with_body(
      method,
      path,
      version,
      action,
      parameters,
      body,
      content_md5,
      content_type,
    )?;
    let mut stream = Stream::new(resp);
    let v = T::from_xml(&mut stream)?;
    Ok(v)
  }

  #[cfg(test)]
  pub fn request_raw<P>(
    &self,
    method: Method,
    path: &str,
    version: &str,
    action: &str,
    parameters: P,
  ) -> MwsResult<(StatusCode, String)>
  where
    P: SerializeMwsParams,
  {
    let mut sign = SignatureV2::new(
      &self.options.endpoint,
      &self.options.aws_access_key_id,
      &self.options.secret_key,
      self.options.mws_auth_token.as_ref().map(AsRef::as_ref),
    );
    for (k, v) in parameters.into_mws_params() {
      sign.add(&k, v);
    }
    let url = sign
      .generate_url(method.clone(), path, version, action)?
      .to_string();
    let mut resp = self
      .http_client
      .request(method, &url)
      .send()
      .map_err(MwsError::from)
      .and_then(handle_error_status)?;
    let mut s = String::new();
    resp.read_to_string(&mut s)?;
    Ok((resp.status().clone(), s))
  }
}

fn handle_error_status(resp: Response) -> MwsResult<Response> {
  if resp.status().is_success() {
    Ok(resp)
  } else {
    use std::io::Cursor;

    let mut resp = resp;
    let mut body = String::new();
    resp.read_to_string(&mut body)?;
    let mut s = Stream::new(Cursor::new(body.clone()));
    match ErrorResponseInfo::from_xml(&mut s) {
      Ok(info) => Err(MwsError::ErrorResponse(ErrorResponse {
        status: resp.status().clone(),
        raw: body,
        info: Some(info),
      })),
      Err(_) => Err(MwsError::ErrorResponse(ErrorResponse {
        status: resp.status().clone(),
        raw: body,
        info: None,
      })),
    }
  }
}

#[cfg(test)]
pub fn get_test_client() -> Client {
  use std::env;
  Client::new(ClientOptions {
    endpoint: env::var("Endpoint").expect("get Endpoint"),
    seller_id: env::var("SellerId").expect("get SellerId"),
    mws_auth_token: None,
    aws_access_key_id: env::var("AWSAccessKeyId").expect("get AWSAccessKeyId"),
    secret_key: env::var("SecretKey").expect("get SecretKey"),
  })
  .expect("create client")
}

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

  #[test]
  #[ignore]
  fn it_works() {
    dotenv().ok();
    let client = get_test_client();
    let (status, body) = client
      .request_raw(
        Method::Post,
        "/Orders/2013-09-01",
        "2013-09-01",
        "GetServiceStatus",
        (),
      )
      .expect("send request");
    assert!(status.is_success());
    assert!(body.starts_with("<?xml"));

    use std::io::Cursor;
    let (status, body) = client
      .request_raw(
        Method::Post,
        "/Fake/2013-09-01",
        "2013-09-01",
        "GetServiceStatus",
        (),
      )
      .expect("send request");
    assert!(!status.is_success());
    let source = Cursor::new(body);
    let mut s = Stream::new(source);
    let err_info = ErrorResponseInfo::from_xml(&mut s).expect("decode error response");
    assert_eq!(err_info.errors.len(), 1);
    assert_eq!(
      err_info.errors[0],
      ErrorResponseError {
        error_type: "Sender".to_string(),
        code: "InvalidAddress".to_string(),
        message: "Section Fake/2013-09-01 is invalid".to_string(),
        detail: "".to_string(),
      }
    );
  }
}
<commit_msg>feat: Expose `request_raw`<commit_after>use reqwest;
pub use reqwest::header::ContentType;
use reqwest::Response;
pub use reqwest::{Method, StatusCode};
use result::{MwsError, MwsResult};
use sign::SignatureV2;
use std::io::Read;
use xmlhelper::decode::{FromXmlStream, Stream};
use SerializeMwsParams;

#[derive(Debug)]
pub struct ErrorResponse {
  pub status: StatusCode,
  pub info: Option<ErrorResponseInfo>,
  pub raw: String,
}

#[derive(Debug, Default)]
pub struct ErrorResponseInfo {
  pub errors: Vec<ErrorResponseError>,
  pub request_id: String,
}

#[derive(Debug, Default, PartialEq)]
pub struct ErrorResponseError {
  pub error_type: String,
  pub code: String,
  pub message: String,
  pub detail: String,
}

impl ErrorResponseInfo {
  fn from_xml_stream<R: ::std::io::Read>(s: &mut Stream<R>) -> MwsResult<ErrorResponseInfo> {
    use xmlhelper::decode::{characters, element, fold_elements, start_document};
    start_document(s)?;
    element(s, "ErrorResponse", |s| {
      fold_elements(s, ErrorResponseInfo::default(), |s, resp| {
        match s.local_name() {
          "Error" => {
            let err = fold_elements(s, ErrorResponseError::default(), |s, err| {
              match s.local_name() {
                "Type" => {
                  err.error_type = characters(s)?;
                }
                "Code" => {
                  err.code = characters(s)?;
                }
                "Message" => {
                  err.message = characters(s)?;
                }
                "Detail" => {
                  err.detail = characters(s)?;
                }
                _ => {}
              }
              Ok(())
            })?;
            resp.errors.push(err);
          }
          "RequestID" => {
            resp.request_id = characters(s)?;
          }
          _ => {}
        }
        Ok(())
      })
    })
    .into()
  }
}

impl FromXmlStream<Stream<reqwest::Response>> for ErrorResponseInfo {
  fn from_xml(s: &mut Stream<reqwest::Response>) -> MwsResult<ErrorResponseInfo> {
    ErrorResponseInfo::from_xml_stream(s)
  }
}

impl FromXmlStream<Stream<::std::io::Cursor<String>>> for ErrorResponseInfo {
  fn from_xml(s: &mut Stream<::std::io::Cursor<String>>) -> MwsResult<ErrorResponseInfo> {
    ErrorResponseInfo::from_xml_stream(s)
  }
}

/// [Reference](http://docs.developer.amazonservices.com/en_CA/dev_guide/DG_Endpoints.html)
pub struct ClientOptions {
  /// Your software can access Amazon Marketplace Web Service (Amazon MWS) using region-specific endpoints.
  pub endpoint: String,

  /// Your seller or merchant identifier.
  pub seller_id: String,

  /// Represents the authorization of a specific developer of a web application by a specific Amazon seller.
  pub mws_auth_token: Option<String>,

  /// Your Amazon MWS account is identified by your access key Id, which Amazon MWS uses to look up your Secret Access Key.
  pub aws_access_key_id: String,
  pub secret_key: String,
}

pub struct Client {
  options: ClientOptions,
  http_client: reqwest::Client,
}

impl Client {
  pub fn new(options: ClientOptions) -> MwsResult<Client> {
    Ok(Client {
      options: options,
      http_client: reqwest::Client::new(),
    })
  }

  pub fn with_http_client(options: ClientOptions, http_client: reqwest::Client) -> Client {
    Client {
      options: options,
      http_client: http_client,
    }
  }

  pub fn request<P>(
    &self,
    method: Method,
    path: &str,
    version: &str,
    action: &str,
    parameters: P,
  ) -> MwsResult<reqwest::Response>
  where
    P: SerializeMwsParams,
  {
    let mut sign = SignatureV2::new(
      &self.options.endpoint,
      &self.options.aws_access_key_id,
      &self.options.secret_key,
      self.options.mws_auth_token.as_ref().map(AsRef::as_ref),
    );
    for (k, v) in parameters.into_mws_params() {
      sign.add(&k, v);
    }
    sign.add("SellerId", &self.options.seller_id);
    //sign.add("Merchant", self.options.seller_id.as_ref());
    let url = sign
      .generate_url(method.clone(), path, version, action)?
      .to_string();
    //println!("request: {}", url);
    self
      .http_client
      .request(method, &url)
      .send()
      .map_err(MwsError::from)
      .and_then(handle_error_status)
  }

  pub fn request_with_body<P, R>(
    &self,
    method: Method,
    path: &str,
    version: &str,
    action: &str,
    parameters: P,
    body: R,
    content_md5: String,
    content_type: ContentType,
  ) -> MwsResult<reqwest::Response>
  where
    P: SerializeMwsParams,
    R: Read + Send + 'static,
  {
    let mut sign = SignatureV2::new(
      &self.options.endpoint,
      &self.options.aws_access_key_id,
      &self.options.secret_key,
      self.options.mws_auth_token.as_ref().map(AsRef::as_ref),
    );
    for (k, v) in parameters.into_mws_params() {
      sign.add(&k, v);
    }
    sign.add("SellerId", &self.options.seller_id);
    sign.add("ContentMD5Value", content_md5);
    //sign.add("Merchant", self.options.seller_id.as_ref());
    let url = sign
      .generate_url(method.clone(), path, version, action)?
      .to_string();
    //println!("request: {}", url);

    self
      .http_client
      .request(method, &url)
      .header(content_type)
      .body(reqwest::Body::new(body))
      .send()
      .map_err(MwsError::from)
      .and_then(handle_error_status)
  }

  pub fn request_xml<P, T>(
    &self,
    method: Method,
    path: &str,
    version: &str,
    action: &str,
    parameters: P,
  ) -> MwsResult<T>
  where
    P: SerializeMwsParams,
    T: FromXmlStream<Stream<reqwest::Response>>,
  {
    let resp = self.request(method, path, version, action, parameters)?;
    let mut stream = Stream::new(resp);
    let v = T::from_xml(&mut stream)?;
    Ok(v)
  }

  pub fn request_xml_with_body<P, R, T>(
    &self,
    method: Method,
    path: &str,
    version: &str,
    action: &str,
    parameters: P,
    body: R,
    content_md5: String,
    content_type: ContentType,
  ) -> MwsResult<T>
  where
    P: SerializeMwsParams,
    T: FromXmlStream<Stream<reqwest::Response>>,
    R: Read + Send + 'static,
  {
    let resp = self.request_with_body(
      method,
      path,
      version,
      action,
      parameters,
      body,
      content_md5,
      content_type,
    )?;
    let mut stream = Stream::new(resp);
    let v = T::from_xml(&mut stream)?;
    Ok(v)
  }

  pub fn request_raw<P>(
    &self,
    method: Method,
    path: &str,
    version: &str,
    action: &str,
    parameters: P,
  ) -> MwsResult<(StatusCode, String)>
  where
    P: SerializeMwsParams,
  {
    let mut sign = SignatureV2::new(
      &self.options.endpoint,
      &self.options.aws_access_key_id,
      &self.options.secret_key,
      self.options.mws_auth_token.as_ref().map(AsRef::as_ref),
    );
    for (k, v) in parameters.into_mws_params() {
      sign.add(&k, v);
    }
    let url = sign
      .generate_url(method.clone(), path, version, action)?
      .to_string();
    let mut resp = self
      .http_client
      .request(method, &url)
      .send()
      .map_err(MwsError::from)
      .and_then(handle_error_status)?;
    let mut s = String::new();
    resp.read_to_string(&mut s)?;
    Ok((resp.status().clone(), s))
  }
}

fn handle_error_status(resp: Response) -> MwsResult<Response> {
  if resp.status().is_success() {
    Ok(resp)
  } else {
    use std::io::Cursor;

    let mut resp = resp;
    let mut body = String::new();
    resp.read_to_string(&mut body)?;
    let mut s = Stream::new(Cursor::new(body.clone()));
    match ErrorResponseInfo::from_xml(&mut s) {
      Ok(info) => Err(MwsError::ErrorResponse(ErrorResponse {
        status: resp.status().clone(),
        raw: body,
        info: Some(info),
      })),
      Err(_) => Err(MwsError::ErrorResponse(ErrorResponse {
        status: resp.status().clone(),
        raw: body,
        info: None,
      })),
    }
  }
}

#[cfg(test)]
pub fn get_test_client() -> Client {
  use std::env;
  Client::new(ClientOptions {
    endpoint: env::var("Endpoint").expect("get Endpoint"),
    seller_id: env::var("SellerId").expect("get SellerId"),
    mws_auth_token: None,
    aws_access_key_id: env::var("AWSAccessKeyId").expect("get AWSAccessKeyId"),
    secret_key: env::var("SecretKey").expect("get SecretKey"),
  })
  .expect("create client")
}

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

  #[test]
  #[ignore]
  fn it_works() {
    dotenv().ok();
    let client = get_test_client();
    let (status, body) = client
      .request_raw(
        Method::Post,
        "/Orders/2013-09-01",
        "2013-09-01",
        "GetServiceStatus",
        (),
      )
      .expect("send request");
    assert!(status.is_success());
    assert!(body.starts_with("<?xml"));

    use std::io::Cursor;
    let (status, body) = client
      .request_raw(
        Method::Post,
        "/Fake/2013-09-01",
        "2013-09-01",
        "GetServiceStatus",
        (),
      )
      .expect("send request");
    assert!(!status.is_success());
    let source = Cursor::new(body);
    let mut s = Stream::new(source);
    let err_info = ErrorResponseInfo::from_xml(&mut s).expect("decode error response");
    assert_eq!(err_info.errors.len(), 1);
    assert_eq!(
      err_info.errors[0],
      ErrorResponseError {
        error_type: "Sender".to_string(),
        code: "InvalidAddress".to_string(),
        message: "Section Fake/2013-09-01 is invalid".to_string(),
        detail: "".to_string(),
      }
    );
  }
}
<|endoftext|>


<commit_before>//! This module defines the main function for the view/render/event thread.

use cgmath::{Vector2};
use gl;
use sdl2;
use sdl2::event::Event;
use sdl2::video;
use std::mem;
use stopwatch;
use time;
use yaglw::gl_context::GLContext;

use common::communicate::ClientToServer;
use common::entity::EntityId;
use common::interval_timer::IntervalTimer;

use hud::make_hud;
use process_event::process_event;
use render::render;
use view::View;
use view_update::{ClientToView, apply_client_to_view};

pub const FRAMES_PER_SECOND: u64 = 30;

pub const GL_MAJOR_VERSION: u8 = 3;
pub const GL_MINOR_VERSION: u8 = 3;

enum ViewIteration {
  Quit,
  Continue,
}

#[allow(missing_docs)]
pub fn view_thread<Recv0, Recv1, UpdateServer>(
  player_id: EntityId,
  recv0: &mut Recv0,
  recv1: &mut Recv1,
  update_server: &mut UpdateServer,
) where
  Recv0: FnMut() -> Option<ClientToView>,
  Recv1: FnMut() -> Option<ClientToView>,
  UpdateServer: FnMut(ClientToServer),
{
  let sdl = sdl2::init().unwrap();
  let _event = sdl.event().unwrap();
  let video = sdl.video().unwrap();
  let gl_attr = video.gl_attr();

  gl_attr.set_context_profile(video::GLProfile::Core);
  gl_attr.set_context_version(GL_MAJOR_VERSION, GL_MINOR_VERSION);

  // Open the window as fullscreen at the current resolution.
  let mut window =
    video.window(
      "Playform",
      1200, 1024,
    );

  let window = window.position(0, 0);
  let window = window.opengl();

  let mut window = window.build().unwrap();

  assert_eq!(gl_attr.context_profile(), video::GLProfile::Core);
  assert_eq!(gl_attr.context_version(), (GL_MAJOR_VERSION, GL_MINOR_VERSION));

  let mut event_pump = sdl.event_pump().unwrap();

  let _sdl_gl_context = window.gl_create_context().unwrap();

  // Load the OpenGL function pointers.
  gl::load_with(|s| unsafe {
    mem::transmute(video.gl_get_proc_address(s))
  });

  let gl = unsafe {
    GLContext::new()
  };

  gl.print_stats();

  let window_size = {
    let (w, h) = window.size();
    Vector2::new(w as i32, h as i32)
  };

  let mut view = View::new(gl, window_size);

  make_hud(&mut view);

  let render_interval = {
    let nanoseconds_per_second = 1000000000;
    nanoseconds_per_second / FRAMES_PER_SECOND
  };
  let mut render_timer;
  {
    let now = time::precise_time_ns();
    render_timer = IntervalTimer::new(render_interval, now);
  }

  let mut has_focus = true;

  let mut last_update = time::precise_time_ns();

  loop {
    let view_iteration =
      stopwatch::time("view_iteration", || {
        let now = time::precise_time_ns();
        if now - last_update >= render_interval {
          warn!("{:?}ms since last view update", (now - last_update) / 1000000);
        }
        last_update = now;

        for event in event_pump.poll_iter() {
          match event {
            Event::Quit{..} => {
              return ViewIteration::Quit
            }
            Event::AppTerminating{..} => {
              return ViewIteration::Quit
            }
            Event::Window{win_event_id: event_id, ..} => {
              // Manage has_focus so that we don't capture the cursor when the
              // window is in the background
              match event_id {
                sdl2::event::WindowEventId::FocusGained => {
                  has_focus = true;
                  sdl.mouse().show_cursor(false);
                }
                sdl2::event::WindowEventId::FocusLost => {
                  has_focus = false;
                  sdl.mouse().show_cursor(true);
                }
                _ => {}
              }
            }
            event => {
              if has_focus {
                process_event(
                  &sdl,
                  player_id,
                  update_server,
                  &mut view,
                  &mut window,
                  event,
                );
              }
            },
          }
        }

        stopwatch::time("apply_view_updates0", || {
          let start = time::precise_time_ns();
          while let Some(update) = recv0() {
            apply_client_to_view(update, &mut view);

            if time::precise_time_ns() - start >= 1_000_000 {
              break
            }
          }
        });

        stopwatch::time("apply_view_updates1", || {
          let start = time::precise_time_ns();
          while let Some(update) = recv1() {
            apply_client_to_view(update, &mut view);

            if time::precise_time_ns() - start >= 1_000_000 {
              break
            }
          }
        });

        let renders = render_timer.update(time::precise_time_ns());
        if renders > 0 {
          stopwatch::time("render", || {
            render(&mut view);
            // swap buffers
            window.gl_swap_window();
          });
        }

        ViewIteration::Continue
      });

    match view_iteration {
      ViewIteration::Quit => break,
      ViewIteration::Continue => {},
    }
  }

  debug!("view exiting.");
}
<commit_msg>view message prioritization better-enforced<commit_after>//! This module defines the main function for the view/render/event thread.

use cgmath::{Vector2};
use gl;
use sdl2;
use sdl2::event::Event;
use sdl2::video;
use std::mem;
use stopwatch;
use time;
use yaglw::gl_context::GLContext;

use common::communicate::ClientToServer;
use common::entity::EntityId;
use common::interval_timer::IntervalTimer;

use hud::make_hud;
use process_event::process_event;
use render::render;
use view::View;
use view_update::{ClientToView, apply_client_to_view};

pub const FRAMES_PER_SECOND: u64 = 30;

pub const GL_MAJOR_VERSION: u8 = 3;
pub const GL_MINOR_VERSION: u8 = 3;

enum ViewIteration {
  Quit,
  Continue,
}

#[allow(missing_docs)]
pub fn view_thread<Recv0, Recv1, UpdateServer>(
  player_id: EntityId,
  recv0: &mut Recv0,
  recv1: &mut Recv1,
  update_server: &mut UpdateServer,
) where
  Recv0: FnMut() -> Option<ClientToView>,
  Recv1: FnMut() -> Option<ClientToView>,
  UpdateServer: FnMut(ClientToServer),
{
  let sdl = sdl2::init().unwrap();
  let _event = sdl.event().unwrap();
  let video = sdl.video().unwrap();
  let gl_attr = video.gl_attr();

  gl_attr.set_context_profile(video::GLProfile::Core);
  gl_attr.set_context_version(GL_MAJOR_VERSION, GL_MINOR_VERSION);

  // Open the window as fullscreen at the current resolution.
  let mut window =
    video.window(
      "Playform",
      1200, 1024,
    );

  let window = window.position(0, 0);
  let window = window.opengl();

  let mut window = window.build().unwrap();

  assert_eq!(gl_attr.context_profile(), video::GLProfile::Core);
  assert_eq!(gl_attr.context_version(), (GL_MAJOR_VERSION, GL_MINOR_VERSION));

  let mut event_pump = sdl.event_pump().unwrap();

  let _sdl_gl_context = window.gl_create_context().unwrap();

  // Load the OpenGL function pointers.
  gl::load_with(|s| unsafe {
    mem::transmute(video.gl_get_proc_address(s))
  });

  let gl = unsafe {
    GLContext::new()
  };

  gl.print_stats();

  let window_size = {
    let (w, h) = window.size();
    Vector2::new(w as i32, h as i32)
  };

  let mut view = View::new(gl, window_size);

  make_hud(&mut view);

  let render_interval = {
    let nanoseconds_per_second = 1000000000;
    nanoseconds_per_second / FRAMES_PER_SECOND
  };
  let mut render_timer;
  {
    let now = time::precise_time_ns();
    render_timer = IntervalTimer::new(render_interval, now);
  }

  let mut has_focus = true;

  let mut last_update = time::precise_time_ns();

  loop {
    let view_iteration =
      stopwatch::time("view_iteration", || {
        let now = time::precise_time_ns();
        if now - last_update >= render_interval {
          warn!("{:?}ms since last view update", (now - last_update) / 1000000);
        }
        last_update = now;

        for event in event_pump.poll_iter() {
          match event {
            Event::Quit{..} => {
              return ViewIteration::Quit
            }
            Event::AppTerminating{..} => {
              return ViewIteration::Quit
            }
            Event::Window{win_event_id: event_id, ..} => {
              // Manage has_focus so that we don't capture the cursor when the
              // window is in the background
              match event_id {
                sdl2::event::WindowEventId::FocusGained => {
                  has_focus = true;
                  sdl.mouse().show_cursor(false);
                }
                sdl2::event::WindowEventId::FocusLost => {
                  has_focus = false;
                  sdl.mouse().show_cursor(true);
                }
                _ => {}
              }
            }
            event => {
              if has_focus {
                process_event(
                  &sdl,
                  player_id,
                  update_server,
                  &mut view,
                  &mut window,
                  event,
                );
              }
            },
          }
        }

        stopwatch::time("apply_view_updates", || {
          let start = time::precise_time_ns();
          loop {
            if let Some(update) = recv0() {
              apply_client_to_view(update, &mut view);
            } else if let Some(update) = recv1() {
              apply_client_to_view(update, &mut view);
            } else {
              break
            }

            if time::precise_time_ns() - start >= 1_000_000 {
              break
            }
          }
        });

        let renders = render_timer.update(time::precise_time_ns());
        if renders > 0 {
          stopwatch::time("render", || {
            render(&mut view);
            // swap buffers
            window.gl_swap_window();
          });
        }

        ViewIteration::Continue
      });

    match view_iteration {
      ViewIteration::Quit => break,
      ViewIteration::Continue => {},
    }
  }

  debug!("view exiting.");
}
<|endoftext|>


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

//! Visitor traits for selectors.

#![deny(missing_docs)]

use attr::NamespaceConstraint;
use parser::{Combinator, Component, SelectorImpl, SelectorIter};

/// A trait to visit selector properties.
///
/// All the `visit_foo` methods return a boolean indicating whether the
/// traversal should continue or not.
pub trait SelectorVisitor {
    /// The selector implementation this visitor wants to visit.
    type Impl: SelectorImpl;

    /// Visit an attribute selector that may match (there are other selectors
    /// that may never match, like those containing whitespace or the empty
    /// string).
    fn visit_attribute_selector(
        &mut self,
        _namespace: &NamespaceConstraint<&<Self::Impl as SelectorImpl>::NamespaceUrl>,
        _local_name: &<Self::Impl as SelectorImpl>::LocalName,
        _local_name_lower: &<Self::Impl as SelectorImpl>::LocalName,
    ) -> bool {
        true
    }

    /// Visit a simple selector.
    fn visit_simple_selector(&mut self, _: &Component<Self::Impl>) -> bool {
        true
    }

    /// Visits a complex selector.
    ///
    /// Gets the combinator to the right of the selector, or `None` if the
    /// selector is the rightmost one.
    fn visit_complex_selector(&mut self,
                              _combinator_to_right: Option<Combinator>)
                              -> bool {
        true
    }
}
<commit_msg>Auto merge of #18232 - ferjm:unused.selectoriter, r=emilio<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Visitor traits for selectors.

#![deny(missing_docs)]

use attr::NamespaceConstraint;
use parser::{Combinator, Component, SelectorImpl};

/// A trait to visit selector properties.
///
/// All the `visit_foo` methods return a boolean indicating whether the
/// traversal should continue or not.
pub trait SelectorVisitor {
    /// The selector implementation this visitor wants to visit.
    type Impl: SelectorImpl;

    /// Visit an attribute selector that may match (there are other selectors
    /// that may never match, like those containing whitespace or the empty
    /// string).
    fn visit_attribute_selector(
        &mut self,
        _namespace: &NamespaceConstraint<&<Self::Impl as SelectorImpl>::NamespaceUrl>,
        _local_name: &<Self::Impl as SelectorImpl>::LocalName,
        _local_name_lower: &<Self::Impl as SelectorImpl>::LocalName,
    ) -> bool {
        true
    }

    /// Visit a simple selector.
    fn visit_simple_selector(&mut self, _: &Component<Self::Impl>) -> bool {
        true
    }

    /// Visits a complex selector.
    ///
    /// Gets the combinator to the right of the selector, or `None` if the
    /// selector is the rightmost one.
    fn visit_complex_selector(&mut self,
                              _combinator_to_right: Option<Combinator>)
                              -> bool {
        true
    }
}
<|endoftext|>