<commit_before>// Copyright 2017 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate exonum;
extern crate router;
extern crate bodyparser;
extern crate iron;

use exonum::blockchain::{self, Blockchain, Service, GenesisConfig, ValidatorKeys, Transaction,
                         ApiContext};
use exonum::node::{Node, NodeConfig, NodeApiConfig, TransactionSend, ApiSender, NodeChannel};
use exonum::messages::{RawTransaction, FromRaw, Message};
use exonum::storage::{Fork, MemoryDB, MapIndex};
use exonum::crypto::{PublicKey, Hash, HexValue};
use exonum::encoding::{self, Field};
use exonum::api::{Api, ApiError};
use iron::prelude::*;
use iron::Handler;
use router::Router;

// // // // // // // // // // CONSTANTS // // // // // // // // // //

const SERVICE_ID: u16 = 1;

const TX_CREATE_WALLET_ID: u16 = 1;

const TX_TRANSFER_ID: u16 = 2;

const INIT_BALANCE: u64 = 100;

// // // // // // // // // // PERSISTENT DATA // // // // // // // // // //

encoding_struct! {
    struct Wallet {
        const SIZE = 48;

        field pub_key:            &PublicKey  [00 => 32]
        field name:               &str        [32 => 40]
        field balance:            u64         [40 => 48]
    }
}

impl Wallet {
    pub fn increase(&mut self, amount: u64) {
        let balance = self.balance() + amount;
        Field::write(&balance, &mut self.raw, 40, 48);
    }

    pub fn decrease(&mut self, amount: u64) {
        let balance = self.balance() - amount;
        Field::write(&balance, &mut self.raw, 40, 48);
    }
}

// // // // // // // // // // STORAGE DATA LAYOUT // // // // // // // // // //

pub struct CurrencySchema<'a> {
    view: &'a mut Fork,
}

impl<'a> CurrencySchema<'a> {
    pub fn wallets(&mut self) -> MapIndex<&mut Fork, PublicKey, Wallet> {
        let prefix = blockchain::gen_prefix(SERVICE_ID, 0, &());
        MapIndex::new(prefix, self.view)
    }

    pub fn wallet(&mut self, pub_key: &PublicKey) -> Option<Wallet> {
        self.wallets().get(pub_key)
    }
}

// // // // // // // // // // TRANSACTIONS // // // // // // // // // //

message! {
    struct TxCreateWallet {
        const TYPE = SERVICE_ID;
        const ID = TX_CREATE_WALLET_ID;
        const SIZE = 40;

        field pub_key:     &PublicKey  [00 => 32]
        field name:        &str        [32 => 40]
    }
}

message! {
    struct TxTransfer {
        const TYPE = SERVICE_ID;
        const ID = TX_TRANSFER_ID;
        const SIZE = 80;

        field from:        &PublicKey  [00 => 32]
        field to:          &PublicKey  [32 => 64]
        field amount:      u64         [64 => 72]
        field seed:        u64         [72 => 80]
    }
}

// // // // // // // // // // CONTRACTS // // // // // // // // // //

impl Transaction for TxCreateWallet {
    fn verify(&self) -> bool {
        self.verify_signature(self.pub_key())
    }

    fn execute(&self, view: &mut Fork) {
        let mut schema = CurrencySchema { view };
        if schema.wallet(self.pub_key()).is_none() {
            let wallet = Wallet::new(self.pub_key(), self.name(), INIT_BALANCE);
            println!("Create the wallet: {:?}", wallet);
            schema.wallets().put(self.pub_key(), wallet)
        }
    }
}

impl Transaction for TxTransfer {
    fn verify(&self) -> bool {
        (*self.from() != *self.to()) && self.verify_signature(self.from())
    }

    fn execute(&self, view: &mut Fork) {
        let mut schema = CurrencySchema { view };
        let sender = schema.wallet(self.from());
        let receiver = schema.wallet(self.to());
        if let (Some(mut sender), Some(mut receiver)) = (sender, receiver) {
            let amount = self.amount();
            if sender.balance() >= amount {
                sender.decrease(amount);
                receiver.increase(amount);
                println!("Transfer between wallets: {:?} => {:?}", sender, receiver);
                let mut wallets = schema.wallets();
                wallets.put(self.from(), sender);
                wallets.put(self.to(), receiver);
            }
        }
    }
}

// // // // // // // // // // REST API // // // // // // // // // //

#[derive(Clone)]
struct CryptocurrencyApi {
    channel: ApiSender<NodeChannel>,
    blockchain: Blockchain,
}

impl CryptocurrencyApi {
    fn get_wallet(&self, pub_key: &PublicKey) -> Option<Wallet> {
        let mut view = self.blockchain.fork();
        let mut schema = CurrencySchema { view: &mut view };
        schema.wallet(pub_key)
    }

    fn get_wallets(&self) -> Option<Vec<Wallet>> {
        let mut view = self.blockchain.fork();
        let mut schema = CurrencySchema { view: &mut view };
        let idx = schema.wallets();
        let wallets: Vec<Wallet> = idx.values().collect();
        if wallets.is_empty() {
            None
        } else {
            Some(wallets)
        }
    }
}

impl Api for CryptocurrencyApi {
    fn wire(&self, router: &mut Router) {

        #[serde(untagged)]
        #[derive(Clone, Serialize, Deserialize)]
        enum TransactionRequest {
            CreateWallet(TxCreateWallet),
            Transfer(TxTransfer),
        }

        impl Into<Box<Transaction>> for TransactionRequest {
            fn into(self) -> Box<Transaction> {
                match self {
                    TransactionRequest::CreateWallet(trans) => Box::new(trans),
                    TransactionRequest::Transfer(trans) => Box::new(trans),
                }
            }
        }

        #[derive(Serialize, Deserialize)]
        struct TransactionResponse {
            tx_hash: Hash,
        }

        let self_ = self.clone();
        let transaction = move |req: &mut Request| -> IronResult<Response> {
            match req.get::<bodyparser::Struct<TransactionRequest>>() {
                Ok(Some(transaction)) => {
                    let transaction: Box<Transaction> = transaction.into();
                    let tx_hash = transaction.hash();
                    self_.channel.send(transaction).map_err(ApiError::Events)?;
                    let json = TransactionResponse { tx_hash };
                    self_.ok_response(&serde_json::to_value(&json).unwrap())
                }
                Ok(None) => Err(ApiError::IncorrectRequest("Empty request body".into()))?,
                Err(e) => Err(ApiError::IncorrectRequest(Box::new(e)))?,
            }
        };

        let self_ = self.clone();
        let wallets_info = move |_: &mut Request| -> IronResult<Response> {
            if let Some(wallets) = self_.get_wallets() {
                self_.ok_response(&serde_json::to_value(wallets).unwrap())
            } else {
                self_.not_found_response(
                    &serde_json::to_value("Wallets database is empty")
                        .unwrap(),
                )
            }
        };

        let self_ = self.clone();
        let wallet_info = move |req: &mut Request| -> IronResult<Response> {
            let path = req.url.path();
            let wallet_key = path.last().unwrap();
            let public_key = PublicKey::from_hex(wallet_key).map_err(ApiError::FromHex)?;
            if let Some(wallet) = self_.get_wallet(&public_key) {
                self_.ok_response(&serde_json::to_value(wallet).unwrap())
            } else {
                self_.not_found_response(&serde_json::to_value("Wallet not found").unwrap())
            }
        };

        router.post("/v1/wallets/transaction", transaction, "transaction");
        router.get("/v1/wallets", wallets_info, "wallets_info");
        router.get("/v1/wallet/:pub_key", wallet_info, "wallet_info");
    }
}

// // // // // // // // // // SERVICE DECLARATION // // // // // // // // // //

struct CurrencyService;

impl Service for CurrencyService {
    fn service_name(&self) -> &'static str {
        "cryptocurrency"
    }

    fn service_id(&self) -> u16 {
        SERVICE_ID
    }

    fn tx_from_raw(&self, raw: RawTransaction) -> Result<Box<Transaction>, encoding::Error> {
        let trans: Box<Transaction> = match raw.message_type() {
            TX_TRANSFER_ID => Box::new(TxTransfer::from_raw(raw)?),
            TX_CREATE_WALLET_ID => Box::new(TxCreateWallet::from_raw(raw)?),
            _ => {
                return Err(encoding::Error::IncorrectMessageType {
                    message_type: raw.message_type(),
                });
            }
        };
        Ok(trans)
    }

    fn public_api_handler(&self, ctx: &ApiContext) -> Option<Box<Handler>> {
        let mut router = Router::new();
        let api = CryptocurrencyApi {
            channel: ctx.node_channel().clone(),
            blockchain: ctx.blockchain().clone(),
        };
        api.wire(&mut router);
        Some(Box::new(router))
    }
}

// // // // // // // // // // ENTRY POINT // // // // // // // // // //

fn main() {
    exonum::helpers::init_logger().unwrap();

    println!("Creating in-memory database...");
    let db = MemoryDB::new();
    let services: Vec<Box<Service>> = vec![Box::new(CurrencyService)];
    let blockchain = Blockchain::new(Box::new(db), services);

    let (consensus_public_key, consensus_secret_key) = exonum::crypto::gen_keypair();
    let (service_public_key, service_secret_key) = exonum::crypto::gen_keypair();

    let peer_address = "0.0.0.0:2000".parse().unwrap();
    let api_address = "0.0.0.0:8000".parse().unwrap();

    let validator_keys = ValidatorKeys {
        consensus_key: consensus_public_key,
        service_key: service_public_key,
    };
    let genesis = GenesisConfig::new(vec![validator_keys].into_iter());

    let api_cfg = NodeApiConfig {
        public_api_address: Some(api_address),
        ..Default::default()
    };

    let node_cfg = NodeConfig {
        listen_address: peer_address,
        peers: vec![],
        service_public_key,
        service_secret_key,
        consensus_public_key,
        consensus_secret_key,
        genesis,
        external_address: None,
        network: Default::default(),
        whitelist: Default::default(),
        api: api_cfg,
        mempool: Default::default(),
        services_configs: Default::default(),
    };

    println!("Starting a single node...");
    let mut node = Node::new(blockchain, node_cfg);

    println!("Blockchain is ready for transactions!");
    node.run().unwrap();
}
<commit_msg>add comments to the code<commit_after>// Copyright 2017 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Import crates with necessary types into a new project.

extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate exonum;
extern crate router;
extern crate bodyparser;
extern crate iron;

// Import necessary types from crates.

use exonum::blockchain::{self, Blockchain, Service, GenesisConfig, ValidatorKeys, Transaction,
                         ApiContext};
use exonum::node::{Node, NodeConfig, NodeApiConfig, TransactionSend, ApiSender, NodeChannel};
use exonum::messages::{RawTransaction, FromRaw, Message};
use exonum::storage::{Fork, MemoryDB, MapIndex};
use exonum::crypto::{PublicKey, Hash, HexValue};
use exonum::encoding::{self, Field};
use exonum::api::{Api, ApiError};
use iron::prelude::*;
use iron::Handler;
use router::Router;

// // // // // // // // // // CONSTANTS // // // // // // // // // //

// Define constants for transaction types within the service.

const SERVICE_ID: u16 = 1;

const TX_CREATE_WALLET_ID: u16 = 1;

const TX_TRANSFER_ID: u16 = 2;

const INIT_BALANCE: u64 = 100;

// // // // // // // // // // PERSISTENT DATA // // // // // // // // // //

encoding_struct! {
    struct Wallet {
        const SIZE = 48;

        field pub_key:            &PublicKey  [00 => 32]
        field name:               &str        [32 => 40]
        field balance:            u64         [40 => 48]
    }
}

impl Wallet {
    pub fn increase(&mut self, amount: u64) {
        let balance = self.balance() + amount;
        Field::write(&balance, &mut self.raw, 40, 48);
    }

    pub fn decrease(&mut self, amount: u64) {
        let balance = self.balance() - amount;
        Field::write(&balance, &mut self.raw, 40, 48);
    }
}

// // // // // // // // // // STORAGE DATA LAYOUT // // // // // // // // // //

pub struct CurrencySchema<'a> {
    view: &'a mut Fork,
}

impl<'a> CurrencySchema<'a> {
    pub fn wallets(&mut self) -> MapIndex<&mut Fork, PublicKey, Wallet> {
        let prefix = blockchain::gen_prefix(SERVICE_ID, 0, &());
        MapIndex::new(prefix, self.view)
    }

    pub fn wallet(&mut self, pub_key: &PublicKey) -> Option<Wallet> {
        self.wallets().get(pub_key)
    }
}

// // // // // // // // // // TRANSACTIONS // // // // // // // // // //

message! {
    struct TxCreateWallet {
        const TYPE = SERVICE_ID;
        const ID = TX_CREATE_WALLET_ID;
        const SIZE = 40;

        field pub_key:     &PublicKey  [00 => 32]
        field name:        &str        [32 => 40]
    }
}

message! {
    struct TxTransfer {
        const TYPE = SERVICE_ID;
        const ID = TX_TRANSFER_ID;
        const SIZE = 80;

        field from:        &PublicKey  [00 => 32]
        field to:          &PublicKey  [32 => 64]
        field amount:      u64         [64 => 72]
        field seed:        u64         [72 => 80]
    }
}

// // // // // // // // // // CONTRACTS // // // // // // // // // //

impl Transaction for TxCreateWallet {
    fn verify(&self) -> bool {
        self.verify_signature(self.pub_key())
    }

    fn execute(&self, view: &mut Fork) {
        let mut schema = CurrencySchema { view };
        if schema.wallet(self.pub_key()).is_none() {
            let wallet = Wallet::new(self.pub_key(), self.name(), INIT_BALANCE);
            println!("Create the wallet: {:?}", wallet);
            schema.wallets().put(self.pub_key(), wallet)
        }
    }
}

impl Transaction for TxTransfer {
    fn verify(&self) -> bool {
        (*self.from() != *self.to()) && self.verify_signature(self.from())
    }

    fn execute(&self, view: &mut Fork) {
        let mut schema = CurrencySchema { view };
        let sender = schema.wallet(self.from());
        let receiver = schema.wallet(self.to());
        if let (Some(mut sender), Some(mut receiver)) = (sender, receiver) {
            let amount = self.amount();
            if sender.balance() >= amount {
                sender.decrease(amount);
                receiver.increase(amount);
                println!("Transfer between wallets: {:?} => {:?}", sender, receiver);
                let mut wallets = schema.wallets();
                wallets.put(self.from(), sender);
                wallets.put(self.to(), receiver);
            }
        }
    }
}

// // // // // // // // // // REST API // // // // // // // // // //

#[derive(Clone)]
struct CryptocurrencyApi {
    channel: ApiSender<NodeChannel>,
}

impl Api for CryptocurrencyApi {
    fn wire(&self, router: &mut Router) {

        #[serde(untagged)]
        #[derive(Clone, Serialize, Deserialize)]
        enum TransactionRequest {
            CreateWallet(TxCreateWallet),
            Transfer(TxTransfer),
        }

        impl Into<Box<Transaction>> for TransactionRequest {
            fn into(self) -> Box<Transaction> {
                match self {
                    TransactionRequest::CreateWallet(trans) => Box::new(trans),
                    TransactionRequest::Transfer(trans) => Box::new(trans),
                }
            }
        }

        #[derive(Serialize, Deserialize)]
        struct TransactionResponse {
            tx_hash: Hash,
        }

        let self_ = self.clone();
        let transaction = move |req: &mut Request| -> IronResult<Response> {
            match req.get::<bodyparser::Struct<TransactionRequest>>() {
                Ok(Some(transaction)) => {
                    let transaction: Box<Transaction> = transaction.into();
                    let tx_hash = transaction.hash();
                    self_.channel.send(transaction).map_err(ApiError::Events)?;
                    let json = TransactionResponse { tx_hash };
                    self_.ok_response(&serde_json::to_value(&json).unwrap())
                }
                Ok(None) => Err(ApiError::IncorrectRequest("Empty request body".into()))?,
                Err(e) => Err(ApiError::IncorrectRequest(Box::new(e)))?,
            }
        };
        let route_post = "/v1/wallets/transaction";
        router.post(&route_post, transaction, "transaction");
    }
}

// // // // // // // // // // SERVICE DECLARATION // // // // // // // // // //

struct CurrencyService;

impl Service for CurrencyService {
    fn service_name(&self) -> &'static str {
        "cryptocurrency"
    }

    fn service_id(&self) -> u16 {
        SERVICE_ID
    }

    fn tx_from_raw(&self, raw: RawTransaction) -> Result<Box<Transaction>, encoding::Error> {
        let trans: Box<Transaction> = match raw.message_type() {
            TX_TRANSFER_ID => Box::new(TxTransfer::from_raw(raw)?),
            TX_CREATE_WALLET_ID => Box::new(TxCreateWallet::from_raw(raw)?),
            _ => {
                return Err(encoding::Error::IncorrectMessageType {
                    message_type: raw.message_type(),
                });
            }
        };
        Ok(trans)
    }

    fn public_api_handler(&self, ctx: &ApiContext) -> Option<Box<Handler>> {
        let mut router = Router::new();
        let api = CryptocurrencyApi { channel: ctx.node_channel().clone() };
        api.wire(&mut router);
        Some(Box::new(router))
    }
}

// // // // // // // // // // ENTRY POINT // // // // // // // // // //

fn main() {
    exonum::helpers::init_logger().unwrap();

    println!("Creating in-memory database...");
    let db = MemoryDB::new();
    let services: Vec<Box<Service>> = vec![Box::new(CurrencyService)];
    let blockchain = Blockchain::new(Box::new(db), services);

    let (consensus_public_key, consensus_secret_key) = exonum::crypto::gen_keypair();
    let (service_public_key, service_secret_key) = exonum::crypto::gen_keypair();

    let peer_address = "0.0.0.0:2000".parse().unwrap();
    let api_address = "0.0.0.0:8000".parse().unwrap();

    let validator_keys = ValidatorKeys {
        consensus_key: consensus_public_key,
        service_key: service_public_key,
    };
    let genesis = GenesisConfig::new(vec![validator_keys].into_iter());

    let api_cfg = NodeApiConfig {
        public_api_address: Some(api_address),
        ..Default::default()
    };

    let node_cfg = NodeConfig {
        listen_address: peer_address,
        peers: vec![],
        service_public_key,
        service_secret_key,
        consensus_public_key,
        consensus_secret_key,
        genesis,
        external_address: None,
        network: Default::default(),
        whitelist: Default::default(),
        api: api_cfg,
        mempool: Default::default(),
        services_configs: Default::default(),
    };

    println!("Starting a single node...");
    let mut node = Node::new(blockchain, node_cfg);

    println!("Blockchain is ready for transactions!");
    node.run().unwrap();
}
<|endoftext|>


<commit_before>extern crate argparse;
use argparse::{ArgumentParser, Store};

fn main() {
    let mut size:i32 = 20;
    let mut skin = "███".to_string();
    let padding = vec!["  ", " ", "", " ", "  ", "   ", "    ", "   "];

    {  // this block limits scope of borrows by ap.refer() method
        let mut ap = ArgumentParser::new();

        ap.set_description("Print a beautiful millipede (written in rust)");
        ap.refer(&mut size)
            .add_option(&["--size"], Store,
            "Changes the size");
        ap.refer(&mut skin)
            .add_option(&["--skin"], Store,
            "Changes the skin");
        ap.parse_args_or_exit();
    }
    if size >= 0 {
        let mut i: usize = 0;

        println!("    ╚⊙ ⊙╝");
        while i < size as usize {
            println!("{}╚═({})═╝", padding[i % 8], skin);
            i = i + 1;
        }
    } else {
        let mut i: usize = size.abs() as usize;

        while i > 0 {
            println!("{}╔═({})═╗", padding[i % 8], skin);
            i = i - 1;
        }
        println!("   ╔⊙ ⊙╗");
    }
}
<commit_msg>Add --reverse<commit_after>extern crate argparse;
use argparse::{ArgumentParser, StoreTrue, Store};

fn main() {
    let mut size: usize = 20;
    let mut reverse: bool = false;
    let mut skin = "███".to_string();
    let padding = vec!["  ", " ", "", " ", "  ", "   ", "    ", "   "];

    {  // this block limits scope of borrows by ap.refer() method
        let mut ap = ArgumentParser::new();

        ap.set_description("Print a beautiful millipede (written in rust)");
        ap.refer(&mut size)
            .add_option(&["-w", "--width"], Store,
            "millipede width");
        ap.refer(&mut skin)
            .add_option(&["-s", "--skin"], Store,
            "millipede skin");
        ap.refer(&mut reverse)
            .add_option(&["-r", "--reverse"], StoreTrue,
            "reverse the millipede");
        ap.parse_args_or_exit();
    }
    if reverse == false {
        let mut i: usize = 0;

        println!("    ╚⊙ ⊙╝");
        while i < size {
            println!("{}╚═({})═╝", padding[i % 8], skin);
            i = i + 1;
        }
    } else {
        let mut i: usize = size;

        while i > 0 {
            println!("{}╔═({})═╗", padding[i % 8], skin);
            i = i - 1;
        }
        println!("   ╔⊙ ⊙╗");
    }
}
<|endoftext|>


<commit_before>#![feature(attr_literals)]

#[macro_use]
extern crate clap;
#[macro_use]
extern crate error_chain;
extern crate mhost;
extern crate resolv_conf;
extern crate tokio_core;
extern crate trust_dns;

use mhost::{multiple_lookup, DnsQuery};

use clap::{App, Arg, Shell};
use std::fmt;
use std::fs::File;
use std::io::Read;
use std::net::IpAddr;
use std::str::FromStr;
use std::time::Duration;
use tokio_core::reactor::Core;
use trust_dns::rr::{RData, RecordType};

static DEFAULT_RECORD_TYPES: &'static [&str] = &["a", "aaaa", "mx"];

// Arbitrary list of public DNS servers
static DEFAULT_DNS_SERVERS: &'static [&str] = &[
    // "Level3",
    "209.244.0.3",
    "209.244.0.4",
    // "Verisign",
    "64.6.64.6",
    "64.6.65.6",
    // "Google",
    "8.8.8.8",
    "8.8.4.4",
    // "DNS.WATCH",
    "84.200.69.80",
    "84.200.70.40",
    // "OpenDNS Home",
    "208.67.222.222",
    "208.67.220.220",
    // "SafeDNS",
    "195.46.39.39",
    "195.46.39.40",
    // "Dyn",
    "216.146.35.35",
    "216.146.36.36",
    // "FreeDNS",
    "37.235.1.174",
    "37.235.1.177",
    // "Alternate DNS",
    "198.101.242.72",
    "23.253.163.53",
];

// Newtype pattern for Display implementation
struct DnsResponse<'a>(pub &'a mhost::DnsResponse);

// Display impl for plain, basic output
impl<'a> fmt::Display for DnsResponse<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let DnsResponse(dns_response) = *self;
        if dns_response.answers.is_empty() {
            return write!(f, "DNS server {} has not records.", dns_response.server);
        }
        let _ = write!(f, "DNS server {} responded with\n", dns_response.server);
        let mut answers: Vec<String> = dns_response
            .answers
            .iter()
            .map(|answer| match *answer.rdata() {
                RData::A(ip) => format!(" * IPv4: {}", ip),
                RData::AAAA(ip) => format!(" * IPv6: {}", ip),
                RData::CNAME(ref name) => format!(" * CNAME: {}", name),
                RData::MX(ref mx) => {
                    format!(
                        " * MX: {} with preference {}",
                        mx.exchange(),
                        mx.preference()
                    )
                }
                RData::NS(ref name) => format!(" * NS: {}", name),
                RData::SOA(ref soa) => {
                    format!(
                        " * SOA: {} {} {} {} {} {} {}",
                        soa.mname(),
                        soa.rname(),
                        soa.serial(),
                        soa.refresh(),
                        soa.retry(),
                        soa.expire(),
                        soa.minimum()
                    )
                }
                RData::TXT(ref txt) => format!(" * TXT: {}", txt.txt_data().join(" ")),
                RData::PTR(ref ptr) => format!(" * PTR: {}", ptr.to_string()),
                ref x => format!(" * unclassified answer: {:?}", x),
            })
            .collect();
        answers.sort();
        write!(f, "{}", answers.join("\n"))
    }
}

fn run() -> Result<()> {
    let args = build_cli().get_matches();

    if args.is_present("completions") {
        let bin_name = env!("CARGO_PKG_NAME");
        let shell= args.value_of("completions").unwrap();
        build_cli().gen_completions_to(bin_name, shell.parse::<Shell>().unwrap(), &mut std::io::stdout());
        return Ok(());
    }

    // Check if domain_name is an IP address -> PTR query and ignore -t, else normal query
    let record_types = if IpAddr::from_str(&args.value_of("domain name").unwrap()).is_ok() {
        vec!["PTR"]
            .iter()
            .map(|rt| RecordType::from_str(&rt.to_uppercase()).unwrap())
            .collect()
    } else if let Some(record_types) = args.values_of_lossy("record types") {
        record_types
            .iter()
            .map(|rt| RecordType::from_str(&rt.to_uppercase()).unwrap())
            .collect()
    } else {
        DEFAULT_RECORD_TYPES
            .iter()
            .map(|rt| RecordType::from_str(&rt.to_uppercase()).unwrap())
            .collect()
    };

    let mut servers: Vec<_> = if let Some(dns_servers) = args.values_of_lossy("DNS servers") {
        dns_servers
            .iter()
            .map(|server| (IpAddr::from_str(server).unwrap(), 53))
            .collect()
    } else {
        DEFAULT_DNS_SERVERS
            .iter()
            .map(|server| (IpAddr::from_str(server).unwrap(), 53))
            .collect()
    };
    if !args.is_present("dont use local dns servers") {
        let mut buf = Vec::with_capacity(4096);
        let mut f = File::open("/etc/resolv.conf").unwrap();
        f.read_to_end(&mut buf).unwrap();
        let cfg = resolv_conf::Config::parse(&buf[..]).unwrap();
        let mut local_servers: Vec<_> = cfg.nameservers.into_iter().map(|s| (s, 53)).collect();
        servers.append(&mut local_servers);
    }

    let timeout = value_t!(args, "timeout", u64).map(|t| Duration::from_secs(t)).unwrap();

    // Check if domain_name is an IP address -> PTR query and ignore -t, else normal query
    let mut query = if let Ok(ip) = IpAddr::from_str(&args.value_of("domain name").unwrap()) {
        DnsQuery::from(ip, record_types)
    } else {
        DnsQuery::new(&args.value_of("domain name").unwrap(), record_types)
    };
    query = query.set_timeout(timeout);

    let mut io_loop = Core::new().unwrap();
    let lookup = multiple_lookup(&io_loop.handle(), query, servers);
    let result = io_loop.run(lookup);

    match result {
        Ok(ref responses) => {
            for response in responses {
                match *response {
                    Ok(ref x) => println!("{}", DnsResponse(x)),
                    Err(ref e) => println!("Error: {}", e),
                }
            }
        }
        Err(_) => {
            println!("General Error");
        }
    }

    Ok(())
}

fn build_cli() -> App<'static, 'static> {
    let name  = env!("CARGO_PKG_NAME");
    let version = env!("CARGO_PKG_VERSION");
    let about = env!("CARGO_PKG_DESCRIPTION");

    App::new(name)
        .version(version)
        .about(about)
        .arg(Arg::with_name("domain name")
            .index(1)
            .required(true)
            .help("domain name or IP address to look up"))
        .arg(Arg::with_name("timeout")
            .long("timeout")
            .default_value("5")
            .help("timeout for server responses in sec")
            .takes_value(true))
        .arg(Arg::with_name("record types")
            .long("type")
            .short("t")
            .takes_value(true)
            .multiple(true)
            .number_of_values(1)
            .possible_values(&["a", "aaaa", "any", "cname", "dnskey", "mx", "ns", "opt", "ptr", "soa", "srv", "txt"])
            .help("Select resource record type [default: a, aaaa, mx]"))
        .arg(Arg::with_name("dont use local dns servers")
            .short("L")
            .help("Do not use local (/etc/resolv.conf) DNS servers"))
        .arg(Arg::with_name("DNS servers")
            .long("server")
            .short("s")
            .takes_value(true)
            .multiple(true)
            .number_of_values(1)
            .help("DNS servers to use; if empty use predefined, public DNS server"))
        .arg(Arg::with_name("completions")
            .long("completions")
            .takes_value(true)
            .hidden(true)
            .possible_values(&["bash", "fish", "zsh"])
            .help("The shell to generate the script for"))
}

error_chain!{}

quick_main!(run);
<commit_msg>One more nightly feature removed from main<commit_after>#[macro_use]
extern crate clap;
#[macro_use]
extern crate error_chain;
extern crate mhost;
extern crate resolv_conf;
extern crate tokio_core;
extern crate trust_dns;

use mhost::{multiple_lookup, DnsQuery};

use clap::{App, Arg, Shell};
use std::fmt;
use std::fs::File;
use std::io::Read;
use std::net::IpAddr;
use std::str::FromStr;
use std::time::Duration;
use tokio_core::reactor::Core;
use trust_dns::rr::{RData, RecordType};

static DEFAULT_RECORD_TYPES: &'static [&str] = &["a", "aaaa", "mx"];

// Arbitrary list of public DNS servers
static DEFAULT_DNS_SERVERS: &'static [&str] = &[
    // "Level3",
    "209.244.0.3",
    "209.244.0.4",
    // "Verisign",
    "64.6.64.6",
    "64.6.65.6",
    // "Google",
    "8.8.8.8",
    "8.8.4.4",
    // "DNS.WATCH",
    "84.200.69.80",
    "84.200.70.40",
    // "OpenDNS Home",
    "208.67.222.222",
    "208.67.220.220",
    // "SafeDNS",
    "195.46.39.39",
    "195.46.39.40",
    // "Dyn",
    "216.146.35.35",
    "216.146.36.36",
    // "FreeDNS",
    "37.235.1.174",
    "37.235.1.177",
    // "Alternate DNS",
    "198.101.242.72",
    "23.253.163.53",
];

// Newtype pattern for Display implementation
struct DnsResponse<'a>(pub &'a mhost::DnsResponse);

// Display impl for plain, basic output
impl<'a> fmt::Display for DnsResponse<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let DnsResponse(dns_response) = *self;
        if dns_response.answers.is_empty() {
            return write!(f, "DNS server {} has not records.", dns_response.server);
        }
        let _ = write!(f, "DNS server {} responded with\n", dns_response.server);
        let mut answers: Vec<String> = dns_response
            .answers
            .iter()
            .map(|answer| match *answer.rdata() {
                RData::A(ip) => format!(" * IPv4: {}", ip),
                RData::AAAA(ip) => format!(" * IPv6: {}", ip),
                RData::CNAME(ref name) => format!(" * CNAME: {}", name),
                RData::MX(ref mx) => {
                    format!(
                        " * MX: {} with preference {}",
                        mx.exchange(),
                        mx.preference()
                    )
                }
                RData::NS(ref name) => format!(" * NS: {}", name),
                RData::SOA(ref soa) => {
                    format!(
                        " * SOA: {} {} {} {} {} {} {}",
                        soa.mname(),
                        soa.rname(),
                        soa.serial(),
                        soa.refresh(),
                        soa.retry(),
                        soa.expire(),
                        soa.minimum()
                    )
                }
                RData::TXT(ref txt) => format!(" * TXT: {}", txt.txt_data().join(" ")),
                RData::PTR(ref ptr) => format!(" * PTR: {}", ptr.to_string()),
                ref x => format!(" * unclassified answer: {:?}", x),
            })
            .collect();
        answers.sort();
        write!(f, "{}", answers.join("\n"))
    }
}

fn run() -> Result<()> {
    let args = build_cli().get_matches();

    if args.is_present("completions") {
        let bin_name = env!("CARGO_PKG_NAME");
        let shell= args.value_of("completions").unwrap();
        build_cli().gen_completions_to(bin_name, shell.parse::<Shell>().unwrap(), &mut std::io::stdout());
        return Ok(());
    }

    // Check if domain_name is an IP address -> PTR query and ignore -t, else normal query
    let record_types = if IpAddr::from_str(&args.value_of("domain name").unwrap()).is_ok() {
        vec!["PTR"]
            .iter()
            .map(|rt| RecordType::from_str(&rt.to_uppercase()).unwrap())
            .collect()
    } else if let Some(record_types) = args.values_of_lossy("record types") {
        record_types
            .iter()
            .map(|rt| RecordType::from_str(&rt.to_uppercase()).unwrap())
            .collect()
    } else {
        DEFAULT_RECORD_TYPES
            .iter()
            .map(|rt| RecordType::from_str(&rt.to_uppercase()).unwrap())
            .collect()
    };

    let mut servers: Vec<_> = if let Some(dns_servers) = args.values_of_lossy("DNS servers") {
        dns_servers
            .iter()
            .map(|server| (IpAddr::from_str(server).unwrap(), 53))
            .collect()
    } else {
        DEFAULT_DNS_SERVERS
            .iter()
            .map(|server| (IpAddr::from_str(server).unwrap(), 53))
            .collect()
    };
    if !args.is_present("dont use local dns servers") {
        let mut buf = Vec::with_capacity(4096);
        let mut f = File::open("/etc/resolv.conf").unwrap();
        f.read_to_end(&mut buf).unwrap();
        let cfg = resolv_conf::Config::parse(&buf[..]).unwrap();
        let mut local_servers: Vec<_> = cfg.nameservers.into_iter().map(|s| (s, 53)).collect();
        servers.append(&mut local_servers);
    }

    let timeout = value_t!(args, "timeout", u64).map(|t| Duration::from_secs(t)).unwrap();

    // Check if domain_name is an IP address -> PTR query and ignore -t, else normal query
    let mut query = if let Ok(ip) = IpAddr::from_str(&args.value_of("domain name").unwrap()) {
        DnsQuery::from(ip, record_types)
    } else {
        DnsQuery::new(&args.value_of("domain name").unwrap(), record_types)
    };
    query = query.set_timeout(timeout);

    let mut io_loop = Core::new().unwrap();
    let lookup = multiple_lookup(&io_loop.handle(), query, servers);
    let result = io_loop.run(lookup);

    match result {
        Ok(ref responses) => {
            for response in responses {
                match *response {
                    Ok(ref x) => println!("{}", DnsResponse(x)),
                    Err(ref e) => println!("Error: {}", e),
                }
            }
        }
        Err(_) => {
            println!("General Error");
        }
    }

    Ok(())
}

fn build_cli() -> App<'static, 'static> {
    let name  = env!("CARGO_PKG_NAME");
    let version = env!("CARGO_PKG_VERSION");
    let about = env!("CARGO_PKG_DESCRIPTION");

    App::new(name)
        .version(version)
        .about(about)
        .arg(Arg::with_name("domain name")
            .index(1)
            .required(true)
            .help("domain name or IP address to look up"))
        .arg(Arg::with_name("timeout")
            .long("timeout")
            .default_value("5")
            .help("timeout for server responses in sec")
            .takes_value(true))
        .arg(Arg::with_name("record types")
            .long("type")
            .short("t")
            .takes_value(true)
            .multiple(true)
            .number_of_values(1)
            .possible_values(&["a", "aaaa", "any", "cname", "dnskey", "mx", "ns", "opt", "ptr", "soa", "srv", "txt"])
            .help("Select resource record type [default: a, aaaa, mx]"))
        .arg(Arg::with_name("dont use local dns servers")
            .short("L")
            .help("Do not use local (/etc/resolv.conf) DNS servers"))
        .arg(Arg::with_name("DNS servers")
            .long("server")
            .short("s")
            .takes_value(true)
            .multiple(true)
            .number_of_values(1)
            .help("DNS servers to use; if empty use predefined, public DNS server"))
        .arg(Arg::with_name("completions")
            .long("completions")
            .takes_value(true)
            .hidden(true)
            .possible_values(&["bash", "fish", "zsh"])
            .help("The shell to generate the script for"))
}

error_chain!{}

quick_main!(run);
<|endoftext|>


<commit_before>extern crate docopt;
extern crate regex;

use std::fs;
use std::io;
use std::io::Write;
use std::path::Path;
use docopt::Docopt;
use regex::Regex;

const USAGE: &'static str = "
Usage: r3name --pattern <pattern> --replacement <replacement> [--dry-run] <path>...
       r3name -h | --help
       r3name --version

Options:
    -h --help                    Show this screen.
    --version                    Show version.
    --pattern <pattern>          Pattern to match in the provided paths
    --replacement <replacement>  String used to replace pattern matches
    --dry-run                    Show what would be renamed, but don't rename anything.
";

const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION");

fn rename_path(path: &str, pattern: &Regex, replacement: &str, dry_run: bool) {
    if !pattern.is_match(path) {
        println!("Skipping `{}`", path);
        return;
    }

    if !Path::new(path).exists() {
        writeln!(io::stderr(), "Source path `{}` doesn't exist", path).unwrap();
        return;
    }

    let new_path = pattern.replace(path, replacement);

    if Path::new(&new_path).exists() {
        writeln!(io::stderr(), "Failed to rename `{}`: destination path `{}` already exists", path, new_path).unwrap();
        return;
    }

    if dry_run {
        println!("Would rename `{}` -> `{}`", path, new_path);
        return;
    }

    match fs::rename(path, &new_path) {
        Ok(_) => println!("Renamed `{}` -> `{}`", path, new_path),
        Err(error) => {
            writeln!(io::stderr(), "Failed to rename `{}` -> `{}`: {}", path, new_path, error).unwrap();
            return;
        }
    };
}

fn main() {
    let args = Docopt::new(USAGE).and_then(|d| d.parse()).unwrap_or_else(|e| e.exit());

    if args.get_bool("--version") {
        println!("r3name {}", VERSION.unwrap_or("unknown"));
        return;
    }

    let pattern = args.get_str("--pattern");
    let pattern = match Regex::new(pattern) {
        Ok(regex) => regex,
        Err(error) => {
            writeln!(io::stderr(), "Invalid pattern: {}", error).unwrap();
            return;
        }
    };

    let replacement = args.get_str("--replacement");
    let dry_run = args.get_bool("--dry-run");

    for path in args.get_vec("<path>") {
        rename_path(path, &pattern, replacement, dry_run);
    }
}
<commit_msg>Propagate errors to the program's main function<commit_after>extern crate docopt;
extern crate regex;

use std::fmt;
use std::fs;
use std::io;
use std::io::Write;
use std::path::Path;
use docopt::Docopt;
use regex::Regex;

const USAGE: &'static str = "
Usage: r3name --pattern <pattern> --replacement <replacement> [--dry-run] <path>...
       r3name -h | --help
       r3name --version

Options:
    -h --help                    Show this screen.
    --version                    Show version.
    --pattern <pattern>          Pattern to match in the provided paths
    --replacement <replacement>  String used to replace pattern matches
    --dry-run                    Show what would be renamed, but don't rename anything.
";

const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION");

#[derive(Debug)]
enum RenameError<'a> {
    RegexDoesNotMatch(&'a Regex, &'a str),
    SourceDoesNotExist(&'a str),
    DestinationExists(String),
    FsError(io::Error)
}

impl<'a> fmt::Display for RenameError<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            RenameError::RegexDoesNotMatch(pattern, path) =>
                write!(f, "path `{}` doesn't match regex `{}`)", path, pattern),
            RenameError::SourceDoesNotExist(path) =>
                write!(f, "source path `{}` doesn't exist", path),
            RenameError::DestinationExists(ref path) =>
                write!(f, "destination path `{}` already exists", path),
            RenameError::FsError(ref error) => write!(f, "{}", error)
        }
    }
}

fn rename_path<'a>(path: &'a str, pattern: &'a Regex, replacement: &str, dry_run: bool)
                   -> Result<String, RenameError<'a>> {
    if !pattern.is_match(path) {
        return Err(RenameError::RegexDoesNotMatch(pattern, path));
    }

    if !Path::new(path).exists() {
        return Err(RenameError::SourceDoesNotExist(path));
    }

    let new_path = pattern.replace(path, replacement);

    if Path::new(&new_path).exists() {
        return Err(RenameError::DestinationExists(new_path));
    }

    if dry_run {
        return Ok(new_path);
    }

    return fs::rename(path, &new_path).map(|()| new_path).map_err(RenameError::FsError);
}

fn main() {
    let args = Docopt::new(USAGE).and_then(|d| d.parse()).unwrap_or_else(|e| e.exit());

    if args.get_bool("--version") {
        println!("r3name {}", VERSION.unwrap_or("unknown"));
        return;
    }

    let pattern = args.get_str("--pattern");
    let pattern = match Regex::new(pattern) {
        Ok(regex) => regex,
        Err(error) => {
            writeln!(io::stderr(), "Invalid pattern: {}", error).unwrap();
            return;
        }
    };

    let replacement = args.get_str("--replacement");
    let dry_run = args.get_bool("--dry-run");

    for path in args.get_vec("<path>") {
        match rename_path(path, &pattern, replacement, dry_run) {
            Ok(new_path) => {
                let prefix = if dry_run { "Would rename" } else { "Renamed" };
                println!("{} `{}` -> `{}`", prefix, path, new_path);
            },
            Err(RenameError::RegexDoesNotMatch(_, _)) =>
                println!("Skipping `{}` (doesn't match regex `{}`)", path, pattern),
            Err(error) => writeln!(io::stderr(), "Failed to rename `{}`: {}", path, error).unwrap()
        }
    }
}
<|endoftext|>


<commit_before>
use std::io;
use std::io::fs;
use std::os;
use std::io::process::{Command};
use std::path::Path;

mod ash;

fn main() {
  println!("ash: A shell");
  println!("Incredibly in beta");
  println!("May eat your left shoes.");

  let mut cwd = os::getcwd();

  loop {
    print!("{}", ash::format::format(&cwd));
    let rawinput = io::stdin().read_line().ok().expect("Error Occured");
    let input = rawinput.as_slice().trim();
    if input == "" { continue } //skip blank enters
    let opts: Vec<&str> = input.split_str(" ").collect();
    let (cmd, args) = (opts[0], opts.slice(1, opts.len()));
    match cmd {
      "cd" => {
        //set the current directory
        if args[0] == "~" {
          //go home
          cwd = os::homedir().unwrap();
          continue;
        }
        let path: Option<Path> = if args[0].starts_with("~") { // ~/projects
          match os::homedir() {
            Some(e) => {
              let dir = e.join(args[0].slice_from(2)); //[~/]
              Some(dir)
            }, //hacky but whatever
            None => Path::new_opt(args[0])
          }
        } else if args[0].starts_with(".") || !args[0].starts_with("/") { // ./bin or ../ash || cd src
          Some(cwd.join(args[0]))
        } else {
          Path::new_opt(args[0])
        };
        match path {
          Some(new) => {
            match fs::stat(&new) {
              Ok(stat) => {
                match stat.kind {
                  TypeDirectory => {cwd = new} // why is this so unhappy
                }
              },
              Err(e) => println!("No such file or directory: \"{}\"", args[0])
            }
          }
          None => println!("Failed to locate path")
        }
      }
      _ => {
        let process = Command::new(cmd).cwd(&cwd).args(args).output();

        match process {
          Ok(output) => {
            println!("{}", String::from_utf8_lossy(output.output.as_slice()));

          },
          Err(e) => {
            println!("Error Occured: {}", e);
          },
        };
      }
    }
  }
}
<commit_msg>Fixed badly written code for the cd command<commit_after>
use std::io;
use std::io::fs;
use std::os;
use std::io::process::{Command};
use std::path::Path;

mod ash;

fn main() {
  println!("ash: A shell");
  println!("Incredibly in beta");
  println!("May eat your left shoes.");

  let mut cwd = os::getcwd();

  loop {
    print!("{}", ash::format::format(&cwd));
    let rawinput = io::stdin().read_line().ok().expect("Error Occured");
    let input = rawinput.as_slice().trim();
    if input == "" { continue } //skip blank enters
    let opts: Vec<&str> = input.split_str(" ").collect();
    let (cmd, args) = (opts[0], opts.slice(1, opts.len()));
    match cmd {
      "cd" => {
        //set the current directory
        if args[0] == "~" {
          //go home
          cwd = os::homedir().unwrap();
          continue;
        }
        let path: Option<Path> = if args[0].starts_with("~") { // ~/projects
          match os::homedir() {
            Some(e) => {
              let dir = e.join(args[0].slice_from(2)); //[~/]
              Some(dir)
            }, //hacky but whatever
            None => Path::new_opt(args[0])
          }
        } else if args[0].starts_with(".") || !args[0].starts_with("/") { // ./bin or ../ash || cd src
          Some(cwd.join(args[0]))
        } else {
          Path::new_opt(args[0])
        };
        match path {
          Some(new) => {
            match fs::stat(&new) {
              Ok(stat) => {
                if stat.kind == io::TypeDirectory {
                  cwd = new
                }
              },
              Err(e) => println!("No such file or directory: \"{}\"", args[0])
            }
          }
          None => println!("Failed to locate path")
        }
      }
      _ => {
        let process = Command::new(cmd).cwd(&cwd).args(args).output();

        match process {
          Ok(output) => {
            println!("{}", String::from_utf8_lossy(output.output.as_slice()));

          },
          Err(e) => {
            println!("Error Occured: {}", e);
          },
        };
      }
    }
  }
}
<|endoftext|>


<commit_before>extern crate libc;

use libc::{c_int, c_char};
use std::ffi::{CString,CStr};
use std::ptr;
use std::mem;
use std::{i32,str};
use wiredtiger::{KeyValuePair};

mod wiredtiger_def;
mod wiredtiger;

fn main() {
  match wiredtiger::open() {
    Ok(mut connection) => {
      connection.open_session()
        .and_then(|mut session|{
          match session.create_table("users"){
            Ok(_) => {
              session.open_cursor("users").and_then(|mut cursor| {
                try!(cursor.insert_pair("1", "John Doe"));
                try!(cursor.insert_pair("2", "Jane Doe"));
                try!(cursor.reset());
                for el in cursor.filter(|kvp| kvp.value.contains("a")) {
                  println!("Record {}:{}", el.key, el.value);
                }
                Ok(())
              })
            },
            e => e
          }
        });
    },
    Err(m) => println!("{:?}", m)
  }
}
<commit_msg>Simplified sample<commit_after>extern crate libc;

use libc::{c_int, c_char};
use std::ffi::{CString,CStr};
use std::ptr;
use std::mem;
use std::{i32,str};
use wiredtiger::{KeyValuePair};

mod wiredtiger_def;
mod wiredtiger;

fn main() {
  match wiredtiger::open() {
    Ok(mut connection) => {
      connection.open_session().and_then(|mut session|{
        try!(session.create_table("users"));
        let mut cursor = try!(session.open_cursor("users"));
        try!(cursor.insert_pair("1", "John Doe"));
        try!(cursor.insert_pair("2", "Jane Doe"));
        try!(cursor.reset());
        for el in cursor.filter(|kvp| kvp.value.contains("a")) {
          println!("Record {}:{}", el.key, el.value);
        }
        Ok(())
      });
    },
    Err(m) => println!("{:?}", m)
  }
}
<|endoftext|>


<commit_before>extern crate base64;
#[macro_use(crate_version)]
extern crate clap;
extern crate openssl;
extern crate rpassword;
extern crate serde_json;

mod agile_keychain;
mod cli;
mod json_value_ext;
mod op_vault;

use cli::Cli;

fn main() {
    Cli::new().run();
}
<commit_msg>Allow dead code crate-wide<commit_after>#![allow(dead_code)] // until we release 0.1.0...

extern crate base64;
#[macro_use(crate_version)]
extern crate clap;
extern crate openssl;
extern crate rpassword;
extern crate serde_json;

mod agile_keychain;
mod cli;
mod json_value_ext;
mod op_vault;

use cli::Cli;

fn main() {
    Cli::new().run();
}
<|endoftext|>


<commit_before>extern crate clap;
#[macro_use]
extern crate log;
#[macro_use]
extern crate lazy_static;
extern crate env_logger;
extern crate url;
extern crate reqwest;
#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;
extern crate pbr;

use std::env;
use std::fs::OpenOptions;
use std::io::Read;
use std::io::Write;
use std::str::FromStr;

use clap::{App, AppSettings, Arg};
use pbr::{ProgressBar, Units};
use reqwest::header::{Headers, ContentLength, Range};

mod format;
mod video_info;

use video_info::YTDL_PROXY_URL;

#[derive(Debug)]
struct Options {
    no_progress: bool,
    info_only: bool,
    silent: bool,
    debug: bool,
    append: bool,
    json: bool,
    download_url: bool,
    filter: Vec<String>,
    byte_range: String,
    output_file: String,
    start_offset: i32,
    proxy_url: String,
}

fn main() {
    env_logger::init().expect("env logger init fail");

    let flags = vec![
        Arg::with_name("output")
            .short("o")
            .long("output")
            .value_name("FILE")
            .help("write output to a file")
            .takes_value(true),
        Arg::with_name("proxy-url")
            .short("p")
            .long("proxy-url")
            .value_name("PROXY_URL")
            .help("use proxy for the request")
            .takes_value(true),
        Arg::with_name("no-progress")
            .long("no-progress")
            .help("write output to a file"),
        Arg::with_name("range")
            .short("r")
            .long("range")
            .value_name("RANGE")
            .help("download a specific range of bytes of the video, [start]-[end]")
            .takes_value(true),
        Arg::with_name("url")
            .help("youtube url")
            .required(true)
            .index(1),
        Arg::with_name("download-url")
            .short("-u")
            .long("download-url")
            .help("prints download url to stdout"),
        Arg::with_name("json")
            .short("j")
            .long("json")
            .help("print info json to stdout"),
        Arg::with_name("debug")
            .short("d")
            .long("debug")
            .help("output debug log"),
        Arg::with_name("filter")
            .short("f")
            .long("filter")
            .value_name("FILTER")
            .multiple(true)
            .help("filter available formats, syntax: val1 val2 val3")
            .takes_value(true),
        Arg::with_name("append")
            .short("-a")
            .long("--append")
            .help("append to output file instead of overwriting"),
        Arg::with_name("start-offset")
            .long("start-offset")
            .value_name("STARTOFFSET")
            .help("offset the start of the video")
            .takes_value(true),
        Arg::with_name("silent")
            .short("s")
            .long("silent")
            .help("only output error, also diables progressbar"),
        Arg::with_name("info")
            .short("i")
            .long("info")
            .help("only output info")];

    let matches = App::new("ytdl")
        .setting(AppSettings::ArgRequiredElseHelp)
        .version("0.1.1")
        .about("download youtube videos")
        .args(&flags)
        .get_matches();

    let mut filter = vec![];
    if matches.is_present("filter") {
        filter = matches.values_of("filter").unwrap().map(|x| x.to_string()).collect();
    }

    let mut options = Options {
        no_progress: matches.is_present("no-progress"),
        info_only: matches.is_present("info"),
        silent: matches.is_present("silent"),
        debug: matches.is_present("debug"),
        append: matches.is_present("append"),
        json: matches.is_present("json"),
        download_url: matches.is_present("download-url"),
        filter: filter,
        output_file: matches.value_of("output").unwrap_or_default().to_string(),
        byte_range: matches.value_of("range").unwrap_or_default().to_string(),
        start_offset: matches.value_of("start-offset").unwrap_or("0").parse::<i32>().unwrap(),
        proxy_url: matches.value_of("proxy-url").unwrap_or_default().to_string(),
    };

    if !options.proxy_url.is_empty() {
        env::set_var(YTDL_PROXY_URL, &options.proxy_url);
    }

    let identifier = matches.value_of("url").unwrap_or_default();
    if options.filter.is_empty() {
        options.filter = vec![
            format!("{}:mp4", format::FORMAT_EXTENSION_KEY),
            format!("!{}:", format::FORMAT_VIDEO_ENCODING_KEY),
            format!("!{}:", format::FORMAT_AUDIO_ENCODING_KEY),
            format!("best"),
        ];
    }

    handler(identifier, &options);
}

fn handler(identifier: &str, options: &Options) {
    info!("fetching video info...");
    let info = match video_info::get_video_info(identifier) {
        Ok(i) => i,
        Err(e) => {
            println!("unable to fetch video info: {}", e.to_string());
            return;
        }
    };

    if options.info_only {
        println!("Author: {}", info.author);
        println!("Duration: {}s", info.duration);
        return
    } else if options.json {
        println!("{}", serde_json::to_string(&info).unwrap_or_default());
        return
    }

    let formats = &info.formats;
    for x in &options.filter {

    }

    if formats.len() == 0 {
        println!("no formats available that match criteria");
        return
    }

    let mut download_url = match video_info::get_download_url(&formats[0]) {
        Ok(u) => u,
        Err(e) => {
            println!("unable to get download url: {}", e.to_string());
            return
        }
    };

    if options.start_offset != 0 {
        download_url.query_pairs_mut().append_pair("begin", &format!("{}", &options.start_offset * 1000));
    }

    if options.download_url {
        println!("{}", download_url.as_str());
    }

    let filename = if !options.output_file.is_empty() {
        options.output_file.clone()
    } else {
        video_info::get_filename(&info, &formats[0])
    };

    let mut file = if options.append {
        OpenOptions::new()
            .write(true)
            .create(true)
            .append(true)
            .open(&filename)
            .expect("create output file fail")
    } else {
        OpenOptions::new()
            .write(true)
            .create(true)
            .open(&filename)
            .expect("create output file fail")
    };

    info!("download to {}", filename);
    let mut headers = Headers::new();
    if !options.byte_range.is_empty() {
        let r = Range::from_str(&options.byte_range).expect("invalid range str");
        headers.set(r);
    }

    let client = video_info::get_client().expect("get request client fail");
    let mut resp = client
        .get(download_url.as_str())
        .expect("download fail")
        .headers(headers)
        .send()
        .expect("download fail");

    let file_size = resp.headers().get::<ContentLength>()
                .map(|l| **l)
                .unwrap_or(0);

    let mut pb = ProgressBar::new(file_size);
    pb.format("╢▌▌░╟");
    pb.set_units(Units::Bytes);
    pb.show_percent = true;
    pb.show_speed = true;
    pb.show_time_left = true;
    pb.set_width(Some(100));
    let mut buf = [0; 128 * 1024];

    loop {
        match resp.read(&mut buf) {
            Ok(len) => {
                file.write_all(&buf[..len]).expect("write to file fail");
                if !options.silent && !options.no_progress {
                    pb.add(len as u64);
                }
                if len == 0 {
                    break;
                }
            }
            Err(e) => panic!("{}", e.to_string()),
        };
    }
}

fn parse_filter() {

}<commit_msg>feat: range bytes<commit_after>extern crate clap;
#[macro_use]
extern crate log;
#[macro_use]
extern crate lazy_static;
extern crate env_logger;
extern crate url;
extern crate reqwest;
#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;
extern crate pbr;

use std::env;
use std::fs::OpenOptions;
use std::io::Read;
use std::io::Write;
use std::str::FromStr;

use clap::{App, AppSettings, Arg};
use pbr::{ProgressBar, Units};
use reqwest::header::{Headers, ContentLength, Range};

mod format;
mod video_info;

use video_info::YTDL_PROXY_URL;

#[derive(Debug)]
struct Options {
    no_progress: bool,
    info_only: bool,
    silent: bool,
    debug: bool,
    append: bool,
    json: bool,
    download_url: bool,
    filter: Vec<String>,
    byte_range: String,
    output_file: String,
    start_offset: i32,
    proxy_url: String,
}

fn main() {
    env_logger::init().expect("env logger init fail");

    let flags = vec![
        Arg::with_name("output")
            .short("o")
            .long("output")
            .value_name("FILE")
            .help("write output to a file")
            .takes_value(true),
        Arg::with_name("proxy-url")
            .short("p")
            .long("proxy-url")
            .value_name("PROXY_URL")
            .help("use proxy for the request")
            .takes_value(true),
        Arg::with_name("no-progress")
            .long("no-progress")
            .help("write output to a file"),
        Arg::with_name("range")
            .short("r")
            .long("range")
            .value_name("RANGE")
            .help("download a specific range of bytes of the video, [start]-[end]")
            .takes_value(true),
        Arg::with_name("url")
            .help("youtube url")
            .required(true)
            .index(1),
        Arg::with_name("download-url")
            .short("-u")
            .long("download-url")
            .help("prints download url to stdout"),
        Arg::with_name("json")
            .short("j")
            .long("json")
            .help("print info json to stdout"),
        Arg::with_name("debug")
            .short("d")
            .long("debug")
            .help("output debug log"),
        Arg::with_name("filter")
            .short("f")
            .long("filter")
            .value_name("FILTER")
            .multiple(true)
            .help("filter available formats, syntax: val1 val2 val3")
            .takes_value(true),
        Arg::with_name("append")
            .short("-a")
            .long("--append")
            .help("append to output file instead of overwriting"),
        Arg::with_name("start-offset")
            .long("start-offset")
            .value_name("STARTOFFSET")
            .help("offset the start of the video")
            .takes_value(true),
        Arg::with_name("silent")
            .short("s")
            .long("silent")
            .help("only output error, also diables progressbar"),
        Arg::with_name("info")
            .short("i")
            .long("info")
            .help("only output info")];

    let matches = App::new("ytdl")
        .setting(AppSettings::ArgRequiredElseHelp)
        .version("0.1.1")
        .about("download youtube videos")
        .args(&flags)
        .get_matches();

    let mut filter = vec![];
    if matches.is_present("filter") {
        filter = matches.values_of("filter").unwrap().map(|x| x.to_string()).collect();
    }

    let mut options = Options {
        no_progress: matches.is_present("no-progress"),
        info_only: matches.is_present("info"),
        silent: matches.is_present("silent"),
        debug: matches.is_present("debug"),
        append: matches.is_present("append"),
        json: matches.is_present("json"),
        download_url: matches.is_present("download-url"),
        filter: filter,
        output_file: matches.value_of("output").unwrap_or_default().to_string(),
        byte_range: matches.value_of("range").unwrap_or_default().to_string(),
        start_offset: matches.value_of("start-offset").unwrap_or("0").parse::<i32>().unwrap(),
        proxy_url: matches.value_of("proxy-url").unwrap_or_default().to_string(),
    };

    if !options.proxy_url.is_empty() {
        env::set_var(YTDL_PROXY_URL, &options.proxy_url);
    }

    let identifier = matches.value_of("url").unwrap_or_default();
    if options.filter.is_empty() {
        options.filter = vec![
            format!("{}:mp4", format::FORMAT_EXTENSION_KEY),
            format!("!{}:", format::FORMAT_VIDEO_ENCODING_KEY),
            format!("!{}:", format::FORMAT_AUDIO_ENCODING_KEY),
            format!("best"),
        ];
    }

    handler(identifier, &options);
}

fn handler(identifier: &str, options: &Options) {
    info!("fetching video info...");
    let info = match video_info::get_video_info(identifier) {
        Ok(i) => i,
        Err(e) => {
            println!("unable to fetch video info: {}", e.to_string());
            return;
        }
    };

    if options.info_only {
        println!("Author: {}", info.author);
        println!("Duration: {}s", info.duration);
        return
    } else if options.json {
        println!("{}", serde_json::to_string(&info).unwrap_or_default());
        return
    }

    let formats = &info.formats;
    for x in &options.filter {

    }

    if formats.len() == 0 {
        println!("no formats available that match criteria");
        return
    }

    let mut download_url = match video_info::get_download_url(&formats[0]) {
        Ok(u) => u,
        Err(e) => {
            println!("unable to get download url: {}", e.to_string());
            return
        }
    };

    if options.start_offset != 0 {
        download_url.query_pairs_mut().append_pair("begin", &format!("{}", &options.start_offset * 1000));
    }

    if options.download_url {
        println!("{}", download_url.as_str());
    }

    let filename = if !options.output_file.is_empty() {
        options.output_file.clone()
    } else {
        video_info::get_filename(&info, &formats[0])
    };

    let mut file = if options.append {
        OpenOptions::new()
            .write(true)
            .create(true)
            .append(true)
            .open(&filename)
            .expect("create output file fail")
    } else {
        OpenOptions::new()
            .write(true)
            .create(true)
            .open(&filename)
            .expect("create output file fail")
    };

    info!("download to {}", filename);
    let mut headers = Headers::new();
    if !options.byte_range.is_empty() {
        let r = Range::from_str(&format!("bytes={}", &options.byte_range)).expect("invalid range str");
        headers.set(r);
    }

    let client = video_info::get_client().expect("get request client fail");
    let mut resp = client
        .get(download_url.as_str())
        .expect("download fail")
        .headers(headers)
        .send()
        .expect("download fail");

    let file_size = resp.headers().get::<ContentLength>()
                .map(|l| **l)
                .unwrap_or(0);

    let mut pb = ProgressBar::new(file_size);
    pb.format("╢▌▌░╟");
    pb.set_units(Units::Bytes);
    pb.show_percent = true;
    pb.show_speed = true;
    pb.show_time_left = true;
    pb.set_width(Some(100));
    let mut buf = [0; 128 * 1024];

    loop {
        match resp.read(&mut buf) {
            Ok(len) => {
                file.write_all(&buf[..len]).expect("write to file fail");
                if !options.silent && !options.no_progress {
                    pb.add(len as u64);
                }
                if len == 0 {
                    break;
                }
            }
            Err(e) => panic!("{}", e.to_string()),
        };
    }

    println!("");
}

fn parse_filter() {

}<|endoftext|>


<commit_before>extern crate tempfile;
extern crate rustyline;

mod lexer;
mod parser;
mod evaluator;
mod error;
mod constants;
mod function;

use rustyline::error::ReadlineError;
use rustyline::Editor;
use tempfile::NamedTempFile;
use constants::VERSION;
use lexer::Lexer;
use parser::Parser;
use evaluator::Evaluator;
use error::{ParserError, EvaluatorError};

fn prelude() {
    println!("bspl {}", VERSION);
    println!("Bit-Shift-Print Loop");
}

fn error_message(width: usize, msg: &'static str) {
    println!("{caret:>width$}\n.. {}", msg, caret = "^", width = width);
}

fn display_results(results: Vec<String>) {
    let mut result_iter = results.iter();
    while let Some(result) = result_iter.next() {
        println!(".. {}", result);
    }

    if let Some(final_result) = results.last() {
        let parsed_final_result: i32 = final_result.parse().unwrap();
        println!(".. Dec: {}", parsed_final_result);
        println!(".. Hex: 0x{:x}", parsed_final_result);
        println!(".. Bin: 0b{:b}", parsed_final_result);
    }
}

fn repl() {
    let mut repl = Editor::<()>::new();
    let prompt = "=> ";
    let tmp_file = NamedTempFile::new().unwrap();
    let _ = repl.load_history(tmp_file.path());

    let mut lexer = Lexer::new();
    let mut parser = Parser::default();
    let evaluator = Evaluator::default();

    loop {
        match repl.readline(prompt) {
            Ok(line) => {
                repl.add_history_entry(&line);
                match parser.parse(lexer.analyze(&line)) {
                    Ok(parsed_tokens) => {
                        match evaluator.evaluate(parsed_tokens) {
                            Ok(result) => {
                                display_results(result);
                            }
                            Err(EvaluatorError::MissingArgument(position)) => {
                                error_message(position + prompt.len() + 1, "Missing Argument");
                            }
                            Err(EvaluatorError::TooManyArguments) => {
                                error_message(prompt.len() + 1, "Too Many Arguments");
                            }
                            Err(EvaluatorError::NegativeShift(position)) => {
                                error_message(position + prompt.len() + 1, "Negative Shift");
                            }
                        }
                    }
                    Err(ParserError::RadixError(position)) => {
                        error_message(position + prompt.len() + 1, "Radix Error");
                    }
                    Err(ParserError::UnknownOperator(position)) => {
                        error_message(position + prompt.len() + 1, "Unknown Operator");
                    }
                    Err(ParserError::MissingOpeningBracket(position)) => {
                        error_message(position + prompt.len() + 1, "Missing Opening Bracket");
                    }
                    Err(ParserError::MissingClosingBracket(position)) => {
                        error_message(position + prompt.len() + 1, "Missing Closing Bracket");
                    }
                    Err(ParserError::InvalidSyntax(position)) => {
                        error_message(position + prompt.len() + 1, "Invalid Syntax");
                    }
                }
            }
            Err(ReadlineError::Eof) => break,
            Err(ReadlineError::Interrupted) => {
                println!("Press Ctrl-D to leave bspl");
                continue;
            }
            Err(err) => {
                println!("Error: {:?}", err);
                break;
            }

        }
        repl.save_history(tmp_file.path()).unwrap();
    }
}

pub fn main() {
    prelude();
    repl();
}
<commit_msg>Check if parsed_final_result is an integer<commit_after>extern crate tempfile;
extern crate rustyline;

mod lexer;
mod parser;
mod evaluator;
mod error;
mod constants;
mod function;

use rustyline::error::ReadlineError;
use rustyline::Editor;
use tempfile::NamedTempFile;
use constants::VERSION;
use lexer::Lexer;
use parser::Parser;
use evaluator::Evaluator;
use error::{ParserError, EvaluatorError};

fn prelude() {
    println!("bspl {}", VERSION);
    println!("Bit-Shift-Print Loop");
}

fn error_message(width: usize, msg: &'static str) {
    println!("{caret:>width$}\n.. {}", msg, caret = "^", width = width);
}

fn display_results(results: Vec<String>) {
    let mut result_iter = results.iter();
    while let Some(result) = result_iter.next() {
        println!(".. {}", result);
    }

    if let Some(final_result) = results.last() {
        let parsed_final_result = final_result.parse::<i32>();
        if parsed_final_result.is_ok() {
            let parsed_final_result = parsed_final_result.unwrap();
            println!(".. Dec: {}", parsed_final_result);
            println!(".. Hex: 0x{:x}", parsed_final_result);
            println!(".. Bin: 0b{:b}", parsed_final_result);
        }
    }
}

fn repl() {
    let mut repl = Editor::<()>::new();
    let prompt = "=> ";
    let tmp_file = NamedTempFile::new().unwrap();
    let _ = repl.load_history(tmp_file.path());

    let mut lexer = Lexer::new();
    let mut parser = Parser::default();
    let evaluator = Evaluator::default();

    loop {
        match repl.readline(prompt) {
            Ok(line) => {
                repl.add_history_entry(&line);
                match parser.parse(lexer.analyze(&line)) {
                    Ok(parsed_tokens) => {
                        match evaluator.evaluate(parsed_tokens) {
                            Ok(result) => {
                                display_results(result);
                            }
                            Err(EvaluatorError::MissingArgument(position)) => {
                                error_message(position + prompt.len() + 1, "Missing Argument");
                            }
                            Err(EvaluatorError::TooManyArguments) => {
                                error_message(prompt.len() + 1, "Too Many Arguments");
                            }
                            Err(EvaluatorError::NegativeShift(position)) => {
                                error_message(position + prompt.len() + 1, "Negative Shift");
                            }
                        }
                    }
                    Err(ParserError::RadixError(position)) => {
                        error_message(position + prompt.len() + 1, "Radix Error");
                    }
                    Err(ParserError::UnknownOperator(position)) => {
                        error_message(position + prompt.len() + 1, "Unknown Operator");
                    }
                    Err(ParserError::MissingOpeningBracket(position)) => {
                        error_message(position + prompt.len() + 1, "Missing Opening Bracket");
                    }
                    Err(ParserError::MissingClosingBracket(position)) => {
                        error_message(position + prompt.len() + 1, "Missing Closing Bracket");
                    }
                    Err(ParserError::InvalidSyntax(position)) => {
                        error_message(position + prompt.len() + 1, "Invalid Syntax");
                    }
                }
            }
            Err(ReadlineError::Eof) => break,
            Err(ReadlineError::Interrupted) => {
                println!("Press Ctrl-D to leave bspl");
                continue;
            }
            Err(err) => {
                println!("Error: {:?}", err);
                break;
            }

        }
        repl.save_history(tmp_file.path()).unwrap();
    }
}

pub fn main() {
    prelude();
    repl();
}
<|endoftext|>


<commit_before>#![feature(ip_addr, convert, std_misc)]

use std::net::UdpSocket;
use std::net::{SocketAddr, IpAddr, Ipv4Addr};
use std::str::FromStr;
use std::string::String;
use std::convert::AsRef;
use std::thread;
use std::mem::transmute;
use std::sync::mpsc;
use std::sync::mpsc::TryRecvError::{Empty, Disconnected};
use std::time::duration::Duration;

#[macro_use]
mod testing;

fn start_server(ip: &str) {
  let ip_addr = Ipv4Addr::from_str(ip).unwrap();
  let addr = SocketAddr::new(IpAddr::V4(ip_addr), 34561);

  let socket = match UdpSocket::bind(addr) {
    Ok(s) => s,
    Err(e) => panic!("Failed to bind socket! {}", e)
  };

  println!("I survived! (now listening??)");

  let mut buf = [0u8; 256];
  loop {
    match socket.recv_from(&mut buf) {
      Ok((len, src_addr)) => {
        println!("Fuckin' got it!!!!!!!!!!!!!!!!!!!!!!!");
      }

      Err(e) => println!("Failed to receive :(! error: {}", e)
    }
    println!("going again!");
  }
}

fn main() {
  let mut stdin = std::io::stdin();

  println!("Enter ip address!");
  let mut ipstr = String::new();
  stdin.read_line(&mut ipstr).unwrap();

  ipstr.pop();
  start_server(ipstr.as_ref());
}

// NOTE This MUST be run before any server tests!
#[test]
fn start_test_server() {
  thread::spawn(move || {
    start_server("127.0.0.1");
  });
}
#[test]
fn server_can_receive_packet() {
  server_test!((socket, host_addr) {
    let mut buf = [0u8; 256];
    buf[0] = 90;
    unsafe { socket.send_to(transmute(buf.as_ref()), host_addr).unwrap(); };
  });
}
<commit_msg>added ping test and bogus ping procedure<commit_after>#![feature(ip_addr, convert, std_misc)]

use std::net::UdpSocket;
use std::net::{SocketAddr, IpAddr, Ipv4Addr};
use std::str::FromStr;
use std::string::String;
use std::convert::AsRef;
use std::thread;
use std::mem::transmute;
use std::sync::mpsc;
use std::sync::mpsc::TryRecvError::{Empty, Disconnected};
use std::time::duration::Duration;

#[macro_use]
mod testing;

fn start_server(ip: &str) {
  let ip_addr = Ipv4Addr::from_str(ip).unwrap();
  let addr = SocketAddr::new(IpAddr::V4(ip_addr), 34561);

  let socket = match UdpSocket::bind(addr) {
    Ok(s) => s,
    Err(e) => panic!("Failed to bind socket! {}", e)
  };

  println!("I survived! (now listening??)");

  let mut buf = [0u8; 256];
  loop {
    match socket.recv_from(&mut buf) {
      Ok((len, src_addr)) => {
        println!("Fuckin' got it!!!!!!!!!!!!!!!!!!!!!!!");
        if len == 1 && buf[0] == 1 {
          // ping!
          unsafe {
            let mut ms = transmute::<_, *mut i32>(&buf[0]);
            // NOTE random value for now...
            *ms = 15;
            socket.send_to(buf.as_ref(), src_addr).unwrap();
          }
        }
      }

      Err(e) => println!("Failed to receive :(! error: {}", e)
    }
    println!("going again!");
  }
}

fn main() {
  let mut stdin = std::io::stdin();

  println!("Enter ip address!");
  let mut ipstr = String::new();
  stdin.read_line(&mut ipstr).unwrap();

  ipstr.pop();
  start_server(ipstr.as_ref());
}

// NOTE This MUST be run before any server tests!
#[test]
fn start_test_server() {
  thread::spawn(move || {
    start_server("127.0.0.1");
  });
}

#[test]
fn server_can_receive_packet() {
  server_test!((socket, host_addr) {
    let mut buf = [0u8; 256];
    buf[0] = 90;
    unsafe { socket.send_to(transmute(buf.as_ref()), host_addr).unwrap(); };
  });
}

#[test]
fn server_can_be_pinged() {
  server_test!((socket, host_addr) {
    unsafe {
      let mut buf = [1u8];
      socket.send_to(transmute(buf.as_ref()), host_addr).unwrap();

      let mut in_buf = [0u8, 256];
      match socket.recv_from(&mut in_buf) {
        Ok((len, src_addr)) => {
          let ms = *transmute::<_, *const i32>(&in_buf[0]);
          println!("Got a ping of {} ms!", ms);
        }

        Err(e) => panic!("Got error {} when trying to ping!", e)
      }
    }
  });
}
<|endoftext|>


<commit_before>extern crate irc;
extern crate toml;
extern crate rustc_serialize;

use irc::client::prelude::*;

#[derive(Clone, RustcDecodable, RustcEncodable, Default, PartialEq, Debug)]
struct EvalbotCfg {
    pub max_channel_lines: u32,
    pub max_channel_line_len: u32,
    pub playpen_timeout: u32,
    pub irc_config: Config
}

fn read_cfg() -> Result<EvalbotCfg, String> {
    use std::io::prelude::*;
    use std::fs::File;
    use rustc_serialize::Decodable;

    let mut f = match File::open("evalbot.toml") {
        Ok(x) => x,
        Err(x) => return Err(format!("could not open evalbot.toml: {}", x))
    };
    let mut s = String::new();
    match f.read_to_string(&mut s) {
        Err(x) => return Err(format!("could not read evalbot.toml: {}", x)),
        _ => ()
    };

    let value = match s.parse::<toml::Value>() {
        Ok(x) => x,
        Err(x) => return Err(format!("could not parse evalbot.toml: {:?}", x))
    };

    let res = match EvalbotCfg::decode(&mut toml::Decoder::new(value)) {
        Ok(x) => x,
        Err(x) => return Err(format!("could not decde evalbot.toml: {}", x))
    };

    Ok(res)
}

fn parse_msg(msg: &Message) -> Option<(bool, String, String)> {
    if let Ok(Command::PRIVMSG(tgt, msg)) = msg.into() {
        let chn = tgt.starts_with('#');
        if !chn && msg.contains('\x01') { return None; }
        let tok: Vec<&str> = msg.trim().splitn(2, '>').collect();
        if tok.len() < 2 && chn { None }
        else if chn {
            match tok[0] {
                "rust" => Some((false, tgt, tok[1].to_owned())),
                "rustraw" => Some((true, tgt, tok[1].to_owned())),
                _ => None
            }
        } else {
            match tok[0] {
                "raw" => Some((true, tgt, tok[1].to_owned())),
                _ => Some((false, tgt, msg.to_owned()))
            }
        }
    } else {
        None
    }
}

fn main() {
    let config = match read_cfg() {
        Ok(x) => x,
        Err(x) => panic!("could not read config; {}", x)
    };
    println!("read config: {:?}", config);

    let conn = IrcServer::from_config(config.irc_config).unwrap();
    loop {
        conn.identify().unwrap();
        for maybe_msg in conn.iter() {
            let msg = match maybe_msg {
                Ok(x) => x,
                Err(x) => {
                    println!("{}, reconnecting", x);
                    break
                }
            };

            let src = msg.get_source_nickname().unwrap_or("");
            if let Some((raw, tgt, code)) = parse_msg(&msg) {
                println!("{} @ {} (raw: {}): {}", src, tgt, raw, code);
                // TODO: evaluate code
            }
        }
    }
}
<commit_msg>Basic functionality works.<commit_after>#![feature(semaphore)]

extern crate irc;
extern crate toml;
extern crate rustc_serialize;

use irc::client::prelude::*;

#[derive(Clone, RustcDecodable, RustcEncodable, Default, PartialEq, Debug)]
struct EvalbotCfg {
    pub chan_output_prefix: String,
    pub max_channel_lines: usize,
    pub max_channel_line_len: usize,
    pub playpen_timeout: usize,
    pub eval_threads: usize,
    pub irc_config: Config
}

unsafe impl Send for EvalbotCfg {}

fn read_cfg() -> Result<EvalbotCfg, String> {
    use std::io::prelude::*;
    use std::fs::File;
    use rustc_serialize::Decodable;

    let mut f = try!(File::open("evalbot.toml")
                     .map_err(|x| format!("could not open evalbot.toml: {}", x)));
    let mut s = String::new();

    try!(f.read_to_string(&mut s)
         .map_err(|x| format!("could not read evalbot.toml: {}", x)));

    let value = try!(s.parse::<toml::Value>()
                     .map_err(|x| format!("could not parse evalbot.toml: {:?}", x)));

    EvalbotCfg::decode(&mut toml::Decoder::new(value))
         .map_err(|x| format!("could not decode evalbot.toml: {}", x))
}

#[derive(Debug)]
struct EvalReq {
    pub raw: bool,
    pub is_channel: bool,
    pub sender: String,
    pub target: String,
    pub code: String
}

fn parse_msg(message: &Message) -> Option<EvalReq> {
    let sender = message.get_source_nickname().unwrap_or("");
    if let Ok(Command::PRIVMSG(target, message)) = message.into() {
        let in_channel = target.starts_with('#');
        if !in_channel && message.contains('\x01') {
            // we don't accept code via CTCP
            return None;
        }

        let tok: Vec<&str> = message.trim().splitn(2, '>').collect();
        // in channel: rust[raw]>code...
        // in private message: [raw>]code...
        let raw = match (in_channel, tok[0]) {
            (true, "rust") => false,
            (true, "rustraw") => true,
            (false, "raw") => true,
            (false, _) => false,
            _ => return None
        };
        let code = if !raw && !in_channel {
            &message as &str
        } else {
            match tok.get(1) { Some(x) => *x, None => return None }
        };
        Some(EvalReq { raw: raw, is_channel: in_channel, sender: sender.to_owned(), target: target.to_owned(), code: code.to_owned() })
    } else {
        None
    }
}

use std::sync::{Arc, Mutex, Semaphore};
use std::thread;
use std::collections::VecDeque;

#[cfg(not(unix))]
fn evaluate(code: &str, _: usize) -> String {
    if let Ok(x) = code.parse::<usize>() {
        std::iter::repeat("X").take(x).collect::<String>()
    } else {
        "not a number".to_owned()
    }
}

#[cfg(unix)]
fn playpen_exec(command: &str, args: &[&str], input: &str, timeout: usize) -> Result<(String, String), String> {
    use std::process::{Command,Stdio};
    use std::io::Write;
    let mut child = try!(Command::new("sudo")
        .arg("playpen")
        .arg("sandbox")
        .arg("--mount-proc")
        .arg("--user=rust")
        .arg(format!("--timeout={}", timeout))
        .arg("--syscalls-file=whitelist")
        .arg("--devices=/dev/urandom:r,/dev/null:w")
        .arg("--memory-limit=128")
        .arg("--")
        .arg(command)
        .args(args)
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .spawn().map_err(|x| format!("couldn't playpen_exec; {}", x)));
    if let Some(ref mut x) = child.stdin {
        try!(x.write_all(input.as_bytes())
             .map_err(|x| format!("couldn't write to stdin; {}", x)));
    } else {
        return Err("no stdin?".to_owned());
    }
    let output = try!(child.wait_with_output().
                      map_err(|x| format!("wait_with_output failed; {}", x)));
    Ok((String::from_utf8_lossy(&output.stdout).into_owned(),
     String::from_utf8_lossy(&output.stderr).into_owned()))
}

#[cfg(unix)]
fn evaluate(code: &str, timeout: usize) -> String {
    let code = format!(
r#"#![allow(dead_code, unused_variables)]
fn show<T: std::fmt::Debug>(e: T) {{ println!("{{:?}}", e) }}
fn main() {{
    show({{
        {}
    }});
}}"#, code);
    let (stdout, stderr) = match playpen_exec("/usr/local/bin/evaluate.sh",
                                        &["-C","opt-level=2"],
                                        &code,
                                        timeout) {
        Ok(x) => x,
        Err(x) => return x
    };
    let stdout = stdout.replace("\u{FF}", "");
    let mut out = String::new();
    for line in stdout.lines() {
        out.push_str(&format!("stdout: {}\n", line));
    }
    for line in stderr.lines() {
        out.push_str(&format!("stderr: {}\n", line));
    }
    out
}

fn wrap_output<'a>(input: &'a str, max_len: usize) -> Vec<&'a str> {
    let mut ret = vec![];
    for line in input.lines() {
        let line = line.trim();
        if line.len() <= max_len {
            ret.push(line);
        } else {
            let mut leftover = line;
            while let Some((idx, _)) = leftover.char_indices().nth(max_len) {
                let (part, after) = leftover.split_at(idx);
                ret.push(part);
                leftover = after;
            }
            if leftover.len() > 0 {
                ret.push(leftover);
            }
        }
    }
    ret
}

fn evaluate_loop<'a, S, T, U>(conn: Arc<S>, requests: Arc<Mutex<VecDeque<EvalReq>>>, has_work: Arc<Semaphore>, cfg: Arc<EvalbotCfg>) -> !
    where T: IrcRead, U: IrcWrite, S: ServerExt<'a, T, U> + Sized {
    macro_rules! send_msg {
        ($dest:expr, $line:expr) => {
            match conn.send_privmsg($dest, $line) {
                Err(x) => println!("failed to send message: {}", x),
                _ => ()
            }
        };
    }
    loop {
        has_work.acquire();
        if let Some(work) = requests.lock().unwrap().pop_front() {
            let result = evaluate(&work.code, cfg.playpen_timeout);
            let result = wrap_output(&result,
                                     if work.is_channel { cfg.max_channel_line_len }
                                     else { 425 });
            // TODO: gist the overflow
            let max_lines = if work.is_channel { cfg.max_channel_lines } else { usize::max_value() };
            let max_lines = std::cmp::min(max_lines, result.len());
            let dest = if work.is_channel { &work.target } else { &work.sender };
            for line in result[0..max_lines].iter() {
                let line = format!("{}{}", if work.is_channel { &cfg.chan_output_prefix as &str } else { "" }, line);
                send_msg!(dest, &line);
            }
            if max_lines <= result.len() {
                send_msg!(dest, "(output truncated)");
            }
        }
    }
}

fn main() {
    let config = match read_cfg() {
        Ok(x) => x,
        Err(x) => panic!("could not read config; {}", x)
    };
    println!("read config: {:?}", config);

    let conn = Arc::new(IrcServer::from_config(config.irc_config.clone()).unwrap());
    let config = Arc::new(config);
    let evalreqs = Arc::new(Mutex::new(VecDeque::new()));
    let has_work = Arc::new(Semaphore::new(0));
    for _ in 0..config.eval_threads {
        let conn = conn.clone();
        let evalreqs = evalreqs.clone();
        let has_work = has_work.clone();
        let config = config.clone();
        thread::spawn(move || { evaluate_loop(conn, evalreqs, has_work, config.clone()); });
    }
    loop {
        conn.identify().unwrap();
        for maybe_msg in conn.iter() {
            let msg = match maybe_msg {
                Ok(x) => x,
                Err(x) => {
                    println!("{}, reconnecting", x);
                    break
                }
            };

            if let Some(x) = parse_msg(&msg) {
                println!("{} @ {} (raw: {}): {}", x.sender, x.target, x.raw, x.code);
                evalreqs.lock().unwrap().push_back(x); // if mutex is poisoned, just bail
                has_work.release();
            }
        }
        conn.reconnect().unwrap();
    }
}
<|endoftext|>