<commit_before>use godcoin::{
    blockchain::index::TxManager,
    constants,
    prelude::{net::ErrorKind, verify::TxErr, *},
};

mod common;
pub use common::*;

#[test]
fn fresh_blockchain() {
    let minter = TestMinter::new();
    let chain = minter.chain();
    assert!(chain.get_block(0).is_some());
    assert!(chain.get_block(1).is_some());
    assert_eq!(chain.get_chain_height(), 1);

    let owner = match chain.get_owner() {
        TxVariant::V0(tx) => match tx {
            TxVariantV0::OwnerTx(tx) => tx,
            _ => unreachable!(),
        },
    };

    assert_eq!(owner.minter, minter.genesis_info().minter_key.0);
    assert_eq!(
        owner.script,
        script::Builder::new().push(OpFrame::False).build()
    );
    assert_eq!(owner.wallet, (&minter.genesis_info().script).into());

    assert!(chain.get_block(2).is_none());
    assert_eq!(chain.index_status(), IndexStatus::Complete);
}

#[test]
fn reindexed_blockchain() {
    let mut 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.00000 TEST");

    // Create a tx we expect to be reindexed
    let tx = {
        let mut tx = TxVariant::V0(TxVariantV0::TransferTx(TransferTx {
            base: create_tx_header("1.00000 TEST"),
            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]);
        tx
    };
    let tx_data = TxPrecompData::from_tx(tx.clone());

    {
        // Broadcast the tx
        let res = minter.send_req(rpc::Request::Broadcast(tx.clone()));
        assert_eq!(res, Some(Ok(rpc::Response::Broadcast)));
        minter.produce_block().unwrap();
    }

    {
        // Unindex the blockchain
        minter.unindexed();
        let chain = minter.chain();
        let manager = TxManager::new(chain.indexer());

        assert_eq!(chain.index_status(), IndexStatus::None);
        assert!(chain.get_block(0).is_none());
        assert!(!manager.has(tx_data.txid()));
    }

    // Test the reindexed status from here
    minter.reindex();
    let chain = minter.chain();
    let manager = TxManager::new(chain.indexer());

    assert_eq!(chain.index_status(), IndexStatus::Complete);
    assert!(chain.get_block(0).is_some());
    assert!(chain.get_block(1).is_some());
    assert!(chain.get_block(2).is_some());
    assert!(chain.get_block(3).is_none());
    assert_eq!(chain.get_chain_height(), 2);
    assert!(manager.has(tx_data.txid()));

    let owner = match chain.get_owner() {
        TxVariant::V0(tx) => match tx {
            TxVariantV0::OwnerTx(tx) => tx,
            _ => unreachable!(),
        },
    };
    assert_eq!(owner.minter, minter.genesis_info().minter_key.0);
    assert_eq!(
        owner.script,
        script::Builder::new().push(OpFrame::False).build()
    );
    assert_eq!(owner.wallet, (&minter.genesis_info().script).into());

    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.checked_sub(amount));

    // Test to ensure that after a reindex the tx cannot be rebroadcasted
    let res = minter.send_req(rpc::Request::Broadcast(tx.clone()));

    assert_eq!(res, Some(Err(ErrorKind::TxValidation(TxErr::TxDupe))));
}

#[test]
fn tx_dupe() {
    let minter = TestMinter::new();
    let mut tx = TxVariant::V0(TxVariantV0::MintTx(MintTx {
        base: create_tx_header("0.00000 TEST"),
        to: (&minter.genesis_info().script).into(),
        amount: get_asset("10.00000 TEST"),
        attachment: vec![],
        attachment_name: "".to_owned(),
        script: minter.genesis_info().script.clone(),
    }));

    tx.append_sign(&minter.genesis_info().wallet_keys[1]);
    tx.append_sign(&minter.genesis_info().wallet_keys[0]);

    let res = minter
        .send_req(rpc::Request::Broadcast(tx.clone()))
        .unwrap();
    assert_eq!(res, Ok(rpc::Response::Broadcast));

    let res = minter.send_req(rpc::Request::Broadcast(tx)).unwrap();
    assert_eq!(res, Err(ErrorKind::TxValidation(TxErr::TxDupe)));
}

#[test]
fn tx_no_dupe_with_different_nonce() {
    let minter = TestMinter::new();
    let mut tx = TxVariant::V0(TxVariantV0::MintTx(MintTx {
        base: create_tx_header("0.00000 TEST"),
        to: (&minter.genesis_info().script).into(),
        amount: get_asset("10.00000 TEST"),
        attachment: vec![],
        attachment_name: "".to_owned(),
        script: minter.genesis_info().script.clone(),
    }));

    tx.append_sign(&minter.genesis_info().wallet_keys[1]);
    tx.append_sign(&minter.genesis_info().wallet_keys[0]);

    let res = minter
        .send_req(rpc::Request::Broadcast(tx.clone()))
        .unwrap();
    assert_eq!(res, Ok(rpc::Response::Broadcast));

    match &mut tx {
        TxVariant::V0(ref mut tx) => {
            tx.nonce = tx.nonce.wrapping_add(1);
            tx.signature_pairs = vec![];
        }
    }
    tx.append_sign(&minter.genesis_info().wallet_keys[1]);
    tx.append_sign(&minter.genesis_info().wallet_keys[0]);

    let res = minter.send_req(rpc::Request::Broadcast(tx)).unwrap();
    assert_eq!(res, Ok(rpc::Response::Broadcast));
}

#[test]
fn tx_sig_validation_err_with_different_nonce() {
    let minter = TestMinter::new();
    let mut tx = TxVariant::V0(TxVariantV0::MintTx(MintTx {
        base: create_tx_header("0.00000 TEST"),
        to: (&minter.genesis_info().script).into(),
        amount: get_asset("10.00000 TEST"),
        attachment: vec![],
        attachment_name: "".to_owned(),
        script: minter.genesis_info().script.clone(),
    }));

    tx.append_sign(&minter.genesis_info().wallet_keys[1]);
    tx.append_sign(&minter.genesis_info().wallet_keys[0]);

    let res = minter
        .send_req(rpc::Request::Broadcast(tx.clone()))
        .unwrap();
    assert_eq!(res, Ok(rpc::Response::Broadcast));

    match &mut tx {
        TxVariant::V0(ref mut tx) => {
            tx.nonce = tx.nonce.wrapping_add(1);
        }
    }
    let res = minter.send_req(rpc::Request::Broadcast(tx)).unwrap();
    assert_eq!(res, Err(ErrorKind::TxValidation(TxErr::ScriptRetFalse)));
}

#[test]
fn tx_expired() {
    let minter = TestMinter::new();
    let expiry = godcoin::get_epoch_time();

    let mut tx = TxVariant::V0(TxVariantV0::MintTx(MintTx {
        base: create_tx_header_with_expiry("0.00000 TEST", expiry),
        to: (&minter.genesis_info().script).into(),
        amount: get_asset("10.00000 TEST"),
        attachment: vec![],
        attachment_name: "".to_owned(),
        script: minter.genesis_info().script.clone(),
    }));

    let res = minter
        .send_req(rpc::Request::Broadcast(tx.clone()))
        .unwrap();
    assert_eq!(res, Err(ErrorKind::TxValidation(TxErr::TxExpired)));

    match &mut tx {
        TxVariant::V0(ref mut tx) => {
            tx.expiry = expiry - 1;
        }
    }
    let res = minter.send_req(rpc::Request::Broadcast(tx)).unwrap();
    assert_eq!(res, Err(ErrorKind::TxValidation(TxErr::TxExpired)));
}

#[test]
fn tx_expiry_far_in_the_future() {
    let minter = TestMinter::new();
    let expiry = godcoin::get_epoch_time();

    let tx = TxVariant::V0(TxVariantV0::MintTx(MintTx {
        base: create_tx_header_with_expiry(
            "0.00000 TEST",
            expiry + constants::TX_MAX_EXPIRY_TIME + 1,
        ),
        to: (&minter.genesis_info().script).into(),
        amount: get_asset("10.00000 TEST"),
        attachment: vec![],
        attachment_name: "".to_owned(),
        script: minter.genesis_info().script.clone(),
    }));

    let res = minter.send_req(rpc::Request::Broadcast(tx)).unwrap();
    assert_eq!(res, Err(ErrorKind::TxValidation(TxErr::TxExpired)));
}

#[test]
fn tx_script_too_large_err() {
    let minter = TestMinter::new();

    let tx = TxVariant::V0(TxVariantV0::MintTx(MintTx {
        base: create_tx_header("0.00000 TEST"),
        to: (&minter.genesis_info().script).into(),
        amount: get_asset("10.00000 TEST"),
        attachment: vec![],
        attachment_name: "".to_owned(),
        script: Script::new((0..=constants::MAX_SCRIPT_BYTE_SIZE).map(|_| 0).collect()),
    }));

    let res = minter.send_req(rpc::Request::Broadcast(tx)).unwrap();
    assert_eq!(res, Err(ErrorKind::TxValidation(TxErr::TxTooLarge)));
}

#[test]
fn tx_too_many_signatures_err() {
    let minter = TestMinter::new();

    let mut tx = TxVariant::V0(TxVariantV0::MintTx(MintTx {
        base: create_tx_header("0.00000 TEST"),
        to: (&minter.genesis_info().script).into(),
        amount: get_asset("10.00000 TEST"),
        attachment: vec![],
        attachment_name: "".to_owned(),
        script: Script::new(vec![]),
    }));
    (0..=constants::MAX_TX_SIGNATURES).for_each(|_| tx.append_sign(&KeyPair::gen()));

    let res = minter.send_req(rpc::Request::Broadcast(tx)).unwrap();
    assert_eq!(res, Err(ErrorKind::TxValidation(TxErr::TooManySignatures)));
}
<commit_msg>Add test for broadcasting a tx with an invalid chain id<commit_after>use godcoin::{
    blockchain::index::TxManager,
    constants,
    prelude::{net::ErrorKind, verify::TxErr, *},
};

mod common;
pub use common::*;

#[test]
fn fresh_blockchain() {
    let minter = TestMinter::new();
    let chain = minter.chain();
    assert!(chain.get_block(0).is_some());
    assert!(chain.get_block(1).is_some());
    assert_eq!(chain.get_chain_height(), 1);

    let owner = match chain.get_owner() {
        TxVariant::V0(tx) => match tx {
            TxVariantV0::OwnerTx(tx) => tx,
            _ => unreachable!(),
        },
    };

    assert_eq!(owner.minter, minter.genesis_info().minter_key.0);
    assert_eq!(
        owner.script,
        script::Builder::new().push(OpFrame::False).build()
    );
    assert_eq!(owner.wallet, (&minter.genesis_info().script).into());

    assert!(chain.get_block(2).is_none());
    assert_eq!(chain.index_status(), IndexStatus::Complete);
}

#[test]
fn reindexed_blockchain() {
    let mut 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.00000 TEST");

    // Create a tx we expect to be reindexed
    let tx = {
        let mut tx = TxVariant::V0(TxVariantV0::TransferTx(TransferTx {
            base: create_tx_header("1.00000 TEST"),
            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]);
        tx
    };
    let tx_data = TxPrecompData::from_tx(tx.clone());

    {
        // Broadcast the tx
        let res = minter.send_req(rpc::Request::Broadcast(tx.clone()));
        assert_eq!(res, Some(Ok(rpc::Response::Broadcast)));
        minter.produce_block().unwrap();
    }

    {
        // Unindex the blockchain
        minter.unindexed();
        let chain = minter.chain();
        let manager = TxManager::new(chain.indexer());

        assert_eq!(chain.index_status(), IndexStatus::None);
        assert!(chain.get_block(0).is_none());
        assert!(!manager.has(tx_data.txid()));
    }

    // Test the reindexed status from here
    minter.reindex();
    let chain = minter.chain();
    let manager = TxManager::new(chain.indexer());

    assert_eq!(chain.index_status(), IndexStatus::Complete);
    assert!(chain.get_block(0).is_some());
    assert!(chain.get_block(1).is_some());
    assert!(chain.get_block(2).is_some());
    assert!(chain.get_block(3).is_none());
    assert_eq!(chain.get_chain_height(), 2);
    assert!(manager.has(tx_data.txid()));

    let owner = match chain.get_owner() {
        TxVariant::V0(tx) => match tx {
            TxVariantV0::OwnerTx(tx) => tx,
            _ => unreachable!(),
        },
    };
    assert_eq!(owner.minter, minter.genesis_info().minter_key.0);
    assert_eq!(
        owner.script,
        script::Builder::new().push(OpFrame::False).build()
    );
    assert_eq!(owner.wallet, (&minter.genesis_info().script).into());

    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.checked_sub(amount));

    // Test to ensure that after a reindex the tx cannot be rebroadcasted
    let res = minter.send_req(rpc::Request::Broadcast(tx.clone()));

    assert_eq!(res, Some(Err(ErrorKind::TxValidation(TxErr::TxDupe))));
}

#[test]
fn tx_dupe() {
    let minter = TestMinter::new();
    let mut tx = TxVariant::V0(TxVariantV0::MintTx(MintTx {
        base: create_tx_header("0.00000 TEST"),
        to: (&minter.genesis_info().script).into(),
        amount: get_asset("10.00000 TEST"),
        attachment: vec![],
        attachment_name: "".to_owned(),
        script: minter.genesis_info().script.clone(),
    }));

    tx.append_sign(&minter.genesis_info().wallet_keys[1]);
    tx.append_sign(&minter.genesis_info().wallet_keys[0]);

    let res = minter
        .send_req(rpc::Request::Broadcast(tx.clone()))
        .unwrap();
    assert_eq!(res, Ok(rpc::Response::Broadcast));

    let res = minter.send_req(rpc::Request::Broadcast(tx)).unwrap();
    assert_eq!(res, Err(ErrorKind::TxValidation(TxErr::TxDupe)));
}

#[test]
fn tx_no_dupe_with_different_nonce() {
    let minter = TestMinter::new();
    let mut tx = TxVariant::V0(TxVariantV0::MintTx(MintTx {
        base: create_tx_header("0.00000 TEST"),
        to: (&minter.genesis_info().script).into(),
        amount: get_asset("10.00000 TEST"),
        attachment: vec![],
        attachment_name: "".to_owned(),
        script: minter.genesis_info().script.clone(),
    }));

    tx.append_sign(&minter.genesis_info().wallet_keys[1]);
    tx.append_sign(&minter.genesis_info().wallet_keys[0]);

    let res = minter
        .send_req(rpc::Request::Broadcast(tx.clone()))
        .unwrap();
    assert_eq!(res, Ok(rpc::Response::Broadcast));

    match &mut tx {
        TxVariant::V0(ref mut tx) => {
            tx.nonce = tx.nonce.wrapping_add(1);
            tx.signature_pairs = vec![];
        }
    }
    tx.append_sign(&minter.genesis_info().wallet_keys[1]);
    tx.append_sign(&minter.genesis_info().wallet_keys[0]);

    let res = minter.send_req(rpc::Request::Broadcast(tx)).unwrap();
    assert_eq!(res, Ok(rpc::Response::Broadcast));
}

#[test]
fn tx_sig_validation_err_with_different_nonce() {
    let minter = TestMinter::new();
    let mut tx = TxVariant::V0(TxVariantV0::MintTx(MintTx {
        base: create_tx_header("0.00000 TEST"),
        to: (&minter.genesis_info().script).into(),
        amount: get_asset("10.00000 TEST"),
        attachment: vec![],
        attachment_name: "".to_owned(),
        script: minter.genesis_info().script.clone(),
    }));

    tx.append_sign(&minter.genesis_info().wallet_keys[1]);
    tx.append_sign(&minter.genesis_info().wallet_keys[0]);

    let res = minter
        .send_req(rpc::Request::Broadcast(tx.clone()))
        .unwrap();
    assert_eq!(res, Ok(rpc::Response::Broadcast));

    match &mut tx {
        TxVariant::V0(ref mut tx) => {
            tx.nonce = tx.nonce.wrapping_add(1);
        }
    }
    let res = minter.send_req(rpc::Request::Broadcast(tx)).unwrap();
    assert_eq!(res, Err(ErrorKind::TxValidation(TxErr::ScriptRetFalse)));
}

#[test]
fn tx_expired() {
    let minter = TestMinter::new();
    let expiry = godcoin::get_epoch_time();

    let mut tx = TxVariant::V0(TxVariantV0::MintTx(MintTx {
        base: create_tx_header_with_expiry("0.00000 TEST", expiry),
        to: (&minter.genesis_info().script).into(),
        amount: get_asset("10.00000 TEST"),
        attachment: vec![],
        attachment_name: "".to_owned(),
        script: minter.genesis_info().script.clone(),
    }));

    let res = minter
        .send_req(rpc::Request::Broadcast(tx.clone()))
        .unwrap();
    assert_eq!(res, Err(ErrorKind::TxValidation(TxErr::TxExpired)));

    match &mut tx {
        TxVariant::V0(ref mut tx) => {
            tx.expiry = expiry - 1;
        }
    }
    let res = minter.send_req(rpc::Request::Broadcast(tx)).unwrap();
    assert_eq!(res, Err(ErrorKind::TxValidation(TxErr::TxExpired)));
}

#[test]
fn tx_expiry_far_in_the_future() {
    let minter = TestMinter::new();
    let expiry = godcoin::get_epoch_time();

    let tx = TxVariant::V0(TxVariantV0::MintTx(MintTx {
        base: create_tx_header_with_expiry(
            "0.00000 TEST",
            expiry + constants::TX_MAX_EXPIRY_TIME + 1,
        ),
        to: (&minter.genesis_info().script).into(),
        amount: get_asset("10.00000 TEST"),
        attachment: vec![],
        attachment_name: "".to_owned(),
        script: minter.genesis_info().script.clone(),
    }));

    let res = minter.send_req(rpc::Request::Broadcast(tx)).unwrap();
    assert_eq!(res, Err(ErrorKind::TxValidation(TxErr::TxExpired)));
}

#[test]
fn tx_script_too_large_err() {
    let minter = TestMinter::new();

    let tx = TxVariant::V0(TxVariantV0::MintTx(MintTx {
        base: create_tx_header("0.00000 TEST"),
        to: (&minter.genesis_info().script).into(),
        amount: get_asset("10.00000 TEST"),
        attachment: vec![],
        attachment_name: "".to_owned(),
        script: Script::new((0..=constants::MAX_SCRIPT_BYTE_SIZE).map(|_| 0).collect()),
    }));

    let res = minter.send_req(rpc::Request::Broadcast(tx)).unwrap();
    assert_eq!(res, Err(ErrorKind::TxValidation(TxErr::TxTooLarge)));
}

#[test]
fn tx_too_many_signatures_err() {
    let minter = TestMinter::new();

    let mut tx = TxVariant::V0(TxVariantV0::MintTx(MintTx {
        base: create_tx_header("0.00000 TEST"),
        to: (&minter.genesis_info().script).into(),
        amount: get_asset("10.00000 TEST"),
        attachment: vec![],
        attachment_name: "".to_owned(),
        script: Script::new(vec![]),
    }));
    (0..=constants::MAX_TX_SIGNATURES).for_each(|_| tx.append_sign(&KeyPair::gen()));

    let res = minter.send_req(rpc::Request::Broadcast(tx)).unwrap();
    assert_eq!(res, Err(ErrorKind::TxValidation(TxErr::TooManySignatures)));
}

#[test]
fn tx_with_bad_chain_id() {
    fn manual_sign(key_pair: &KeyPair, tx: &mut TxVariant, chain_id: [u8; 2]) {
        use sodiumoxide::crypto::hash::sha256;

        let mut buf = Vec::with_capacity(4096);
        tx.serialize_without_sigs(&mut buf);

        let first_round = {
            let mut state = sha256::State::new();
            state.update(&chain_id);
            state.update(&buf);
            state.finalize()
        };

        let second_round = sha256::hash(first_round.as_ref());
        let sig = key_pair.sign(second_round.as_ref());

        match tx {
            TxVariant::V0(ref mut tx) => {
                tx.signature_pairs.push(sig);
            }
        }
    };

    let minter = TestMinter::new();
    let mut tx = TxVariant::V0(TxVariantV0::MintTx(MintTx {
        base: create_tx_header("0.00000 TEST"),
        to: (&minter.genesis_info().script).into(),
        amount: get_asset("10.00000 TEST"),
        attachment: vec![],
        attachment_name: "".to_owned(),
        script: minter.genesis_info().script.clone(),
    }));

    {
        // Test manual signing with the default chain ID to ensure manual_sign is correct
        manual_sign(
            &minter.genesis_info().wallet_keys[1],
            &mut tx,
            constants::CHAIN_ID,
        );
        manual_sign(
            &minter.genesis_info().wallet_keys[0],
            &mut tx,
            constants::CHAIN_ID,
        );

        let res = minter
            .send_req(rpc::Request::Broadcast(tx.clone()))
            .unwrap();
        assert_eq!(res, Ok(rpc::Response::Broadcast));
    }
    {
        // Test manual signing with incorrect chain ID to ensure these transactions cannot be broadcasted
        match &mut tx {
            TxVariant::V0(ref mut tx) => {
                tx.nonce = tx.nonce.wrapping_add(1);
                tx.signature_pairs = vec![];
            }
        }
        let mut new_id = [0x00, 0x00];
        new_id[0] = constants::CHAIN_ID[0].wrapping_add(1);
        new_id[1] = constants::CHAIN_ID[0].wrapping_add(1);
        manual_sign(&minter.genesis_info().wallet_keys[1], &mut tx, new_id);
        manual_sign(&minter.genesis_info().wallet_keys[0], &mut tx, new_id);

        let res = minter
            .send_req(rpc::Request::Broadcast(tx.clone()))
            .unwrap();
        assert_eq!(res, Err(ErrorKind::TxValidation(TxErr::ScriptRetFalse)));
    }
}
<|endoftext|>


<commit_before>use syntax::ast::*;
use rustc::lint::*;
use rustc::middle::ty;
use std::iter;
use std::borrow::Cow;

use utils::{snippet, span_lint, match_path, match_type, walk_ptrs_ty_depth};
use utils::{OPTION_PATH, RESULT_PATH, STRING_PATH};

use self::SelfKind::*;
use self::OutType::*;

#[derive(Copy,Clone)]
pub struct MethodsPass;

declare_lint!(pub OPTION_UNWRAP_USED, Allow,
              "using `Option.unwrap()`, which should at least get a better message using `expect()`");
declare_lint!(pub RESULT_UNWRAP_USED, Allow,
              "using `Result.unwrap()`, which might be better handled");
declare_lint!(pub STR_TO_STRING, Warn,
              "using `to_string()` on a str, which should be `to_owned()`");
declare_lint!(pub STRING_TO_STRING, Warn,
              "calling `String.to_string()` which is a no-op");
declare_lint!(pub SHOULD_IMPLEMENT_TRAIT, Warn,
              "defining a method that should be implementing a std trait");
declare_lint!(pub WRONG_SELF_CONVENTION, Warn,
              "defining a method named with an established prefix (like \"into_\") that takes \
               `self` with the wrong convention");

impl LintPass for MethodsPass {
    fn get_lints(&self) -> LintArray {
        lint_array!(OPTION_UNWRAP_USED, RESULT_UNWRAP_USED, STR_TO_STRING, STRING_TO_STRING,
                    SHOULD_IMPLEMENT_TRAIT, WRONG_SELF_CONVENTION)
    }

    fn check_expr(&mut self, cx: &Context, expr: &Expr) {
        if let ExprMethodCall(ref ident, _, ref args) = expr.node {
            let (obj_ty, ptr_depth) = walk_ptrs_ty_depth(cx.tcx.expr_ty(&args[0]));
            if ident.node.name == "unwrap" {
                if match_type(cx, obj_ty, &OPTION_PATH) {
                    span_lint(cx, OPTION_UNWRAP_USED, expr.span,
                              "used unwrap() on an Option value. If you don't want \
                               to handle the None case gracefully, consider using \
                               expect() to provide a better panic message");
                } else if match_type(cx, obj_ty, &RESULT_PATH) {
                    span_lint(cx, RESULT_UNWRAP_USED, expr.span,
                              "used unwrap() on a Result value. Graceful handling \
                               of Err values is preferred");
                }
            }
            else if ident.node.name == "to_string" {
                if obj_ty.sty == ty::TyStr {
                    let mut arg_str = snippet(cx, args[0].span, "_");
                    if ptr_depth > 1 {
                        arg_str = Cow::Owned(format!(
                            "({}{})",
                            iter::repeat('*').take(ptr_depth - 1).collect::<String>(),
                            arg_str));
                    }
                    span_lint(cx, STR_TO_STRING, expr.span, &format!(
                        "`{}.to_owned()` is faster", arg_str));
                } else if match_type(cx, obj_ty, &STRING_PATH) {
                    span_lint(cx, STRING_TO_STRING, expr.span, "`String.to_string()` is a no-op; use \
                                                                `clone()` to make a copy");
                }
            }
        }
    }

    fn check_item(&mut self, cx: &Context, item: &Item) {
        if let ItemImpl(_, _, _, None, ref ty, ref items) = item.node {
            for implitem in items {
                let name = implitem.ident.name;
                if let MethodImplItem(ref sig, _) = implitem.node {
                    // check missing trait implementations
                    for &(method_name, n_args, self_kind, out_type, trait_name) in &TRAIT_METHODS {
                        if_let_chain! {
                            [
                                name == method_name,
                                sig.decl.inputs.len() == n_args,
                                out_type.matches(&sig.decl.output),
                                self_kind.matches(&sig.explicit_self.node, false)
                            ], {
                                span_lint(cx, SHOULD_IMPLEMENT_TRAIT, implitem.span, &format!(
                                    "defining a method called `{}` on this type; consider implementing \
                                     the `{}` trait or choosing a less ambiguous name", name, trait_name));
                            }
                        }
                    }
                    // check conventions w.r.t. conversion method names and predicates
                    for &(prefix, self_kind) in &CONVENTIONS {
                        if name.as_str().starts_with(prefix) &&
                                !self_kind.matches(&sig.explicit_self.node, is_copy(cx, &ty, &item)) {
                            span_lint(cx, WRONG_SELF_CONVENTION, sig.explicit_self.span, &format!(
                                "methods called `{}*` usually take {}; consider choosing a less \
                                 ambiguous name", prefix, self_kind.description()));
                        }
                    }
                }
            }
        }
    }
}

const CONVENTIONS: [(&'static str, SelfKind); 5] = [
    ("into_", ValueSelf),
    ("to_", RefSelf),
    ("as_", RefSelf),
    ("is_", RefSelf),
    ("from_", NoSelf),
];

const TRAIT_METHODS: [(&'static str, usize, SelfKind, OutType, &'static str); 30] = [
    ("add",        2, ValueSelf,  AnyType,  "std::ops::Add`"),
    ("sub",        2, ValueSelf,  AnyType,  "std::ops::Sub"),
    ("mul",        2, ValueSelf,  AnyType,  "std::ops::Mul"),
    ("div",        2, ValueSelf,  AnyType,  "std::ops::Div"),
    ("rem",        2, ValueSelf,  AnyType,  "std::ops::Rem"),
    ("shl",        2, ValueSelf,  AnyType,  "std::ops::Shl"),
    ("shr",        2, ValueSelf,  AnyType,  "std::ops::Shr"),
    ("bitand",     2, ValueSelf,  AnyType,  "std::ops::BitAnd"),
    ("bitor",      2, ValueSelf,  AnyType,  "std::ops::BitOr"),
    ("bitxor",     2, ValueSelf,  AnyType,  "std::ops::BitXor"),
    ("neg",        1, ValueSelf,  AnyType,  "std::ops::Neg"),
    ("not",        1, ValueSelf,  AnyType,  "std::ops::Not"),
    ("drop",       1, RefMutSelf, UnitType, "std::ops::Drop"),
    ("index",      2, RefSelf,    RefType,  "std::ops::Index"),
    ("index_mut",  2, RefMutSelf, RefType,  "std::ops::IndexMut"),
    ("deref",      1, RefSelf,    RefType,  "std::ops::Deref"),
    ("deref_mut",  1, RefMutSelf, RefType,  "std::ops::DerefMut"),
    ("clone",      1, RefSelf,    AnyType,  "std::clone::Clone"),
    ("borrow",     1, RefSelf,    RefType,  "std::borrow::Borrow"),
    ("borrow_mut", 1, RefMutSelf, RefType,  "std::borrow::BorrowMut"),
    ("as_ref",     1, RefSelf,    RefType,  "std::convert::AsRef"),
    ("as_mut",     1, RefMutSelf, RefType,  "std::convert::AsMut"),
    ("eq",         2, RefSelf,    BoolType, "std::cmp::PartialEq"),
    ("cmp",        2, RefSelf,    AnyType,  "std::cmp::Ord"),
    ("default",    0, NoSelf,     AnyType,  "std::default::Default"),
    ("hash",       2, RefSelf,    UnitType, "std::hash::Hash"),
    ("next",       1, RefMutSelf, AnyType,  "std::iter::Iterator"),
    ("into_iter",  1, ValueSelf,  AnyType,  "std::iter::IntoIterator"),
    ("from_iter",  1, NoSelf,     AnyType,  "std::iter::FromIterator"),
    ("from_str",   1, NoSelf,     AnyType,  "std::str::FromStr"),
];

#[derive(Clone, Copy)]
enum SelfKind {
    ValueSelf,
    RefSelf,
    RefMutSelf,
    NoSelf,
}

impl SelfKind {
    fn matches(&self, slf: &ExplicitSelf_, allow_value_for_ref: bool) -> bool {
        match (self, slf) {
            (&ValueSelf, &SelfValue(_)) => true,
            (&RefSelf, &SelfRegion(_, Mutability::MutImmutable, _)) => true,
            (&RefMutSelf, &SelfRegion(_, Mutability::MutMutable, _)) => true,
            (&RefSelf, &SelfValue(_)) => allow_value_for_ref,
            (&RefMutSelf, &SelfValue(_)) => allow_value_for_ref,
            (&NoSelf, &SelfStatic) => true,
            (_, &SelfExplicit(ref ty, _)) => self.matches_explicit_type(ty, allow_value_for_ref),
            _ => false
        }
    }

    fn matches_explicit_type(&self, ty: &Ty, allow_value_for_ref: bool) -> bool {
        match (self, &ty.node) {
            (&ValueSelf, &TyPath(..)) => true,
            (&RefSelf, &TyRptr(_, MutTy { mutbl: Mutability::MutImmutable, .. })) => true,
            (&RefMutSelf, &TyRptr(_, MutTy { mutbl: Mutability::MutMutable, .. })) => true,
            (&RefSelf, &TyPath(..)) => allow_value_for_ref,
            (&RefMutSelf, &TyPath(..)) => allow_value_for_ref,
            _ => false
        }
    }

    fn description(&self) -> &'static str {
        match *self {
            ValueSelf => "self by value",
            RefSelf => "self by reference",
            RefMutSelf => "self by mutable reference",
            NoSelf => "no self",
        }
    }
}

#[derive(Clone, Copy)]
enum OutType {
    UnitType,
    BoolType,
    AnyType,
    RefType,
}

impl OutType {
    fn matches(&self, ty: &FunctionRetTy) -> bool {
        match (self, ty) {
            (&UnitType, &DefaultReturn(_)) => true,
            (&UnitType, &Return(ref ty)) if ty.node == TyTup(vec![]) => true,
            (&BoolType, &Return(ref ty)) if is_bool(ty) => true,
            (&AnyType, &Return(ref ty)) if ty.node != TyTup(vec![])  => true,
            (&RefType, &Return(ref ty)) => {
                if let TyRptr(_, _) = ty.node { true } else { false }
            }
            _ => false
        }
    }
}

fn is_bool(ty: &Ty) -> bool {
    if let TyPath(None, ref p) = ty.node {
        if match_path(p, &["bool"]) {
            return true;
        }
    }
    false
}

fn is_copy(cx: &Context, ast_ty: &Ty, item: &Item) -> bool {
    match cx.tcx.ast_ty_to_ty_cache.borrow().get(&ast_ty.id) {
        None => false,
        Some(ty) => {
            let env = ty::ParameterEnvironment::for_item(cx.tcx, item.id);
            !ty.moves_by_default(&env, ast_ty.span)
        }
    }
}
<commit_msg>methods: allow multiple self kinds for "is_" methods<commit_after>use syntax::ast::*;
use rustc::lint::*;
use rustc::middle::ty;
use std::iter;
use std::borrow::Cow;

use utils::{snippet, span_lint, match_path, match_type, walk_ptrs_ty_depth};
use utils::{OPTION_PATH, RESULT_PATH, STRING_PATH};

use self::SelfKind::*;
use self::OutType::*;

#[derive(Copy,Clone)]
pub struct MethodsPass;

declare_lint!(pub OPTION_UNWRAP_USED, Allow,
              "using `Option.unwrap()`, which should at least get a better message using `expect()`");
declare_lint!(pub RESULT_UNWRAP_USED, Allow,
              "using `Result.unwrap()`, which might be better handled");
declare_lint!(pub STR_TO_STRING, Warn,
              "using `to_string()` on a str, which should be `to_owned()`");
declare_lint!(pub STRING_TO_STRING, Warn,
              "calling `String.to_string()` which is a no-op");
declare_lint!(pub SHOULD_IMPLEMENT_TRAIT, Warn,
              "defining a method that should be implementing a std trait");
declare_lint!(pub WRONG_SELF_CONVENTION, Warn,
              "defining a method named with an established prefix (like \"into_\") that takes \
               `self` with the wrong convention");

impl LintPass for MethodsPass {
    fn get_lints(&self) -> LintArray {
        lint_array!(OPTION_UNWRAP_USED, RESULT_UNWRAP_USED, STR_TO_STRING, STRING_TO_STRING,
                    SHOULD_IMPLEMENT_TRAIT, WRONG_SELF_CONVENTION)
    }

    fn check_expr(&mut self, cx: &Context, expr: &Expr) {
        if let ExprMethodCall(ref ident, _, ref args) = expr.node {
            let (obj_ty, ptr_depth) = walk_ptrs_ty_depth(cx.tcx.expr_ty(&args[0]));
            if ident.node.name == "unwrap" {
                if match_type(cx, obj_ty, &OPTION_PATH) {
                    span_lint(cx, OPTION_UNWRAP_USED, expr.span,
                              "used unwrap() on an Option value. If you don't want \
                               to handle the None case gracefully, consider using \
                               expect() to provide a better panic message");
                } else if match_type(cx, obj_ty, &RESULT_PATH) {
                    span_lint(cx, RESULT_UNWRAP_USED, expr.span,
                              "used unwrap() on a Result value. Graceful handling \
                               of Err values is preferred");
                }
            }
            else if ident.node.name == "to_string" {
                if obj_ty.sty == ty::TyStr {
                    let mut arg_str = snippet(cx, args[0].span, "_");
                    if ptr_depth > 1 {
                        arg_str = Cow::Owned(format!(
                            "({}{})",
                            iter::repeat('*').take(ptr_depth - 1).collect::<String>(),
                            arg_str));
                    }
                    span_lint(cx, STR_TO_STRING, expr.span, &format!(
                        "`{}.to_owned()` is faster", arg_str));
                } else if match_type(cx, obj_ty, &STRING_PATH) {
                    span_lint(cx, STRING_TO_STRING, expr.span, "`String.to_string()` is a no-op; use \
                                                                `clone()` to make a copy");
                }
            }
        }
    }

    fn check_item(&mut self, cx: &Context, item: &Item) {
        if let ItemImpl(_, _, _, None, ref ty, ref items) = item.node {
            for implitem in items {
                let name = implitem.ident.name;
                if let MethodImplItem(ref sig, _) = implitem.node {
                    // check missing trait implementations
                    for &(method_name, n_args, self_kind, out_type, trait_name) in &TRAIT_METHODS {
                        if_let_chain! {
                            [
                                name == method_name,
                                sig.decl.inputs.len() == n_args,
                                out_type.matches(&sig.decl.output),
                                self_kind.matches(&sig.explicit_self.node, false)
                            ], {
                                span_lint(cx, SHOULD_IMPLEMENT_TRAIT, implitem.span, &format!(
                                    "defining a method called `{}` on this type; consider implementing \
                                     the `{}` trait or choosing a less ambiguous name", name, trait_name));
                            }
                        }
                    }
                    // check conventions w.r.t. conversion method names and predicates
                    let is_copy = is_copy(cx, &ty, &item);
                    for &(prefix, self_kinds) in &CONVENTIONS {
                        if name.as_str().starts_with(prefix) &&
                                !self_kinds.iter().any(|k| k.matches(&sig.explicit_self.node, is_copy)) {
                            span_lint(cx, WRONG_SELF_CONVENTION, sig.explicit_self.span, &format!(
                                "methods called `{}*` usually take {}; consider choosing a less \
                                 ambiguous name", prefix,
                                &self_kinds.iter().map(|k| k.description()).collect::<Vec<_>>().join(" or ")));
                        }
                    }
                }
            }
        }
    }
}

const CONVENTIONS: [(&'static str, &'static [SelfKind]); 5] = [
    ("into_", &[ValueSelf]),
    ("to_",   &[RefSelf]),
    ("as_",   &[RefSelf]),
    ("is_",   &[RefSelf, NoSelf]),
    ("from_", &[NoSelf]),
];

const TRAIT_METHODS: [(&'static str, usize, SelfKind, OutType, &'static str); 30] = [
    ("add",        2, ValueSelf,  AnyType,  "std::ops::Add`"),
    ("sub",        2, ValueSelf,  AnyType,  "std::ops::Sub"),
    ("mul",        2, ValueSelf,  AnyType,  "std::ops::Mul"),
    ("div",        2, ValueSelf,  AnyType,  "std::ops::Div"),
    ("rem",        2, ValueSelf,  AnyType,  "std::ops::Rem"),
    ("shl",        2, ValueSelf,  AnyType,  "std::ops::Shl"),
    ("shr",        2, ValueSelf,  AnyType,  "std::ops::Shr"),
    ("bitand",     2, ValueSelf,  AnyType,  "std::ops::BitAnd"),
    ("bitor",      2, ValueSelf,  AnyType,  "std::ops::BitOr"),
    ("bitxor",     2, ValueSelf,  AnyType,  "std::ops::BitXor"),
    ("neg",        1, ValueSelf,  AnyType,  "std::ops::Neg"),
    ("not",        1, ValueSelf,  AnyType,  "std::ops::Not"),
    ("drop",       1, RefMutSelf, UnitType, "std::ops::Drop"),
    ("index",      2, RefSelf,    RefType,  "std::ops::Index"),
    ("index_mut",  2, RefMutSelf, RefType,  "std::ops::IndexMut"),
    ("deref",      1, RefSelf,    RefType,  "std::ops::Deref"),
    ("deref_mut",  1, RefMutSelf, RefType,  "std::ops::DerefMut"),
    ("clone",      1, RefSelf,    AnyType,  "std::clone::Clone"),
    ("borrow",     1, RefSelf,    RefType,  "std::borrow::Borrow"),
    ("borrow_mut", 1, RefMutSelf, RefType,  "std::borrow::BorrowMut"),
    ("as_ref",     1, RefSelf,    RefType,  "std::convert::AsRef"),
    ("as_mut",     1, RefMutSelf, RefType,  "std::convert::AsMut"),
    ("eq",         2, RefSelf,    BoolType, "std::cmp::PartialEq"),
    ("cmp",        2, RefSelf,    AnyType,  "std::cmp::Ord"),
    ("default",    0, NoSelf,     AnyType,  "std::default::Default"),
    ("hash",       2, RefSelf,    UnitType, "std::hash::Hash"),
    ("next",       1, RefMutSelf, AnyType,  "std::iter::Iterator"),
    ("into_iter",  1, ValueSelf,  AnyType,  "std::iter::IntoIterator"),
    ("from_iter",  1, NoSelf,     AnyType,  "std::iter::FromIterator"),
    ("from_str",   1, NoSelf,     AnyType,  "std::str::FromStr"),
];

#[derive(Clone, Copy)]
enum SelfKind {
    ValueSelf,
    RefSelf,
    RefMutSelf,
    NoSelf,
}

impl SelfKind {
    fn matches(&self, slf: &ExplicitSelf_, allow_value_for_ref: bool) -> bool {
        match (self, slf) {
            (&ValueSelf, &SelfValue(_)) => true,
            (&RefSelf, &SelfRegion(_, Mutability::MutImmutable, _)) => true,
            (&RefMutSelf, &SelfRegion(_, Mutability::MutMutable, _)) => true,
            (&RefSelf, &SelfValue(_)) => allow_value_for_ref,
            (&RefMutSelf, &SelfValue(_)) => allow_value_for_ref,
            (&NoSelf, &SelfStatic) => true,
            (_, &SelfExplicit(ref ty, _)) => self.matches_explicit_type(ty, allow_value_for_ref),
            _ => false
        }
    }

    fn matches_explicit_type(&self, ty: &Ty, allow_value_for_ref: bool) -> bool {
        match (self, &ty.node) {
            (&ValueSelf, &TyPath(..)) => true,
            (&RefSelf, &TyRptr(_, MutTy { mutbl: Mutability::MutImmutable, .. })) => true,
            (&RefMutSelf, &TyRptr(_, MutTy { mutbl: Mutability::MutMutable, .. })) => true,
            (&RefSelf, &TyPath(..)) => allow_value_for_ref,
            (&RefMutSelf, &TyPath(..)) => allow_value_for_ref,
            _ => false
        }
    }

    fn description(&self) -> &'static str {
        match *self {
            ValueSelf => "self by value",
            RefSelf => "self by reference",
            RefMutSelf => "self by mutable reference",
            NoSelf => "no self",
        }
    }
}

#[derive(Clone, Copy)]
enum OutType {
    UnitType,
    BoolType,
    AnyType,
    RefType,
}

impl OutType {
    fn matches(&self, ty: &FunctionRetTy) -> bool {
        match (self, ty) {
            (&UnitType, &DefaultReturn(_)) => true,
            (&UnitType, &Return(ref ty)) if ty.node == TyTup(vec![]) => true,
            (&BoolType, &Return(ref ty)) if is_bool(ty) => true,
            (&AnyType, &Return(ref ty)) if ty.node != TyTup(vec![])  => true,
            (&RefType, &Return(ref ty)) => {
                if let TyRptr(_, _) = ty.node { true } else { false }
            }
            _ => false
        }
    }
}

fn is_bool(ty: &Ty) -> bool {
    if let TyPath(None, ref p) = ty.node {
        if match_path(p, &["bool"]) {
            return true;
        }
    }
    false
}

fn is_copy(cx: &Context, ast_ty: &Ty, item: &Item) -> bool {
    match cx.tcx.ast_ty_to_ty_cache.borrow().get(&ast_ty.id) {
        None => false,
        Some(ty) => {
            let env = ty::ParameterEnvironment::for_item(cx.tcx, item.id);
            !ty.moves_by_default(&env, ast_ty.span)
        }
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::ptr;

use std::ffi::{CString,OsStr};
use std::ops::Deref;
use std::os::unix::io::{RawFd,AsRawFd};

use ::context::{Context};
use ::device::{Device};
use ::handle::prelude::*;

/// Monitors for device events.
///
/// A monitor communicates with the kernel over a socket. Filtering events is performed efficiently
/// in the kernel, and only events that match the filters are received by the socket. Filters must
/// be setup before listening for events.
pub struct Monitor {
    monitor: *mut ::ffi::udev_monitor,
}

impl Drop for Monitor {
    fn drop(&mut self) {
        unsafe {
            let udev = ::ffi::udev_monitor_get_udev(self.monitor);

            ::ffi::udev_monitor_unref(self.monitor);
            ::ffi::udev_unref(udev);
        }
    }
}

impl Monitor {
    /// Creates a new `Monitor`.
    pub fn new(context: &Context) -> ::Result<Self> {
        let name = CString::new("udev").unwrap();

        unsafe {
            let ptr = try_alloc!(
                ::ffi::udev_monitor_new_from_netlink(context.as_ptr(), name.as_ptr())
            );

            ::ffi::udev_ref(context.as_ptr());

            Ok(Monitor { monitor: ptr })
        }
    }

    /// Adds a filter that matches events for devices with the given subsystem.
    pub fn match_subsystem<T: AsRef<OsStr>>(&mut self, subsystem: T) -> ::Result<()> {
        let subsystem = try!(::util::os_str_to_cstring(subsystem));

        ::util::errno_to_result(unsafe {
            ::ffi::udev_monitor_filter_add_match_subsystem_devtype(self.monitor, subsystem.as_ptr(), ptr::null())
        })
    }

    /// Adds a filter that matches events for devices with the given subsystem and device type.
    pub fn match_subsystem_devtype<T: AsRef<OsStr>, U: AsRef<OsStr>>(&mut self, subsystem: T, devtype: U) -> ::Result<()> {
        let subsystem = try!(::util::os_str_to_cstring(subsystem));
        let devtype = try!(::util::os_str_to_cstring(devtype));

        ::util::errno_to_result(unsafe {
            ::ffi::udev_monitor_filter_add_match_subsystem_devtype(self.monitor, subsystem.as_ptr(), devtype.as_ptr())
        })
    }

    /// Adds a filter that matches events for devices with the given tag.
    pub fn match_tag<T: AsRef<OsStr>>(&mut self, tag: T) -> ::Result<()> {
        let tag = try!(::util::os_str_to_cstring(tag));

        ::util::errno_to_result(unsafe {
            ::ffi::udev_monitor_filter_add_match_tag(self.monitor, tag.as_ptr())
        })
    }

    /// Removes all filters currently set on the monitor.
    pub fn clear_filters(&mut self) -> ::Result<()> {
        ::util::errno_to_result(unsafe {
            ::ffi::udev_monitor_filter_remove(self.monitor)
        })
    }

    /// Listens for events matching the current filters.
    ///
    /// This method consumes the `Monitor`.
    pub fn listen(self) -> ::Result<MonitorSocket> {
        try!(::util::errno_to_result(unsafe {
            ::ffi::udev_monitor_enable_receiving(self.monitor)
        }));

        Ok(MonitorSocket { inner: self })
    }
}

/// An active monitor that can receive events.
///
/// The events received by a `MonitorSocket` match the filters setup by the `Monitor` that created
/// the socket.
///
/// Monitors are initially setup to receive events from the kernel via a nonblocking socket. A
/// variant of `poll()` should be used on the file descriptor returned by the `AsRawFd` trait to
/// wait for new events.
pub struct MonitorSocket {
    inner: Monitor
}

/// Provides raw access to the monitor's socket.
impl AsRawFd for MonitorSocket {
    /// Returns the file descriptor of the monitor's socket.
    fn as_raw_fd(&self) -> RawFd {
        unsafe {
            ::ffi::udev_monitor_get_fd(self.inner.monitor)
        }
    }
}

impl MonitorSocket {
    /// Receives the next available event from the monitor.
    ///
    /// This method does not block. If no events are available, it returns `None` immediately.
    pub fn receive_event(&mut self) -> Option<Event> {
        let device = unsafe {
            ::ffi::udev_monitor_receive_device(self.inner.monitor)
        };

        if device.is_null() {
            None
        }
        else {
            let device = unsafe {
                ::device::from_raw(device)
            };

            Some(Event { device: device })
        }
    }
}

/// Types of events that can be received from udev.
#[derive(Debug,Clone,Copy,PartialEq,Eq)]
pub enum EventType {
    /// A device was added.
    Add,

    /// A device changed.
    Change,

    /// A device was removed.
    Remove,

    /// An unknown event occurred.
    Unknown,
}

impl Default for EventType {
    fn default() -> EventType {
        EventType::Unknown
    }
}

impl fmt::Display for EventType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(match self {
            &EventType::Add => "add",
            &EventType::Change => "change",
            &EventType::Remove => "remove",
            &EventType::Unknown => "unknown",
        })
    }
}

/// An event that indicates a change in device state.
pub struct Event {
    device: Device
}

/// Provides access to the device associated with the event.
impl Deref for Event {
    type Target = Device;

    fn deref(&self) -> &Device {
        &self.device
    }
}

impl Event {
    /// Returns the `EventType` corresponding to this event.
    pub fn event_type(&self) -> EventType {
        let value = match self.device.property_value("ACTION") {
            Some(s) => s.to_str(),
            None => None
        };

        match value {
            Some("add") => EventType::Add,
            Some("change") => EventType::Change,
            Some("remove") => EventType::Remove,
            _ => EventType::Unknown
        }
    }

    /// Returns the event's sequence number.
    pub fn sequence_number(&self) -> u64 {
        unsafe {
            ::ffi::udev_device_get_seqnum(self.device.as_ptr()) as u64
        }
    }

    /// Returns the device associated with this event.
    pub fn device(&self) -> &Device {
        &self.device
    }
}
<commit_msg>Remove memory allocation from Monitor::new()<commit_after>use std::fmt;
use std::ptr;

use std::ffi::OsStr;
use std::ops::Deref;
use std::os::unix::io::{RawFd,AsRawFd};

use ::context::{Context};
use ::device::{Device};
use ::handle::prelude::*;

/// Monitors for device events.
///
/// A monitor communicates with the kernel over a socket. Filtering events is performed efficiently
/// in the kernel, and only events that match the filters are received by the socket. Filters must
/// be setup before listening for events.
pub struct Monitor {
    monitor: *mut ::ffi::udev_monitor,
}

impl Drop for Monitor {
    fn drop(&mut self) {
        unsafe {
            let udev = ::ffi::udev_monitor_get_udev(self.monitor);

            ::ffi::udev_monitor_unref(self.monitor);
            ::ffi::udev_unref(udev);
        }
    }
}

impl Monitor {
    /// Creates a new `Monitor`.
    pub fn new(context: &Context) -> ::Result<Self> {
        unsafe {
            let ptr = try_alloc!(
                ::ffi::udev_monitor_new_from_netlink(context.as_ptr(), b"udev\0".as_ptr() as *mut _)
            );

            ::ffi::udev_ref(context.as_ptr());

            Ok(Monitor { monitor: ptr })
        }
    }

    /// Adds a filter that matches events for devices with the given subsystem.
    pub fn match_subsystem<T: AsRef<OsStr>>(&mut self, subsystem: T) -> ::Result<()> {
        let subsystem = try!(::util::os_str_to_cstring(subsystem));

        ::util::errno_to_result(unsafe {
            ::ffi::udev_monitor_filter_add_match_subsystem_devtype(self.monitor, subsystem.as_ptr(), ptr::null())
        })
    }

    /// Adds a filter that matches events for devices with the given subsystem and device type.
    pub fn match_subsystem_devtype<T: AsRef<OsStr>, U: AsRef<OsStr>>(&mut self, subsystem: T, devtype: U) -> ::Result<()> {
        let subsystem = try!(::util::os_str_to_cstring(subsystem));
        let devtype = try!(::util::os_str_to_cstring(devtype));

        ::util::errno_to_result(unsafe {
            ::ffi::udev_monitor_filter_add_match_subsystem_devtype(self.monitor, subsystem.as_ptr(), devtype.as_ptr())
        })
    }

    /// Adds a filter that matches events for devices with the given tag.
    pub fn match_tag<T: AsRef<OsStr>>(&mut self, tag: T) -> ::Result<()> {
        let tag = try!(::util::os_str_to_cstring(tag));

        ::util::errno_to_result(unsafe {
            ::ffi::udev_monitor_filter_add_match_tag(self.monitor, tag.as_ptr())
        })
    }

    /// Removes all filters currently set on the monitor.
    pub fn clear_filters(&mut self) -> ::Result<()> {
        ::util::errno_to_result(unsafe {
            ::ffi::udev_monitor_filter_remove(self.monitor)
        })
    }

    /// Listens for events matching the current filters.
    ///
    /// This method consumes the `Monitor`.
    pub fn listen(self) -> ::Result<MonitorSocket> {
        try!(::util::errno_to_result(unsafe {
            ::ffi::udev_monitor_enable_receiving(self.monitor)
        }));

        Ok(MonitorSocket { inner: self })
    }
}

/// An active monitor that can receive events.
///
/// The events received by a `MonitorSocket` match the filters setup by the `Monitor` that created
/// the socket.
///
/// Monitors are initially setup to receive events from the kernel via a nonblocking socket. A
/// variant of `poll()` should be used on the file descriptor returned by the `AsRawFd` trait to
/// wait for new events.
pub struct MonitorSocket {
    inner: Monitor
}

/// Provides raw access to the monitor's socket.
impl AsRawFd for MonitorSocket {
    /// Returns the file descriptor of the monitor's socket.
    fn as_raw_fd(&self) -> RawFd {
        unsafe {
            ::ffi::udev_monitor_get_fd(self.inner.monitor)
        }
    }
}

impl MonitorSocket {
    /// Receives the next available event from the monitor.
    ///
    /// This method does not block. If no events are available, it returns `None` immediately.
    pub fn receive_event(&mut self) -> Option<Event> {
        let device = unsafe {
            ::ffi::udev_monitor_receive_device(self.inner.monitor)
        };

        if device.is_null() {
            None
        }
        else {
            let device = unsafe {
                ::device::from_raw(device)
            };

            Some(Event { device: device })
        }
    }
}

/// Types of events that can be received from udev.
#[derive(Debug,Clone,Copy,PartialEq,Eq)]
pub enum EventType {
    /// A device was added.
    Add,

    /// A device changed.
    Change,

    /// A device was removed.
    Remove,

    /// An unknown event occurred.
    Unknown,
}

impl Default for EventType {
    fn default() -> EventType {
        EventType::Unknown
    }
}

impl fmt::Display for EventType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(match self {
            &EventType::Add => "add",
            &EventType::Change => "change",
            &EventType::Remove => "remove",
            &EventType::Unknown => "unknown",
        })
    }
}

/// An event that indicates a change in device state.
pub struct Event {
    device: Device
}

/// Provides access to the device associated with the event.
impl Deref for Event {
    type Target = Device;

    fn deref(&self) -> &Device {
        &self.device
    }
}

impl Event {
    /// Returns the `EventType` corresponding to this event.
    pub fn event_type(&self) -> EventType {
        let value = match self.device.property_value("ACTION") {
            Some(s) => s.to_str(),
            None => None
        };

        match value {
            Some("add") => EventType::Add,
            Some("change") => EventType::Change,
            Some("remove") => EventType::Remove,
            _ => EventType::Unknown
        }
    }

    /// Returns the event's sequence number.
    pub fn sequence_number(&self) -> u64 {
        unsafe {
            ::ffi::udev_device_get_seqnum(self.device.as_ptr()) as u64
        }
    }

    /// Returns the device associated with this event.
    pub fn device(&self) -> &Device {
        &self.device
    }
}
<|endoftext|>


<commit_before>use super::{Connection, Message, MessageItem, Error};
use std::collections::BTreeMap;
use std::rc::{Rc, Weak};
use std::cell::{Cell, RefCell};

pub struct Argument<'a> {
    name: &'a str,
    sig: &'a str,
}

pub type MethodResult<'a> = Result<Vec<MessageItem>, (&'a str, String)>;

pub trait MethodHandler<'a> {
    fn handle(&self, &mut Message) -> MethodResult;
}

pub struct Method<'a> {
    in_args: Vec<Argument<'a>>,
    out_args: Vec<Argument<'a>>,
    cb: Box<MethodHandler<'a>+'a>,
}

pub struct Interface<'a> {
    methods: BTreeMap<String, Method<'a>>,
//  TODO: properties and signals
}

struct IObjectPath<'a> {
    conn: &'a Connection,
    path: String,
    registered: Cell<bool>,
    interfaces: RefCell<BTreeMap<String, Interface<'a>>>,
}

pub struct ObjectPath<'a> {
    // We need a weak ref for the introspector, hence this extra boxing
    i: Rc<IObjectPath<'a>>,
}

#[unsafe_destructor]
impl<'a> Drop for IObjectPath<'a> {
    fn drop(&mut self) {
        let _ = self.set_registered(false);
    }
}

impl<'a> IObjectPath<'a> {

    fn set_registered(&self, register: bool) -> Result<(), Error> {
        if register == self.registered.get() { return Ok(()) };
        if register {
            try!(self.conn.register_object_path(&*self.path));
        } else {
            self.conn.unregister_object_path(&*self.path);
        }
        self.registered.set(register);
        Ok(())
    }

    fn introspect<'b>(&self, _: &mut Message) -> MethodResult<'b> {
        let ifacestr = self.interfaces.borrow().iter().fold("".to_string(), |ia, (ik, iv)| {
            format!(r##"{}  <interface name="{}">
{}  </interface>
"##,
                ia, ik, iv.methods.iter().fold("".to_string(), |ma, (mk, mv)| {
                format!(r##"{}    <method name="{}">
{}{}    </method>
"##, ma, mk,
                    mv.in_args.iter().fold("".to_string(), |aa, az| {
                       format!(r##"{}     <arg name="{}" type="{}" direction="in"/>
"##, aa, az.name, az.sig)
                    }), mv.out_args.iter().fold("".to_string(), |aa, az| {
                       format!(r##"{}     <arg name="{}" type="{}" direction="out"/>
"##, aa, az.name, az.sig)
                }))
            }))
        });

        let nodestr = format!(r##"<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node name="{}">
{}</node>"##, self.path, ifacestr);

        Ok(vec!(MessageItem::Str(nodestr)))
    }

}

struct Introspecter<'a> {
    objpath: Weak<IObjectPath<'a>>,
}

impl<'a> MethodHandler<'a> for Introspecter<'a> {
    fn handle(&self, m: &mut Message) -> MethodResult {
        self.objpath.upgrade().unwrap().introspect(m)
    }
}

impl<'a> ObjectPath<'a> {
    pub fn new(conn: &'a Connection, path: &str, introspectable: bool) -> ObjectPath<'a> {
        let i = IObjectPath {
            conn: conn,
            path: path.to_string(),
            registered: Cell::new(false),
            interfaces: RefCell::new(BTreeMap::new()),
        };
        let o = ObjectPath { i: Rc::new(i) };

        if introspectable {
            let mut m = BTreeMap::new();
            m.insert("Introspect".to_string(), Method {
                in_args: vec!(),
                out_args: vec!(Argument { name: "xml_data", sig: "s" }),
                cb: box Introspecter { objpath: o.i.downgrade() },
            });
            o.i.interfaces.borrow_mut().insert("org.freedesktop.DBus.Introspectable".to_string(), Interface { methods: m });
        };
        o
    }

    // Note: This function can not be called from inside a MethodHandler callback.
    pub fn insert_interface(&mut self, name: String, i: Interface<'a>) {
        self.i.interfaces.borrow_mut().insert(name, i);
    }

    pub fn is_registered(&self) -> bool {
        self.i.registered.get()
    }

    pub fn set_registered(&mut self, register: bool) -> Result<(), Error> {
        self.i.set_registered(register)
    }

    /* Return value:
       None => not handled,
       Some(Err(())) => message reply send failed,
       Some(Ok()) => message reply send ok */

    pub fn handle_message(&mut self, msg: &mut Message) -> Option<Result<(), ()>> {
        let (_, path, iface, method) = msg.headers();
        if path.is_none() || path.unwrap() != self.i.path { return None; }
        if iface.is_none() { return None; }
        let reply =
            if let Some(i) = self.i.interfaces.borrow().get(&iface.unwrap()) {
                if let Some(Some(m)) = method.map(|m| i.methods.get(&m)) {
                    match m.cb.handle(msg) {
                        Ok(r) => {
                            let mut z = Message::new_method_return(msg).unwrap();
                            z.append_items(r.as_slice());
                            z
                        }
                        Err((aa,bb)) => Message::new_error(msg, aa, bb.as_slice()).unwrap(),
                    }
                }
                else { Message::new_error(msg, "org.freedesktop.DBus.Error.UnknownMethod", "Unkown method").unwrap() }
            }
            else { Message::new_error(msg, "org.freedesktop.DBus.Error.UnknownInterface", "Unkown interface").unwrap() }
        ;
        Some(self.i.conn.send(reply))
    }
}

#[cfg(test)]
impl<'a> MethodHandler<'a> for int {
    fn handle(&self, _: &mut Message) -> MethodResult {
        Err(("dummy", "dummy".to_string()))
    }
}

#[test]
fn test_introspect() {
    let mut im = BTreeMap::new();
    im.insert("Echo".to_string(), Method {
        in_args: vec!( Argument { name: "request", sig: "s"} ),
        out_args: vec!( Argument { name: "reply", sig: "s"} ),
        cb: box 3i,
    });

    let c = Connection::get_private(super::BusType::Session).unwrap();
    let mut o = ObjectPath::new(&c, "/echo", true);
    o.insert_interface("com.example.echo".to_string(), Interface { methods: im });
    let mut msg = Message::new_method_call("com.example.echoserver", "/echo", "org.freedesktop.DBus.Introspectable", "Introspect").unwrap();

    println!("Introspect result: {}", o.i.introspect(&mut msg));

    let result = r##"[Str(<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node name="/echo">
  <interface name="com.example.echo">
    <method name="Echo">
     <arg name="request" type="s" direction="in"/>
     <arg name="reply" type="s" direction="out"/>
    </method>
  </interface>
  <interface name="org.freedesktop.DBus.Introspectable">
    <method name="Introspect">
     <arg name="xml_data" type="s" direction="out"/>
    </method>
  </interface>
</node>)]"##;

    assert_eq!(result, format!("{}", o.i.introspect(&mut msg).unwrap()));

}

<commit_msg>Add property handlers on object path<commit_after>use super::{Connection, Message, MessageItem, Error};
use std::collections::BTreeMap;
use std::rc::{Rc, Weak};
use std::cell::{Cell, RefCell};

pub struct Argument<'a> {
    name: &'a str,
    sig: &'a str,
}

pub type MethodResult<'a> = Result<Vec<MessageItem>, (&'a str, String)>;
pub type PropertyGetResult = Result<MessageItem, (&'static str, String)>;
pub type PropertySetResult = Result<(), (&'static str, String)>;

pub trait MethodHandler<'a> {
    fn handle(&self, &mut Message) -> MethodResult;
}

pub struct Method<'a> {
    in_args: Vec<Argument<'a>>,
    out_args: Vec<Argument<'a>>,
    cb: Box<MethodHandler<'a>+'a>,
}

pub trait PropertyHandler {
    fn get(&self) -> PropertyGetResult;
    fn set(&self, &MessageItem) -> PropertySetResult;
}

pub trait PropertyGetHandler {
    fn get(&self) -> PropertyGetResult;
}

pub trait PropertySetHandler {
    fn set(&self, &MessageItem) -> PropertySetResult;
}

pub enum PropertyAccess<'a> {
    RO(Box<PropertyGetHandler+'a>),
    RW(Box<PropertyHandler+'a>),
    WO(Box<PropertySetHandler+'a>),
}

pub struct Property<'a> {
    sig: &'a str,
    access: PropertyAccess<'a>,
}

pub struct Interface<'a> {
    methods: BTreeMap<String, Method<'a>>,
    properties: BTreeMap<String, Property<'a>>,
//  TODO: signals
}

struct IObjectPath<'a> {
    conn: &'a Connection,
    path: String,
    registered: Cell<bool>,
    interfaces: RefCell<BTreeMap<String, Interface<'a>>>,
}

pub struct ObjectPath<'a> {
    // We need a weak ref for the introspector, hence this extra boxing
    i: Rc<IObjectPath<'a>>,
}

#[unsafe_destructor]
impl<'a> Drop for IObjectPath<'a> {
    fn drop(&mut self) {
        let _ = self.set_registered(false);
    }
}

impl<'a> IObjectPath<'a> {

    fn set_registered(&self, register: bool) -> Result<(), Error> {
        if register == self.registered.get() { return Ok(()) };
        if register {
            try!(self.conn.register_object_path(&*self.path));
        } else {
            self.conn.unregister_object_path(&*self.path);
        }
        self.registered.set(register);
        Ok(())
    }

    fn introspect<'b>(&self, _: &mut Message) -> MethodResult<'b> {
        let ifacestr = self.interfaces.borrow().iter().fold("".to_string(), |ia, (ik, iv)| {
            format!(r##"{}  <interface name="{}">
{}{}  </interface>
"##,
                ia, ik, iv.methods.iter().fold("".to_string(), |ma, (mk, mv)| {
                format!(r##"{}    <method name="{}">
{}{}    </method>
"##, ma, mk,
                    mv.in_args.iter().fold("".to_string(), |aa, az| {
                       format!(r##"{}      <arg name="{}" type="{}" direction="in"/>
"##, aa, az.name, az.sig)
                    }), mv.out_args.iter().fold("".to_string(), |aa, az| {
                       format!(r##"{}      <arg name="{}" type="{}" direction="out"/>
"##, aa, az.name, az.sig)
                }))
            }), iv.properties.iter().fold("".to_string(), |pa, (pk, pv)| {
                       format!(r##"{}    <property name="{}" type="{}" access="{}"/>
"##, pa, pk, pv.sig, match pv.access {
                PropertyAccess::RO(_) => "read",
                PropertyAccess::RW(_) => "readwrite",
                PropertyAccess::WO(_) => "write",
            })
            }))
        });

        let nodestr = format!(r##"<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node name="{}">
{}</node>"##, self.path, ifacestr);

        Ok(vec!(MessageItem::Str(nodestr)))
    }
}

struct Introspecter<'a> {
    objpath: Weak<IObjectPath<'a>>,
}

impl<'a> MethodHandler<'a> for Introspecter<'a> {
    fn handle(&self, m: &mut Message) -> MethodResult {
        self.objpath.upgrade().unwrap().introspect(m)
    }
}

fn parse_msg_str(a: Option<&MessageItem>) -> Result<&str,(&'static str, String)> {
    let name = if let Some(s) = a { s } else {
        return Err(("org.freedesktop.DBus.Error.InvalidArgs", format!("Invalid argument {}", a)))
    };
    if let &MessageItem::Str(ref s) = name {
        Ok(s.as_slice())
    } else { Err(("org.freedesktop.DBus.Error.InvalidArgs", format!("Invalid argument {}", a))) }
}

fn parse_msg_variant(a: Option<&MessageItem>) -> Result<&MessageItem,(&'static str, String)> {
    let name = if let Some(s) = a { s } else {
        return Err(("org.freedesktop.DBus.Error.InvalidArgs", format!("Invalid argument {}", a)))
    };
    if let &MessageItem::Variant(ref s) = name {
        Ok(&**s)
    } else { Err(("org.freedesktop.DBus.Error.InvalidArgs", format!("Invalid argument {}", a))) }
}

impl PropertyGetHandler for MessageItem {
    fn get(&self) -> PropertyGetResult {
        Ok(self.clone())
    }
}

struct PropertyGet<'a> {
    objpath: Weak<IObjectPath<'a>>,
}

impl<'a> MethodHandler<'a> for PropertyGet<'a> {
    fn handle(&self, msg: &mut Message) -> MethodResult {
        let items = msg.get_items();
        let iface_name = try!(parse_msg_str(items.get(0)));
        let prop_name = try!(parse_msg_str(items.get(1)));

        let istmp = self.objpath.upgrade().unwrap();
        let is = istmp.interfaces.borrow();
        let i = if let Some(s) = is.get(iface_name) { s } else {
            return Err(("org.freedesktop.DBus.Error.UnknownInterface", format!("Unknown interface {}", iface_name)))
        };
        let p = if let Some(s) = i.properties.get(prop_name) { s } else {
            return Err(("org.freedesktop.DBus.Error.UnknownProperty", format!("Unknown property {}", prop_name)))
        };
        let v = try!(match p.access {
            PropertyAccess::RO(ref cb) => cb.get(),
            PropertyAccess::RW(ref cb) => cb.get(),
            PropertyAccess::WO(_) => {
                return Err(("org.freedesktop.DBus.Error.Failed", format!("Property {} is write only", prop_name)))
            }
        });
        Ok(vec!(MessageItem::Variant(box v)))
    }
}

struct PropertyGetAll<'a> {
    objpath: Weak<IObjectPath<'a>>,
}

impl<'a> MethodHandler<'a> for PropertyGetAll<'a> {
    fn handle(&self, msg: &mut Message) -> MethodResult {
        let items = msg.get_items();
        let iface_name = try!(parse_msg_str(items.get(0)));

        let istmp = self.objpath.upgrade().unwrap();
        let is = istmp.interfaces.borrow();
        let i = if let Some(s) = is.get(iface_name) { s } else {
            return Err(("org.freedesktop.DBus.Error.UnknownInterface", format!("Unknown interface {}", iface_name)))
        };
        let mut result = Vec::new();
        for (pname, pv) in i.properties.iter() {
            let v = try!(match pv.access {
                PropertyAccess::RO(ref cb) => cb.get(),
                PropertyAccess::RW(ref cb) => cb.get(),
                PropertyAccess::WO(_) => { continue }
            });
            result.push(MessageItem::DictEntry(box MessageItem::Str(pname.clone()), box v));
        }
        Ok(result)
    }
}

struct PropertySet<'a> {
    objpath: Weak<IObjectPath<'a>>,
}

impl<'a> MethodHandler<'a> for PropertySet<'a> {
    fn handle(&self, msg: &mut Message) -> MethodResult {
        let items = msg.get_items();
        let iface_name = try!(parse_msg_str(items.get(0)));
        let prop_name = try!(parse_msg_str(items.get(1)));
        let value = try!(parse_msg_variant(items.get(2)));

        let istmp = self.objpath.upgrade().unwrap();
        let is = istmp.interfaces.borrow();
        let i = if let Some(s) = is.get(iface_name) { s } else {
            return Err(("org.freedesktop.DBus.Error.UnknownInterface", format!("Unknown interface {}", iface_name)))
        };
        let p = if let Some(s) = i.properties.get(prop_name) { s } else {
            return Err(("org.freedesktop.DBus.Error.UnknownProperty", format!("Unknown property {}", prop_name)))
        };
        try!(match p.access {
            PropertyAccess::WO(ref cb) => cb.set(value),
            PropertyAccess::RW(ref cb) => cb.set(value),
            PropertyAccess::RO(_) => {
                return Err(("org.freedesktop.DBus.Error.PropertyReadOnly", format!("Property {} is read only", prop_name)))
            }
        });
        Ok(vec!())
    }
}

impl<'a> ObjectPath<'a> {
    pub fn new(conn: &'a Connection, path: &str, introspectable: bool) -> ObjectPath<'a> {
        let i = IObjectPath {
            conn: conn,
            path: path.to_string(),
            registered: Cell::new(false),
            interfaces: RefCell::new(BTreeMap::new()),
        };
        let o = ObjectPath { i: Rc::new(i) };

        if introspectable {
            let mut m = BTreeMap::new();
            m.insert("Introspect".to_string(), Method {
                in_args: vec!(),
                out_args: vec!(Argument { name: "xml_data", sig: "s" }),
                cb: box Introspecter { objpath: o.i.downgrade() },
            });
            o.i.interfaces.borrow_mut().insert("org.freedesktop.DBus.Introspectable".to_string(), Interface {
                methods: m,
                properties: BTreeMap::new()
            });
        }
        o
    }

    fn add_property_handler(&mut self) {
        if self.i.interfaces.borrow().contains_key("org.freedesktop.DBus.Properties") { return };

        let mut m = BTreeMap::new();
        m.insert("Get".to_string(), Method {
            in_args: vec!(Argument { name: "interface_name", sig: "s" }, Argument { name: "property_name", sig: "s" }),
            out_args: vec!(Argument { name: "value", sig: "v" }),
            cb: box PropertyGet { objpath: self.i.downgrade() },
        });
        m.insert("GetAll".to_string(), Method {
            in_args: vec!(Argument { name: "interface_name", sig: "s" }),
            out_args: vec!(Argument { name: "props", sig: "a{sv}" }),
            cb: box PropertyGetAll { objpath: self.i.downgrade() },
        });
        m.insert("Set".to_string(), Method {
            in_args: vec!(Argument { name: "interface_name", sig: "s" }, Argument { name: "property_name", sig: "s" },
                Argument { name: "value", sig: "v" }),
            out_args: vec!(),
            cb: box PropertySet { objpath: self.i.downgrade() },
        });
        self.insert_interface("org.freedesktop.DBus.Properties".to_string(), Interface {
            methods: m,
            properties: BTreeMap::new()
        });
    }

    // Note: This function can not be called from inside a MethodHandler callback.
    pub fn insert_interface(&mut self, name: String, i: Interface<'a>) {
        if !i.properties.is_empty() {
            self.add_property_handler();
        }
        self.i.interfaces.borrow_mut().insert(name, i);
    }

    pub fn is_registered(&self) -> bool {
        self.i.registered.get()
    }

    pub fn set_registered(&mut self, register: bool) -> Result<(), Error> {
        self.i.set_registered(register)
    }

    /* Return value:
       None => not handled,
       Some(Err(())) => message reply send failed,
       Some(Ok()) => message reply send ok */

    pub fn handle_message(&mut self, msg: &mut Message) -> Option<Result<(), ()>> {
        let (_, path, iface, method) = msg.headers();
        if path.is_none() || path.unwrap() != self.i.path { return None; }
        if iface.is_none() { return None; }
        let reply =
            if let Some(i) = self.i.interfaces.borrow().get(&iface.unwrap()) {
                if let Some(Some(m)) = method.map(|m| i.methods.get(&m)) {
                    match m.cb.handle(msg) {
                        Ok(r) => {
                            let mut z = Message::new_method_return(msg).unwrap();
                            z.append_items(r.as_slice());
                            z
                        }
                        Err((aa,bb)) => Message::new_error(msg, aa, bb.as_slice()).unwrap(),
                    }
                }
                else { Message::new_error(msg, "org.freedesktop.DBus.Error.UnknownMethod", "Unkown method").unwrap() }
            }
            else { Message::new_error(msg, "org.freedesktop.DBus.Error.UnknownInterface", "Unkown interface").unwrap() }
        ;
        Some(self.i.conn.send(reply))
    }
}

#[cfg(test)]
impl<'a> MethodHandler<'a> for int {
    fn handle(&self, _: &mut Message) -> MethodResult {
        Err(("dummy", "dummy".to_string()))
    }
}

#[cfg(test)]
fn make_objpath<'a>(c: &'a Connection) -> ObjectPath<'a> {
    let mut im = BTreeMap::new();
    im.insert("Echo".to_string(), Method {
        in_args: vec!( Argument { name: "request", sig: "s"} ),
        out_args: vec!( Argument { name: "reply", sig: "s"} ),
        cb: box 3i,
    });
    let mut ip = BTreeMap::new();
    ip.insert("EchoCount".to_string(), Property { sig: "i", access: PropertyAccess::RO(box MessageItem::Int32(7))});
    let mut o = ObjectPath::new(c, "/echo", true);
    o.insert_interface("com.example.echo".to_string(), Interface { methods: im, properties: ip });
    o
}

#[test]
fn test_objpath() {
    let c = Connection::get_private(super::BusType::Session).unwrap();
    let mut o = make_objpath(&c);
    o.set_registered(true).unwrap();
    let busname = format!("com.example.objpath.test{}", ::std::rand::random::<u32>());
    assert_eq!(c.register_name(busname.as_slice(), super::NameFlag::ReplaceExisting as u32).unwrap(), super::RequestNameReply::PrimaryOwner);

    let thread = ::std::thread::Thread::spawn(move || {
        let c = Connection::get_private(super::BusType::Session).unwrap();
        let pr = super::Props::new(&c, &*busname, "/echo", "com.example.echo", 5000);
        assert_eq!(pr.get("EchoCount").unwrap(), super::MessageItem::Int32(7));
    });

    for n in c.iter(1000) {
        println!("objpath msg {}", n);
        if let super::ConnectionItem::MethodCall(mut m) = n {
            if let Some(msg) = o.handle_message(&mut m) {
                msg.unwrap();
                break;
            }
        }
    }

    thread.join().ok().expect("failed to join thread");
}

#[test]
fn test_introspect() {
    let c = Connection::get_private(super::BusType::Session).unwrap();
    let o = make_objpath(&c);
    let mut msg = Message::new_method_call("com.example.echoserver", "/echo", "org.freedesktop.DBus.Introspectable", "Introspect").unwrap();

    println!("Introspect result: {}", o.i.introspect(&mut msg));

    let result = r##"[Str(<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node name="/echo">
  <interface name="com.example.echo">
    <method name="Echo">
      <arg name="request" type="s" direction="in"/>
      <arg name="reply" type="s" direction="out"/>
    </method>
    <property name="EchoCount" type="i" access="read"/>
  </interface>
  <interface name="org.freedesktop.DBus.Introspectable">
    <method name="Introspect">
      <arg name="xml_data" type="s" direction="out"/>
    </method>
  </interface>
  <interface name="org.freedesktop.DBus.Properties">
    <method name="Get">
      <arg name="interface_name" type="s" direction="in"/>
      <arg name="property_name" type="s" direction="in"/>
      <arg name="value" type="v" direction="out"/>
    </method>
    <method name="GetAll">
      <arg name="interface_name" type="s" direction="in"/>
      <arg name="props" type="a{sv}" direction="out"/>
    </method>
    <method name="Set">
      <arg name="interface_name" type="s" direction="in"/>
      <arg name="property_name" type="s" direction="in"/>
      <arg name="value" type="v" direction="in"/>
    </method>
  </interface>
</node>)]"##;

    assert_eq!(result, format!("{}", o.i.introspect(&mut msg).unwrap()));

}

<|endoftext|>


<commit_before>use luminance::{Dim2, Equation, Factor, Framebuffer, Flat, Mode, Pipe, Pipeline, RenderCommand, ShadingCommand, Tess, TessRender, TessVertices, Uniform, Unit, Vertex, VertexFormat};
use std::cell::RefCell;

use compositing::{self, ColorMap, DepthMap};
use resource::Res;
use scene::Scene;
use shader::Program;
use text::TextTexture;
use texture::{RGBA32F, Texture};

/// Vertex used in overlay’s objects. Position coordinates are in *window space*.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Vert {
  /// Position.
  pub pos: [f32; 3],
  /// Color.
  pub color: [f32; 4]
}

impl Vert {
  pub fn new(pos: [f32; 3], color: [f32; 4]) -> Self {
    Vert {
      pos: pos,
      color: color
    }
  }

  /// Convert the vertex’ position to *clip space*.
  ///
  /// > Note: the depth coordinate is not affected.
  fn to_clip_space(&self, converter: &UnitConverter) -> Self {
    let converted = converter.from_win_coord(self.pos[0], self.pos[1]);
    let pos_ = [converted[0], converted[1], self.pos[2]];

    Vert {
      pos: pos_,
      color: self.color
    }
  }
}

impl Vertex for Vert {
  fn vertex_format() -> VertexFormat {
    <([f32; 3], [f32; 4]) as Vertex>::vertex_format()
  }
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Triangle(pub Vert, pub Vert, pub Vert);

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Quad(pub Vert, pub Vert, pub Vert, pub Vert);

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Disc {
  center: Vert,
  radius: f32
}

impl Disc {
  pub fn new(center: Vert, radius: f32) -> Self {
    Disc {
      center: center,
      radius: radius
    }
  }

  fn to_clip_space(&self, converter: &UnitConverter) -> Self {
    Disc {
      center: self.center.to_clip_space(converter),
      radius: self.radius * converter.rw
    }
  }
}

impl Vertex for Disc {
  fn vertex_format() -> VertexFormat {
    <(Vert, f32) as Vertex>::vertex_format()
  }
}

pub type Texture2D = Texture<Flat, Dim2, RGBA32F>;

#[derive(Copy, Clone)]
pub struct Text<'a> {
  text_texture: &'a TextTexture,
  left_lower: Vert
}

impl<'a> Text<'a> {
  pub fn new(text_texture: &'a TextTexture, left_lower: Vert) -> Self {
    Text {
      text_texture: text_texture,
      left_lower: left_lower
    }
  }
}

const DISC_SCREEN_RATIO: &'static Uniform<f32> = &Uniform::new(0);

const TEXT_SAMPLER: &'static Uniform<Unit> = &Uniform::new(0);
const TEXT_POS: &'static Uniform<[f32; 3]> = &Uniform::new(1);
const TEXT_SIZE: &'static Uniform<[f32; 2]> = &Uniform::new(2);
const TEXT_SCALE: &'static Uniform<f32> = &Uniform::new(3);
const TEXT_COLOR: &'static Uniform<[f32; 4]> = &Uniform::new(4);

pub struct Overlay {
  ratio: f32,
  tri_program: Res<Program>,
  tris: RefCell<Tess>,
  disc_program: Res<Program>,
  discs: RefCell<Tess>,
  text_program: Res<Program>,
  text_quad: Tess,
  unit_converter: UnitConverter,
}

impl Overlay {
  pub fn new(w: u32, h: u32, max_tris: usize, max_quads: usize, max_discs: usize, scene: &mut Scene) -> Self {
    let tri_program = scene.get("spectra/overlay/triangle.glsl", vec![]).unwrap();
    let tris = Tess::new(Mode::Triangle, TessVertices::Reserve::<Vert>(max_tris * 3 + max_quads * 4), None);

    let disc_program = scene.get("spectra/overlay/disc.glsl", vec![DISC_SCREEN_RATIO.sem("ratio")]).unwrap();
    let discs = Tess::new(Mode::Point, TessVertices::Reserve::<Disc>(max_discs), None);

    let text_program = scene.get("spectra/overlay/text.glsl", vec![
      TEXT_SAMPLER.sem("text_texture"),
      TEXT_POS.sem("pos"),
      TEXT_SIZE.sem("size"),
      TEXT_SCALE.sem("scale"),
      TEXT_COLOR.sem("color")
    ]).unwrap();

    let text_quad = Tess::attributeless(Mode::TriangleStrip, 4);

    Overlay {
      ratio: w as f32 / h as f32,
      tri_program: tri_program,
      tris: RefCell::new(tris),
      disc_program: disc_program,
      discs: RefCell::new(discs),
      text_program: text_program,
      text_quad: text_quad,
      unit_converter: UnitConverter::new(w, h)
    }
  }

  /// Dispatch render input primitives into GPU buffers.
  fn dispatch(&self, input: &RenderInput) -> (usize, usize) {
    let mut tris_ref = self.tris.borrow_mut();
    let mut tris = tris_ref.as_slice_mut().unwrap();
    let mut tri_i = 0;

    let mut discs_ref = self.discs.borrow_mut();
    let mut discs = discs_ref.as_slice_mut().unwrap();
    let mut disc_i = 0;

    for &Triangle(a, b, c) in input.triangles {
      let abc = [a, b, c];

      for &v in &abc {
        tris[tri_i] = v.to_clip_space(&self.unit_converter);
        tri_i += 1;
      }
    }

    for &Quad(a, b, c, d) in input.quads {
      let abcd = [a, b, c, c, b, d];

      for &v in &abcd {
        tris[tri_i] = v.to_clip_space(&self.unit_converter);
        tri_i += 1;
      }
    }

    for disc in input.discs {
      discs[disc_i] = disc.to_clip_space(&self.unit_converter);
      disc_i += 1;
    }

    (tri_i, disc_i)
  }

  /// Render the overlay.
  pub fn render(&self, framebuffer: &Framebuffer<Flat, Dim2, ColorMap, DepthMap>, input: RenderInput) {
    let (tri_vert_nb, disc_vert_nb) = self.dispatch(&input);

    let tris_ref = self.tris.borrow();
    let tris = TessRender::one_sub(&tris_ref, tri_vert_nb);

    let discs_ref = self.discs.borrow();
    let discs = TessRender::one_sub(&discs_ref, disc_vert_nb);

    let text_quad = TessRender::one_whole(&self.text_quad);
    let text_quad_render = [Pipe::new(text_quad)];

    let tri_program = self.tri_program.borrow();
    let disc_program = self.disc_program.borrow();
    let text_program = self.text_program.borrow();

    // FIXME: no alloc?
    let text_uniforms: Vec<_> = input.texts.map(|(texts, text_scale)| texts.iter().map(|text| {
      let (tex_w, tex_h) = text.text_texture.size();

      [
        TEXT_SAMPLER.alter(Unit::new(0)),
        TEXT_POS.alter(text.left_lower.to_clip_space(&self.unit_converter).pos),
        TEXT_SIZE.alter(self.unit_converter.from_win_dim(tex_w as f32, tex_h as f32)),
        TEXT_SCALE.alter(text_scale),
        TEXT_COLOR.alter(text.left_lower.color),
      ]
    }).collect()).unwrap_or(Vec::new());

    let text_textures: Vec<_> = input.texts.map(|(texts, _)| texts.iter().map(|text| [&***text.text_texture]).collect()).unwrap_or(Vec::new());

    let text_nb = input.texts.map(|(texts, _)| texts.len()).unwrap_or(0);
    let text_render_cmds: Vec<_> = (0..text_nb).map(|i| {
        let blending = (Equation::Additive, Factor::One, Factor::SrcAlphaComplement);
        Pipe::empty()
          .uniforms(&text_uniforms[i])
          .textures(&text_textures[i])
          .unwrap(RenderCommand::new(Some(blending), true, &text_quad_render))
      }).collect();

    let disc_uniforms = [
      DISC_SCREEN_RATIO.alter(self.ratio)
    ];

    let tris = &[Pipe::new(tris)];
    let tris_render_cmds = &[Pipe::new(RenderCommand::new(None, true, tris))];
    let discs = &[Pipe::new(discs)];
    let discs_render_cmds = &[Pipe::new(RenderCommand::new(None, true, discs))];

    Pipeline::new(framebuffer, [0., 0., 0., 1.], &[], &[], &[
      Pipe::new(ShadingCommand::new(&tri_program, tris_render_cmds)),
      Pipe::empty().uniforms(&disc_uniforms).unwrap(ShadingCommand::new(&disc_program, discs_render_cmds)),
      Pipe::new(ShadingCommand::new(&text_program, &text_render_cmds))
    ]).run();
  }
}

#[derive(Clone)]
pub struct RenderInput<'a, 'b> where 'b: 'a {
  triangles: &'a [Triangle],
  quads: &'a [Quad],
  discs: &'a [Disc],
  texts: Option<(&'a [Text<'b>], f32)>
}

impl<'a, 'b> RenderInput<'a, 'b> where 'b: 'a {
  pub fn new() -> Self {
    RenderInput {
      triangles: &[],
      quads: &[],
      discs: &[],
      texts: None,
    }
  }

  pub fn triangles(self, triangles: &'a [Triangle]) -> Self {
    RenderInput {
      triangles: triangles,
      ..self
    }
  }

  pub fn quads(self, quads: &'a [Quad]) -> Self {
    RenderInput {
      quads: quads,
      ..self
    }
  }

  pub fn discs(self, discs: &'a [Disc]) -> Self {
    RenderInput {
      discs: discs,
      ..self
    }
  }

  pub fn texts(self, texts: &'a [Text<'b>], scale: f32) -> Self {
    RenderInput {
      texts: Some((texts, scale.max(0.))),
      ..self
    }
  }
}

/// A *unit converter* is used to position stuff in overlay in a universal and screen independent
/// way.
///
/// The idea is that you provide should handle *window space* coordinates only. This type will then
/// help compute normalized coordinates the renderer can understand.
///
/// For the record, *window space* coordinates are defined as below – given a viewport size of
/// *w × h*:
///
/// ^ y = h
/// |
/// |
/// |    x = w
/// O------>
///
/// The formula used to convert from *window space* to what we call *clip space* is:
///
/// > x' = 2x / w - 1
/// > y' = 2y / h - 1
#[derive(Copy, Clone, Debug, PartialEq)]
struct UnitConverter {
  rw: f32,
  rh: f32,
  twice_rw: f32,
  twice_rh: f32
}

impl UnitConverter {
  /// Build a unit converter by giving it the size of the viewport.
  pub fn new(w: u32, h: u32) -> Self {
    let rw = 1. / (w as f32);
    let rh = 1. / (h as f32);

    UnitConverter {
      rw: rw,
      rh: rh,
      twice_rw: 2. * rw,
      twice_rh: 2. * rh
    }
  }

  /// Convert from *window space* coordinates to clip space* ones.
  pub fn from_win_coord(&self, x: f32, y: f32) -> [f32; 2] {
    let x_ = x * self.twice_rw - 1.;
    let y_ = y * self.twice_rh - 1.;

    [x_.min(1.).max(-1.), y_.min(1.).max(-1.)]
  }

  /// Convert from *window space* dimensions to clip space* ones.
  pub fn from_win_dim(&self, w: f32, h: f32) -> [f32; 2] {
    let w_ = w * self.rw;
    let h_ = h * self.rh;

    [w_.min(1.).max(-1.), h_.min(1.).max(-1.)]
  }
}
<commit_msg>RenderLayer for Overalay.<commit_after>use luminance::{Dim2, Equation, Factor, Framebuffer, Flat, Mode, Pipe, Pipeline, RenderCommand, ShadingCommand, Tess, TessRender, TessVertices, Uniform, Unit, Vertex, VertexFormat};
use std::cell::RefCell;

use compositing::{self, ColorMap, DepthMap, RenderLayer};
use resource::Res;
use scene::Scene;
use shader::Program;
use text::TextTexture;
use texture::{RGBA32F, Texture};

/// Vertex used in overlay’s objects. Position coordinates are in *window space*.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Vert {
  /// Position.
  pub pos: [f32; 3],
  /// Color.
  pub color: [f32; 4]
}

impl Vert {
  pub fn new(pos: [f32; 3], color: [f32; 4]) -> Self {
    Vert {
      pos: pos,
      color: color
    }
  }

  /// Convert the vertex’ position to *clip space*.
  ///
  /// > Note: the depth coordinate is not affected.
  fn to_clip_space(&self, converter: &UnitConverter) -> Self {
    let converted = converter.from_win_coord(self.pos[0], self.pos[1]);
    let pos_ = [converted[0], converted[1], self.pos[2]];

    Vert {
      pos: pos_,
      color: self.color
    }
  }
}

impl Vertex for Vert {
  fn vertex_format() -> VertexFormat {
    <([f32; 3], [f32; 4]) as Vertex>::vertex_format()
  }
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Triangle(pub Vert, pub Vert, pub Vert);

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Quad(pub Vert, pub Vert, pub Vert, pub Vert);

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Disc {
  center: Vert,
  radius: f32
}

impl Disc {
  pub fn new(center: Vert, radius: f32) -> Self {
    Disc {
      center: center,
      radius: radius
    }
  }

  fn to_clip_space(&self, converter: &UnitConverter) -> Self {
    Disc {
      center: self.center.to_clip_space(converter),
      radius: self.radius * converter.rw
    }
  }
}

impl Vertex for Disc {
  fn vertex_format() -> VertexFormat {
    <(Vert, f32) as Vertex>::vertex_format()
  }
}

pub type Texture2D = Texture<Flat, Dim2, RGBA32F>;

#[derive(Copy, Clone)]
pub struct Text<'a> {
  text_texture: &'a TextTexture,
  left_lower: Vert
}

impl<'a> Text<'a> {
  pub fn new(text_texture: &'a TextTexture, left_lower: Vert) -> Self {
    Text {
      text_texture: text_texture,
      left_lower: left_lower
    }
  }
}

const DISC_SCREEN_RATIO: &'static Uniform<f32> = &Uniform::new(0);

const TEXT_SAMPLER: &'static Uniform<Unit> = &Uniform::new(0);
const TEXT_POS: &'static Uniform<[f32; 3]> = &Uniform::new(1);
const TEXT_SIZE: &'static Uniform<[f32; 2]> = &Uniform::new(2);
const TEXT_SCALE: &'static Uniform<f32> = &Uniform::new(3);
const TEXT_COLOR: &'static Uniform<[f32; 4]> = &Uniform::new(4);

pub struct Overlay {
  ratio: f32,
  tri_program: Res<Program>,
  tris: RefCell<Tess>,
  disc_program: Res<Program>,
  discs: RefCell<Tess>,
  text_program: Res<Program>,
  text_quad: Tess,
  unit_converter: UnitConverter,
}

impl Overlay {
  pub fn new(w: u32, h: u32, max_tris: usize, max_quads: usize, max_discs: usize, scene: &mut Scene) -> Self {
    let tri_program = scene.get("spectra/overlay/triangle.glsl", vec![]).unwrap();
    let tris = Tess::new(Mode::Triangle, TessVertices::Reserve::<Vert>(max_tris * 3 + max_quads * 4), None);

    let disc_program = scene.get("spectra/overlay/disc.glsl", vec![DISC_SCREEN_RATIO.sem("ratio")]).unwrap();
    let discs = Tess::new(Mode::Point, TessVertices::Reserve::<Disc>(max_discs), None);

    let text_program = scene.get("spectra/overlay/text.glsl", vec![
      TEXT_SAMPLER.sem("text_texture"),
      TEXT_POS.sem("pos"),
      TEXT_SIZE.sem("size"),
      TEXT_SCALE.sem("scale"),
      TEXT_COLOR.sem("color")
    ]).unwrap();

    let text_quad = Tess::attributeless(Mode::TriangleStrip, 4);

    Overlay {
      ratio: w as f32 / h as f32,
      tri_program: tri_program,
      tris: RefCell::new(tris),
      disc_program: disc_program,
      discs: RefCell::new(discs),
      text_program: text_program,
      text_quad: text_quad,
      unit_converter: UnitConverter::new(w, h)
    }
  }

  /// Dispatch render input primitives into GPU buffers.
  fn dispatch(&self, input: &RenderInput) -> (usize, usize) {
    let mut tris_ref = self.tris.borrow_mut();
    let mut tris = tris_ref.as_slice_mut().unwrap();
    let mut tri_i = 0;

    let mut discs_ref = self.discs.borrow_mut();
    let mut discs = discs_ref.as_slice_mut().unwrap();
    let mut disc_i = 0;

    for &Triangle(a, b, c) in input.triangles {
      let abc = [a, b, c];

      for &v in &abc {
        tris[tri_i] = v.to_clip_space(&self.unit_converter);
        tri_i += 1;
      }
    }

    for &Quad(a, b, c, d) in input.quads {
      let abcd = [a, b, c, c, b, d];

      for &v in &abcd {
        tris[tri_i] = v.to_clip_space(&self.unit_converter);
        tri_i += 1;
      }
    }

    for disc in input.discs {
      discs[disc_i] = disc.to_clip_space(&self.unit_converter);
      disc_i += 1;
    }

    (tri_i, disc_i)
  }

  fn render(&self, framebuffer: &Framebuffer<Flat, Dim2, ColorMap, DepthMap>, input: &RenderInput) {
    let (tri_vert_nb, disc_vert_nb) = self.dispatch(input);

    let tris_ref = self.tris.borrow();
    let tris = TessRender::one_sub(&tris_ref, tri_vert_nb);

    let discs_ref = self.discs.borrow();
    let discs = TessRender::one_sub(&discs_ref, disc_vert_nb);

    let text_quad = TessRender::one_whole(&self.text_quad);
    let text_quad_render = [Pipe::new(text_quad)];

    let tri_program = self.tri_program.borrow();
    let disc_program = self.disc_program.borrow();
    let text_program = self.text_program.borrow();

    // FIXME: no alloc?
    let text_uniforms: Vec<_> = input.texts.map(|(texts, text_scale)| texts.iter().map(|text| {
      let (tex_w, tex_h) = text.text_texture.size();

      [
        TEXT_SAMPLER.alter(Unit::new(0)),
        TEXT_POS.alter(text.left_lower.to_clip_space(&self.unit_converter).pos),
        TEXT_SIZE.alter(self.unit_converter.from_win_dim(tex_w as f32, tex_h as f32)),
        TEXT_SCALE.alter(text_scale),
        TEXT_COLOR.alter(text.left_lower.color),
      ]
    }).collect()).unwrap_or(Vec::new());

    let text_textures: Vec<_> = input.texts.map(|(texts, _)| texts.iter().map(|text| [&***text.text_texture]).collect()).unwrap_or(Vec::new());

    let text_nb = input.texts.map(|(texts, _)| texts.len()).unwrap_or(0);
    let text_render_cmds: Vec<_> = (0..text_nb).map(|i| {
        let blending = (Equation::Additive, Factor::One, Factor::SrcAlphaComplement);
        Pipe::empty()
          .uniforms(&text_uniforms[i])
          .textures(&text_textures[i])
          .unwrap(RenderCommand::new(Some(blending), true, &text_quad_render))
      }).collect();

    let disc_uniforms = [
      DISC_SCREEN_RATIO.alter(self.ratio)
    ];

    let tris = &[Pipe::new(tris)];
    let tris_render_cmds = &[Pipe::new(RenderCommand::new(None, true, tris))];
    let discs = &[Pipe::new(discs)];
    let discs_render_cmds = &[Pipe::new(RenderCommand::new(None, true, discs))];

    Pipeline::new(framebuffer, [0., 0., 0., 1.], &[], &[], &[
      Pipe::new(ShadingCommand::new(&tri_program, tris_render_cmds)),
      Pipe::empty().uniforms(&disc_uniforms).unwrap(ShadingCommand::new(&disc_program, discs_render_cmds)),
      Pipe::new(ShadingCommand::new(&text_program, &text_render_cmds))
    ]).run();
  }

  /// Obtain a `RenderLayer`.
  pub fn render_layer<'a, 'b>(&'a self, input: RenderInput<'a, 'b>) -> RenderLayer<'a> {
    RenderLayer::new(move |framebuffer| self.render(framebuffer, &input))
  }
}

#[derive(Clone)]
pub struct RenderInput<'a, 'b> where 'b: 'a {
  triangles: &'a [Triangle],
  quads: &'a [Quad],
  discs: &'a [Disc],
  texts: Option<(&'a [Text<'b>], f32)>
}

impl<'a, 'b> RenderInput<'a, 'b> where 'b: 'a {
  pub fn new() -> Self {
    RenderInput {
      triangles: &[],
      quads: &[],
      discs: &[],
      texts: None,
    }
  }

  pub fn triangles(self, triangles: &'a [Triangle]) -> Self {
    RenderInput {
      triangles: triangles,
      ..self
    }
  }

  pub fn quads(self, quads: &'a [Quad]) -> Self {
    RenderInput {
      quads: quads,
      ..self
    }
  }

  pub fn discs(self, discs: &'a [Disc]) -> Self {
    RenderInput {
      discs: discs,
      ..self
    }
  }

  pub fn texts(self, texts: &'a [Text<'b>], scale: f32) -> Self {
    RenderInput {
      texts: Some((texts, scale.max(0.))),
      ..self
    }
  }
}

/// A *unit converter* is used to position stuff in overlay in a universal and screen independent
/// way.
///
/// The idea is that you provide should handle *window space* coordinates only. This type will then
/// help compute normalized coordinates the renderer can understand.
///
/// For the record, *window space* coordinates are defined as below – given a viewport size of
/// *w × h*:
///
/// ^ y = h
/// |
/// |
/// |    x = w
/// O------>
///
/// The formula used to convert from *window space* to what we call *clip space* is:
///
/// > x' = 2x / w - 1
/// > y' = 2y / h - 1
#[derive(Copy, Clone, Debug, PartialEq)]
struct UnitConverter {
  rw: f32,
  rh: f32,
  twice_rw: f32,
  twice_rh: f32
}

impl UnitConverter {
  /// Build a unit converter by giving it the size of the viewport.
  pub fn new(w: u32, h: u32) -> Self {
    let rw = 1. / (w as f32);
    let rh = 1. / (h as f32);

    UnitConverter {
      rw: rw,
      rh: rh,
      twice_rw: 2. * rw,
      twice_rh: 2. * rh
    }
  }

  /// Convert from *window space* coordinates to clip space* ones.
  pub fn from_win_coord(&self, x: f32, y: f32) -> [f32; 2] {
    let x_ = x * self.twice_rw - 1.;
    let y_ = y * self.twice_rh - 1.;

    [x_.min(1.).max(-1.), y_.min(1.).max(-1.)]
  }

  /// Convert from *window space* dimensions to clip space* ones.
  pub fn from_win_dim(&self, w: f32, h: f32) -> [f32; 2] {
    let w_ = w * self.rw;
    let h_ = h * self.rh;

    [w_.min(1.).max(-1.), h_.min(1.).max(-1.)]
  }
}
<|endoftext|>