<commit_before>use ::std::io::Cursor;

use crypto::{PublicKey, KeyPair, SigPair};
use serializer::*;
use asset::Asset;

#[macro_use] mod util;

pub mod tx_type;
pub use self::tx_type::*;

pub trait EncodeTx {
    fn encode(&self, v: &mut Vec<u8>);
}

pub trait DecodeTx<T> {
    fn decode(cur: &mut Cursor<&[u8]>, tx: Tx) -> Option<T>;
}

pub trait SignTx {
    fn sign(&self, key_pair: &KeyPair) -> SigPair;
    fn append_sign(&mut self, key_pair: &KeyPair);
}

#[derive(Debug, Clone)]
pub enum TxVariant {
    RewardTx(RewardTx),
    BondTx(BondTx),
    TransferTx(TransferTx)
}

impl TxVariant {
    pub fn encode(&self, v: &mut Vec<u8>) {
        match self {
            TxVariant::RewardTx(tx) => { tx.encode(v) },
            TxVariant::BondTx(tx) => { tx.encode(v) },
            TxVariant::TransferTx(tx) => { tx.encode(v) }
        };
    }

    pub fn encode_with_sigs(&self, v: &mut Vec<u8>) {
        macro_rules! encode_sigs {
            ($name:expr, $vec:expr) => {
                {
                    $vec.push_u16($name.signature_pairs.len() as u16);
                    for sig in &$name.signature_pairs { $vec.push_sig_pair(sig) };
                    $name.encode($vec);
                }
            }
        }

        match self {
            TxVariant::RewardTx(tx) => { encode_sigs!(tx, v) },
            TxVariant::BondTx(tx) => { encode_sigs!(tx, v) },
            TxVariant::TransferTx(tx) => { encode_sigs!(tx, v) }
        };
    }

    pub fn decode_with_sigs(cur: &mut Cursor<&[u8]>) -> Option<TxVariant> {
        let sigs = {
            let len = cur.take_u16()?;
            let mut vec = Vec::with_capacity(len as usize);
            for _ in 0..len { vec.push(cur.take_sig_pair()?) };
            vec
        };
        let mut base = Tx::decode_base(cur)?;
        base.signature_pairs = sigs;
        match base.tx_type {
            TxType::REWARD => Some(TxVariant::RewardTx(RewardTx::decode(cur, base)?)),
            TxType::BOND => Some(TxVariant::BondTx(BondTx::decode(cur, base)?)),
            TxType::TRANSFER => Some(TxVariant::TransferTx(TransferTx::decode(cur, base)?))
        }
    }
}

impl ::std::ops::Deref for TxVariant {
    type Target = Tx;

    fn deref(&self) -> &Self::Target {
        match self {
            TxVariant::RewardTx(tx) => &tx.base,
            TxVariant::BondTx(tx) => &tx.base,
            TxVariant::TransferTx(tx) => &tx.base
        }
    }
}

#[derive(Debug, Clone)]
pub struct Tx {
    pub tx_type: TxType,
    pub timestamp: u32,
    pub fee: Asset,
    pub signature_pairs: Vec<SigPair>
}

impl Tx {
    fn encode_base(&self, v: &mut Vec<u8>) {
        v.push(self.tx_type as u8);
        v.push_u32(self.timestamp);
        v.push_asset(&self.fee);
    }

    fn decode_base(cur: &mut Cursor<&[u8]>) -> Option<Tx> {
        let tx_type = match cur.take_u8()? {
            t if t == TxType::REWARD as u8 => TxType::REWARD,
            t if t == TxType::BOND as u8 => TxType::BOND,
            t if t == TxType::TRANSFER as u8 => TxType::TRANSFER,
            _ => return None
        };
        let timestamp = cur.take_u32()?;
        let fee = cur.take_asset()?;

        Some(Tx {
            tx_type,
            timestamp,
            fee,
            signature_pairs: Vec::new()
        })
    }
}

#[derive(Debug, Clone)]
pub struct RewardTx {
    pub base: Tx,
    pub to: PublicKey,
    pub rewards: Vec<Asset>
}

impl EncodeTx for RewardTx {
    fn encode(&self, v: &mut Vec<u8>) {
        debug_assert_eq!(self.base.signature_pairs.len(), 0);
        self.encode_base(v);
        v.push_pub_key(&self.to);
        v.push_u32(self.rewards.len() as u32);
        for r in &self.rewards { v.push_asset(r) };
    }
}

impl DecodeTx<RewardTx> for RewardTx {
    fn decode(cur: &mut Cursor<&[u8]>, tx: Tx) -> Option<RewardTx> {
        assert_eq!(tx.tx_type, TxType::REWARD);
        let key = cur.take_pub_key()?;

        let len = cur.take_u32()?;
        let mut rewards = Vec::with_capacity(len as usize);
        for _ in 0..len {
            rewards.push(cur.take_asset()?);
        }

        Some(RewardTx {
            base: tx,
            to: key,
            rewards
        })
    }
}

#[derive(Debug, Clone)]
pub struct BondTx {
    pub base: Tx,
    pub minter: PublicKey, // Key that signs blocks
    pub staker: PublicKey, // Hot wallet that receives rewards and stakes its balance
    pub stake_amt: Asset,
    pub bond_fee: Asset
}

impl EncodeTx for BondTx {
    fn encode(&self, v: &mut Vec<u8>) {
        self.encode_base(v);
        v.push_pub_key(&self.minter);
        v.push_pub_key(&self.staker);
        v.push_asset(&self.stake_amt);
        v.push_asset(&self.bond_fee);
    }
}

impl DecodeTx<BondTx> for BondTx {
    fn decode(cur: &mut Cursor<&[u8]>, tx: Tx) -> Option<BondTx> {
        assert_eq!(tx.tx_type, TxType::BOND);
        let minter = cur.take_pub_key()?;
        let staker = cur.take_pub_key()?;
        let stake_amt = cur.take_asset()?;
        let bond_fee = cur.take_asset()?;
        Some(BondTx {
            base: tx,
            minter,
            staker,
            stake_amt,
            bond_fee
        })
    }
}

#[derive(Debug, Clone)]
pub struct TransferTx {
    pub base: Tx,
    pub from: PublicKey,
    pub to: PublicKey,
    pub amount: Asset,
    pub memo: Vec<u8>
}

impl EncodeTx for TransferTx {
    fn encode(&self, v: &mut Vec<u8>) {
        self.encode_base(v);
        v.push_pub_key(&self.from);
        v.push_pub_key(&self.to);
        v.push_asset(&self.amount);
        v.push_bytes(&self.memo);
    }
}

impl DecodeTx<TransferTx> for TransferTx {
    fn decode(cur: &mut Cursor<&[u8]>, tx: Tx) -> Option<TransferTx> {
        assert_eq!(tx.tx_type, TxType::TRANSFER);
        let from = cur.take_pub_key()?;
        let to = cur.take_pub_key()?;
        let amount = cur.take_asset()?;
        let memo = cur.take_bytes()?;
        Some(TransferTx {
            base: tx,
            from,
            to,
            amount,
            memo
        })
    }
}

tx_deref!(RewardTx);
tx_deref!(BondTx);
tx_deref!(TransferTx);

tx_sign!(BondTx);
tx_sign!(TransferTx);

#[cfg(test)]
mod tests {
    use ::std::str::FromStr;
    use super::*;
    use crypto;

    macro_rules! cmp_base_tx {
        ($id:ident, $ty:expr, $ts:expr, $fee:expr) => {
            assert_eq!($id.tx_type, $ty);
            assert_eq!($id.timestamp, $ts);
            assert_eq!($id.fee.to_string(), $fee);
        }
    }

    #[test]
    fn test_encode_tx_with_sigs() {
        let to = crypto::KeyPair::gen_keypair();
        let reward_tx = TxVariant::RewardTx(RewardTx {
            base: Tx {
                tx_type: TxType::REWARD,
                timestamp: 123,
                fee: get_asset("123 GOLD"),
                signature_pairs: vec![]
            },
            to: to.0,
            rewards: vec![get_asset("1.50 GOLD"), get_asset("1.0 SILVER")]
        });

        let mut v = vec![];
        reward_tx.encode_with_sigs(&mut v);

        let mut c = Cursor::<&[u8]>::new(&v);
        TxVariant::decode_with_sigs(&mut c).unwrap();
    }

    #[test]
    fn test_encode_reward() {
        let to = crypto::KeyPair::gen_keypair();
        let reward_tx = RewardTx {
            base: Tx {
                tx_type: TxType::REWARD,
                timestamp: 123,
                fee: get_asset("123 GOLD"),
                signature_pairs: vec![]
            },
            to: to.0,
            rewards: vec![get_asset("1.50 GOLD"), get_asset("1.0 SILVER")]
        };

        let mut v = vec![];
        reward_tx.encode(&mut v);

        let mut c = Cursor::<&[u8]>::new(&v);
        let base = Tx::decode_base(&mut c).unwrap();
        let dec = RewardTx::decode(&mut c, base).unwrap();

        cmp_base_tx!(dec, TxType::REWARD, 123, "123 GOLD");
        assert_eq!(reward_tx.to, dec.to);
        assert_eq!(reward_tx.rewards.len(), dec.rewards.len());
        assert_eq!(reward_tx.rewards[0].to_string(), dec.rewards[0].to_string());
        assert_eq!(reward_tx.rewards[1].to_string(), dec.rewards[1].to_string());
    }

    #[test]
    fn test_encode_bond() {
        let minter = crypto::KeyPair::gen_keypair();
        let staker = crypto::KeyPair::gen_keypair();
        let bond_tx = BondTx {
            base: Tx {
                tx_type: TxType::BOND,
                timestamp: 1230,
                fee: get_asset("123 GOLD"),
                signature_pairs: vec![]
            },
            minter: minter.0,
            staker: staker.0,
            stake_amt: get_asset("1.0456 GOLD"),
            bond_fee: get_asset("1.00000000 GOLD")
        };

        let mut v = vec![];
        bond_tx.encode(&mut v);

        let mut c = Cursor::<&[u8]>::new(&v);
        let base = Tx::decode_base(&mut c).unwrap();
        let dec = BondTx::decode(&mut c, base).unwrap();

        cmp_base_tx!(dec, TxType::BOND, 1230, "123 GOLD");
        assert_eq!(bond_tx.minter, dec.minter);
        assert_eq!(bond_tx.staker, dec.staker);
        assert_eq!(bond_tx.stake_amt.to_string(), dec.stake_amt.to_string());
        assert_eq!(bond_tx.bond_fee.to_string(), dec.bond_fee.to_string());
    }

    #[test]
    fn test_encode_transfer() {
        let from = crypto::KeyPair::gen_keypair();
        let to = crypto::KeyPair::gen_keypair();
        let transfer_tx = TransferTx {
            base: Tx {
                tx_type: TxType::TRANSFER,
                timestamp: 1234567890,
                fee: get_asset("1.23 GOLD"),
                signature_pairs: vec![]
            },
            from: from.0,
            to: to.0,
            amount: get_asset("1.0456 GOLD"),
            memo: Vec::from(String::from("Hello world!").as_bytes())
        };

        let mut v = vec![];
        transfer_tx.encode(&mut v);

        let mut c = Cursor::<&[u8]>::new(&v);
        let base = Tx::decode_base(&mut c).unwrap();
        let dec = TransferTx::decode(&mut c, base).unwrap();

        cmp_base_tx!(dec, TxType::TRANSFER, 1234567890, "1.23 GOLD");
        assert_eq!(transfer_tx.from, dec.from);
        assert_eq!(transfer_tx.to, dec.to);
        assert_eq!(transfer_tx.amount.to_string(), dec.amount.to_string());
        assert_eq!(transfer_tx.memo, dec.memo);
    }

    fn get_asset(s: &str) -> Asset {
        Asset::from_str(s).unwrap()
    }
}

<commit_msg>Simplify encode_transfer test<commit_after>use ::std::io::Cursor;

use crypto::{PublicKey, KeyPair, SigPair};
use serializer::*;
use asset::Asset;

#[macro_use] mod util;

pub mod tx_type;
pub use self::tx_type::*;

pub trait EncodeTx {
    fn encode(&self, v: &mut Vec<u8>);
}

pub trait DecodeTx<T> {
    fn decode(cur: &mut Cursor<&[u8]>, tx: Tx) -> Option<T>;
}

pub trait SignTx {
    fn sign(&self, key_pair: &KeyPair) -> SigPair;
    fn append_sign(&mut self, key_pair: &KeyPair);
}

#[derive(Debug, Clone)]
pub enum TxVariant {
    RewardTx(RewardTx),
    BondTx(BondTx),
    TransferTx(TransferTx)
}

impl TxVariant {
    pub fn encode(&self, v: &mut Vec<u8>) {
        match self {
            TxVariant::RewardTx(tx) => { tx.encode(v) },
            TxVariant::BondTx(tx) => { tx.encode(v) },
            TxVariant::TransferTx(tx) => { tx.encode(v) }
        };
    }

    pub fn encode_with_sigs(&self, v: &mut Vec<u8>) {
        macro_rules! encode_sigs {
            ($name:expr, $vec:expr) => {
                {
                    $vec.push_u16($name.signature_pairs.len() as u16);
                    for sig in &$name.signature_pairs { $vec.push_sig_pair(sig) };
                    $name.encode($vec);
                }
            }
        }

        match self {
            TxVariant::RewardTx(tx) => { encode_sigs!(tx, v) },
            TxVariant::BondTx(tx) => { encode_sigs!(tx, v) },
            TxVariant::TransferTx(tx) => { encode_sigs!(tx, v) }
        };
    }

    pub fn decode_with_sigs(cur: &mut Cursor<&[u8]>) -> Option<TxVariant> {
        let sigs = {
            let len = cur.take_u16()?;
            let mut vec = Vec::with_capacity(len as usize);
            for _ in 0..len { vec.push(cur.take_sig_pair()?) };
            vec
        };
        let mut base = Tx::decode_base(cur)?;
        base.signature_pairs = sigs;
        match base.tx_type {
            TxType::REWARD => Some(TxVariant::RewardTx(RewardTx::decode(cur, base)?)),
            TxType::BOND => Some(TxVariant::BondTx(BondTx::decode(cur, base)?)),
            TxType::TRANSFER => Some(TxVariant::TransferTx(TransferTx::decode(cur, base)?))
        }
    }
}

impl ::std::ops::Deref for TxVariant {
    type Target = Tx;

    fn deref(&self) -> &Self::Target {
        match self {
            TxVariant::RewardTx(tx) => &tx.base,
            TxVariant::BondTx(tx) => &tx.base,
            TxVariant::TransferTx(tx) => &tx.base
        }
    }
}

#[derive(Debug, Clone)]
pub struct Tx {
    pub tx_type: TxType,
    pub timestamp: u32,
    pub fee: Asset,
    pub signature_pairs: Vec<SigPair>
}

impl Tx {
    fn encode_base(&self, v: &mut Vec<u8>) {
        v.push(self.tx_type as u8);
        v.push_u32(self.timestamp);
        v.push_asset(&self.fee);
    }

    fn decode_base(cur: &mut Cursor<&[u8]>) -> Option<Tx> {
        let tx_type = match cur.take_u8()? {
            t if t == TxType::REWARD as u8 => TxType::REWARD,
            t if t == TxType::BOND as u8 => TxType::BOND,
            t if t == TxType::TRANSFER as u8 => TxType::TRANSFER,
            _ => return None
        };
        let timestamp = cur.take_u32()?;
        let fee = cur.take_asset()?;

        Some(Tx {
            tx_type,
            timestamp,
            fee,
            signature_pairs: Vec::new()
        })
    }
}

#[derive(Debug, Clone)]
pub struct RewardTx {
    pub base: Tx,
    pub to: PublicKey,
    pub rewards: Vec<Asset>
}

impl EncodeTx for RewardTx {
    fn encode(&self, v: &mut Vec<u8>) {
        debug_assert_eq!(self.base.signature_pairs.len(), 0);
        self.encode_base(v);
        v.push_pub_key(&self.to);
        v.push_u32(self.rewards.len() as u32);
        for r in &self.rewards { v.push_asset(r) };
    }
}

impl DecodeTx<RewardTx> for RewardTx {
    fn decode(cur: &mut Cursor<&[u8]>, tx: Tx) -> Option<RewardTx> {
        assert_eq!(tx.tx_type, TxType::REWARD);
        let key = cur.take_pub_key()?;

        let len = cur.take_u32()?;
        let mut rewards = Vec::with_capacity(len as usize);
        for _ in 0..len {
            rewards.push(cur.take_asset()?);
        }

        Some(RewardTx {
            base: tx,
            to: key,
            rewards
        })
    }
}

#[derive(Debug, Clone)]
pub struct BondTx {
    pub base: Tx,
    pub minter: PublicKey, // Key that signs blocks
    pub staker: PublicKey, // Hot wallet that receives rewards and stakes its balance
    pub stake_amt: Asset,
    pub bond_fee: Asset
}

impl EncodeTx for BondTx {
    fn encode(&self, v: &mut Vec<u8>) {
        self.encode_base(v);
        v.push_pub_key(&self.minter);
        v.push_pub_key(&self.staker);
        v.push_asset(&self.stake_amt);
        v.push_asset(&self.bond_fee);
    }
}

impl DecodeTx<BondTx> for BondTx {
    fn decode(cur: &mut Cursor<&[u8]>, tx: Tx) -> Option<BondTx> {
        assert_eq!(tx.tx_type, TxType::BOND);
        let minter = cur.take_pub_key()?;
        let staker = cur.take_pub_key()?;
        let stake_amt = cur.take_asset()?;
        let bond_fee = cur.take_asset()?;
        Some(BondTx {
            base: tx,
            minter,
            staker,
            stake_amt,
            bond_fee
        })
    }
}

#[derive(Debug, Clone)]
pub struct TransferTx {
    pub base: Tx,
    pub from: PublicKey,
    pub to: PublicKey,
    pub amount: Asset,
    pub memo: Vec<u8>
}

impl EncodeTx for TransferTx {
    fn encode(&self, v: &mut Vec<u8>) {
        self.encode_base(v);
        v.push_pub_key(&self.from);
        v.push_pub_key(&self.to);
        v.push_asset(&self.amount);
        v.push_bytes(&self.memo);
    }
}

impl DecodeTx<TransferTx> for TransferTx {
    fn decode(cur: &mut Cursor<&[u8]>, tx: Tx) -> Option<TransferTx> {
        assert_eq!(tx.tx_type, TxType::TRANSFER);
        let from = cur.take_pub_key()?;
        let to = cur.take_pub_key()?;
        let amount = cur.take_asset()?;
        let memo = cur.take_bytes()?;
        Some(TransferTx {
            base: tx,
            from,
            to,
            amount,
            memo
        })
    }
}

tx_deref!(RewardTx);
tx_deref!(BondTx);
tx_deref!(TransferTx);

tx_sign!(BondTx);
tx_sign!(TransferTx);

#[cfg(test)]
mod tests {
    use ::std::str::FromStr;
    use super::*;
    use crypto;

    macro_rules! cmp_base_tx {
        ($id:ident, $ty:expr, $ts:expr, $fee:expr) => {
            assert_eq!($id.tx_type, $ty);
            assert_eq!($id.timestamp, $ts);
            assert_eq!($id.fee.to_string(), $fee);
        }
    }

    #[test]
    fn test_encode_tx_with_sigs() {
        let to = crypto::KeyPair::gen_keypair();
        let reward_tx = TxVariant::RewardTx(RewardTx {
            base: Tx {
                tx_type: TxType::REWARD,
                timestamp: 123,
                fee: get_asset("123 GOLD"),
                signature_pairs: vec![]
            },
            to: to.0,
            rewards: vec![get_asset("1.50 GOLD"), get_asset("1.0 SILVER")]
        });

        let mut v = vec![];
        reward_tx.encode_with_sigs(&mut v);

        let mut c = Cursor::<&[u8]>::new(&v);
        TxVariant::decode_with_sigs(&mut c).unwrap();
    }

    #[test]
    fn test_encode_reward() {
        let to = crypto::KeyPair::gen_keypair();
        let reward_tx = RewardTx {
            base: Tx {
                tx_type: TxType::REWARD,
                timestamp: 123,
                fee: get_asset("123 GOLD"),
                signature_pairs: vec![]
            },
            to: to.0,
            rewards: vec![get_asset("1.50 GOLD"), get_asset("1.0 SILVER")]
        };

        let mut v = vec![];
        reward_tx.encode(&mut v);

        let mut c = Cursor::<&[u8]>::new(&v);
        let base = Tx::decode_base(&mut c).unwrap();
        let dec = RewardTx::decode(&mut c, base).unwrap();

        cmp_base_tx!(dec, TxType::REWARD, 123, "123 GOLD");
        assert_eq!(reward_tx.to, dec.to);
        assert_eq!(reward_tx.rewards.len(), dec.rewards.len());
        assert_eq!(reward_tx.rewards[0].to_string(), dec.rewards[0].to_string());
        assert_eq!(reward_tx.rewards[1].to_string(), dec.rewards[1].to_string());
    }

    #[test]
    fn test_encode_bond() {
        let minter = crypto::KeyPair::gen_keypair();
        let staker = crypto::KeyPair::gen_keypair();
        let bond_tx = BondTx {
            base: Tx {
                tx_type: TxType::BOND,
                timestamp: 1230,
                fee: get_asset("123 GOLD"),
                signature_pairs: vec![]
            },
            minter: minter.0,
            staker: staker.0,
            stake_amt: get_asset("1.0456 GOLD"),
            bond_fee: get_asset("1.00000000 GOLD")
        };

        let mut v = vec![];
        bond_tx.encode(&mut v);

        let mut c = Cursor::<&[u8]>::new(&v);
        let base = Tx::decode_base(&mut c).unwrap();
        let dec = BondTx::decode(&mut c, base).unwrap();

        cmp_base_tx!(dec, TxType::BOND, 1230, "123 GOLD");
        assert_eq!(bond_tx.minter, dec.minter);
        assert_eq!(bond_tx.staker, dec.staker);
        assert_eq!(bond_tx.stake_amt.to_string(), dec.stake_amt.to_string());
        assert_eq!(bond_tx.bond_fee.to_string(), dec.bond_fee.to_string());
    }

    #[test]
    fn test_encode_transfer() {
        let from = crypto::KeyPair::gen_keypair();
        let to = crypto::KeyPair::gen_keypair();
        let transfer_tx = TransferTx {
            base: Tx {
                tx_type: TxType::TRANSFER,
                timestamp: 1234567890,
                fee: get_asset("1.23 GOLD"),
                signature_pairs: vec![]
            },
            from: from.0,
            to: to.0,
            amount: get_asset("1.0456 GOLD"),
            memo: Vec::from(b"Hello world!")
        };

        let mut v = vec![];
        transfer_tx.encode(&mut v);

        let mut c = Cursor::<&[u8]>::new(&v);
        let base = Tx::decode_base(&mut c).unwrap();
        let dec = TransferTx::decode(&mut c, base).unwrap();

        cmp_base_tx!(dec, TxType::TRANSFER, 1234567890, "1.23 GOLD");
        assert_eq!(transfer_tx.from, dec.from);
        assert_eq!(transfer_tx.to, dec.to);
        assert_eq!(transfer_tx.amount.to_string(), dec.amount.to_string());
        assert_eq!(transfer_tx.memo, dec.memo);
    }

    fn get_asset(s: &str) -> Asset {
        Asset::from_str(s).unwrap()
    }
}

<|endoftext|>


<commit_before>use itertools::Itertools;
use hir::{Problem, source_binder};
use ra_ide_api_light::Severity;
use ra_db::SourceDatabase;
use ra_syntax::{
    Location, SourceFile, SyntaxKind, TextRange, SyntaxNode,
    ast::{self, AstNode},

};
use ra_text_edit::{TextEdit, TextEditBuilder};

use crate::{Diagnostic, FileId, FileSystemEdit, SourceChange, SourceFileEdit, db::RootDatabase};

pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> {
    let syntax = db.parse(file_id);

    let mut res = syntax_diagnostics(file_id, &syntax);
    if let Some(m) = source_binder::module_from_file_id(db, file_id) {
        for (name_node, problem) in m.problems(db) {
            let source_root = db.file_source_root(file_id);
            let diag = match problem {
                Problem::UnresolvedModule { candidate } => {
                    let create_file =
                        FileSystemEdit::CreateFile { source_root, path: candidate.clone() };
                    let fix = SourceChange {
                        label: "create module".to_string(),
                        source_file_edits: Vec::new(),
                        file_system_edits: vec![create_file],
                        cursor_position: None,
                    };
                    Diagnostic {
                        range: name_node.range(),
                        message: "unresolved module".to_string(),
                        severity: Severity::Error,
                        fix: Some(fix),
                    }
                }
                Problem::NotDirOwner { move_to, candidate } => {
                    let move_file = FileSystemEdit::MoveFile {
                        src: file_id,
                        dst_source_root: source_root,
                        dst_path: move_to.clone(),
                    };
                    let create_file =
                        FileSystemEdit::CreateFile { source_root, path: move_to.join(candidate) };
                    let fix = SourceChange {
                        label: "move file and create module".to_string(),
                        source_file_edits: Vec::new(),
                        file_system_edits: vec![move_file, create_file],
                        cursor_position: None,
                    };
                    Diagnostic {
                        range: name_node.range(),
                        message: "can't declare module at this location".to_string(),
                        severity: Severity::Error,
                        fix: Some(fix),
                    }
                }
            };
            res.push(diag)
        }
    };
    res
}

fn syntax_diagnostics(file_id: FileId, file: &SourceFile) -> Vec<Diagnostic> {
    fn location_to_range(location: Location) -> TextRange {
        match location {
            Location::Offset(offset) => TextRange::offset_len(offset, 1.into()),
            Location::Range(range) => range,
        }
    }

    let mut errors: Vec<Diagnostic> = file
        .errors()
        .into_iter()
        .map(|err| Diagnostic {
            range: location_to_range(err.location()),
            message: format!("Syntax Error: {}", err),
            severity: Severity::Error,
            fix: None,
        })
        .collect();

    for node in file.syntax().descendants() {
        check_unnecessary_braces_in_use_statement(file_id, &mut errors, node);
        check_struct_shorthand_initialization(file_id, &mut errors, node);
    }

    errors
}

fn check_unnecessary_braces_in_use_statement(
    file_id: FileId,
    acc: &mut Vec<Diagnostic>,
    node: &SyntaxNode,
) -> Option<()> {
    let use_tree_list = ast::UseTreeList::cast(node)?;
    if let Some((single_use_tree,)) = use_tree_list.use_trees().collect_tuple() {
        let range = use_tree_list.syntax().range();
        let edit =
            text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(single_use_tree)
                .unwrap_or_else(|| {
                    let to_replace = single_use_tree.syntax().text().to_string();
                    let mut edit_builder = TextEditBuilder::default();
                    edit_builder.delete(range);
                    edit_builder.insert(range.start(), to_replace);
                    edit_builder.finish()
                });

        acc.push(Diagnostic {
            range,
            message: format!("Unnecessary braces in use statement"),
            severity: Severity::WeakWarning,
            fix: Some(SourceChange {
                label: "Remove unnecessary braces".to_string(),
                source_file_edits: vec![SourceFileEdit { file_id, edit }],
                file_system_edits: Vec::new(),
                cursor_position: None,
            }),
        });
    }

    Some(())
}

fn text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(
    single_use_tree: &ast::UseTree,
) -> Option<TextEdit> {
    let use_tree_list_node = single_use_tree.syntax().parent()?;
    if single_use_tree.path()?.segment()?.syntax().first_child()?.kind() == SyntaxKind::SELF_KW {
        let start = use_tree_list_node.prev_sibling()?.range().start();
        let end = use_tree_list_node.range().end();
        let range = TextRange::from_to(start, end);
        let mut edit_builder = TextEditBuilder::default();
        edit_builder.delete(range);
        return Some(edit_builder.finish());
    }
    None
}

fn check_struct_shorthand_initialization(
    file_id: FileId,
    acc: &mut Vec<Diagnostic>,
    node: &SyntaxNode,
) -> Option<()> {
    let struct_lit = ast::StructLit::cast(node)?;
    let named_field_list = struct_lit.named_field_list()?;
    for named_field in named_field_list.fields() {
        if let (Some(name_ref), Some(expr)) = (named_field.name_ref(), named_field.expr()) {
            let field_name = name_ref.syntax().text().to_string();
            let field_expr = expr.syntax().text().to_string();
            if field_name == field_expr {
                let mut edit_builder = TextEditBuilder::default();
                edit_builder.delete(named_field.syntax().range());
                edit_builder.insert(named_field.syntax().range().start(), field_name);
                let edit = edit_builder.finish();

                acc.push(Diagnostic {
                    range: named_field.syntax().range(),
                    message: format!("Shorthand struct initialization"),
                    severity: Severity::WeakWarning,
                    fix: Some(SourceChange {
                        label: "use struct shorthand initialization".to_string(),
                        source_file_edits: vec![SourceFileEdit { file_id, edit }],
                        file_system_edits: Vec::new(),
                        cursor_position: None,
                    }),
                });
            }
        }
    }
    Some(())
}

#[cfg(test)]
mod tests {
    use test_utils::assert_eq_text;

    use super::*;

    type DiagnosticChecker = fn(FileId, &mut Vec<Diagnostic>, &SyntaxNode) -> Option<()>;

    fn check_not_applicable(code: &str, func: DiagnosticChecker) {
        let file = SourceFile::parse(code);
        let mut diagnostics = Vec::new();
        for node in file.syntax().descendants() {
            func(FileId(0), &mut diagnostics, node);
        }
        assert!(diagnostics.is_empty());
    }

    fn check_apply(before: &str, after: &str, func: DiagnosticChecker) {
        let file = SourceFile::parse(before);
        let mut diagnostics = Vec::new();
        for node in file.syntax().descendants() {
            func(FileId(0), &mut diagnostics, node);
        }
        let diagnostic =
            diagnostics.pop().unwrap_or_else(|| panic!("no diagnostics for:\n{}\n", before));
        let mut fix = diagnostic.fix.unwrap();
        let edit = fix.source_file_edits.pop().unwrap().edit;
        let actual = edit.apply(&before);
        assert_eq_text!(after, &actual);
    }

    #[test]
    fn test_check_unnecessary_braces_in_use_statement() {
        check_not_applicable(
            "
            use a;
            use a::{c, d::e};
        ",
            check_unnecessary_braces_in_use_statement,
        );
        check_apply("use {b};", "use b;", check_unnecessary_braces_in_use_statement);
        check_apply("use a::{c};", "use a::c;", check_unnecessary_braces_in_use_statement);
        check_apply("use a::{self};", "use a;", check_unnecessary_braces_in_use_statement);
        check_apply(
            "use a::{c, d::{e}};",
            "use a::{c, d::e};",
            check_unnecessary_braces_in_use_statement,
        );
    }

    #[test]
    fn test_check_struct_shorthand_initialization() {
        check_not_applicable(
            r#"
            struct A {
                a: &'static str
            }

            fn main() {
                A {
                    a: "hello"
                }
            }
        "#,
            check_struct_shorthand_initialization,
        );

        check_apply(
            r#"
struct A {
    a: &'static str
}

fn main() {
    let a = "haha";
    A {
        a: a
    }
}
        "#,
            r#"
struct A {
    a: &'static str
}

fn main() {
    let a = "haha";
    A {
        a
    }
}
        "#,
            check_struct_shorthand_initialization,
        );

        check_apply(
            r#"
struct A {
    a: &'static str,
    b: &'static str
}

fn main() {
    let a = "haha";
    let b = "bb";
    A {
        a: a,
        b
    }
}
        "#,
            r#"
struct A {
    a: &'static str,
    b: &'static str
}

fn main() {
    let a = "haha";
    let b = "bb";
    A {
        a,
        b
    }
}
        "#,
            check_struct_shorthand_initialization,
        );
    }
}
<commit_msg>cleanup<commit_after>use itertools::Itertools;
use hir::{Problem, source_binder};
use ra_ide_api_light::Severity;
use ra_db::SourceDatabase;
use ra_syntax::{
    Location, SourceFile, SyntaxKind, TextRange, SyntaxNode,
    ast::{self, AstNode},

};
use ra_text_edit::{TextEdit, TextEditBuilder};

use crate::{Diagnostic, FileId, FileSystemEdit, SourceChange, SourceFileEdit, db::RootDatabase};

pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> {
    let source_file = db.parse(file_id);
    let mut res = Vec::new();

    syntax_errors(&mut res, &source_file);

    for node in source_file.syntax().descendants() {
        check_unnecessary_braces_in_use_statement(&mut res, file_id, node);
        check_struct_shorthand_initialization(&mut res, file_id, node);
    }

    if let Some(m) = source_binder::module_from_file_id(db, file_id) {
        check_module(&mut res, db, file_id, m);
    };
    res
}

fn syntax_errors(acc: &mut Vec<Diagnostic>, source_file: &SourceFile) {
    fn location_to_range(location: Location) -> TextRange {
        match location {
            Location::Offset(offset) => TextRange::offset_len(offset, 1.into()),
            Location::Range(range) => range,
        }
    }

    acc.extend(source_file.errors().into_iter().map(|err| Diagnostic {
        range: location_to_range(err.location()),
        message: format!("Syntax Error: {}", err),
        severity: Severity::Error,
        fix: None,
    }));
}

fn check_unnecessary_braces_in_use_statement(
    acc: &mut Vec<Diagnostic>,
    file_id: FileId,
    node: &SyntaxNode,
) -> Option<()> {
    let use_tree_list = ast::UseTreeList::cast(node)?;
    if let Some((single_use_tree,)) = use_tree_list.use_trees().collect_tuple() {
        let range = use_tree_list.syntax().range();
        let edit =
            text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(single_use_tree)
                .unwrap_or_else(|| {
                    let to_replace = single_use_tree.syntax().text().to_string();
                    let mut edit_builder = TextEditBuilder::default();
                    edit_builder.delete(range);
                    edit_builder.insert(range.start(), to_replace);
                    edit_builder.finish()
                });

        acc.push(Diagnostic {
            range,
            message: format!("Unnecessary braces in use statement"),
            severity: Severity::WeakWarning,
            fix: Some(SourceChange {
                label: "Remove unnecessary braces".to_string(),
                source_file_edits: vec![SourceFileEdit { file_id, edit }],
                file_system_edits: Vec::new(),
                cursor_position: None,
            }),
        });
    }

    Some(())
}

fn text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(
    single_use_tree: &ast::UseTree,
) -> Option<TextEdit> {
    let use_tree_list_node = single_use_tree.syntax().parent()?;
    if single_use_tree.path()?.segment()?.syntax().first_child()?.kind() == SyntaxKind::SELF_KW {
        let start = use_tree_list_node.prev_sibling()?.range().start();
        let end = use_tree_list_node.range().end();
        let range = TextRange::from_to(start, end);
        let mut edit_builder = TextEditBuilder::default();
        edit_builder.delete(range);
        return Some(edit_builder.finish());
    }
    None
}

fn check_struct_shorthand_initialization(
    acc: &mut Vec<Diagnostic>,
    file_id: FileId,
    node: &SyntaxNode,
) -> Option<()> {
    let struct_lit = ast::StructLit::cast(node)?;
    let named_field_list = struct_lit.named_field_list()?;
    for named_field in named_field_list.fields() {
        if let (Some(name_ref), Some(expr)) = (named_field.name_ref(), named_field.expr()) {
            let field_name = name_ref.syntax().text().to_string();
            let field_expr = expr.syntax().text().to_string();
            if field_name == field_expr {
                let mut edit_builder = TextEditBuilder::default();
                edit_builder.delete(named_field.syntax().range());
                edit_builder.insert(named_field.syntax().range().start(), field_name);
                let edit = edit_builder.finish();

                acc.push(Diagnostic {
                    range: named_field.syntax().range(),
                    message: format!("Shorthand struct initialization"),
                    severity: Severity::WeakWarning,
                    fix: Some(SourceChange {
                        label: "use struct shorthand initialization".to_string(),
                        source_file_edits: vec![SourceFileEdit { file_id, edit }],
                        file_system_edits: Vec::new(),
                        cursor_position: None,
                    }),
                });
            }
        }
    }
    Some(())
}

fn check_module(
    acc: &mut Vec<Diagnostic>,
    db: &RootDatabase,
    file_id: FileId,
    module: hir::Module,
) {
    let source_root = db.file_source_root(file_id);
    for (name_node, problem) in module.problems(db) {
        let diag = match problem {
            Problem::UnresolvedModule { candidate } => {
                let create_file =
                    FileSystemEdit::CreateFile { source_root, path: candidate.clone() };
                let fix = SourceChange {
                    label: "create module".to_string(),
                    source_file_edits: Vec::new(),
                    file_system_edits: vec![create_file],
                    cursor_position: None,
                };
                Diagnostic {
                    range: name_node.range(),
                    message: "unresolved module".to_string(),
                    severity: Severity::Error,
                    fix: Some(fix),
                }
            }
            Problem::NotDirOwner { move_to, candidate } => {
                let move_file = FileSystemEdit::MoveFile {
                    src: file_id,
                    dst_source_root: source_root,
                    dst_path: move_to.clone(),
                };
                let create_file =
                    FileSystemEdit::CreateFile { source_root, path: move_to.join(candidate) };
                let fix = SourceChange {
                    label: "move file and create module".to_string(),
                    source_file_edits: Vec::new(),
                    file_system_edits: vec![move_file, create_file],
                    cursor_position: None,
                };
                Diagnostic {
                    range: name_node.range(),
                    message: "can't declare module at this location".to_string(),
                    severity: Severity::Error,
                    fix: Some(fix),
                }
            }
        };
        acc.push(diag)
    }
}

#[cfg(test)]
mod tests {
    use test_utils::assert_eq_text;

    use super::*;

    type DiagnosticChecker = fn(&mut Vec<Diagnostic>, FileId, &SyntaxNode) -> Option<()>;

    fn check_not_applicable(code: &str, func: DiagnosticChecker) {
        let file = SourceFile::parse(code);
        let mut diagnostics = Vec::new();
        for node in file.syntax().descendants() {
            func(&mut diagnostics, FileId(0), node);
        }
        assert!(diagnostics.is_empty());
    }

    fn check_apply(before: &str, after: &str, func: DiagnosticChecker) {
        let file = SourceFile::parse(before);
        let mut diagnostics = Vec::new();
        for node in file.syntax().descendants() {
            func(&mut diagnostics, FileId(0), node);
        }
        let diagnostic =
            diagnostics.pop().unwrap_or_else(|| panic!("no diagnostics for:\n{}\n", before));
        let mut fix = diagnostic.fix.unwrap();
        let edit = fix.source_file_edits.pop().unwrap().edit;
        let actual = edit.apply(&before);
        assert_eq_text!(after, &actual);
    }

    #[test]
    fn test_check_unnecessary_braces_in_use_statement() {
        check_not_applicable(
            "
            use a;
            use a::{c, d::e};
        ",
            check_unnecessary_braces_in_use_statement,
        );
        check_apply("use {b};", "use b;", check_unnecessary_braces_in_use_statement);
        check_apply("use a::{c};", "use a::c;", check_unnecessary_braces_in_use_statement);
        check_apply("use a::{self};", "use a;", check_unnecessary_braces_in_use_statement);
        check_apply(
            "use a::{c, d::{e}};",
            "use a::{c, d::e};",
            check_unnecessary_braces_in_use_statement,
        );
    }

    #[test]
    fn test_check_struct_shorthand_initialization() {
        check_not_applicable(
            r#"
            struct A {
                a: &'static str
            }

            fn main() {
                A {
                    a: "hello"
                }
            }
        "#,
            check_struct_shorthand_initialization,
        );

        check_apply(
            r#"
struct A {
    a: &'static str
}

fn main() {
    let a = "haha";
    A {
        a: a
    }
}
        "#,
            r#"
struct A {
    a: &'static str
}

fn main() {
    let a = "haha";
    A {
        a
    }
}
        "#,
            check_struct_shorthand_initialization,
        );

        check_apply(
            r#"
struct A {
    a: &'static str,
    b: &'static str
}

fn main() {
    let a = "haha";
    let b = "bb";
    A {
        a: a,
        b
    }
}
        "#,
            r#"
struct A {
    a: &'static str,
    b: &'static str
}

fn main() {
    let a = "haha";
    let b = "bb";
    A {
        a,
        b
    }
}
        "#,
            check_struct_shorthand_initialization,
        );
    }
}
<|endoftext|>


<commit_before>/* diosix capsule-provided service management

 * (c) Chris Williams, 2019-2020.
 *
 * See LICENSE for usage and copying.
 */

use super::lock::Mutex;
use hashbrown::hash_map::{HashMap, Entry};
use alloc::collections::vec_deque::VecDeque;
use super::message;
use super::error::Cause;
use super::capsule::{self, CapsuleID};

pub type ServiceID = usize;

/* todo: a fixed list of known system services,
such as video, sound, serial, network, etc
that privileged / trusted capsules can register.
then other capsules can message those services
to access those underlying resources. */

/* maintain a table of registered services */
lazy_static!
{
    static ref SERVICES: Mutex<HashMap<ServiceID, Service>> = Mutex::new("system service table", HashMap::new());
}

/* describe an individual service */
struct Service
{
    capsuleid: CapsuleID,       /* capsule that's registered this service */
    msgs: VecDeque<message::Message>  /* queue of messages to deliver to service */
}

impl Service
{
    pub fn queue(&mut self, msg: message::Message)
    {
        self.msgs.push_front(msg);
    }
}

/* register a service for a capsule. this will fail if the service is
   already registered or if the capsule has no right to run the service
   or if the capsule doesn't exist.
    => sid = ID of service to register
       cid = ID of capsule to handle this service
    <= return Ok for success, or a failure code */
pub fn register(sid: ServiceID, cid: CapsuleID) -> Result<(), Cause>
{
    match capsule::is_service_allowed(cid, sid)
    {
        Some(flag) => if flag == false
        {
            return Err(Cause::ServiceNotAllowed);
        },
        None => return Err(Cause::CapsuleBadID)
    };

    let service = Service
    {
        capsuleid: cid,
        msgs: VecDeque::new()
    };

    /* ensure we do not double register a service */
    if let Entry::Vacant(v) = SERVICES.lock().entry(sid)
    {
        v.insert(service);
        return Ok(());
    }
    else
    {
        return Err(Cause::ServiceAlreadyRegistered)
    }
}

/* deregister a service so that its capsule is no longer responsible for it
   => sid = service to deregister
   <= Ok for success, or an error code for failure */
pub fn deregister(sid: ServiceID) -> Result<(), Cause>
{
    match SERVICES.lock().remove(&sid)
    {
        None => Err(Cause::ServiceNotFound),
        _ => Ok(())
    }
}

/* send the given message msg to a registered service */
pub fn send(msg: message::Message) -> Result<(), Cause>
{
    let sid = match msg.get_receiver()
    {
        message::Recipient::Service(sid) => sid,
        _ => return Err(Cause::MessageBadType)
    };

    if let Some(service) = SERVICES.lock().get_mut(&sid)
    {
        service.queue(msg);
        Ok(())
    }
    else
    {
        return Err(Cause::ServiceNotAllowed)
    }
}<commit_msg>Use an enum to define service types<commit_after>/* diosix capsule-provided service management

 * (c) Chris Williams, 2019-2020.
 *
 * See LICENSE for usage and copying.
 */

use super::lock::Mutex;
use hashbrown::hash_map::{HashMap, Entry};
use alloc::collections::vec_deque::VecDeque;
use alloc::vec::Vec;
use super::message;
use super::error::Cause;
use super::capsule::{self, CapsuleID};

/* available type of services that can be offered by a capsule */
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum ServiceType
{
    ConsoleInterface = 0 /* act as the console interface manager */
}

/* select either a particular service or all services */
pub enum SelectService
{
    AllServices,
    SingleService(ServiceType)
}

/* todo: a fixed list of known system services,
such as video, sound, serial, network, etc
that privileged / trusted capsules can register.
then other capsules can message those services
to access those underlying resources. */

/* maintain a table of registered services */
lazy_static!
{
    static ref SERVICES: Mutex<HashMap<ServiceType, Service>> = Mutex::new("system service table", HashMap::new());
}

/* return true if the given service type is registered */
pub fn is_registered(stype: ServiceType) -> bool
{
    let tbl = SERVICES.lock();
    tbl.contains_key(&stype)
}

/* describe an individual service */
struct Service
{
    capsuleid: CapsuleID,       /* capsule that's registered this service */
    msgs: VecDeque<message::Message>  /* queue of messages to deliver to service */
}

impl Service
{
    pub fn queue(&mut self, msg: message::Message)
    {
        self.msgs.push_front(msg);
    }

    pub fn get_capsule_id(&self) -> CapsuleID { self.capsuleid }
}

/* register a service for a capsule. this will fail if the
   capsule has no right to run the service, or if the capsule doesn't exist,
   or if another capsule has already claimed the service type.
   be aware if the capsule has already claimed the service, it will
   return Err(Cause::ServiceAlreadyOwner). this will be the result if a
   restarted capsule registers its service(s) again. services aren't released
   during a restart to provide a non-stop continuation of services.
    => stype = type of service to register
       cid = ID of capsule to handle this service
    <= return Ok for success, or a failure code */
pub fn register(stype: ServiceType, cid: CapsuleID) -> Result<(), Cause>
{
    if capsule::is_service_allowed(cid, stype)? == false
    {
        return Err(Cause::ServiceNotAllowed);
    }

    let service = Service
    {
        capsuleid: cid,
        msgs: VecDeque::new()
    };

    match SERVICES.lock().entry(stype)
    {
        Entry::Vacant(v) =>
        {
            v.insert(service);
        },
        Entry::Occupied(o) => if o.get().get_capsule_id() != cid
        {
            /* another capsule owns this service */
            return Err(Cause::ServiceAlreadyRegistered);
        }
        else
        {
            /* this capsule already owns this service */
            return Err(Cause::ServiceAlreadyOwner)
        }
    }

    Ok(())
}

/* deregister one or all services belonding to a capsule
   so that it is no longer responsible for them
   => stype = service to deregister, or None for all of them
      cid = ID of capsule to strip of its services
   <= Ok for success, or an error code for failure */
pub fn deregister(stype: SelectService, cid: CapsuleID) -> Result<(), Cause>
{
    let mut tbl = SERVICES.lock();
    let mut to_remove = Vec::new();

    for (registered, owner) in (&tbl).iter()
    {
        /* remove either everything that matches the capsule ID, or a particular service */
        match stype
        {
            SelectService::AllServices => if owner.get_capsule_id() == cid
            {
                to_remove.push(*registered);
            },
            SelectService::SingleService(s) => if owner.get_capsule_id() == cid && s == *registered
            {
                to_remove.push(*registered);
            }
        }
    }

    /* now remove the vicims */
    for victim in to_remove
    {
        tbl.remove(&victim);
    }

    Ok(())
}

/* send the given message msg to a registered service */
pub fn send(msg: message::Message) -> Result<(), Cause>
{
    let stype = match msg.get_receiver()
    {
        message::Recipient::Service(stype) => stype,
        _ => return Err(Cause::MessageBadType)
    };

    if let Some(service) = SERVICES.lock().get_mut(&stype)
    {
        service.queue(msg);
        Ok(())
    }
    else
    {
        return Err(Cause::ServiceNotAllowed)
    }
}<|endoftext|>


<commit_before>
extern crate lapack;

use self::lapack::fortran::*;
use error::LapackError;
use ndarray::LinalgScalar;

/// Eigenvalue decomposition for Hermite matrix
pub trait LapackScalar: LinalgScalar {
    /// execute *syev subroutine
    fn eigh(row_size: usize, matrix: Vec<Self>) -> Result<(Vec<Self>, Vec<Self>), LapackError>;
    fn inv(size: usize, matrix: Vec<Self>) -> Result<Vec<Self>, LapackError>;
    fn norm_1(rows: usize, cols: usize, matrix: Vec<Self>) -> Self;
    fn norm_i(rows: usize, cols: usize, matrix: Vec<Self>) -> Self;
    fn norm_f(rows: usize, cols: usize, matrix: Vec<Self>) -> Self;
}

impl LapackScalar for f64 {
    fn eigh(n: usize, mut a: Vec<Self>) -> Result<(Vec<Self>, Vec<Self>), LapackError> {
        let mut w = vec![0.0; n ];
        let mut work = vec![0.0; 4 * n ];
        let mut info = 0;
        dsyev(b'V',
              b'U',
              n as i32,
              &mut a,
              n as i32,
              &mut w,
              &mut work,
              4 * n as i32,
              &mut info);
        if info == 0 {
            Ok((w, a))
        } else {
            Err(From::from(info))
        }
    }
    fn inv(size: usize, mut a: Vec<Self>) -> Result<Vec<Self>, LapackError> {
        let n = size as i32;
        let lda = n;
        let mut ipiv = vec![0; size];
        let mut info = 0;
        dgetrf(n, n, &mut a, lda, &mut ipiv, &mut info);
        if info != 0 {
            return Err(From::from(info));
        }
        let lwork = n;
        let mut work = vec![0.0; size];
        dgetri(n, &mut a, lda, &mut ipiv, &mut work, lwork, &mut info);
        if info == 0 {
            Ok(a)
        } else {
            Err(From::from(info))
        }
    }
    fn norm_1(m: usize, n: usize, mut a: Vec<Self>) -> Self {
        let mut work = Vec::<Self>::new();
        dlange(b'o', m as i32, n as i32, &mut a, m as i32, &mut work)
    }
    fn norm_i(m: usize, n: usize, mut a: Vec<Self>) -> Self {
        let mut work = vec![0.0; m];
        dlange(b'i', m as i32, n as i32, &mut a, m as i32, &mut work)
    }
    fn norm_f(m: usize, n: usize, mut a: Vec<Self>) -> Self {
        let mut work = Vec::<Self>::new();
        dlange(b'f', m as i32, n as i32, &mut a, m as i32, &mut work)
    }
}

impl LapackScalar for f32 {
    fn eigh(n: usize, mut a: Vec<Self>) -> Result<(Vec<Self>, Vec<Self>), LapackError> {
        let mut w = vec![0.0; n];
        let mut work = vec![0.0; 4 * n];
        let mut info = 0;
        ssyev(b'V',
              b'U',
              n as i32,
              &mut a,
              n as i32,
              &mut w,
              &mut work,
              4 * n as i32,
              &mut info);
        if info == 0 {
            Ok((w, a))
        } else {
            Err(From::from(info))
        }
    }
    fn inv(size: usize, matrix: Vec<Self>) -> Result<Vec<Self>, LapackError> {
        panic!("Not implemented.");
        Ok(matrix)
    }
    fn norm_1(m: usize, n: usize, mut a: Vec<Self>) -> Self {
        let mut work = Vec::<Self>::new();
        slange(b'o', m as i32, n as i32, &mut a, m as i32, &mut work)
    }
    fn norm_i(m: usize, n: usize, mut a: Vec<Self>) -> Self {
        let mut work = vec![0.0; m];
        slange(b'i', m as i32, n as i32, &mut a, m as i32, &mut work)
    }
    fn norm_f(m: usize, n: usize, mut a: Vec<Self>) -> Self {
        let mut work = Vec::<Self>::new();
        slange(b'f', m as i32, n as i32, &mut a, m as i32, &mut work)
    }
}
<commit_msg>Use provided function for eigh et al.<commit_after>
extern crate lapack;

use self::lapack::fortran::*;
use error::LapackError;
use ndarray::LinalgScalar;

pub trait LapackScalar: LinalgScalar {
    fn syev(jobz: u8,
            uplo: u8,
            n: i32,
            a: &mut Vec<Self>,
            lda: i32,
            w: &mut Vec<Self>,
            work: &mut Vec<Self>,
            lwork: i32,
            info: &mut i32);
    fn getrf(m: i32, n: i32, a: &mut Vec<Self>, lda: i32, ipiv: &mut [i32], info: &mut i32);
    fn getri(n: i32,
             a: &mut Vec<Self>,
             lda: i32,
             ipiv: &[i32],
             work: &mut Vec<Self>,
             lwork: i32,
             info: &mut i32);
    fn lange(norm: u8, m: i32, n: i32, a: &Vec<Self>, lda: i32, work: &mut Vec<Self>) -> Self;

    fn eigh(n: usize, mut a: Vec<Self>) -> Result<(Vec<Self>, Vec<Self>), LapackError> {
        let mut w = vec![Self::zero(); n];
        let mut work = vec![Self::zero(); 4 * n];
        let mut info = 0;
        Self::syev(b'V',
                   b'U',
                   n as i32,
                   &mut a,
                   n as i32,
                   &mut w,
                   &mut work,
                   4 * n as i32,
                   &mut info);
        if info == 0 {
            Ok((w, a))
        } else {
            Err(From::from(info))
        }
    }
    fn inv(size: usize, mut a: Vec<Self>) -> Result<Vec<Self>, LapackError> {
        let n = size as i32;
        let lda = n;
        let mut ipiv = vec![0; size];
        let mut info = 0;
        Self::getrf(n, n, &mut a, lda, &mut ipiv, &mut info);
        if info != 0 {
            return Err(From::from(info));
        }
        let lwork = n;
        let mut work = vec![Self::zero(); size];
        Self::getri(n, &mut a, lda, &mut ipiv, &mut work, lwork, &mut info);
        if info == 0 {
            Ok(a)
        } else {
            Err(From::from(info))
        }
    }
    fn norm_1(m: usize, n: usize, mut a: Vec<Self>) -> Self {
        let mut work = Vec::<Self>::new();
        Self::lange(b'o', m as i32, n as i32, &mut a, m as i32, &mut work)
    }
    fn norm_i(m: usize, n: usize, mut a: Vec<Self>) -> Self {
        let mut work = vec![Self::zero(); m];
        Self::lange(b'i', m as i32, n as i32, &mut a, m as i32, &mut work)
    }
    fn norm_f(m: usize, n: usize, mut a: Vec<Self>) -> Self {
        let mut work = Vec::<Self>::new();
        Self::lange(b'f', m as i32, n as i32, &mut a, m as i32, &mut work)
    }
}

impl LapackScalar for f64 {
    fn syev(jobz: u8,
            uplo: u8,
            n: i32,
            a: &mut Vec<Self>,
            lda: i32,
            w: &mut Vec<Self>,
            work: &mut Vec<Self>,
            lwork: i32,
            info: &mut i32) {
        dsyev(jobz, uplo, n, a, lda, w, work, lwork, info);
    }
    fn getrf(m: i32, n: i32, a: &mut Vec<Self>, lda: i32, ipiv: &mut [i32], info: &mut i32) {
        dgetrf(m, n, a, lda, ipiv, info);
    }
    fn getri(n: i32,
             a: &mut Vec<Self>,
             lda: i32,
             ipiv: &[i32],
             work: &mut Vec<Self>,
             lwork: i32,
             info: &mut i32) {
        dgetri(n, a, lda, ipiv, work, lwork, info);
    }
    fn lange(norm: u8, m: i32, n: i32, a: &Vec<Self>, lda: i32, work: &mut Vec<Self>) -> Self {
        dlange(norm, m, n, a, lda, work)
    }
}
<|endoftext|>


<commit_before>
extern crate regex;
use mysql::conn::pool::{MyPool,MyPooledConn};
use mysql::conn::Stmt;

use std::process::{Command, Stdio, Child};
use std::error::Error;
use std::io::prelude::*;
use std::io::BufReader;
use std::path::Path;
use lib::config::ConfigGen;
use std::convert::Into;
use lib::config::ConfigCodecs;

use lib::DownloadError;

macro_rules! regex(
    ($s:expr) => (regex::Regex::new($s).unwrap());
);

#[derive(Clone)]
pub struct DownloadDB<'a> {
    pub url: String,
    pub quality: i16,
    pub playlist: bool,
    pub compress: bool,
    pub codecs: &'a ConfigCodecs,
    pub qid: i64,
    pub folder: String, // download folder, changes for playlists
    pub pool: MyPool,
}

pub struct Downloader<'a> {
    pub ddb: &'a DownloadDB<'a>,
    defaults: &'a ConfigGen,
    // pool: MyPool,
}

impl<'a> Downloader<'a>{
    pub fn new(ddb: &'a DownloadDB<'a>, defaults: &'a ConfigGen) -> Downloader<'a>{
        Downloader {ddb: ddb, defaults: defaults}
    }

    ///Regex matching: [download]  13.4% of 275.27MiB at 525.36KiB/s ETA 07:52
    ///
    ///Downloads a video, updates the DB
    ///TODO: get the sql statements out of the class
    ///TODO: wrap errors
    ///Doesn't care about DMCAs, will emit errors on them
    ///download_audio: ignore quality & download config set audio for split containers
    pub fn download_file(&self, file_path: &str, download_audio: bool) -> Result<bool,DownloadError> {
        println!("{:?}", self.ddb.url);
        let curr_quality = if &self.ddb.quality == &self.ddb.codecs.audio_mp3_alias {
            &self.ddb.codecs.audio_mp3_source
        }else{
            &self.ddb.quality
        };

        println!("quality: {}",curr_quality);
        let mut child = try!(self.run_download_process(file_path,curr_quality));
        let stdout = BufReader::new(child.stdout.take().unwrap());

        let mut conn = self.ddb.pool.get_conn().unwrap();
        let mut statement = self.prepare_progress_updater(&mut conn);
        let re = regex!(r"\d+\.\d");

        for line in stdout.lines(){
            match line{
                Err(why) => panic!("couldn't read cmd stdout: {}", Error::description(&why)),
                Ok(text) => {
                        //println!("Out: {}",text);
                        match re.find(&text) {
                            Some(s) => { //println!("Match at {}", s.0);
                                        println!("{}", &text[s.0..s.1]); // ONLY with ASCII chars makeable!
                                        try!(self.update_progress(&mut statement, &text[s.0..s.1].to_string()));
                                    },
                            None => {/*println!("Detected no % match.")*/},
                        }
                    },
            }
        }

        try!(child.wait()); // waits for finish & then exists zombi process fixes #10

        Ok(true)
    }

    ///Trys to get the original name of a file, while checking for availability
    ///
    pub fn get_file_name(&self) -> Result<String,DownloadError> {
        let mut child = try!(self.run_filename_process());
        let mut stdout_buffer = BufReader::new(child.stdout.take().unwrap());
        let mut stderr_buffer = BufReader::new(child.stderr.take().unwrap());

        let mut stdout: String = String::new();
        try!(stdout_buffer.read_to_string(&mut stdout));
        let mut stderr: String = String::new();
        try!(stderr_buffer.read_to_string(&mut stderr));

        try!(child.wait());
        //println!("stderr: {:?}", stderr);
        //println!("stdout: {:?}", stdout);
        if stderr.is_empty() == true {
            println!("get_file_name: {:?}", stdout);
            Ok(stdout.trim().to_string())
        }else{
            if stderr.contains("not available in your country") || stderr.contains("contains content from") {
                return Err(DownloadError::DMCAError);
            }else{
                return Err(DownloadError::DownloadError(stderr));
            }
        }
    }

    fn run_download_process(&self, file_path: &str, quality: &i16) -> Result<Child,DownloadError> {
        match Command::new("youtube-dl")
                                    .arg("--newline")
                                    .arg("-r")
                                    .arg(format!("{}M",self.defaults.download_mbps))
                                    .arg("-f")
                                    .arg(quality.to_string())
                                    .arg("-o")
                                    .arg(file_path)
                                    .arg(&self.ddb.url)
                                    .stdin(Stdio::null())
                                    .stdout(Stdio::piped())
                                    .spawn() {
            Err(why) => Err(DownloadError::InternalError(Error::description(&why).into())),
            Ok(process) => Ok(process),
        }
    }

    fn run_filename_process(&self) -> Result<Child,DownloadError> {
        match Command::new("youtube-dl")
                                    .arg("--get-filename")
                                    .arg("-o")
                                    .arg("%(title)s")
                                    .arg(&self.ddb.url)
                                    .stdin(Stdio::null())
                                    .stdout(Stdio::piped())
                                    .stderr(Stdio::piped())
                                    .spawn() {
            Err(why) => Err(DownloadError::InternalError(Error::description(&why).into())),
            Ok(process) => Ok(process),
        }
    }

    // MyPooledConn does only live when MyOpts is alive -> lifetime needs to be declared
    fn prepare_progress_updater(&'a self,conn: &'a mut MyPooledConn) -> Stmt<'a> { // no livetime needed: struct livetime used
        conn.prepare("UPDATE querydetails SET progress = ? WHERE qid = ?").unwrap()
    }

    ///updater called from the stdout progress
    fn update_progress(&self,stmt: &mut Stmt, progress: &String) -> Result<(),DownloadError>{
        try!(stmt.execute(&[progress,&self.ddb.qid]).map(|_| Ok(())))
        //-> only return errors, ignore the return value of stmt.execute
    }

    ///This function does a 3rd party binding in case it's needed
    ///due to the country restrictions
    ///Because hyper doesn't support timeout settings atm, we're calling an external
    ///lib
    ///The returned value contains the original video name, the lib downloads & saves
    ///the file at the given folder under the given name
    pub fn lib_request_video(&self) -> Result<String,DownloadError> {
        let mut child = try!(self.lib_request_video_cmd());
        println!("Requesting video via lib..");
        let mut stdout_buffer = BufReader::new(child.stdout.take().unwrap());
        let mut stderr_buffer = BufReader::new(child.stderr.take().unwrap());

        let mut stdout: String = String::new();
        try!(stdout_buffer.read_to_string(&mut stdout));
        let mut stderr: String = String::new();
        try!(stderr_buffer.read_to_string(&mut stderr));

        try!(child.wait());

        if !stderr.is_empty() {
            println!("stderr: {:?}", stderr);
            println!("stdout: {}",stdout);
            return Err(DownloadError::InternalError(stderr));
        }
        //this ONLY works because `filename ` is ascii..
        let out = stdout[stdout.find("filename ").unwrap()+9..].trim().to_string();
        //stdout.trim();

        Ok(out)
    }

    ///Generate the lib-cmd `request [..]?v=asdf -folder /downloads -a -name testfile`
    fn lib_request_video_cmd(&self) -> Result<Child,DownloadError> {
        let java_path = Path::new(&self.defaults.jar_cmd);
        println!("{:?}", format!("{}/java -jar {}/offliberty.jar",self.defaults.jar_cmd,self.defaults.jar_folder));
        match Command::new("./java")
                                        .current_dir(java_path)
                                        .arg("-jar")
                                        .arg(format!("{}/offliberty.jar",&self.defaults.jar_folder))
                                        .arg("request")
                                        .arg(&self.ddb.url)
                                        .arg("-folder")
                                        .arg(&self.ddb.folder)
                                        .arg(self.gen_request_str())
                                        .arg("-name")
                                        .arg(self.ddb.qid.to_string()) //eq. format! https://botbot.me/mozilla/rust/msg/37524131/
                                        .stdin(Stdio::null())
                                        .stdout(Stdio::piped())
                                        .stderr(Stdio::piped())
                                        .spawn() {
                Err(why) => {println!("{:?}",why); Err(DownloadError::InternalError(Error::description(&why).into()))},
                Ok(process) => Ok(process),
            }
    }

    ///Generate -a or -v, based on if an audio or video quality is requested
    fn gen_request_str(&self) -> &'a str{
        if self.is_audio() {
            "-a"
        } else {
            "-v"
        }
    }

    ///Check if the quality is 141, standing for audio or not
    pub fn is_audio(&self) -> bool {
        match self.ddb.quality {
            k if(k == self.ddb.codecs.audio_raw_hq) => true,
            k if(k == self.ddb.codecs.audio_raw_mq) => true,
            k if(k == self.ddb.codecs.audio_mp3_alias) => true,
            _ => false,
        }
    }

    ///Return wether the audio file is m4a or not
    pub fn is_m4a(&self) -> bool {
        match self.ddb.quality {
            k if(k == self.ddb.codecs.audio_mp3_alias) => false,
            _ => true,
        }
    }
}

#[test]
fn conversion(){

}<commit_msg>*changed is_audio according to the new itags +audio quality settings<commit_after>
extern crate regex;
use mysql::conn::pool::{MyPool,MyPooledConn};
use mysql::conn::Stmt;

use std::process::{Command, Stdio, Child};
use std::error::Error;
use std::io::prelude::*;
use std::io::BufReader;
use std::path::Path;
use lib::config::ConfigGen;
use std::convert::Into;
use lib::config::ConfigCodecs;
use lib::config::Extensions;

use lib::DownloadError;

macro_rules! regex(
    ($s:expr) => (regex::Regex::new($s).unwrap());
);

#[derive(Clone)]
pub struct DownloadDB<'a> {
    pub url: String,
    pub quality: i16,
    pub playlist: bool,
    pub compress: bool,
    pub codecs: &'a ConfigCodecs,
    pub extensions: &'a Extensions,
    pub qid: i64,
    pub folder: String, // download folder, changes for playlists
    pub pool: MyPool,
}

pub struct Downloader<'a> {
    pub ddb: &'a DownloadDB<'a>,
    defaults: &'a ConfigGen,
    // pool: MyPool,
}

impl<'a> Downloader<'a>{
    pub fn new(ddb: &'a DownloadDB<'a>, defaults: &'a ConfigGen) -> Downloader<'a>{
        Downloader {ddb: ddb, defaults: defaults}
    }

    ///Regex matching: [download]  13.4% of 275.27MiB at 525.36KiB/s ETA 07:52
    ///
    ///Downloads a video, updates the DB
    ///TODO: get the sql statements out of the class
    ///TODO: wrap errors
    ///Doesn't care about DMCAs, will emit errors on them
    ///download_audio: ignore quality & download audio_raw codec for split containers
    pub fn download_file(&self, file_path: &str, download_audio: bool) -> Result<bool,DownloadError> {
        println!("{:?}", self.ddb.url);
        let curr_quality = if &self.ddb.quality == &self.ddb.codecs.audio_mp3_alias {
            &self.ddb.codecs.audio_mp3_source
        }else{
            if download_audio {
                &self.ddb.codecs.audio_raw
            }else{
                &self.ddb.quality
            }
        };

        println!("quality: {}",curr_quality);
        let mut child = try!(self.run_download_process(file_path,curr_quality));
        let stdout = BufReader::new(child.stdout.take().unwrap());

        let mut conn = self.ddb.pool.get_conn().unwrap();
        let mut statement = self.prepare_progress_updater(&mut conn);
        let re = regex!(r"\d+\.\d");

        for line in stdout.lines(){
            match line{
                Err(why) => panic!("couldn't read cmd stdout: {}", Error::description(&why)),
                Ok(text) => {
                        //println!("Out: {}",text);
                        match re.find(&text) {
                            Some(s) => { //println!("Match at {}", s.0);
                                        println!("{}", &text[s.0..s.1]); // ONLY with ASCII chars makeable!
                                        try!(self.update_progress(&mut statement, &text[s.0..s.1].to_string()));
                                    },
                            None => {/*println!("Detected no % match.")*/},
                        }
                    },
            }
        }

        try!(child.wait()); // waits for finish & then exists zombi process fixes #10

        Ok(true)
    }

    ///Trys to get the original name of a file, while checking for availability
    ///
    pub fn get_file_name(&self) -> Result<String,DownloadError> {
        let mut child = try!(self.run_filename_process());
        let mut stdout_buffer = BufReader::new(child.stdout.take().unwrap());
        let mut stderr_buffer = BufReader::new(child.stderr.take().unwrap());

        let mut stdout: String = String::new();
        try!(stdout_buffer.read_to_string(&mut stdout));
        let mut stderr: String = String::new();
        try!(stderr_buffer.read_to_string(&mut stderr));

        try!(child.wait());
        //println!("stderr: {:?}", stderr);
        //println!("stdout: {:?}", stdout);
        if stderr.is_empty() == true {
            println!("get_file_name: {:?}", stdout);
            Ok(stdout.trim().to_string())
        }else{
            if stderr.contains("not available in your country") || stderr.contains("contains content from") {
                return Err(DownloadError::DMCAError);
            }else{
                return Err(DownloadError::DownloadError(stderr));
            }
        }
    }

    fn run_download_process(&self, file_path: &str, quality: &i16) -> Result<Child,DownloadError> {
        match Command::new("youtube-dl")
                                    .arg("--newline")
                                    .arg("-r")
                                    .arg(format!("{}M",self.defaults.download_mbps))
                                    .arg("-f")
                                    .arg(quality.to_string())
                                    .arg("-o")
                                    .arg(file_path)
                                    .arg(&self.ddb.url)
                                    .stdin(Stdio::null())
                                    .stdout(Stdio::piped())
                                    .spawn() {
            Err(why) => Err(DownloadError::InternalError(Error::description(&why).into())),
            Ok(process) => Ok(process),
        }
    }

    fn run_filename_process(&self) -> Result<Child,DownloadError> {
        match Command::new("youtube-dl")
                                    .arg("--get-filename")
                                    .arg("-o")
                                    .arg("%(title)s")
                                    .arg(&self.ddb.url)
                                    .stdin(Stdio::null())
                                    .stdout(Stdio::piped())
                                    .stderr(Stdio::piped())
                                    .spawn() {
            Err(why) => Err(DownloadError::InternalError(Error::description(&why).into())),
            Ok(process) => Ok(process),
        }
    }

    // MyPooledConn does only live when MyOpts is alive -> lifetime needs to be declared
    fn prepare_progress_updater(&'a self,conn: &'a mut MyPooledConn) -> Stmt<'a> { // no livetime needed: struct livetime used
        conn.prepare("UPDATE querydetails SET progress = ? WHERE qid = ?").unwrap()
    }

    ///updater called from the stdout progress
    fn update_progress(&self,stmt: &mut Stmt, progress: &String) -> Result<(),DownloadError>{
        try!(stmt.execute(&[progress,&self.ddb.qid]).map(|_| Ok(())))
        //-> only return errors, ignore the return value of stmt.execute
    }

    ///This function does a 3rd party binding in case it's needed
    ///due to the country restrictions
    ///Because hyper doesn't support timeout settings atm, we're calling an external
    ///lib
    ///The returned value contains the original video name, the lib downloads & saves
    ///the file at the given folder under the given name
    pub fn lib_request_video(&self) -> Result<String,DownloadError> {
        let mut child = try!(self.lib_request_video_cmd());
        println!("Requesting video via lib..");
        let mut stdout_buffer = BufReader::new(child.stdout.take().unwrap());
        let mut stderr_buffer = BufReader::new(child.stderr.take().unwrap());

        let mut stdout: String = String::new();
        try!(stdout_buffer.read_to_string(&mut stdout));
        let mut stderr: String = String::new();
        try!(stderr_buffer.read_to_string(&mut stderr));

        try!(child.wait());

        if !stderr.is_empty() {
            println!("stderr: {:?}", stderr);
            println!("stdout: {}",stdout);
            return Err(DownloadError::InternalError(stderr));
        }
        //this ONLY works because `filename ` is ascii..
        let out = stdout[stdout.find("filename ").unwrap()+9..].trim().to_string();
        //stdout.trim();

        Ok(out)
    }

    ///Generate the lib-cmd `request [..]?v=asdf -folder /downloads -a -name testfile`
    fn lib_request_video_cmd(&self) -> Result<Child,DownloadError> {
        let java_path = Path::new(&self.defaults.jar_cmd);
        println!("{:?}", format!("{}/java -jar {}/offliberty.jar",self.defaults.jar_cmd,self.defaults.jar_folder));
        match Command::new("./java")
                                        .current_dir(java_path)
                                        .arg("-jar")
                                        .arg(format!("{}/offliberty.jar",&self.defaults.jar_folder))
                                        .arg("request")
                                        .arg(&self.ddb.url)
                                        .arg("-folder")
                                        .arg(&self.ddb.folder)
                                        .arg(self.gen_request_str())
                                        .arg("-name")
                                        .arg(self.ddb.qid.to_string()) //eq. format! https://botbot.me/mozilla/rust/msg/37524131/
                                        .stdin(Stdio::null())
                                        .stdout(Stdio::piped())
                                        .stderr(Stdio::piped())
                                        .spawn() {
                Err(why) => {println!("{:?}",why); Err(DownloadError::InternalError(Error::description(&why).into()))},
                Ok(process) => Ok(process),
            }
    }

    ///Generate -a or -v, based on if an audio or video quality is requested
    fn gen_request_str(&self) -> &'a str{
        if self.is_audio() {
            "-a"
        } else {
            "-v"
        }
    }

    ///Check if the quality is 141, standing for audio or not
    pub fn is_audio(&self) -> bool {
        if self.ddb.quality == self.ddb.codecs.audio_raw {
            true
        } else if self.ddb.quality == self.ddb.codecs.audio_source_hq {
            true
        }else {
            false
        }
    }
}

#[test]
fn conversion(){

}<|endoftext|>


<commit_before>use flowgger::config::Config;
use flowgger::merger::Merger;
use openssl::bn::BigNum;
use openssl::dh::DH;
use openssl::ssl::*;
use openssl::x509::X509FileType;
use std::error::Error;
use std::io;
use std::io::{stderr, BufWriter, ErrorKind, Write};
use std::net::TcpStream;
use std::path::{Path, PathBuf};
use std::sync::mpsc::Receiver;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
use super::Output;

const DEFAULT_CIPHERS: &'static str = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA";
const DEFAULT_CONNECT: &'static str = "127.0.0.1:6514";
const DEFAULT_SLEEP_AFTER_CONNECTION_FAILURE: u32 = 1000;
const TLS_DEFAULT_TIMEOUT: u64 = 3600;
const TLS_DEFAULT_THREADS: u32 = 1;

pub struct TlsOutput {
    config: TlsConfig,
    threads: u32
}

#[derive(Clone)]
struct TlsConfig {
    timeout: Option<Duration>,
    connect: String
}

struct TlsWorker {
    arx: Arc<Mutex<Receiver<Vec<u8>>>>,
    merger: Option<Box<Merger + Send>>,
    config: TlsConfig
}

impl TlsWorker {
    fn new(arx: Arc<Mutex<Receiver<Vec<u8>>>>, merger: Option<Box<Merger + Send>>, config: TlsConfig) -> TlsWorker {
        TlsWorker {
            arx: arx,
            merger: merger,
            config: config
        }
    }

    fn handle_connection(&self, connect: &str) -> io::Result<()> {
        let client = try!(new_tcp(connect));
        let _ = writeln!(stderr(), "Connected to {}", connect);
        let tls_method = SslMethod::Sslv23;
        let mut ctx = SslContext::new(tls_method).unwrap();
        let ciphers = DEFAULT_CIPHERS;
        set_fs(&mut ctx);
        ctx.set_cipher_list(&ciphers).unwrap();
        let sslclient = match SslStream::connect(&ctx, client) {
            Err(_) => return Err(io::Error::new(io::ErrorKind::ConnectionAborted, "SSL handshake aborted by the server")),
            Ok(sslclient) => sslclient
        };
        let _ = writeln!(stderr(), "Completed SSL handshake with {}", connect);
        let mut writer = BufWriter::new(sslclient);
        let merger = &self.merger;
        loop {
            let mut bytes = match { self.arx.lock().unwrap().recv() } {
                Ok(line) => line,
                Err(_) => return Err(io::Error::new(io::ErrorKind::Other, "Cannot read the message queue any more"))
            };
            if let Some(ref merger) = *merger {
                merger.frame(&mut bytes);
            }
            match writer.write_all(&bytes) {
                Ok(_) => { },
                Err(e) => match e.kind() {
                    ErrorKind::Interrupted => continue,
                    _ => return Err(e)
                }
            };
        }
    }

    fn run(self) {
        loop {
            let connect = &self.config.connect;
            if let Err(e) = self.handle_connection(connect) {
                match e.kind() {
                    ErrorKind::ConnectionRefused => {
                        let _ = writeln!(stderr(), "Connection to {} refused", connect);
                    }
                    ErrorKind::ConnectionAborted | ErrorKind::ConnectionReset => {
                        let _ = writeln!(stderr(), "Connection to {} aborted by the server", connect);
                    }
                    _ => {
                        let _ = writeln!(stderr(), "Error while communicating with {} - {}", connect, e);
                    }
                }
            }
            thread::sleep_ms(DEFAULT_SLEEP_AFTER_CONNECTION_FAILURE);
            let _ = writeln!(stderr(), "Attempting to reconnect");
        }
    }
}

fn new_tcp(connect: &str) -> Result<TcpStream, io::Error> {
    loop {
        match TcpStream::connect(connect) {
            Ok(stream) => return Ok(stream),
            Err(e) => {
                return Err(e);
            }
        }
    }
}

impl TlsOutput {
    pub fn new(config: &Config) -> TlsOutput {
        let connect = config.lookup("output.connect").map_or(DEFAULT_CONNECT, |x|x.as_str().
            expect("input.listen must be an ip:port string")).to_owned();
        let timeout = config.lookup("output.tls_timeout").
            map_or(TLS_DEFAULT_TIMEOUT, |x| x.as_integer().
                expect("output.tls_timeout must be an unsigned integer") as u64);
        let threads = config.lookup("output.tls_threads").
            map_or(TLS_DEFAULT_THREADS, |x| x.as_integer().
                expect("output.tls_threads must be a 32-bit integer") as u32);
        let tls_config = TlsConfig {
            connect: connect,
            timeout: Some(Duration::from_secs(timeout))
        };
        TlsOutput {
            config: tls_config,
            threads: threads
        }
    }
}

impl Output for TlsOutput {
    fn start(&self, arx: Arc<Mutex<Receiver<Vec<u8>>>>, merger: Option<Box<Merger>>) {
        for _ in 0..self.threads {
            let arx = arx.clone();
            let config = self.config.clone();
            let merger = match merger {
                Some(ref merger) => Some(merger.clone_boxed()) as Option<Box<Merger + Send>>,
                None => None
            };
            thread::spawn(move || {
                let worker = TlsWorker::new(arx, merger, config);
                worker.run();
            });
        }
    }
}

#[cfg(feature = "ecdh")]
fn set_ecdh(ctx: &mut SslContext) {
    ctx.set_ecdh_auto(true).unwrap();
}

#[cfg(not(feature = "ecdh"))]
fn set_ecdh(ctx: &mut SslContext) {
    let _ = ctx;
}

fn set_fs(ctx: &mut SslContext) {
    let p = BigNum::from_hex_str("87A8E61DB4B6663CFFBBD19C651959998CEEF608660DD0F25D2CEED4435E3B00E00DF8F1D61957D4FAF7DF4561B2AA3016C3D91134096FAA3BF4296D830E9A7C209E0C6497517ABD5A8A9D306BCF67ED91F9E6725B4758C022E0B1EF4275BF7B6C5BFC11D45F9088B941F54EB1E59BB8BC39A0BF12307F5C4FDB70C581B23F76B63ACAE1CAA6B7902D52526735488A0EF13C6D9A51BFA4AB3AD8347796524D8EF6A167B5A41825D967E144E5140564251CCACB83E6B486F6B3CA3F7971506026C0B857F689962856DED4010ABD0BE621C3A3960A54E710C375F26375D7014103A4B54330C198AF126116D2276E11715F693877FAD7EF09CADB094AE91E1A1597").unwrap();
    let g = BigNum::from_hex_str("3FB32C9B73134D0B2E77506660EDBD484CA7B18F21EF205407F4793A1A0BA12510DBC15077BE463FFF4FED4AAC0BB555BE3A6C1B0C6B47B1BC3773BF7E8C6F62901228F8C28CBB18A55AE31341000A650196F931C77A57F2DDF463E5E9EC144B777DE62AAAB8A8628AC376D282D6ED3864E67982428EBC831D14348F6F2F9193B5045AF2767164E1DFC967C1FB3F2E55A4BD1BFFE83B9C80D052B985D182EA0ADB2A3B7313D3FE14C8484B1E052588B9B7D2BBD2DF016199ECD06E1557CD0915B3353BBB64E0EC377FD028370DF92B52C7891428CDC67EB6184B523D1DB246C32F63078490F00EF8D647D148D47954515E2327CFEF98C582664B4C0F6CC41659").unwrap();
    let q = BigNum::from_hex_str("8CF83642A709A097B447997640129DA299B1A47D1EB3750BA308B0FE64F5FBD3").unwrap();
    let dh = DH::from_params(p, g, q).unwrap();
    ctx.set_tmp_dh(dh).unwrap();
    set_ecdh(ctx);
}
<commit_msg>TLS client: use a shared context<commit_after>use flowgger::config::Config;
use flowgger::merger::Merger;
use openssl::bn::BigNum;
use openssl::dh::DH;
use openssl::ssl::*;
use openssl::x509::X509FileType;
use std::error::Error;
use std::io;
use std::io::{stderr, BufWriter, ErrorKind, Write};
use std::net::TcpStream;
use std::path::{Path, PathBuf};
use std::sync::mpsc::Receiver;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
use super::Output;

const DEFAULT_CIPHERS: &'static str = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA";
const DEFAULT_CONNECT: &'static str = "127.0.0.1:6514";
const DEFAULT_SLEEP_AFTER_CONNECTION_FAILURE: u32 = 1000;
const TLS_DEFAULT_TIMEOUT: u64 = 3600;
const TLS_DEFAULT_THREADS: u32 = 1;

pub struct TlsOutput {
    config: TlsConfig,
    threads: u32
}

#[derive(Clone)]
struct TlsConfig {
    timeout: Option<Duration>,
    connect: String,
    arc_ctx: Arc<SslContext>
}

struct TlsWorker {
    arx: Arc<Mutex<Receiver<Vec<u8>>>>,
    merger: Option<Box<Merger + Send>>,
    tls_config: TlsConfig
}

impl TlsWorker {
    fn new(arx: Arc<Mutex<Receiver<Vec<u8>>>>, merger: Option<Box<Merger + Send>>, tls_config: TlsConfig) -> TlsWorker {
        TlsWorker {
            arx: arx,
            merger: merger,
            tls_config: tls_config
        }
    }

    fn handle_connection(&self, connect: &str) -> io::Result<()> {
        let client = try!(new_tcp(connect));
        let _ = writeln!(stderr(), "Connected to {}", connect);
        let sslclient = match SslStream::connect(&*self.tls_config.arc_ctx, client) {
            Err(_) => return Err(io::Error::new(io::ErrorKind::ConnectionAborted,
                "SSL handshake aborted by the server")),
            Ok(sslclient) => sslclient
        };
        let _ = writeln!(stderr(), "Completed SSL handshake with {}", connect);
        let mut writer = BufWriter::new(sslclient);
        let merger = &self.merger;
        loop {
            let mut bytes = match { self.arx.lock().unwrap().recv() } {
                Ok(line) => line,
                Err(_) => return Err(io::Error::new(io::ErrorKind::Other, "Cannot read the message queue any more"))
            };
            if let Some(ref merger) = *merger {
                merger.frame(&mut bytes);
            }
            match writer.write_all(&bytes) {
                Ok(_) => { },
                Err(e) => match e.kind() {
                    ErrorKind::Interrupted => continue,
                    _ => return Err(e)
                }
            };
        }
    }

    fn run(self) {
        loop {
            let connect = &self.tls_config.connect;
            if let Err(e) = self.handle_connection(connect) {
                match e.kind() {
                    ErrorKind::ConnectionRefused => {
                        let _ = writeln!(stderr(), "Connection to {} refused", connect);
                    }
                    ErrorKind::ConnectionAborted | ErrorKind::ConnectionReset => {
                        let _ = writeln!(stderr(), "Connection to {} aborted by the server", connect);
                    }
                    _ => {
                        let _ = writeln!(stderr(), "Error while communicating with {} - {}", connect, e);
                    }
                }
            }
            thread::sleep_ms(DEFAULT_SLEEP_AFTER_CONNECTION_FAILURE);
            let _ = writeln!(stderr(), "Attempting to reconnect");
        }
    }
}

fn new_tcp(connect: &str) -> Result<TcpStream, io::Error> {
    loop {
        match TcpStream::connect(connect) {
            Ok(stream) => return Ok(stream),
            Err(e) => {
                return Err(e);
            }
        }
    }
}

impl TlsOutput {
    pub fn new(config: &Config) -> TlsOutput {
        let connect = config.lookup("output.connect").map_or(DEFAULT_CONNECT, |x|x.as_str().
            expect("input.listen must be an ip:port string")).to_owned();
        let timeout = config.lookup("output.tls_timeout").
            map_or(TLS_DEFAULT_TIMEOUT, |x| x.as_integer().
                expect("output.tls_timeout must be an unsigned integer") as u64);
        let threads = config.lookup("output.tls_threads").
            map_or(TLS_DEFAULT_THREADS, |x| x.as_integer().
                expect("output.tls_threads must be a 32-bit integer") as u32);
        let tls_method = SslMethod::Sslv23;
        let mut ctx = SslContext::new(tls_method).unwrap();
        let ciphers = DEFAULT_CIPHERS;
        set_fs(&mut ctx);
        ctx.set_cipher_list(&ciphers).unwrap();
        let arc_ctx = Arc::new(ctx);
        let tls_config = TlsConfig {
            connect: connect,
            timeout: Some(Duration::from_secs(timeout)),
            arc_ctx: arc_ctx
        };
        TlsOutput {
            config: tls_config,
            threads: threads
        }
    }
}

impl Output for TlsOutput {
    fn start(&self, arx: Arc<Mutex<Receiver<Vec<u8>>>>, merger: Option<Box<Merger>>) {
        for _ in 0..self.threads {
            let arx = arx.clone();
            let config = self.config.clone();
            let merger = match merger {
                Some(ref merger) => Some(merger.clone_boxed()) as Option<Box<Merger + Send>>,
                None => None
            };
            thread::spawn(move || {
                let worker = TlsWorker::new(arx, merger, config);
                worker.run();
            });
        }
    }
}

#[cfg(feature = "ecdh")]
fn set_ecdh(ctx: &mut SslContext) {
    ctx.set_ecdh_auto(true).unwrap();
}

#[cfg(not(feature = "ecdh"))]
fn set_ecdh(ctx: &mut SslContext) {
    let _ = ctx;
}

fn set_fs(ctx: &mut SslContext) {
    let p = BigNum::from_hex_str("87A8E61DB4B6663CFFBBD19C651959998CEEF608660DD0F25D2CEED4435E3B00E00DF8F1D61957D4FAF7DF4561B2AA3016C3D91134096FAA3BF4296D830E9A7C209E0C6497517ABD5A8A9D306BCF67ED91F9E6725B4758C022E0B1EF4275BF7B6C5BFC11D45F9088B941F54EB1E59BB8BC39A0BF12307F5C4FDB70C581B23F76B63ACAE1CAA6B7902D52526735488A0EF13C6D9A51BFA4AB3AD8347796524D8EF6A167B5A41825D967E144E5140564251CCACB83E6B486F6B3CA3F7971506026C0B857F689962856DED4010ABD0BE621C3A3960A54E710C375F26375D7014103A4B54330C198AF126116D2276E11715F693877FAD7EF09CADB094AE91E1A1597").unwrap();
    let g = BigNum::from_hex_str("3FB32C9B73134D0B2E77506660EDBD484CA7B18F21EF205407F4793A1A0BA12510DBC15077BE463FFF4FED4AAC0BB555BE3A6C1B0C6B47B1BC3773BF7E8C6F62901228F8C28CBB18A55AE31341000A650196F931C77A57F2DDF463E5E9EC144B777DE62AAAB8A8628AC376D282D6ED3864E67982428EBC831D14348F6F2F9193B5045AF2767164E1DFC967C1FB3F2E55A4BD1BFFE83B9C80D052B985D182EA0ADB2A3B7313D3FE14C8484B1E052588B9B7D2BBD2DF016199ECD06E1557CD0915B3353BBB64E0EC377FD028370DF92B52C7891428CDC67EB6184B523D1DB246C32F63078490F00EF8D647D148D47954515E2327CFEF98C582664B4C0F6CC41659").unwrap();
    let q = BigNum::from_hex_str("8CF83642A709A097B447997640129DA299B1A47D1EB3750BA308B0FE64F5FBD3").unwrap();
    let dh = DH::from_params(p, g, q).unwrap();
    ctx.set_tmp_dh(dh).unwrap();
    set_ecdh(ctx);
}
<|endoftext|>