<commit_before>use rand;
use rand_distr::{Normal,Distribution};
use math::{histogram::Histogram, traits::ToIterator};

fn mean(data: &[f32]) -> Option<f32> {
    let sum: f32 = data.iter().sum();
    Some(sum / data.len() as f32)
}

fn standard_deviation(data: &[f32]) -> Option<f32> {
    let mean = mean(data).unwrap();
    let sum = data.iter().fold(0.0, |acc, &x| acc + (x - mean).powi(2));
    Some((sum / data.len() as f32).sqrt())
}

fn print_histogram(data: &[f32], maxwidth: usize, bincount: usize, ch: char) {
    // maxwidth -- the maxwidth of the histogram in # of characters
    // bincount -- number of bins in the histogram
    // ch -- character used to plot the graph
    let min_val = data.iter().cloned().fold(f32::NAN, f32::min);
    let max_val = data.iter().cloned().fold(f32::NAN, f32::max);
    let histogram = Histogram::new(Some(&data.to_vec()), bincount, min_val, max_val).unwrap();
    let max_bin_value = histogram.get_counters().iter().max().unwrap();
    println!("");
    for x in histogram.to_iter() {
        let (bin_min, bin_max, freq) = x;
        let bar_width = (((freq as f64)/(*max_bin_value as f64))*(maxwidth as f64)) as u32;
        let bar_as_string = (1..bar_width).fold(String::new(), |b, _| b + &ch.to_string());
        println!("({:>6},{:>6}) |{} {:.2}%",
                 format!("{:.2}", bin_min),
                 format!("{:.2}", bin_max),
                 bar_as_string,
                 (freq as f64)*100.0/(data.len() as f64));
    }
    println!("");
}

fn main() {
    let expected_mean: f32 = 0.0;
    let expected_std_deviation: f32 = 4.0;
    let normal = Normal::new(expected_mean, expected_std_deviation).unwrap();

    let mut rng = rand::thread_rng();
    for &number_of_samples in &[1000, 10_000, 1_000_000] {
        let data: Vec<f32> = normal
            .sample_iter(&mut rng)
            .take(number_of_samples)
            .collect();
        println!("Statistics for sample size {}:", number_of_samples);
        println!("\tMean: {:?}", mean(&data).unwrap());
        println!("\tStandard deviation: {:?}", standard_deviation(&data).unwrap());
        print_histogram(&data, 80, 40, '-');
    }
}

#[cfg(test)]
mod tests {
    use super::{mean, standard_deviation};
    use std::f32;

    fn approx(statistics: Option<f32>, value: f32) -> bool {
        (statistics.unwrap() - value).abs() <= f32::EPSILON
    }

    #[test]
    fn test_mean() {
        // let empty = vec![];
        // assert_eq!(mean(&empty), None);
        assert!(approx(mean(&[1.0]), 1.0));
        assert!(approx(mean(&[1.0, 3.0]), 2.0));
        assert!(approx(mean(&[1.0, 2.0, 3.0]), 2.0));
    }

    #[test]
    fn test_standard_deviation() {
        let empty = vec![];
        // assert_eq!(standard_deviation(&empty), None);
        assert!(approx(standard_deviation(&[0.0]), 0.0));
        assert!(approx(standard_deviation(&[1.0, 1.0, 1.0]), 0.0));
        assert!(approx(
            standard_deviation(&[1.0, 2.0, 3.0]),
            (2f32 / 3f32).sqrt()
        ));
    }
}
<commit_msg>Modifying tests<commit_after>use rand;
use rand_distr::{Normal,Distribution};
use math::{histogram::Histogram, traits::ToIterator};

fn mean(data: &[f32]) -> Option<f32> {
    let sum: f32 = data.iter().sum();
    Some(sum / data.len() as f32)
}

fn standard_deviation(data: &[f32]) -> Option<f32> {
    let mean = mean(data).expect("invalid mean");
    let sum = data.iter().fold(0.0, |acc, &x| acc + (x - mean).powi(2));
    Some((sum / data.len() as f32).sqrt())
}

fn print_histogram(data: &[f32], maxwidth: usize, bincount: usize, ch: char) {
    // maxwidth -- the maxwidth of the histogram in # of characters
    // bincount -- number of bins in the histogram
    // ch -- character used to plot the graph
    let min_val = data.iter().cloned().fold(f32::NAN, f32::min);
    let max_val = data.iter().cloned().fold(f32::NAN, f32::max);
    let histogram = Histogram::new(Some(&data.to_vec()), bincount, min_val, max_val).unwrap();
    let max_bin_value = histogram.get_counters().iter().max().unwrap();
    println!("");
    for x in histogram.to_iter() {
        let (bin_min, bin_max, freq) = x;
        let bar_width = (((freq as f64)/(*max_bin_value as f64))*(maxwidth as f64)) as u32;
        let bar_as_string = (1..bar_width).fold(String::new(), |b, _| b + &ch.to_string());
        println!("({:>6},{:>6}) |{} {:.2}%",
                 format!("{:.2}", bin_min),
                 format!("{:.2}", bin_max),
                 bar_as_string,
                 (freq as f64)*100.0/(data.len() as f64));
    }
    println!("");
}

fn main() {
    let expected_mean: f32 = 0.0;
    let expected_std_deviation: f32 = 4.0;
    let normal = Normal::new(expected_mean, expected_std_deviation).unwrap();

    let mut rng = rand::thread_rng();
    for &number_of_samples in &[1000, 10_000, 1_000_000] {
        let data: Vec<f32> = normal
            .sample_iter(&mut rng)
            .take(number_of_samples)
            .collect();
        println!("Statistics for sample size {}:", number_of_samples);
        println!("\tMean: {:?}", mean(&data).expect("invalid mean"));
        println!("\tStandard deviation: {:?}", standard_deviation(&data).expect("invalid standard deviation"));
        print_histogram(&data, 80, 40, '-');
    }
}

#[cfg(test)]
mod tests {
    use super::{mean, standard_deviation, print_histogram};
    use std::f32;

    fn approx(statistics: Option<f32>, value: f32) -> bool {
        (statistics.unwrap() - value).abs() <= f32::EPSILON
    }

    #[test]
    fn test_mean() {
        assert!(approx(mean(&[1.0]), 1.0));
        assert!(approx(mean(&[1.0, 3.0]), 2.0));
        assert!(approx(mean(&[1.0, 2.0, 3.0]), 2.0));
    }

    #[test]
    fn test_standard_deviation() {
        assert!(approx(standard_deviation(&[0.0]), 0.0));
        assert!(approx(standard_deviation(&[1.0, 1.0, 1.0]), 0.0));
        assert!(approx(
            standard_deviation(&[1.0, 2.0, 3.0]),
            (2f32 / 3f32).sqrt()
        ));
    }

    #[test]
    fn test_print_histogram() {
        print_histogram(&[0.0,1.0,2.0,3.0], 10, 5, '-');
    }
}
<|endoftext|>


<commit_before>use super::*;
use byteorder::BigEndian;
use byteorder::ByteOrder;
use native::storage::*;
use std::collections::VecDeque;
use std::str::FromStr;
use util::{H256, U256};
use zktx::base::*;
use zktx::c2p::*;
use zktx::convert::*;
use zktx::incrementalmerkletree::*;
use zktx::p2c::*;
use zktx::pedersen::PedersenDigest;

static TREE_DEPTH: usize = 60;
#[derive(Clone)]
// address 512  banlance 512
pub struct ZkPrivacy {
    balances: Map,
    last_spent: Map,
    coins: Array,
    nullifier_set: Array,
    // merkle tree
    left: Scalar,
    right: Scalar,
    parents: Array,
    admin: Scalar,
    output: Vec<u8>,
}

impl Contract for ZkPrivacy {
    fn exec(&mut self, params: ActionParams, ext: &mut Ext) -> Result<GasLeft, evm::Error> {
        let signature = BigEndian::read_u32(params.clone().data.unwrap().get(0..4).unwrap());
        match signature {
            0 => self.init(params, ext),
            0x05e3cb61 => self.set_banlance(params, ext),
            0xd0b07e52 => self.get_banlance(params, ext),
            0xc73b5a8f => self.send_verify(params, ext),
            0x882b30d2 => self.receive_verify(params, ext),
            _ => Err(evm::Error::OutOfGas),
        }
    }
    fn create(&self) -> Box<Contract> {
        Box::new(ZkPrivacy::default())
    }
}

impl Default for ZkPrivacy {
    fn default() -> Self {
        ZkPrivacy {
            output: Vec::new(),
            balances: Map::new(H256::from(0)),
            last_spent: Map::new(H256::from(1)),
            coins: Array::new(H256::from(2)),
            nullifier_set: Array::new(H256::from(3)),
            left: Scalar::new(H256::from(4)),
            right: Scalar::new(H256::from(5)),
            parents: Array::new(H256::from(6)),
            admin: Scalar::new(H256::from(7)),
        }
    }
}

impl ZkPrivacy {
    fn init(&mut self, params: ActionParams, ext: &mut Ext) -> Result<GasLeft, evm::Error> {
        let gas_cost = U256::from(5000);
        if params.gas < gas_cost {
            return Err(evm::Error::OutOfGas);
        }
        let sender = U256::from(H256::from(params.sender));
        self.admin.set(ext, sender)?;
        Ok(GasLeft::Known(params.gas - gas_cost))
    }

    fn set_banlance(&mut self, params: ActionParams, ext: &mut Ext) -> Result<GasLeft, evm::Error> {
        let gas_cost = U256::from(10000);
        if params.gas < gas_cost {
            return Err(evm::Error::OutOfGas);
        }
        let data = params.data.expect("invalid data");
        let mut index = 4;

        let mut len = 128;
        let addr = String::from_utf8(Vec::from(
            data.get(index..index + len).expect("no enough data"),
        )).unwrap();
        index = index + len;

        len = 128;
        let balance = String::from_utf8(Vec::from(
            data.get(index..index + len).expect("no enough data"),
        )).unwrap();

        info!("set_banlance {} {}", addr, balance);

        self.balances.set_bytes(ext, addr, balance)?;
        Ok(GasLeft::Known(params.gas - gas_cost))
    }

    fn get_banlance(&mut self, params: ActionParams, ext: &mut Ext) -> Result<GasLeft, evm::Error> {
        let gas_cost = U256::from(10000);
        if params.gas < gas_cost {
            return Err(evm::Error::OutOfGas);
        }
        let data = params.data.expect("invalid data");
        let index = 4;

        let len = 128;
        let addr = String::from_utf8(Vec::from(
            data.get(index..index + len).expect("no enough data"),
        )).unwrap();

        self.output.resize(128, 0);
        let balance: String = self.balances.get_bytes(ext, addr.clone())?;
        for v in balance.as_bytes() {
            self.output.push(*v);
        }
        info!("get_banlance {} {}", addr, balance);

        Ok(GasLeft::NeedsReturn(
            U256::from(params.gas - gas_cost),
            self.output.as_slice(),
        ))
    }

    fn send_verify(&mut self, params: ActionParams, ext: &mut Ext) -> Result<GasLeft, evm::Error> {
        let gas_cost = U256::from(10000000);
        if params.gas < gas_cost {
            return Err(evm::Error::OutOfGas);
        }
        let data = params.data.expect("invalid data");
        let mut index = 4;

        // get address
        let mut len = 128;
        let addr = String::from_utf8(Vec::from(
            data.get(index..index + len).expect("no enough data"),
        )).unwrap();
        index = index + len;

        // get proof
        len = 770;
        let proof = String::from_utf8(Vec::from(
            data.get(index..index + len).expect("no enough data"),
        )).unwrap();
        index = index + len;

        // get coin
        len = 64;
        let coin = String::from_utf8(Vec::from(
            data.get(index..index + len).expect("no enough data"),
        )).unwrap();
        index = index + len;

        // get delt_ba
        len = 128;
        let delt_ba = String::from_utf8(Vec::from(
            data.get(index..index + len).expect("no enough data"),
        )).unwrap();
        index = index + len;

        // get enc
        len = 192;
        let enc = String::from_utf8(Vec::from(
            data.get(index..index + len).expect("no enough data"),
        )).unwrap();

        // get block_number
        let block_number = U256::from(ext.env_info().number);

        info!(
            "send_verify args: {} {} {} {} {} {}",
            addr, proof, coin, delt_ba, enc, block_number
        );

        // check coin is dup
        let coins_len = self.coins.get_len(ext)?;
        for i in 0..coins_len {
            let coin_added: String = *self.coins.get_bytes(ext, i)?;
            if coin == coin_added {
                return Err(evm::Error::Internal("dup coin".to_string()));
            }
        }

        // compare block number
        let last_block_number = self.last_spent.get(ext, addr.clone())?;
        if last_block_number >= block_number {
            return Err(evm::Error::Internal(
                "block_number less than last".to_string(),
            ));
        }

        // get banlance
        let balance: String = self.balances.get_bytes(ext, addr.clone())?;
        info!("balance {}", balance);

        let ret = p2c_verify(
            balance.clone(),
            coin.clone(),
            delt_ba.clone(),
            enc.clone(),
            addr.clone(),
            proof,
        );
        if ret.is_err() {
            return Err(evm::Error::Internal("p2c_verify error".to_string()));
        }

        if !ret.unwrap() {
            return Err(evm::Error::Internal("p2c_verify failed".to_string()));
        }

        // update last spent
        self.last_spent.set(ext, addr.clone(), block_number)?;
        // add coin
        self.coins.set_bytes(ext, coins_len, coin.clone())?;
        self.coins.set_len(ext, coins_len + 1)?;

        // restore merkle tree form storage
        let mut tree = IncrementalMerkleTree::new(TREE_DEPTH);
        let left_str: String = *self.left.get_bytes(ext)?;
        let tree_left;
        if left_str.len() == 0 {
            tree_left = None;
        } else {
            tree_left = Some(PedersenDigest(str2u644(left_str)));
        }
        let right_str: String = *self.right.get_bytes(ext)?;
        let tree_right;
        if right_str.len() == 0 {
            tree_right = None;
        } else {
            tree_right = Some(PedersenDigest(str2u644(right_str)));
        }
        let mut parents = Vec::new();
        let parents_len = self.parents.get_len(ext)?;
        for i in 0..parents_len {
            let hash_str: String = *self.parents.get_bytes(ext, i)?;
            let mut hash;
            if hash_str.len() == 0 {
                hash = None;
            } else {
                hash = Some(PedersenDigest(str2u644(hash_str)));
            }
            parents.push(hash);
        }
        tree.restore(tree_left, tree_right, parents);

        // add coin to merkle tree
        tree.append(PedersenDigest(str2u644(coin.clone())));

        // get path of the coin
        let path = tree.path(VecDeque::new());
        let authentication_path = path.authentication_path;
        let index = path.index;

        // export merkle tree to storage
        let left = match tree.export_left() {
            Some(hash) => u6442str(hash.0),
            None => "".to_string(),
        };
        self.left.set_bytes(ext, left)?;

        let right = match tree.export_right() {
            Some(hash) => u6442str(hash.0),
            None => "".to_string(),
        };
        self.right.set_bytes(ext, right)?;

        let mut i = 0;
        for opt_hash in tree.export_parents().iter() {
            let str = match opt_hash {
                &Some(ref hash) => u6442str(hash.0),
                &None => "".to_string(),
            };
            self.parents.set_bytes(ext, i, str)?;
            i = i + 1;
        }
        self.parents.set_len(ext, i)?;

        // sub balance
        let new_balance = ecc_sub(balance, delt_ba);
        self.balances.set_bytes(ext, addr, new_balance)?;

        let mut data = Vec::new();
        data.extend_from_slice(coin.as_bytes());
        data.extend_from_slice(enc.as_bytes());
        for hash in authentication_path {
            data.extend_from_slice(u6442str(hash.0).as_bytes());
        }
        for flag in index {
            if flag {
                data.push(1u8);
            } else {
                data.push(0u8);
            }
        }
        ext.log(
            vec![
                H256::from_str("0xc73b5a8f31a1a078a14123cc93687f4a59389c76caf88d5d2154d3f3ce25ff49").unwrap(),
            ],
            &data,
        );

        info!("send_verify OK data len {}", data.len());

        Ok(GasLeft::Known(params.gas - gas_cost))
    }

    fn receive_verify(&mut self, params: ActionParams, ext: &mut Ext) -> Result<GasLeft, evm::Error> {
        let gas_cost = U256::from(1000000);
        if params.gas < gas_cost {
            return Err(evm::Error::OutOfGas);
        }
        let data = params.data.expect("invalid data");
        let mut index = 4;

        // get address
        let mut len = 128;
        let addr = String::from_utf8(Vec::from(
            data.get(index..index + len).expect("no enough data"),
        )).unwrap();
        index = index + len;

        // get proof
        len = 770;
        let proof = String::from_utf8(Vec::from(
            data.get(index..index + len).expect("no enough data"),
        )).unwrap();
        index = index + len;

        // get nullifier
        len = 64;
        let nullifier = String::from_utf8(Vec::from(
            data.get(index..index + len).expect("no enough data"),
        )).unwrap();
        index = index + len;

        // get root
        len = 64;
        let root = String::from_utf8(Vec::from(
            data.get(index..index + len).expect("no enough data"),
        )).unwrap();
        index = index + len;

        // get delt_ba
        len = 128;
        let delt_ba = String::from_utf8(Vec::from(
            data.get(index..index + len).expect("no enough data"),
        )).unwrap();

        info!(
            "receive_verify args: {} {} {} {} {}",
            addr, proof, nullifier, root, delt_ba
        );

        // check nullifier is dup
        let nullifier_set_len = self.nullifier_set.get_len(ext)?;
        for i in 0..nullifier_set_len {
            let nullifier_in_set: String = *self.nullifier_set.get_bytes(ext, i)?;
            if nullifier == nullifier_in_set {
                return Err(evm::Error::Internal("dup nullifier".to_string()));
            }
        }

        // check root
        // str2u644(root.clone()) == tree.root()
        // restore merkle tree form storage
        let mut tree = IncrementalMerkleTree::new(TREE_DEPTH);
        let left_str: String = *self.left.get_bytes(ext)?;
        let tree_left;
        if left_str.len() == 0 {
            tree_left = None;
        } else {
            tree_left = Some(PedersenDigest(str2u644(left_str)));
        }
        let right_str: String = *self.right.get_bytes(ext)?;
        let tree_right;
        if right_str.len() == 0 {
            tree_right = None;
        } else {
            tree_right = Some(PedersenDigest(str2u644(right_str)));
        }
        let mut parents = Vec::new();
        let parents_len = self.parents.get_len(ext)?;
        for i in 0..parents_len {
            let hash_str: String = *self.parents.get_bytes(ext, i)?;
            let mut hash;
            if hash_str.len() == 0 {
                hash = None;
            } else {
                hash = Some(PedersenDigest(str2u644(hash_str)));
            }
            parents.push(hash);
        }
        tree.restore(tree_left, tree_right, parents);
        if str2u644(root.clone()) != tree.root().0 {
            return Err(evm::Error::Internal("invalid root hash".to_string()));
        }

        let ret = c2p_verify(nullifier.clone(), root, delt_ba.clone(), proof);
        if ret.is_err() {
            return Err(evm::Error::Internal("c2p_verify error".to_string()));
        }

        if !ret.unwrap() {
            return Err(evm::Error::Internal("c2p_verify failed".to_string()));
        }
        // add nullifier into nullifier_set
        self.nullifier_set
            .set_bytes(ext, nullifier_set_len, nullifier)?;
        self.nullifier_set.set_len(ext, nullifier_set_len + 1)?;
        // add balance
        let balance: String = self.balances.get_bytes(ext, addr.clone())?;
        info!("balance {}", balance);
        let new_balance = ecc_add(balance, delt_ba);
        self.balances.set_bytes(ext, addr, new_balance)?;

        info!("receive_verify OK");

        Ok(GasLeft::Known(params.gas - gas_cost))
    }
}
<commit_msg>fix can't complite with zktx(privatetx) features<commit_after>use super::*;
use byteorder::BigEndian;
use byteorder::ByteOrder;
use native::storage::*;
use std::collections::VecDeque;
use std::str::FromStr;
use util::{H256, U256};
use zktx::base::*;
use zktx::c2p::*;
use zktx::convert::*;
use zktx::incrementalmerkletree::*;
use zktx::p2c::*;
use zktx::pedersen::PedersenDigest;
use evm::ReturnData;

static TREE_DEPTH: usize = 60;
#[derive(Clone)]
// address 512  banlance 512
pub struct ZkPrivacy {
    balances: Map,
    last_spent: Map,
    coins: Array,
    nullifier_set: Array,
    // merkle tree
    left: Scalar,
    right: Scalar,
    parents: Array,
    admin: Scalar,
    output: Vec<u8>,
}

impl Contract for ZkPrivacy {
    fn exec(&mut self, params: ActionParams, ext: &mut Ext) -> Result<GasLeft, evm::Error> {
        let signature = BigEndian::read_u32(params.clone().data.unwrap().get(0..4).unwrap());
        match signature {
            0 => self.init(params, ext),
            0x05e3cb61 => self.set_balance(params, ext),
            0xd0b07e52 => self.get_balance(params, ext),
            0xc73b5a8f => self.send_verify(params, ext),
            0x882b30d2 => self.receive_verify(params, ext),
            _ => Err(evm::Error::OutOfGas),
        }
    }
    fn create(&self) -> Box<Contract> {
        Box::new(ZkPrivacy::default())
    }
}

impl Default for ZkPrivacy {
    fn default() -> Self {
        ZkPrivacy {
            output: Vec::new(),
            balances: Map::new(H256::from(0)),
            last_spent: Map::new(H256::from(1)),
            coins: Array::new(H256::from(2)),
            nullifier_set: Array::new(H256::from(3)),
            left: Scalar::new(H256::from(4)),
            right: Scalar::new(H256::from(5)),
            parents: Array::new(H256::from(6)),
            admin: Scalar::new(H256::from(7)),
        }
    }
}

impl ZkPrivacy {
    fn init(&mut self, params: ActionParams, ext: &mut Ext) -> Result<GasLeft, evm::Error> {
        let gas_cost = U256::from(5000);
        if params.gas < gas_cost {
            return Err(evm::Error::OutOfGas);
        }
        let sender = U256::from(H256::from(params.sender));
        self.admin.set(ext, sender)?;
        Ok(GasLeft::Known(params.gas - gas_cost))
    }

    fn set_balance(&mut self, params: ActionParams, ext: &mut Ext) -> Result<GasLeft, evm::Error> {
        let gas_cost = U256::from(10000);
        if params.gas < gas_cost {
            return Err(evm::Error::OutOfGas);
        }
        let data = params.data.expect("invalid data");
        let mut index = 4;

        let mut len = 128;
        let addr = String::from_utf8(Vec::from(
            data.get(index..index + len).expect("no enough data"),
        )).unwrap();
        index = index + len;

        len = 128;
        let balance = String::from_utf8(Vec::from(
            data.get(index..index + len).expect("no enough data"),
        )).unwrap();

        info!("set_banlance {} {}", addr, balance);

        self.balances.set_bytes(ext, addr, balance)?;
        Ok(GasLeft::Known(params.gas - gas_cost))
    }

    fn get_balance(&mut self, params: ActionParams, ext: &mut Ext) -> Result<GasLeft, evm::Error> {
        let gas_cost = U256::from(10000);
        if params.gas < gas_cost {
            return Err(evm::Error::OutOfGas);
        }
        let data = params.data.expect("invalid data");
        let index = 4;

        let len = 128;
        let addr = String::from_utf8(Vec::from(
            data.get(index..index + len).expect("no enough data"),
        )).unwrap();

        self.output.resize(128, 0);
        let balance: String = self.balances.get_bytes(ext, addr.clone())?;
        for v in balance.as_bytes() {
            self.output.push(*v);
        }
        info!("get_banlance {} {}", addr, balance);

        Ok(GasLeft::NeedsReturn {
            gas_left: U256::from(params.gas - gas_cost),
            data: ReturnData::new(self.output.clone(), 0, self.output.len()),
            apply_state: true,
        })
    }

    fn send_verify(&mut self, params: ActionParams, ext: &mut Ext) -> Result<GasLeft, evm::Error> {
        let gas_cost = U256::from(10000000);
        if params.gas < gas_cost {
            return Err(evm::Error::OutOfGas);
        }
        let data = params.data.expect("invalid data");
        let mut index = 4;

        // get address
        let mut len = 128;
        let addr = String::from_utf8(Vec::from(
            data.get(index..index + len).expect("no enough data"),
        )).unwrap();
        index = index + len;

        // get proof
        len = 770;
        let proof = String::from_utf8(Vec::from(
            data.get(index..index + len).expect("no enough data"),
        )).unwrap();
        index = index + len;

        // get coin
        len = 64;
        let coin = String::from_utf8(Vec::from(
            data.get(index..index + len).expect("no enough data"),
        )).unwrap();
        index = index + len;

        // get delt_ba
        len = 128;
        let delt_ba = String::from_utf8(Vec::from(
            data.get(index..index + len).expect("no enough data"),
        )).unwrap();
        index = index + len;

        // get enc
        len = 192;
        let enc = String::from_utf8(Vec::from(
            data.get(index..index + len).expect("no enough data"),
        )).unwrap();

        // get block_number
        let block_number = U256::from(ext.env_info().number);

        info!(
            "send_verify args: {} {} {} {} {} {}",
            addr, proof, coin, delt_ba, enc, block_number
        );

        // check coin is dup
        let coins_len = self.coins.get_len(ext)?;
        for i in 0..coins_len {
            let coin_added: String = *self.coins.get_bytes(ext, i)?;
            if coin == coin_added {
                return Err(evm::Error::Internal("dup coin".to_string()));
            }
        }

        // compare block number
        let last_block_number = self.last_spent.get(ext, addr.clone())?;
        if last_block_number >= block_number {
            return Err(evm::Error::Internal(
                "block_number less than last".to_string(),
            ));
        }

        // get banlance
        let balance: String = self.balances.get_bytes(ext, addr.clone())?;
        info!("balance {}", balance);

        let ret = p2c_verify(
            balance.clone(),
            coin.clone(),
            delt_ba.clone(),
            enc.clone(),
            addr.clone(),
            proof,
        );
        if ret.is_err() {
            return Err(evm::Error::Internal("p2c_verify error".to_string()));
        }

        if !ret.unwrap() {
            return Err(evm::Error::Internal("p2c_verify failed".to_string()));
        }

        // update last spent
        self.last_spent.set(ext, addr.clone(), block_number)?;
        // add coin
        self.coins.set_bytes(ext, coins_len, coin.clone())?;
        self.coins.set_len(ext, coins_len + 1)?;

        // restore merkle tree form storage
        let mut tree = IncrementalMerkleTree::new(TREE_DEPTH);
        let left_str: String = *self.left.get_bytes(ext)?;
        let tree_left;
        if left_str.len() == 0 {
            tree_left = None;
        } else {
            tree_left = Some(PedersenDigest(str2u644(left_str)));
        }
        let right_str: String = *self.right.get_bytes(ext)?;
        let tree_right;
        if right_str.len() == 0 {
            tree_right = None;
        } else {
            tree_right = Some(PedersenDigest(str2u644(right_str)));
        }
        let mut parents = Vec::new();
        let parents_len = self.parents.get_len(ext)?;
        for i in 0..parents_len {
            let hash_str: String = *self.parents.get_bytes(ext, i)?;
            let mut hash;
            if hash_str.len() == 0 {
                hash = None;
            } else {
                hash = Some(PedersenDigest(str2u644(hash_str)));
            }
            parents.push(hash);
        }
        tree.restore(tree_left, tree_right, parents);

        // add coin to merkle tree
        tree.append(PedersenDigest(str2u644(coin.clone())));

        // get path of the coin
        let path = tree.path(VecDeque::new());
        let authentication_path = path.authentication_path;
        let index = path.index;

        // export merkle tree to storage
        let left = match tree.export_left() {
            Some(hash) => u6442str(hash.0),
            None => "".to_string(),
        };
        self.left.set_bytes(ext, left)?;

        let right = match tree.export_right() {
            Some(hash) => u6442str(hash.0),
            None => "".to_string(),
        };
        self.right.set_bytes(ext, right)?;

        let mut i = 0;
        for opt_hash in tree.export_parents().iter() {
            let str = match opt_hash {
                &Some(ref hash) => u6442str(hash.0),
                &None => "".to_string(),
            };
            self.parents.set_bytes(ext, i, str)?;
            i = i + 1;
        }
        self.parents.set_len(ext, i)?;

        // sub balance
        let new_balance = ecc_sub(balance, delt_ba);
        self.balances.set_bytes(ext, addr, new_balance)?;

        let mut data = Vec::new();
        data.extend_from_slice(coin.as_bytes());
        data.extend_from_slice(enc.as_bytes());
        for hash in authentication_path {
            data.extend_from_slice(u6442str(hash.0).as_bytes());
        }
        for flag in index {
            if flag {
                data.push(1u8);
            } else {
                data.push(0u8);
            }
        }
        let _ = ext.log(
            vec![
                H256::from_str("0xc73b5a8f31a1a078a14123cc93687f4a59389c76caf88d5d2154d3f3ce25ff49").unwrap(),
            ],
            &data,
        );

        info!("send_verify OK data len {}", data.len());

        Ok(GasLeft::Known(params.gas - gas_cost))
    }

    fn receive_verify(&mut self, params: ActionParams, ext: &mut Ext) -> Result<GasLeft, evm::Error> {
        let gas_cost = U256::from(1000000);
        if params.gas < gas_cost {
            return Err(evm::Error::OutOfGas);
        }
        let data = params.data.expect("invalid data");
        let mut index = 4;

        // get address
        let mut len = 128;
        let addr = String::from_utf8(Vec::from(
            data.get(index..index + len).expect("no enough data"),
        )).unwrap();
        index = index + len;

        // get proof
        len = 770;
        let proof = String::from_utf8(Vec::from(
            data.get(index..index + len).expect("no enough data"),
        )).unwrap();
        index = index + len;

        // get nullifier
        len = 64;
        let nullifier = String::from_utf8(Vec::from(
            data.get(index..index + len).expect("no enough data"),
        )).unwrap();
        index = index + len;

        // get root
        len = 64;
        let root = String::from_utf8(Vec::from(
            data.get(index..index + len).expect("no enough data"),
        )).unwrap();
        index = index + len;

        // get delt_ba
        len = 128;
        let delt_ba = String::from_utf8(Vec::from(
            data.get(index..index + len).expect("no enough data"),
        )).unwrap();

        info!(
            "receive_verify args: {} {} {} {} {}",
            addr, proof, nullifier, root, delt_ba
        );

        // check nullifier is dup
        let nullifier_set_len = self.nullifier_set.get_len(ext)?;
        for i in 0..nullifier_set_len {
            let nullifier_in_set: String = *self.nullifier_set.get_bytes(ext, i)?;
            if nullifier == nullifier_in_set {
                return Err(evm::Error::Internal("dup nullifier".to_string()));
            }
        }

        // check root
        // str2u644(root.clone()) == tree.root()
        // restore merkle tree form storage
        let mut tree = IncrementalMerkleTree::new(TREE_DEPTH);
        let left_str: String = *self.left.get_bytes(ext)?;
        let tree_left;
        if left_str.len() == 0 {
            tree_left = None;
        } else {
            tree_left = Some(PedersenDigest(str2u644(left_str)));
        }
        let right_str: String = *self.right.get_bytes(ext)?;
        let tree_right;
        if right_str.len() == 0 {
            tree_right = None;
        } else {
            tree_right = Some(PedersenDigest(str2u644(right_str)));
        }
        let mut parents = Vec::new();
        let parents_len = self.parents.get_len(ext)?;
        for i in 0..parents_len {
            let hash_str: String = *self.parents.get_bytes(ext, i)?;
            let mut hash;
            if hash_str.len() == 0 {
                hash = None;
            } else {
                hash = Some(PedersenDigest(str2u644(hash_str)));
            }
            parents.push(hash);
        }
        tree.restore(tree_left, tree_right, parents);
        if str2u644(root.clone()) != tree.root().0 {
            return Err(evm::Error::Internal("invalid root hash".to_string()));
        }

        let ret = c2p_verify(nullifier.clone(), root, delt_ba.clone(), proof);
        if ret.is_err() {
            return Err(evm::Error::Internal("c2p_verify error".to_string()));
        }

        if !ret.unwrap() {
            return Err(evm::Error::Internal("c2p_verify failed".to_string()));
        }
        // add nullifier into nullifier_set
        self.nullifier_set
            .set_bytes(ext, nullifier_set_len, nullifier)?;
        self.nullifier_set.set_len(ext, nullifier_set_len + 1)?;
        // add balance
        let balance: String = self.balances.get_bytes(ext, addr.clone())?;
        info!("balance {}", balance);
        let new_balance = ecc_add(balance, delt_ba);
        self.balances.set_bytes(ext, addr, new_balance)?;

        info!("receive_verify OK");

        Ok(GasLeft::Known(params.gas - gas_cost))
    }
}
<|endoftext|>


<commit_before>/*******************************************************************************
 *
 * kit/kernel/kernel.rs
 * - main kernel entry point and top level management
 *
 * vim:ts=4:sw=4:et:tw=80:ft=rust
 *
 * Copyright (C) 2015, Devyn Cairns
 * Redistribution of this file is permitted under the terms of the simplified
 * BSD license. See LICENSE for more information.
 *
 * Based on OSDev Bare Bones tutorial
 * http://wiki.osdev.org/Bare_Bones
 *
 ******************************************************************************/

#![crate_type="lib"]
#![feature(core)]
#![feature(asm)]
#![feature(no_std)]
#![feature(lang_items)]
#![no_std]

#[macro_use]
extern crate core;

use core::prelude::*;
use core::fmt::Write;

use terminal::color;
use terminal::Terminal;

mod terminal;

#[no_mangle]
pub extern fn kernel_main() -> ! {

    terminal::initialize();
    terminal::set_color(color::RED, color::WHITE);

    write!(&mut Terminal, "+ Hello. I'm {}.\n", "Kit").ok();

    loop {
        unsafe { asm!("hlt"); }
    }
}

#[lang = "stack_exhausted"]
extern fn stack_exhausted() {
}

#[lang = "eh_personality"]
extern fn eh_personality() {
}

#[lang = "panic_fmt"]
#[allow(unused_variables)]
extern fn panic_fmt(args: &core::fmt::Arguments,
                    file: &str,
                    line: u32) -> ! {
    loop {
        unsafe { asm!("hlt"); }
    }
}
<commit_msg>[kernel] Proper panic!() support<commit_after>/*******************************************************************************
 *
 * kit/kernel/kernel.rs
 * - main kernel entry point and top level management
 *
 * vim:ts=4:sw=4:et:tw=80:ft=rust
 *
 * Copyright (C) 2015, Devyn Cairns
 * Redistribution of this file is permitted under the terms of the simplified
 * BSD license. See LICENSE for more information.
 *
 * Based on OSDev Bare Bones tutorial
 * http://wiki.osdev.org/Bare_Bones
 *
 ******************************************************************************/

#![crate_type="lib"]
#![feature(core)]
#![feature(asm)]
#![feature(no_std)]
#![feature(lang_items)]
#![no_std]

#[macro_use]
extern crate core;

use core::prelude::*;
use core::fmt::Write;

use terminal::color;
use terminal::Terminal;

mod terminal;

#[no_mangle]
pub extern fn kernel_main() -> ! {

    terminal::initialize();
    terminal::set_color(color::RED, color::WHITE);

    terminal::write("+ Hello, I'm Kit.\n");

    panic!("2 + 2 = {}", 2 + 2);
}

#[lang = "stack_exhausted"]
extern fn stack_exhausted() {
}

#[lang = "eh_personality"]
extern fn eh_personality() {
}

#[lang = "panic_fmt"]
#[allow(unused_must_use)]
extern fn panic_fmt(fmt: core::fmt::Arguments,
                    file: &'static str,
                    line: usize) -> ! {

    terminal::set_color(color::WHITE, color::RED);

    write!(&mut Terminal, "\nKernel panic in {}:{}:\n  {}\n\n", file, line, fmt);

    unsafe {
        asm!("cli");

        loop {
            asm!("hlt");
        }
    }
}
<|endoftext|>


<commit_before>#![feature(rustc_attrs)]

#[no_mangle]
extern "C" fn foo() -> i32 {
    -1
}

#[export_name = "bar"]
fn bar() -> i32 {
    -2
}

#[rustc_std_internal_symbol]
fn baz() -> i32 {
    -3
}

// Make sure shims take precedence.
#[no_mangle]
extern "C" fn exit(_: i32) -> ! {
    unreachable!()
}

#[no_mangle]
extern "C" fn ExitProcess(_: u32) -> ! {
    unreachable!()
}

fn main() {
    // Repeat calls to make sure the `Instance` cache is not broken.
    for _ in 0..3 {
        extern "C" {
            fn foo() -> i32;
        }

        assert_eq!(unsafe { foo() }, -1);

        extern "Rust" {
            fn bar() -> i32;
            fn baz() -> i32;
        }

        assert_eq!(unsafe { bar() }, -2);
        assert_eq!(unsafe { baz() }, -3);

        #[allow(clashing_extern_declarations)]
        {
            extern "Rust" {
                fn foo() -> i32;
            }

            assert_eq!(
                unsafe {
                    std::mem::transmute::<unsafe fn() -> i32, unsafe extern "C" fn() -> i32>(foo)()
                },
                -1
            );

            extern "C" {
                fn bar() -> i32;
                fn baz() -> i32;
            }

            unsafe {
                let transmute = |f| {
                    std::mem::transmute::<unsafe extern "C" fn() -> i32, unsafe fn() -> i32>(f)
                };
                assert_eq!(transmute(bar)(), -2);
                assert_eq!(transmute(baz)(), -3);
            }
        }
    }
}
<commit_msg>Remove erroneous `exit()` and `ExitProcess()` in `tests/run-pass/function_calls/exported_symbol.rs`<commit_after>#![feature(rustc_attrs)]

#[no_mangle]
extern "C" fn foo() -> i32 {
    -1
}

#[export_name = "bar"]
fn bar() -> i32 {
    -2
}

#[rustc_std_internal_symbol]
fn baz() -> i32 {
    -3
}

fn main() {
    // Repeat calls to make sure the `Instance` cache is not broken.
    for _ in 0..3 {
        extern "C" {
            fn foo() -> i32;
        }

        assert_eq!(unsafe { foo() }, -1);

        extern "Rust" {
            fn bar() -> i32;
            fn baz() -> i32;
        }

        assert_eq!(unsafe { bar() }, -2);
        assert_eq!(unsafe { baz() }, -3);

        #[allow(clashing_extern_declarations)]
        {
            extern "Rust" {
                fn foo() -> i32;
            }

            assert_eq!(
                unsafe {
                    std::mem::transmute::<unsafe fn() -> i32, unsafe extern "C" fn() -> i32>(foo)()
                },
                -1
            );

            extern "C" {
                fn bar() -> i32;
                fn baz() -> i32;
            }

            unsafe {
                let transmute = |f| {
                    std::mem::transmute::<unsafe extern "C" fn() -> i32, unsafe fn() -> i32>(f)
                };
                assert_eq!(transmute(bar)(), -2);
                assert_eq!(transmute(baz)(), -3);
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![cfg(not(test))]

extern crate log;
extern crate rustfmt;
extern crate toml;
extern crate env_logger;
extern crate getopts;

use rustfmt::{run, Input};
use rustfmt::config::{Config, WriteMode};

use std::env;
use std::fs::{self, File};
use std::io::{self, ErrorKind, Read, Write};
use std::path::{Path, PathBuf};
use std::str::FromStr;

use getopts::{Matches, Options};

/// Rustfmt operations.
enum Operation {
    /// Format files and their child modules.
    Format {
        files: Vec<PathBuf>,
        config_path: Option<PathBuf>,
    },
    /// Print the help message.
    Help,
    // Print version information
    Version,
    /// Print detailed configuration help.
    ConfigHelp,
    /// Invalid program input.
    InvalidInput {
        reason: String,
    },
    /// No file specified, read from stdin
    Stdin {
        input: String,
        config_path: Option<PathBuf>,
    },
}

/// Try to find a project file in the given directory and its parents. Returns the path of a the
/// nearest project file if one exists, or `None` if no project file was found.
fn lookup_project_file(dir: &Path) -> io::Result<Option<PathBuf>> {
    let mut current = if dir.is_relative() {
        try!(env::current_dir()).join(dir)
    } else {
        dir.to_path_buf()
    };

    current = try!(fs::canonicalize(current));

    loop {
        let config_file = current.join("rustfmt.toml");
        match fs::metadata(&config_file) {
            Ok(md) => {
                // Properly handle unlikely situation of a directory named `rustfmt.toml`.
                if md.is_file() {
                    return Ok(Some(config_file));
                }
            }
            // If it's not found, we continue searching; otherwise something went wrong and we
            // return the error.
            Err(e) => {
                if e.kind() != ErrorKind::NotFound {
                    return Err(e);
                }
            }
        }

        // If the current directory has no parent, we're done searching.
        if !current.pop() {
            return Ok(None);
        }
    }
}

/// Resolve the config for input in `dir`.
///
/// Returns the `Config` to use, and the path of the project file if there was
/// one.
fn resolve_config(dir: &Path) -> io::Result<(Config, Option<PathBuf>)> {
    let path = try!(lookup_project_file(dir));
    if path.is_none() {
        return Ok((Config::default(), None));
    }
    let path = path.unwrap();
    let mut file = try!(File::open(&path));
    let mut toml = String::new();
    try!(file.read_to_string(&mut toml));
    Ok((Config::from_toml(&toml), Some(path)))
}

/// read the given config file path recursively if present else read the project file path
fn match_cli_path_or_file(config_path: Option<PathBuf>,
                          input_file: &Path)
                          -> io::Result<(Config, Option<PathBuf>)> {

    if let Some(config_file) = config_path {
        let (toml, path) = try!(resolve_config(config_file.as_ref()));
        if path.is_some() {
            return Ok((toml, path));
        }
    }
    resolve_config(input_file)
}

fn update_config(config: &mut Config, matches: &Matches) -> Result<(), String> {
    config.verbose = matches.opt_present("verbose");
    config.skip_children = matches.opt_present("skip-children");

    let write_mode = matches.opt_str("write-mode");
    match matches.opt_str("write-mode").map(|wm| WriteMode::from_str(&wm)) {
        None => Ok(()),
        Some(Ok(write_mode)) => {
            config.write_mode = write_mode;
            Ok(())
        }
        Some(Err(_)) => Err(format!("Invalid write-mode: {}", write_mode.expect("cannot happen"))),
    }
}

fn make_opts() -> Options {
    let mut opts = Options::new();
    opts.optflag("h", "help", "show this message");
    opts.optflag("V", "version", "show version information");
    opts.optflag("v", "verbose", "show progress");
    opts.optopt("",
                "write-mode",
                "mode to write in (not usable when piping from stdin)",
                "[replace|overwrite|display|diff|coverage|checkstyle]");
    opts.optflag("", "skip-children", "don't reformat child modules");

    opts.optflag("",
                 "config-help",
                 "show details of rustfmt configuration options");
    opts.optopt("",
                "config-path",
                "Recursively searches the given path for the rustfmt.toml config file. If not \
                 found reverts to the input file path",
                "[Path for the configuration file]");

    opts
}

fn execute() -> i32 {
    let opts = make_opts();

    let matches = match opts.parse(env::args().skip(1)) {
        Ok(m) => m,
        Err(e) => {
            print_usage(&opts, &e.to_string());
            return 1;
        }
    };

    let operation = determine_operation(&matches);

    match operation {
        Operation::InvalidInput { reason } => {
            print_usage(&opts, &reason);
            1
        }
        Operation::Help => {
            print_usage(&opts, "");
            0
        }
        Operation::Version => {
            print_version();
            0
        }
        Operation::ConfigHelp => {
            Config::print_docs();
            0
        }
        Operation::Stdin { input, config_path } => {
            // try to read config from local directory
            let (mut config, _) = match_cli_path_or_file(config_path, &env::current_dir().unwrap())
                                      .expect("Error resolving config");

            // write_mode is always Plain for Stdin.
            config.write_mode = WriteMode::Plain;

            run(Input::Text(input), &config);
            0
        }
        Operation::Format { files, config_path } => {
            let mut config = Config::default();
            let mut path = None;
            // Load the config path file if provided
            if let Some(config_file) = config_path {
                let (cfg_tmp, path_tmp) = resolve_config(config_file.as_ref())
                                              .expect(&format!("Error resolving config for {:?}",
                                                               config_file));
                config = cfg_tmp;
                path = path_tmp;
            };
            if let Some(path) = path.as_ref() {
                println!("Using rustfmt config file {}", path.display());
            }
            for file in files {
                // Check the file directory if the config-path could not be read or not provided
                if path.is_none() {
                    let (config_tmp, path_tmp) = resolve_config(file.parent().unwrap())
                                                     .expect(&format!("Error resolving config \
                                                                       for {}",
                                                                      file.display()));
                    if let Some(path) = path_tmp.as_ref() {
                        println!("Using rustfmt config file {} for {}",
                                 path.display(),
                                 file.display());
                    }
                    config = config_tmp;
                }

                if let Err(e) = update_config(&mut config, &matches) {
                    print_usage(&opts, &e);
                    return 1;
                }
                run(Input::File(file), &config);
            }
            0
        }
    }
}

fn main() {
    let _ = env_logger::init();
    let exit_code = execute();

    // Make sure standard output is flushed before we exit.
    std::io::stdout().flush().unwrap();

    // Exit with given exit code.
    //
    // NOTE: This immediately terminates the process without doing any cleanup,
    // so make sure to finish all necessary cleanup before this is called.
    std::process::exit(exit_code);
}

fn print_usage(opts: &Options, reason: &str) {
    let reason = format!("{}\nusage: {} [options] <file>...",
                         reason,
                         env::args_os().next().unwrap().to_string_lossy());
    println!("{}", opts.usage(&reason));
}

fn print_version() {
    println!("{}.{}.{}{}",
             option_env!("CARGO_PKG_VERSION_MAJOR").unwrap_or("X"),
             option_env!("CARGO_PKG_VERSION_MINOR").unwrap_or("X"),
             option_env!("CARGO_PKG_VERSION_PATCH").unwrap_or("X"),
             option_env!("CARGO_PKG_VERSION_PRE").unwrap_or(""));
}

fn determine_operation(matches: &Matches) -> Operation {
    if matches.opt_present("h") {
        return Operation::Help;
    }

    if matches.opt_present("config-help") {
        return Operation::ConfigHelp;
    }

    if matches.opt_present("version") {
        return Operation::Version;
    }

    // Read the config_path and convert to parent dir if a file is provided.
    let config_path: Option<PathBuf> = matches.opt_str("config-path")
                                              .map(PathBuf::from)
                                              .and_then(|dir| {
                                                  if dir.is_file() {
                                                      return dir.parent().map(|v| v.into());
                                                  }
                                                  Some(dir)
                                              });

    // if no file argument is supplied, read from stdin
    if matches.free.is_empty() {

        let mut buffer = String::new();
        match io::stdin().read_to_string(&mut buffer) {
            Ok(..) => (),
            Err(e) => return Operation::InvalidInput { reason: e.to_string() },
        }

        return Operation::Stdin {
            input: buffer,
            config_path: config_path,
        };
    }

    let files: Vec<_> = matches.free.iter().map(PathBuf::from).collect();

    Operation::Format {
        files: files,
        config_path: config_path,
    }
}
<commit_msg>rustfmt: Make error handling more idiomatic<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![cfg(not(test))]

extern crate log;
extern crate rustfmt;
extern crate toml;
extern crate env_logger;
extern crate getopts;

use rustfmt::{run, Input};
use rustfmt::config::{Config, WriteMode};

use std::{env, error};
use std::fs::{self, File};
use std::io::{self, ErrorKind, Read, Write};
use std::path::{Path, PathBuf};
use std::str::FromStr;

use getopts::{Matches, Options};

type FmtError = Box<error::Error + Send + Sync>;
type FmtResult<T> = std::result::Result<T, FmtError>;

/// Rustfmt operations.
enum Operation {
    /// Format files and their child modules.
    Format {
        files: Vec<PathBuf>,
        config_path: Option<PathBuf>,
    },
    /// Print the help message.
    Help,
    // Print version information
    Version,
    /// Print detailed configuration help.
    ConfigHelp,
    /// No file specified, read from stdin
    Stdin {
        input: String,
        config_path: Option<PathBuf>,
    },
}

/// Try to find a project file in the given directory and its parents. Returns the path of a the
/// nearest project file if one exists, or `None` if no project file was found.
fn lookup_project_file(dir: &Path) -> FmtResult<Option<PathBuf>> {
    let mut current = if dir.is_relative() {
        try!(env::current_dir()).join(dir)
    } else {
        dir.to_path_buf()
    };

    current = try!(fs::canonicalize(current));

    loop {
        let config_file = current.join("rustfmt.toml");
        match fs::metadata(&config_file) {
            Ok(md) => {
                // Properly handle unlikely situation of a directory named `rustfmt.toml`.
                if md.is_file() {
                    return Ok(Some(config_file));
                }
            }
            // If it's not found, we continue searching; otherwise something went wrong and we
            // return the error.
            Err(e) => {
                if e.kind() != ErrorKind::NotFound {
                    return Err(FmtError::from(e));
                }
            }
        }

        // If the current directory has no parent, we're done searching.
        if !current.pop() {
            return Ok(None);
        }
    }
}

/// Resolve the config for input in `dir`.
///
/// Returns the `Config` to use, and the path of the project file if there was
/// one.
fn resolve_config(dir: &Path) -> FmtResult<(Config, Option<PathBuf>)> {
    let path = try!(lookup_project_file(dir));
    if path.is_none() {
        return Ok((Config::default(), None));
    }
    let path = path.unwrap();
    let mut file = try!(File::open(&path));
    let mut toml = String::new();
    try!(file.read_to_string(&mut toml));
    Ok((Config::from_toml(&toml), Some(path)))
}

/// read the given config file path recursively if present else read the project file path
fn match_cli_path_or_file(config_path: Option<PathBuf>,
                          input_file: &Path)
                          -> FmtResult<(Config, Option<PathBuf>)> {

    if let Some(config_file) = config_path {
        let (toml, path) = try!(resolve_config(config_file.as_ref()));
        if path.is_some() {
            return Ok((toml, path));
        }
    }
    resolve_config(input_file)
}

fn update_config(config: &mut Config, matches: &Matches) -> FmtResult<()> {
    config.verbose = matches.opt_present("verbose");
    config.skip_children = matches.opt_present("skip-children");

    let write_mode = matches.opt_str("write-mode");
    match matches.opt_str("write-mode").map(|wm| WriteMode::from_str(&wm)) {
        None => Ok(()),
        Some(Ok(write_mode)) => {
            config.write_mode = write_mode;
            Ok(())
        }
        Some(Err(_)) => {
            Err(FmtError::from(format!("Invalid write-mode: {}",
                                       write_mode.expect("cannot happen"))))
        }
    }
}

fn make_opts() -> Options {
    let mut opts = Options::new();
    opts.optflag("h", "help", "show this message");
    opts.optflag("V", "version", "show version information");
    opts.optflag("v", "verbose", "show progress");
    opts.optopt("",
                "write-mode",
                "mode to write in (not usable when piping from stdin)",
                "[replace|overwrite|display|diff|coverage|checkstyle]");
    opts.optflag("", "skip-children", "don't reformat child modules");

    opts.optflag("",
                 "config-help",
                 "show details of rustfmt configuration options");
    opts.optopt("",
                "config-path",
                "Recursively searches the given path for the rustfmt.toml config file. If not \
                 found reverts to the input file path",
                "[Path for the configuration file]");

    opts
}

fn execute(opts: &Options) -> FmtResult<()> {
    let matches = try!(opts.parse(env::args().skip(1)));

    match try!(determine_operation(&matches)) {
        Operation::Help => {
            print_usage(&opts, "");
        }
        Operation::Version => {
            print_version();
        }
        Operation::ConfigHelp => {
            Config::print_docs();
        }
        Operation::Stdin { input, config_path } => {
            // try to read config from local directory
            let (mut config, _) = match_cli_path_or_file(config_path, &env::current_dir().unwrap())
                                      .expect("Error resolving config");

            // write_mode is always Plain for Stdin.
            config.write_mode = WriteMode::Plain;

            run(Input::Text(input), &config);
        }
        Operation::Format { files, config_path } => {
            let mut config = Config::default();
            let mut path = None;
            // Load the config path file if provided
            if let Some(config_file) = config_path {
                let (cfg_tmp, path_tmp) = resolve_config(config_file.as_ref())
                                              .expect(&format!("Error resolving config for {:?}",
                                                               config_file));
                config = cfg_tmp;
                path = path_tmp;
            };
            if let Some(path) = path.as_ref() {
                println!("Using rustfmt config file {}", path.display());
            }
            for file in files {
                // Check the file directory if the config-path could not be read or not provided
                if path.is_none() {
                    let (config_tmp, path_tmp) = resolve_config(file.parent().unwrap())
                                                     .expect(&format!("Error resolving config \
                                                                       for {}",
                                                                      file.display()));
                    if let Some(path) = path_tmp.as_ref() {
                        println!("Using rustfmt config file {} for {}",
                                 path.display(),
                                 file.display());
                    }
                    config = config_tmp;
                }

                try!(update_config(&mut config, &matches));
                run(Input::File(file), &config);
            }
        }
    }
    Ok(())
}

fn main() {
    let _ = env_logger::init();

    let opts = make_opts();

    let exit_code = match execute(&opts) {
        Ok(..) => 0,
        Err(e) => {
            print_usage(&opts, &e.to_string());
            1
        }
    };
    // Make sure standard output is flushed before we exit.
    std::io::stdout().flush().unwrap();

    // Exit with given exit code.
    //
    // NOTE: This immediately terminates the process without doing any cleanup,
    // so make sure to finish all necessary cleanup before this is called.
    std::process::exit(exit_code);
}

fn print_usage(opts: &Options, reason: &str) {
    let reason = format!("{}\nusage: {} [options] <file>...",
                         reason,
                         env::args_os().next().unwrap().to_string_lossy());
    println!("{}", opts.usage(&reason));
}

fn print_version() {
    println!("{}.{}.{}{}",
             option_env!("CARGO_PKG_VERSION_MAJOR").unwrap_or("X"),
             option_env!("CARGO_PKG_VERSION_MINOR").unwrap_or("X"),
             option_env!("CARGO_PKG_VERSION_PATCH").unwrap_or("X"),
             option_env!("CARGO_PKG_VERSION_PRE").unwrap_or(""));
}

fn determine_operation(matches: &Matches) -> FmtResult<Operation> {
    if matches.opt_present("h") {
        return Ok(Operation::Help);
    }

    if matches.opt_present("config-help") {
        return Ok(Operation::ConfigHelp);
    }

    if matches.opt_present("version") {
        return Ok(Operation::Version);
    }

    // Read the config_path and convert to parent dir if a file is provided.
    let config_path: Option<PathBuf> = matches.opt_str("config-path")
                                              .map(PathBuf::from)
                                              .and_then(|dir| {
                                                  if dir.is_file() {
                                                      return dir.parent().map(|v| v.into());
                                                  }
                                                  Some(dir)
                                              });

    // if no file argument is supplied, read from stdin
    if matches.free.is_empty() {

        let mut buffer = String::new();
        try!(io::stdin().read_to_string(&mut buffer));

        return Ok(Operation::Stdin {
            input: buffer,
            config_path: config_path,
        });
    }

    let files: Vec<_> = matches.free.iter().map(PathBuf::from).collect();

    Ok(Operation::Format {
        files: files,
        config_path: config_path,
    })
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![cfg(not(test))]

extern crate env_logger;
extern crate getopts;
extern crate rustfmt_nightly as rustfmt;

use std::{env, error};
use std::fs::File;
use std::io::{self, Read, Write};
use std::path::{Path, PathBuf};
use std::str::FromStr;

use getopts::{HasArg, Matches, Occur, Options};

use rustfmt::{run, Input, Summary};
use rustfmt::file_lines::FileLines;
use rustfmt::config::{get_toml_path, Color, Config, WriteMode};

type FmtError = Box<error::Error + Send + Sync>;
type FmtResult<T> = std::result::Result<T, FmtError>;

/// Rustfmt operations.
enum Operation {
    /// Format files and their child modules.
    Format {
        files: Vec<PathBuf>,
        config_path: Option<PathBuf>,
        minimal_config_path: Option<String>,
    },
    /// Print the help message.
    Help,
    // Print version information
    Version,
    /// Print detailed configuration help.
    ConfigHelp,
    /// Output default config to a file, or stdout if None
    ConfigOutputDefault {
        path: Option<String>,
    },
    /// No file specified, read from stdin
    Stdin {
        input: String,
        config_path: Option<PathBuf>,
    },
}

/// Parsed command line options.
#[derive(Clone, Debug, Default)]
struct CliOptions {
    skip_children: bool,
    verbose: bool,
    write_mode: Option<WriteMode>,
    color: Option<Color>,
    file_lines: FileLines, // Default is all lines in all files.
    unstable_features: bool,
}

impl CliOptions {
    fn from_matches(matches: &Matches) -> FmtResult<CliOptions> {
        let mut options = CliOptions::default();
        options.skip_children = matches.opt_present("skip-children");
        options.verbose = matches.opt_present("verbose");
        let unstable_features = matches.opt_present("unstable-features");
        let rust_nightly = option_env!("CFG_RELEASE_CHANNEL")
            .map(|c| c == "nightly")
            .unwrap_or(false);
        if unstable_features && !rust_nightly {
            return Err(FmtError::from(
                "Unstable features are only available on Nightly channel",
            ));
        } else {
            options.unstable_features = unstable_features;
        }

        if let Some(ref write_mode) = matches.opt_str("write-mode") {
            if let Ok(write_mode) = WriteMode::from_str(write_mode) {
                options.write_mode = Some(write_mode);
            } else {
                return Err(FmtError::from(format!(
                    "Invalid write-mode: {}",
                    write_mode
                )));
            }
        }

        if let Some(ref color) = matches.opt_str("color") {
            match Color::from_str(color) {
                Ok(color) => options.color = Some(color),
                _ => return Err(FmtError::from(format!("Invalid color: {}", color))),
            }
        }

        if let Some(ref file_lines) = matches.opt_str("file-lines") {
            options.file_lines = file_lines.parse()?;
        }

        Ok(options)
    }

    fn apply_to(self, config: &mut Config) {
        config.set().skip_children(self.skip_children);
        config.set().verbose(self.verbose);
        config.set().file_lines(self.file_lines);
        config.set().unstable_features(self.unstable_features);
        if let Some(write_mode) = self.write_mode {
            config.set().write_mode(write_mode);
        }
        if let Some(color) = self.color {
            config.set().color(color);
        }
    }
}

/// read the given config file path recursively if present else read the project file path
fn match_cli_path_or_file(
    config_path: Option<PathBuf>,
    input_file: &Path,
) -> FmtResult<(Config, Option<PathBuf>)> {
    if let Some(config_file) = config_path {
        let toml = Config::from_toml_path(config_file.as_ref())?;
        return Ok((toml, Some(config_file)));
    }
    Config::from_resolved_toml_path(input_file).map_err(FmtError::from)
}

fn make_opts() -> Options {
    let mut opts = Options::new();
    opts.optflag("h", "help", "show this message");
    opts.optflag("V", "version", "show version information");
    opts.optflag("v", "verbose", "print verbose output");
    opts.optopt(
        "",
        "write-mode",
        "how to write output (not usable when piping from stdin)",
        "[replace|overwrite|display|plain|diff|coverage|checkstyle]",
    );
    opts.optopt(
        "",
        "color",
        "use colored output (if supported)",
        "[always|never|auto]",
    );
    opts.optflag("", "skip-children", "don't reformat child modules");

    opts.optflag(
        "",
        "unstable-features",
        "Enables unstable features. Only available on nightly channel",
    );

    opts.optflag(
        "",
        "config-help",
        "show details of rustfmt configuration options",
    );
    opts.opt(
        "",
        "dump-default-config",
        "Dumps default configuration to PATH. PATH defaults to stdout, if omitted.",
        "PATH",
        HasArg::Maybe,
        Occur::Optional,
    );
    opts.optopt(
        "",
        "dump-minimal-config",
        "Dumps configuration options that were checked during formatting to a file.",
        "PATH",
    );
    opts.optopt(
        "",
        "config-path",
        "Recursively searches the given path for the rustfmt.toml config file. If not \
         found reverts to the input file path",
        "[Path for the configuration file]",
    );
    opts.optopt(
        "",
        "file-lines",
        "Format specified line ranges. See README for more detail on the JSON format.",
        "JSON",
    );

    opts
}

fn execute(opts: &Options) -> FmtResult<Summary> {
    let matches = opts.parse(env::args().skip(1))?;

    match determine_operation(&matches)? {
        Operation::Help => {
            print_usage_to_stdout(opts, "");
            Summary::print_exit_codes();
            Ok(Summary::default())
        }
        Operation::Version => {
            print_version();
            Ok(Summary::default())
        }
        Operation::ConfigHelp => {
            Config::print_docs();
            Ok(Summary::default())
        }
        Operation::ConfigOutputDefault { path } => {
            let toml = Config::default().all_options().to_toml()?;
            if let Some(path) = path {
                let mut file = File::create(path)?;
                file.write_all(toml.as_bytes())?;
            } else {
                io::stdout().write_all(toml.as_bytes())?;
            }
            Ok(Summary::default())
        }
        Operation::Stdin { input, config_path } => {
            // try to read config from local directory
            let (mut config, _) =
                match_cli_path_or_file(config_path, &env::current_dir().unwrap())?;

            // write_mode is always Plain for Stdin.
            config.set().write_mode(WriteMode::Plain);

            // parse file_lines
            if let Some(ref file_lines) = matches.opt_str("file-lines") {
                config.set().file_lines(file_lines.parse()?);
                for f in config.file_lines().files() {
                    if f != "stdin" {
                        eprintln!("Warning: Extra file listed in file_lines option '{}'", f);
                    }
                }
            }

            let mut error_summary = Summary::default();
            if config.version_meets_requirement(&mut error_summary) {
                error_summary.add(run(Input::Text(input), &config));
            }

            Ok(error_summary)
        }
        Operation::Format {
            files,
            config_path,
            minimal_config_path,
        } => {
            let options = CliOptions::from_matches(&matches)?;

            for f in options.file_lines.files() {
                if !files.contains(&PathBuf::from(f)) {
                    eprintln!("Warning: Extra file listed in file_lines option '{}'", f);
                }
            }

            let mut config = Config::default();
            // Load the config path file if provided
            if let Some(config_file) = config_path.as_ref() {
                config = Config::from_toml_path(config_file.as_ref())?;
            };

            if options.verbose {
                if let Some(path) = config_path.as_ref() {
                    println!("Using rustfmt config file {}", path.display());
                }
            }

            let mut error_summary = Summary::default();
            for file in files {
                if !file.exists() {
                    eprintln!("Error: file `{}` does not exist", file.to_str().unwrap());
                    error_summary.add_operational_error();
                } else if file.is_dir() {
                    eprintln!("Error: `{}` is a directory", file.to_str().unwrap());
                    error_summary.add_operational_error();
                } else {
                    // Check the file directory if the config-path could not be read or not provided
                    if config_path.is_none() {
                        let (config_tmp, path_tmp) =
                            Config::from_resolved_toml_path(file.parent().unwrap())?;
                        if options.verbose {
                            if let Some(path) = path_tmp.as_ref() {
                                println!(
                                    "Using rustfmt config file {} for {}",
                                    path.display(),
                                    file.display()
                                );
                            }
                        }
                        config = config_tmp;
                    }

                    if !config.version_meets_requirement(&mut error_summary) {
                        break;
                    }

                    options.clone().apply_to(&mut config);
                    error_summary.add(run(Input::File(file), &config));
                }
            }

            // If we were given a path via dump-minimal-config, output any options
            // that were used during formatting as TOML.
            if let Some(path) = minimal_config_path {
                let mut file = File::create(path)?;
                let toml = config.used_options().to_toml()?;
                file.write_all(toml.as_bytes())?;
            }

            Ok(error_summary)
        }
    }
}

fn main() {
    let _ = env_logger::init();

    let opts = make_opts();

    let exit_code = match execute(&opts) {
        Ok(summary) => {
            if summary.has_operational_errors() {
                1
            } else if summary.has_parsing_errors() {
                2
            } else if summary.has_formatting_errors() {
                3
            } else if summary.has_diff {
                // should only happen in diff mode
                4
            } else {
                assert!(summary.has_no_errors());
                0
            }
        }
        Err(e) => {
            print_usage_to_stderr(&opts, &e.to_string());
            1
        }
    };
    // Make sure standard output is flushed before we exit.
    std::io::stdout().flush().unwrap();

    // Exit with given exit code.
    //
    // NOTE: This immediately terminates the process without doing any cleanup,
    // so make sure to finish all necessary cleanup before this is called.
    std::process::exit(exit_code);
}

macro_rules! print_usage {
    ($print:ident, $opts:ident, $reason:expr) => ({
        let msg = format!(
            "{}\n\nusage: {} [options] <file>...",
            $reason,
            env::args_os().next().unwrap().to_string_lossy()
        );
        $print!("{}", $opts.usage(&msg));
    })
}

fn print_usage_to_stdout(opts: &Options, reason: &str) {
    print_usage!(println, opts, reason);
}

fn print_usage_to_stderr(opts: &Options, reason: &str) {
    print_usage!(eprintln, opts, reason);
}

fn print_version() {
    println!(
        "{}-nightly{}",
        env!("CARGO_PKG_VERSION"),
        include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt"))
    )
}

fn determine_operation(matches: &Matches) -> FmtResult<Operation> {
    if matches.opt_present("h") {
        return Ok(Operation::Help);
    }

    if matches.opt_present("config-help") {
        return Ok(Operation::ConfigHelp);
    }

    if matches.opt_present("dump-default-config") {
        // NOTE for some reason when configured with HasArg::Maybe + Occur::Optional opt_default
        // doesn't recognize `--foo bar` as a long flag with an argument but as a long flag with no
        // argument *plus* a free argument. Thus we check for that case in this branch -- this is
        // required for backward compatibility.
        if let Some(path) = matches.free.get(0) {
            return Ok(Operation::ConfigOutputDefault {
                path: Some(path.clone()),
            });
        } else {
            return Ok(Operation::ConfigOutputDefault {
                path: matches.opt_str("dump-default-config"),
            });
        }
    }

    if matches.opt_present("version") {
        return Ok(Operation::Version);
    }

    let config_path_not_found = |path: &str| -> FmtResult<Operation> {
        Err(FmtError::from(format!(
            "Error: unable to find a config file for the given path: `{}`",
            path
        )))
    };

    // Read the config_path and convert to parent dir if a file is provided.
    // If a config file cannot be found from the given path, return error.
    let config_path: Option<PathBuf> = match matches.opt_str("config-path").map(PathBuf::from) {
        Some(ref path) if !path.exists() => return config_path_not_found(path.to_str().unwrap()),
        Some(ref path) if path.is_dir() => {
            let config_file_path = get_toml_path(path)?;
            if config_file_path.is_some() {
                config_file_path
            } else {
                return config_path_not_found(path.to_str().unwrap());
            }
        }
        path => path,
    };

    // If no path is given, we won't output a minimal config.
    let minimal_config_path = matches.opt_str("dump-minimal-config");

    // if no file argument is supplied, read from stdin
    if matches.free.is_empty() {
        let mut buffer = String::new();
        io::stdin().read_to_string(&mut buffer)?;

        return Ok(Operation::Stdin {
            input: buffer,
            config_path: config_path,
        });
    }

    let files: Vec<_> = matches
        .free
        .iter()
        .map(|s| {
            let p = PathBuf::from(s);
            // we will do comparison later, so here tries to canonicalize first
            // to get the expected behavior.
            p.canonicalize().unwrap_or(p)
        })
        .collect();

    Ok(Operation::Format {
        files: files,
        config_path: config_path,
        minimal_config_path: minimal_config_path,
    })
}
<commit_msg>Add error-on-unformatted command line option<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![cfg(not(test))]

extern crate env_logger;
extern crate getopts;
extern crate rustfmt_nightly as rustfmt;

use std::{env, error};
use std::fs::File;
use std::io::{self, Read, Write};
use std::path::{Path, PathBuf};
use std::str::FromStr;

use getopts::{HasArg, Matches, Occur, Options};

use rustfmt::{run, Input, Summary};
use rustfmt::file_lines::FileLines;
use rustfmt::config::{get_toml_path, Color, Config, WriteMode};

type FmtError = Box<error::Error + Send + Sync>;
type FmtResult<T> = std::result::Result<T, FmtError>;

/// Rustfmt operations.
enum Operation {
    /// Format files and their child modules.
    Format {
        files: Vec<PathBuf>,
        config_path: Option<PathBuf>,
        minimal_config_path: Option<String>,
    },
    /// Print the help message.
    Help,
    // Print version information
    Version,
    /// Print detailed configuration help.
    ConfigHelp,
    /// Output default config to a file, or stdout if None
    ConfigOutputDefault {
        path: Option<String>,
    },
    /// No file specified, read from stdin
    Stdin {
        input: String,
        config_path: Option<PathBuf>,
    },
}

/// Parsed command line options.
#[derive(Clone, Debug, Default)]
struct CliOptions {
    skip_children: bool,
    verbose: bool,
    write_mode: Option<WriteMode>,
    color: Option<Color>,
    file_lines: FileLines, // Default is all lines in all files.
    unstable_features: bool,
}

impl CliOptions {
    fn from_matches(matches: &Matches) -> FmtResult<CliOptions> {
        let mut options = CliOptions::default();
        options.skip_children = matches.opt_present("skip-children");
        options.verbose = matches.opt_present("verbose");
        let unstable_features = matches.opt_present("unstable-features");
        let rust_nightly = option_env!("CFG_RELEASE_CHANNEL")
            .map(|c| c == "nightly")
            .unwrap_or(false);
        if unstable_features && !rust_nightly {
            return Err(FmtError::from(
                "Unstable features are only available on Nightly channel",
            ));
        } else {
            options.unstable_features = unstable_features;
        }

        if let Some(ref write_mode) = matches.opt_str("write-mode") {
            if let Ok(write_mode) = WriteMode::from_str(write_mode) {
                options.write_mode = Some(write_mode);
            } else {
                return Err(FmtError::from(format!(
                    "Invalid write-mode: {}",
                    write_mode
                )));
            }
        }

        if let Some(ref color) = matches.opt_str("color") {
            match Color::from_str(color) {
                Ok(color) => options.color = Some(color),
                _ => return Err(FmtError::from(format!("Invalid color: {}", color))),
            }
        }

        if let Some(ref file_lines) = matches.opt_str("file-lines") {
            options.file_lines = file_lines.parse()?;
        }

        Ok(options)
    }

    fn apply_to(self, config: &mut Config) {
        config.set().skip_children(self.skip_children);
        config.set().verbose(self.verbose);
        config.set().file_lines(self.file_lines);
        config.set().unstable_features(self.unstable_features);
        if let Some(write_mode) = self.write_mode {
            config.set().write_mode(write_mode);
        }
        if let Some(color) = self.color {
            config.set().color(color);
        }
    }
}

/// read the given config file path recursively if present else read the project file path
fn match_cli_path_or_file(
    config_path: Option<PathBuf>,
    input_file: &Path,
) -> FmtResult<(Config, Option<PathBuf>)> {
    if let Some(config_file) = config_path {
        let toml = Config::from_toml_path(config_file.as_ref())?;
        return Ok((toml, Some(config_file)));
    }
    Config::from_resolved_toml_path(input_file).map_err(FmtError::from)
}

fn make_opts() -> Options {
    let mut opts = Options::new();
    opts.optflag("h", "help", "show this message");
    opts.optflag("V", "version", "show version information");
    opts.optflag("v", "verbose", "print verbose output");
    opts.optopt(
        "",
        "write-mode",
        "how to write output (not usable when piping from stdin)",
        "[replace|overwrite|display|plain|diff|coverage|checkstyle]",
    );
    opts.optopt(
        "",
        "color",
        "use colored output (if supported)",
        "[always|never|auto]",
    );
    opts.optflag("", "skip-children", "don't reformat child modules");

    opts.optflag(
        "",
        "unstable-features",
        "Enables unstable features. Only available on nightly channel",
    );

    opts.optflag(
        "",
        "config-help",
        "show details of rustfmt configuration options",
    );
    opts.optflag(
        "",
        "error-on-unformatted",
        "Error if unable to get comments or string literals within max_width, \
         or they are left with trailing whitespaces",
    );
    opts.opt(
        "",
        "dump-default-config",
        "Dumps default configuration to PATH. PATH defaults to stdout, if omitted.",
        "PATH",
        HasArg::Maybe,
        Occur::Optional,
    );
    opts.optopt(
        "",
        "dump-minimal-config",
        "Dumps configuration options that were checked during formatting to a file.",
        "PATH",
    );
    opts.optopt(
        "",
        "config-path",
        "Recursively searches the given path for the rustfmt.toml config file. If not \
         found reverts to the input file path",
        "[Path for the configuration file]",
    );
    opts.optopt(
        "",
        "file-lines",
        "Format specified line ranges. See README for more detail on the JSON format.",
        "JSON",
    );

    opts
}

fn execute(opts: &Options) -> FmtResult<Summary> {
    let matches = opts.parse(env::args().skip(1))?;

    match determine_operation(&matches)? {
        Operation::Help => {
            print_usage_to_stdout(opts, "");
            Summary::print_exit_codes();
            Ok(Summary::default())
        }
        Operation::Version => {
            print_version();
            Ok(Summary::default())
        }
        Operation::ConfigHelp => {
            Config::print_docs();
            Ok(Summary::default())
        }
        Operation::ConfigOutputDefault { path } => {
            let toml = Config::default().all_options().to_toml()?;
            if let Some(path) = path {
                let mut file = File::create(path)?;
                file.write_all(toml.as_bytes())?;
            } else {
                io::stdout().write_all(toml.as_bytes())?;
            }
            Ok(Summary::default())
        }
        Operation::Stdin { input, config_path } => {
            // try to read config from local directory
            let (mut config, _) =
                match_cli_path_or_file(config_path, &env::current_dir().unwrap())?;

            // write_mode is always Plain for Stdin.
            config.set().write_mode(WriteMode::Plain);

            // parse file_lines
            if let Some(ref file_lines) = matches.opt_str("file-lines") {
                config.set().file_lines(file_lines.parse()?);
                for f in config.file_lines().files() {
                    if f != "stdin" {
                        eprintln!("Warning: Extra file listed in file_lines option '{}'", f);
                    }
                }
            }

            let mut error_summary = Summary::default();
            if config.version_meets_requirement(&mut error_summary) {
                error_summary.add(run(Input::Text(input), &config));
            }

            Ok(error_summary)
        }
        Operation::Format {
            files,
            config_path,
            minimal_config_path,
        } => {
            let options = CliOptions::from_matches(&matches)?;

            for f in options.file_lines.files() {
                if !files.contains(&PathBuf::from(f)) {
                    eprintln!("Warning: Extra file listed in file_lines option '{}'", f);
                }
            }

            let mut config = Config::default();
            // Load the config path file if provided
            if let Some(config_file) = config_path.as_ref() {
                config = Config::from_toml_path(config_file.as_ref())?;
            };

            if options.verbose {
                if let Some(path) = config_path.as_ref() {
                    println!("Using rustfmt config file {}", path.display());
                }
            }

            let mut error_summary = Summary::default();
            for file in files {
                if !file.exists() {
                    eprintln!("Error: file `{}` does not exist", file.to_str().unwrap());
                    error_summary.add_operational_error();
                } else if file.is_dir() {
                    eprintln!("Error: `{}` is a directory", file.to_str().unwrap());
                    error_summary.add_operational_error();
                } else {
                    // Check the file directory if the config-path could not be read or not provided
                    if config_path.is_none() {
                        let (config_tmp, path_tmp) =
                            Config::from_resolved_toml_path(file.parent().unwrap())?;
                        if options.verbose {
                            if let Some(path) = path_tmp.as_ref() {
                                println!(
                                    "Using rustfmt config file {} for {}",
                                    path.display(),
                                    file.display()
                                );
                            }
                        }
                        config = config_tmp;
                    }

                    if !config.version_meets_requirement(&mut error_summary) {
                        break;
                    }

                    options.clone().apply_to(&mut config);
                    error_summary.add(run(Input::File(file), &config));
                }
            }

            // If we were given a path via dump-minimal-config, output any options
            // that were used during formatting as TOML.
            if let Some(path) = minimal_config_path {
                let mut file = File::create(path)?;
                let toml = config.used_options().to_toml()?;
                file.write_all(toml.as_bytes())?;
            }

            Ok(error_summary)
        }
    }
}

fn main() {
    let _ = env_logger::init();

    let opts = make_opts();

    let exit_code = match execute(&opts) {
        Ok(summary) => {
            if summary.has_operational_errors() {
                1
            } else if summary.has_parsing_errors() {
                2
            } else if summary.has_formatting_errors() {
                3
            } else if summary.has_diff {
                // should only happen in diff mode
                4
            } else {
                assert!(summary.has_no_errors());
                0
            }
        }
        Err(e) => {
            print_usage_to_stderr(&opts, &e.to_string());
            1
        }
    };
    // Make sure standard output is flushed before we exit.
    std::io::stdout().flush().unwrap();

    // Exit with given exit code.
    //
    // NOTE: This immediately terminates the process without doing any cleanup,
    // so make sure to finish all necessary cleanup before this is called.
    std::process::exit(exit_code);
}

macro_rules! print_usage {
    ($print:ident, $opts:ident, $reason:expr) => ({
        let msg = format!(
            "{}\n\nusage: {} [options] <file>...",
            $reason,
            env::args_os().next().unwrap().to_string_lossy()
        );
        $print!("{}", $opts.usage(&msg));
    })
}

fn print_usage_to_stdout(opts: &Options, reason: &str) {
    print_usage!(println, opts, reason);
}

fn print_usage_to_stderr(opts: &Options, reason: &str) {
    print_usage!(eprintln, opts, reason);
}

fn print_version() {
    println!(
        "{}-nightly{}",
        env!("CARGO_PKG_VERSION"),
        include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt"))
    )
}

fn determine_operation(matches: &Matches) -> FmtResult<Operation> {
    if matches.opt_present("h") {
        return Ok(Operation::Help);
    }

    if matches.opt_present("config-help") {
        return Ok(Operation::ConfigHelp);
    }

    if matches.opt_present("dump-default-config") {
        // NOTE for some reason when configured with HasArg::Maybe + Occur::Optional opt_default
        // doesn't recognize `--foo bar` as a long flag with an argument but as a long flag with no
        // argument *plus* a free argument. Thus we check for that case in this branch -- this is
        // required for backward compatibility.
        if let Some(path) = matches.free.get(0) {
            return Ok(Operation::ConfigOutputDefault {
                path: Some(path.clone()),
            });
        } else {
            return Ok(Operation::ConfigOutputDefault {
                path: matches.opt_str("dump-default-config"),
            });
        }
    }

    if matches.opt_present("version") {
        return Ok(Operation::Version);
    }

    let config_path_not_found = |path: &str| -> FmtResult<Operation> {
        Err(FmtError::from(format!(
            "Error: unable to find a config file for the given path: `{}`",
            path
        )))
    };

    // Read the config_path and convert to parent dir if a file is provided.
    // If a config file cannot be found from the given path, return error.
    let config_path: Option<PathBuf> = match matches.opt_str("config-path").map(PathBuf::from) {
        Some(ref path) if !path.exists() => return config_path_not_found(path.to_str().unwrap()),
        Some(ref path) if path.is_dir() => {
            let config_file_path = get_toml_path(path)?;
            if config_file_path.is_some() {
                config_file_path
            } else {
                return config_path_not_found(path.to_str().unwrap());
            }
        }
        path => path,
    };

    // If no path is given, we won't output a minimal config.
    let minimal_config_path = matches.opt_str("dump-minimal-config");

    // if no file argument is supplied, read from stdin
    if matches.free.is_empty() {
        let mut buffer = String::new();
        io::stdin().read_to_string(&mut buffer)?;

        return Ok(Operation::Stdin {
            input: buffer,
            config_path: config_path,
        });
    }

    let files: Vec<_> = matches
        .free
        .iter()
        .map(|s| {
            let p = PathBuf::from(s);
            // we will do comparison later, so here tries to canonicalize first
            // to get the expected behavior.
            p.canonicalize().unwrap_or(p)
        })
        .collect();

    Ok(Operation::Format {
        files: files,
        config_path: config_path,
        minimal_config_path: minimal_config_path,
    })
}
<|endoftext|>