<commit_before>use crate::cli;
use crate::command_prelude::*;
use anyhow::{bail, format_err};
use cargo::core::dependency::DepKind;
use cargo::ops::tree::{self, EdgeKind};
use cargo::ops::Packages;
use cargo::util::CargoResult;
use std::collections::HashSet;
use std::str::FromStr;

pub fn cli() -> App {
    subcommand("tree")
        .about("Display a tree visualization of a dependency graph")
        .arg(opt("quiet", "Suppress status messages").short("q"))
        .arg_manifest_path()
        .arg_package_spec_no_all(
            "Package to be used as the root of the tree",
            "Display the tree for all packages in the workspace",
            "Exclude specific workspace members",
        )
        .arg(Arg::with_name("all").long("all").short("a").hidden(true))
        .arg(
            Arg::with_name("all-targets")
                .long("all-targets")
                .hidden(true),
        )
        .arg_features()
        .arg_target_triple(
            "Filter dependencies matching the given target-triple (default host platform)",
        )
        .arg(
            Arg::with_name("no-dev-dependencies")
                .long("no-dev-dependencies")
                .hidden(true),
        )
        .arg(
            multi_opt(
                "edges",
                "KINDS",
                "The kinds of dependencies to display \
                 (features, normal, build, dev, all, no-dev, no-build, no-normal)",
            )
            .short("e"),
        )
        .arg(
            optional_multi_opt(
                "invert",
                "SPEC",
                "Invert the tree direction and focus on the given package",
            )
            .short("i"),
        )
        .arg(Arg::with_name("no-indent").long("no-indent").hidden(true))
        .arg(
            Arg::with_name("prefix-depth")
                .long("prefix-depth")
                .hidden(true),
        )
        .arg(
            opt(
                "prefix",
                "Change the prefix (indentation) of how each entry is displayed",
            )
            .value_name("PREFIX")
            .possible_values(&["depth", "indent", "none"])
            .default_value("indent"),
        )
        .arg(opt(
            "no-dedupe",
            "Do not de-duplicate (repeats all shared dependencies)",
        ))
        .arg(
            opt(
                "duplicates",
                "Show only dependencies which come in multiple versions (implies -i)",
            )
            .short("d")
            .alias("duplicate"),
        )
        .arg(
            opt("charset", "Character set to use in output: utf8, ascii")
                .value_name("CHARSET")
                .possible_values(&["utf8", "ascii"])
                .default_value("utf8"),
        )
        .arg(
            opt("format", "Format string used for printing dependencies")
                .value_name("FORMAT")
                .short("f")
                .default_value("{p}"),
        )
        .arg(
            // Backwards compatibility with old cargo-tree.
            Arg::with_name("version")
                .long("version")
                .short("V")
                .hidden(true),
        )
        .after_help("Run `cargo help tree` for more detailed information.\n")
}

pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
    if args.is_present("version") {
        let verbose = args.occurrences_of("verbose") > 0;
        let version = cli::get_version_string(verbose);
        cargo::drop_print!(config, "{}", version);
        return Ok(());
    }
    let prefix = if args.is_present("no-indent") {
        config
            .shell()
            .warn("the --no-indent flag has been changed to --prefix=none")?;
        "none"
    } else if args.is_present("prefix-depth") {
        config
            .shell()
            .warn("the --prefix-depth flag has been changed to --prefix=depth")?;
        "depth"
    } else {
        args.value_of("prefix").unwrap()
    };
    let prefix = tree::Prefix::from_str(prefix).map_err(|e| anyhow::anyhow!("{}", e))?;

    let no_dedupe = args.is_present("no-dedupe") || args.is_present("all");
    if args.is_present("all") {
        config.shell().warn(
            "The `cargo tree` --all flag has been changed to --no-dedupe, \
             and may be removed in a future version.\n\
             If you are looking to display all workspace members, use the --workspace flag.",
        )?;
    }

    let targets = if args.is_present("all-targets") {
        config
            .shell()
            .warn("the --all-targets flag has been changed to --target=all")?;
        vec!["all".to_string()]
    } else {
        args._values_of("target")
    };
    let target = tree::Target::from_cli(targets);

    let edge_kinds = parse_edge_kinds(config, args)?;
    let graph_features = edge_kinds.contains(&EdgeKind::Feature);

    let packages = args.packages_from_flags()?;
    let mut invert = args
        .values_of("invert")
        .map_or_else(|| Vec::new(), |is| is.map(|s| s.to_string()).collect());
    if args.is_present_with_zero_values("invert") {
        match &packages {
            Packages::Packages(ps) => {
                // Backwards compatibility with old syntax of `cargo tree -i -p foo`.
                invert.extend(ps.clone());
            }
            _ => {
                return Err(format_err!(
                    "The `-i` flag requires a package name.\n\
\n\
The `-i` flag is used to inspect the reverse dependencies of a specific\n\
package. It will invert the tree and display the packages that depend on the\n\
given package.\n\
\n\
Note that in a workspace, by default it will only display the package's\n\
reverse dependencies inside the tree of the workspace member in the current\n\
directory. The --workspace flag can be used to extend it so that it will show\n\
the package's reverse dependencies across the entire workspace. The -p flag\n\
can be used to display the package's reverse dependencies only with the\n\
subtree of the package given to -p.\n\
"
                )
                .into());
            }
        }
    }

    let ws = args.workspace(config)?;
    let charset = tree::Charset::from_str(args.value_of("charset").unwrap())
        .map_err(|e| anyhow::anyhow!("{}", e))?;
    let opts = tree::TreeOptions {
        features: values(args, "features"),
        all_features: args.is_present("all-features"),
        no_default_features: args.is_present("no-default-features"),
        packages,
        target,
        edge_kinds,
        invert,
        prefix,
        no_dedupe,
        duplicates: args.is_present("duplicates"),
        charset,
        format: args.value_of("format").unwrap().to_string(),
        graph_features,
    };

    tree::build_and_print(&ws, &opts)?;
    Ok(())
}

fn parse_edge_kinds(config: &Config, args: &ArgMatches<'_>) -> CargoResult<HashSet<EdgeKind>> {
    let mut kinds: Vec<&str> = args
        .values_of("edges")
        .map_or_else(|| Vec::new(), |es| es.flat_map(|e| e.split(',')).collect());
    if args.is_present("no-dev-dependencies") {
        config
            .shell()
            .warn("the --no-dev-dependencies flag has changed to -e=no-dev")?;
        kinds.push("no-dev");
    }
    if kinds.is_empty() {
        kinds.extend(&["normal", "build", "dev"]);
    }

    let mut result = HashSet::new();
    let insert_defaults = |result: &mut HashSet<EdgeKind>| {
        result.insert(EdgeKind::Dep(DepKind::Normal));
        result.insert(EdgeKind::Dep(DepKind::Build));
        result.insert(EdgeKind::Dep(DepKind::Development));
    };
    let unknown = |k| {
        bail!(
            "unknown edge kind `{}`, valid values are \
                \"normal\", \"build\", \"dev\", \
                \"no-normal\", \"no-build\", \"no-dev\", \
                \"features\", or \"all\"",
            k
        )
    };
    if kinds.iter().any(|k| k.starts_with("no-")) {
        insert_defaults(&mut result);
        for kind in &kinds {
            match *kind {
                "no-normal" => result.remove(&EdgeKind::Dep(DepKind::Normal)),
                "no-build" => result.remove(&EdgeKind::Dep(DepKind::Build)),
                "no-dev" => result.remove(&EdgeKind::Dep(DepKind::Development)),
                "features" => result.insert(EdgeKind::Feature),
                "normal" | "build" | "dev" | "all" => {
                    bail!("`no-` dependency kinds cannot be mixed with other dependency kinds")
                }
                k => return unknown(k),
            };
        }
        return Ok(result);
    }
    for kind in &kinds {
        match *kind {
            "all" => {
                insert_defaults(&mut result);
                result.insert(EdgeKind::Feature);
            }
            "features" => {
                result.insert(EdgeKind::Feature);
            }
            "normal" => {
                result.insert(EdgeKind::Dep(DepKind::Normal));
            }
            "build" => {
                result.insert(EdgeKind::Dep(DepKind::Build));
            }
            "dev" => {
                result.insert(EdgeKind::Dep(DepKind::Development));
            }
            k => return unknown(k),
        }
    }
    if kinds.len() == 1 && kinds[0] == "features" {
        insert_defaults(&mut result);
    }
    Ok(result)
}
<commit_msg>doc(tree): mention special target `all` in CLI help<commit_after>use crate::cli;
use crate::command_prelude::*;
use anyhow::{bail, format_err};
use cargo::core::dependency::DepKind;
use cargo::ops::tree::{self, EdgeKind};
use cargo::ops::Packages;
use cargo::util::CargoResult;
use std::collections::HashSet;
use std::str::FromStr;

pub fn cli() -> App {
    subcommand("tree")
        .about("Display a tree visualization of a dependency graph")
        .arg(opt("quiet", "Suppress status messages").short("q"))
        .arg_manifest_path()
        .arg_package_spec_no_all(
            "Package to be used as the root of the tree",
            "Display the tree for all packages in the workspace",
            "Exclude specific workspace members",
        )
        .arg(Arg::with_name("all").long("all").short("a").hidden(true))
        .arg(
            Arg::with_name("all-targets")
                .long("all-targets")
                .hidden(true),
        )
        .arg_features()
        .arg_target_triple(
            "Filter dependencies matching the given target-triple (default host platform). \
            Pass `all` to include all targets.",
        )
        .arg(
            Arg::with_name("no-dev-dependencies")
                .long("no-dev-dependencies")
                .hidden(true),
        )
        .arg(
            multi_opt(
                "edges",
                "KINDS",
                "The kinds of dependencies to display \
                 (features, normal, build, dev, all, no-dev, no-build, no-normal)",
            )
            .short("e"),
        )
        .arg(
            optional_multi_opt(
                "invert",
                "SPEC",
                "Invert the tree direction and focus on the given package",
            )
            .short("i"),
        )
        .arg(Arg::with_name("no-indent").long("no-indent").hidden(true))
        .arg(
            Arg::with_name("prefix-depth")
                .long("prefix-depth")
                .hidden(true),
        )
        .arg(
            opt(
                "prefix",
                "Change the prefix (indentation) of how each entry is displayed",
            )
            .value_name("PREFIX")
            .possible_values(&["depth", "indent", "none"])
            .default_value("indent"),
        )
        .arg(opt(
            "no-dedupe",
            "Do not de-duplicate (repeats all shared dependencies)",
        ))
        .arg(
            opt(
                "duplicates",
                "Show only dependencies which come in multiple versions (implies -i)",
            )
            .short("d")
            .alias("duplicate"),
        )
        .arg(
            opt("charset", "Character set to use in output: utf8, ascii")
                .value_name("CHARSET")
                .possible_values(&["utf8", "ascii"])
                .default_value("utf8"),
        )
        .arg(
            opt("format", "Format string used for printing dependencies")
                .value_name("FORMAT")
                .short("f")
                .default_value("{p}"),
        )
        .arg(
            // Backwards compatibility with old cargo-tree.
            Arg::with_name("version")
                .long("version")
                .short("V")
                .hidden(true),
        )
        .after_help("Run `cargo help tree` for more detailed information.\n")
}

pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
    if args.is_present("version") {
        let verbose = args.occurrences_of("verbose") > 0;
        let version = cli::get_version_string(verbose);
        cargo::drop_print!(config, "{}", version);
        return Ok(());
    }
    let prefix = if args.is_present("no-indent") {
        config
            .shell()
            .warn("the --no-indent flag has been changed to --prefix=none")?;
        "none"
    } else if args.is_present("prefix-depth") {
        config
            .shell()
            .warn("the --prefix-depth flag has been changed to --prefix=depth")?;
        "depth"
    } else {
        args.value_of("prefix").unwrap()
    };
    let prefix = tree::Prefix::from_str(prefix).map_err(|e| anyhow::anyhow!("{}", e))?;

    let no_dedupe = args.is_present("no-dedupe") || args.is_present("all");
    if args.is_present("all") {
        config.shell().warn(
            "The `cargo tree` --all flag has been changed to --no-dedupe, \
             and may be removed in a future version.\n\
             If you are looking to display all workspace members, use the --workspace flag.",
        )?;
    }

    let targets = if args.is_present("all-targets") {
        config
            .shell()
            .warn("the --all-targets flag has been changed to --target=all")?;
        vec!["all".to_string()]
    } else {
        args._values_of("target")
    };
    let target = tree::Target::from_cli(targets);

    let edge_kinds = parse_edge_kinds(config, args)?;
    let graph_features = edge_kinds.contains(&EdgeKind::Feature);

    let packages = args.packages_from_flags()?;
    let mut invert = args
        .values_of("invert")
        .map_or_else(|| Vec::new(), |is| is.map(|s| s.to_string()).collect());
    if args.is_present_with_zero_values("invert") {
        match &packages {
            Packages::Packages(ps) => {
                // Backwards compatibility with old syntax of `cargo tree -i -p foo`.
                invert.extend(ps.clone());
            }
            _ => {
                return Err(format_err!(
                    "The `-i` flag requires a package name.\n\
\n\
The `-i` flag is used to inspect the reverse dependencies of a specific\n\
package. It will invert the tree and display the packages that depend on the\n\
given package.\n\
\n\
Note that in a workspace, by default it will only display the package's\n\
reverse dependencies inside the tree of the workspace member in the current\n\
directory. The --workspace flag can be used to extend it so that it will show\n\
the package's reverse dependencies across the entire workspace. The -p flag\n\
can be used to display the package's reverse dependencies only with the\n\
subtree of the package given to -p.\n\
"
                )
                .into());
            }
        }
    }

    let ws = args.workspace(config)?;
    let charset = tree::Charset::from_str(args.value_of("charset").unwrap())
        .map_err(|e| anyhow::anyhow!("{}", e))?;
    let opts = tree::TreeOptions {
        features: values(args, "features"),
        all_features: args.is_present("all-features"),
        no_default_features: args.is_present("no-default-features"),
        packages,
        target,
        edge_kinds,
        invert,
        prefix,
        no_dedupe,
        duplicates: args.is_present("duplicates"),
        charset,
        format: args.value_of("format").unwrap().to_string(),
        graph_features,
    };

    tree::build_and_print(&ws, &opts)?;
    Ok(())
}

fn parse_edge_kinds(config: &Config, args: &ArgMatches<'_>) -> CargoResult<HashSet<EdgeKind>> {
    let mut kinds: Vec<&str> = args
        .values_of("edges")
        .map_or_else(|| Vec::new(), |es| es.flat_map(|e| e.split(',')).collect());
    if args.is_present("no-dev-dependencies") {
        config
            .shell()
            .warn("the --no-dev-dependencies flag has changed to -e=no-dev")?;
        kinds.push("no-dev");
    }
    if kinds.is_empty() {
        kinds.extend(&["normal", "build", "dev"]);
    }

    let mut result = HashSet::new();
    let insert_defaults = |result: &mut HashSet<EdgeKind>| {
        result.insert(EdgeKind::Dep(DepKind::Normal));
        result.insert(EdgeKind::Dep(DepKind::Build));
        result.insert(EdgeKind::Dep(DepKind::Development));
    };
    let unknown = |k| {
        bail!(
            "unknown edge kind `{}`, valid values are \
                \"normal\", \"build\", \"dev\", \
                \"no-normal\", \"no-build\", \"no-dev\", \
                \"features\", or \"all\"",
            k
        )
    };
    if kinds.iter().any(|k| k.starts_with("no-")) {
        insert_defaults(&mut result);
        for kind in &kinds {
            match *kind {
                "no-normal" => result.remove(&EdgeKind::Dep(DepKind::Normal)),
                "no-build" => result.remove(&EdgeKind::Dep(DepKind::Build)),
                "no-dev" => result.remove(&EdgeKind::Dep(DepKind::Development)),
                "features" => result.insert(EdgeKind::Feature),
                "normal" | "build" | "dev" | "all" => {
                    bail!("`no-` dependency kinds cannot be mixed with other dependency kinds")
                }
                k => return unknown(k),
            };
        }
        return Ok(result);
    }
    for kind in &kinds {
        match *kind {
            "all" => {
                insert_defaults(&mut result);
                result.insert(EdgeKind::Feature);
            }
            "features" => {
                result.insert(EdgeKind::Feature);
            }
            "normal" => {
                result.insert(EdgeKind::Dep(DepKind::Normal));
            }
            "build" => {
                result.insert(EdgeKind::Dep(DepKind::Build));
            }
            "dev" => {
                result.insert(EdgeKind::Dep(DepKind::Development));
            }
            k => return unknown(k),
        }
    }
    if kinds.len() == 1 && kinds[0] == "features" {
        insert_defaults(&mut result);
    }
    Ok(result)
}
<|endoftext|>


<commit_before>mod explorer;
pub mod helpers;

extern crate time;
extern crate serde;
extern crate jsonway;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate exonum;
extern crate rustless;
extern crate valico;

extern crate clap;
extern crate env_logger;
extern crate log;
extern crate term;
extern crate colored;

use std::ops::Deref;
use std::marker::PhantomData;
use std::error;
use std::fmt;
use serde_json::value::from_value;

use serde::{Serialize, Serializer};
use serde::de;
use serde::de::{Visitor, Deserialize, Deserializer};
use rustless::json::ToJson;
use rustless::{Api, Nesting};
use valico::json_dsl;

use exonum::crypto::{Hash, PublicKey, SecretKey, HexValue, ToHex};
use exonum::storage::Error as StorageError;
use exonum::messages::RawTransaction;
use exonum::blockchain::{Blockchain, Schema};
use exonum::services::configuration::ConfigurationSchema;
use exonum::node::{Node, NodeConfig, NodeChannel, TxSender};
use exonum::services::configuration::{ConfigTx, TxConfigPropose, TxConfigVote};

pub use explorer::{TransactionInfo, BlockchainExplorer, BlockInfo};

pub type Channel = TxSender<NodeChannel>;

#[derive(Debug)]
pub struct ValueNotFound(String);

impl fmt::Display for ValueNotFound {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "ValueNotFound: {}", self.0)
    }
}

impl error::Error for ValueNotFound {
    fn description(&self) -> &str {
        self.0.as_ref()
    }
}

impl ValueNotFound {
    pub fn new<T: Into<String>>(s: T) -> ValueNotFound {
        ValueNotFound(s.into())
    }
}

#[derive(Clone, Debug)]
pub struct HexField<T: AsRef<[u8]> + Clone>(pub T);

impl<T> Deref for HexField<T>
    where T: AsRef<[u8]> + Clone
{
    type Target = T;

    fn deref(&self) -> &T {
        &self.0
    }
}

impl<T> Serialize for HexField<T>
    where T: AsRef<[u8]> + Clone
{
    fn serialize<S>(&self, ser: &mut S) -> Result<(), S::Error>
        where S: Serializer
    {
        ser.serialize_str(&self.0.as_ref().to_hex())
    }
}

struct HexVisitor<T>
    where T: AsRef<[u8]> + HexValue
{
    _p: PhantomData<T>,
}

impl<T> Visitor for HexVisitor<T>
    where T: AsRef<[u8]> + HexValue + Clone
{
    type Value = HexField<T>;

    fn visit_str<E>(&mut self, s: &str) -> Result<HexField<T>, E>
        where E: de::Error
    {
        let v = T::from_hex(s).map_err(|_| de::Error::custom("Invalid hex"))?;
        Ok(HexField(v))
    }
}

impl<T> Deserialize for HexField<T>
    where T: AsRef<[u8]> + HexValue + Clone
{
    fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
        where D: Deserializer
    {
        deserializer.deserialize_str(HexVisitor { _p: PhantomData })
    }
}

#[derive(Serialize)]
struct VoteValue {
    revoke: bool,
}

#[derive(Serialize)]
struct ConfigProposeInfo {
    from: PublicKey,
    height: u64,
    config: Vec<u8>,
    actual_from_height: u64,
}

#[derive(Deserialize)]
struct ConfigProposeRequest {
    pub height: u64,
    pub config: Vec<u8>,
    pub actual_from_height: u64,
}

#[derive(Deserialize)]
struct ConfigVoteRequest {
    pub height: u64,
    pub hash_propose: Hash,
    pub seed: u64,
    pub revoke: bool,
}

impl ConfigProposeRequest {
    fn into_tx(&self, pub_key: &PublicKey, sec_key: &SecretKey) -> TxConfigPropose {
        TxConfigPropose::new(pub_key,
                             self.height,
                             self.config.as_ref(),
                             self.actual_from_height,
                             &sec_key)
    }
}

impl ConfigVoteRequest {
    fn into_tx(&self, pub_key: &PublicKey, sec_key: &SecretKey) -> TxConfigVote {
        TxConfigVote::new(pub_key,
                          self.height,
                          &self.hash_propose,
                          self.seed,
                          self.revoke,
                          &sec_key)
    }
}

impl From<TxConfigPropose> for ConfigProposeInfo {
    fn from(src: TxConfigPropose) -> ConfigProposeInfo {
        ConfigProposeInfo {
            from: *src.from(),
            height: src.height(),
            config: src.config().to_vec(),
            actual_from_height: src.actual_from_height(),
        }
    }
}

pub fn make_api<T>(api: &mut Api, b: Blockchain, cfg: NodeConfig)
    where T: TransactionInfo + From<RawTransaction>
{

    let node = Node::new(b.clone(), cfg.clone());
    let channel = node.channel();

    api.namespace("blockchain", move |api| {
        api.get("config/actual", |endpoint| {
            let b = b.clone();
            endpoint.summary("Returns actual configuration");
            endpoint.handle(move |client, _| {
                match Schema::new(&b.view()).get_actual_configuration() {
                    Ok(config) => client.json(&config.to_json()),
                    Err(e) => client.error(e),
                }
            })
        });
        api.put("config/propose", |endpoint| {

            let c = cfg.clone();
            let ch = channel.clone();
            endpoint.summary("Puts new ConfigPropose");
            endpoint.params(|params| {
                params.opt_typed("config", json_dsl::string());
                params.opt_typed("height", json_dsl::u64());
                params.opt_typed("actual_from_height", json_dsl::u64());
            });
            endpoint.handle(move |client, params| {
                match from_value::<ConfigProposeRequest>(params.clone()) {
                    Ok(config_propose_request) => {
                        let config_propose =
                            config_propose_request.into_tx(&c.public_key, &c.secret_key);

                        let tx = ConfigTx::ConfigPropose(config_propose);
                        let tx_hash = HexValue::to_hex(&tx.hash());
                        match ch.send(tx) {
                            Ok(_) => {
                                let json = &jsonway::object(|json| json.set("tx_hash", tx_hash))
                                    .unwrap();
                                client.json(json)
                            }
                            Err(e) => client.error(e),
                        }
                    }
                    Err(_) => client.error(ValueNotFound::new("Can't parse ConfigPropose request")),
                }
            })
        });
        api.put("config/vote", |endpoint| {

            let c = cfg.clone();
            let ch = channel.clone();
            endpoint.summary("Puts new ConfigVote");
            endpoint.params(|params| {
                params.opt_typed("height", json_dsl::u64());
                params.opt_typed("hash_propose", json_dsl::string());
                params.opt_typed("seed", json_dsl::u64());
                params.opt_typed("revoke", json_dsl::boolean());
            });

            endpoint.handle(move |client, params| {
                match from_value::<ConfigVoteRequest>(params.clone()) {
                    Ok(config_vote_request) => {
                        let config_vote = config_vote_request.into_tx(&c.public_key, &c.secret_key);

                        let tx = ConfigTx::ConfigVote(config_vote);
                        let tx_hash = HexValue::to_hex(&tx.hash());
                        match ch.send(tx) {
                            Ok(_) => {
                                let json = &jsonway::object(|json| json.set("tx_hash", tx_hash))
                                    .unwrap();
                                client.json(json)
                            }
                            Err(e) => client.error(e),
                        }
                    }
                    Err(_) => client.error(ValueNotFound::new("Can't parse ConfigVote request")),
                }
            })
        });
        api.get("config/propose/:hash", |endpoint| {
            let b = b.clone();
            endpoint.summary("Returns config propose by hash");
            endpoint.params(|params| {
                params.opt_typed("hash", json_dsl::string());
            });
            endpoint.handle(move |client, params| {
                let hash_propose = Hash::from_hex(params.find("hash").unwrap().as_str().unwrap())
                    .unwrap();
                match ConfigurationSchema::new(&b.view()).get_config_propose(&hash_propose).unwrap() {
                    Some(config_propose) => {
                        let info = ConfigProposeInfo::from(config_propose);
                        return client.json(&info.to_json());
                    }
                    None => {
                        return client.error(ValueNotFound::new("Unable to find ConfigPropose for hash"))
                    }
                }
            })
        });
        api.get("config/vote/:from", |endpoint| {
            let b = b.clone();
            endpoint.summary("Returns config votes array");
            endpoint.params(|params| {
                params.opt_typed("from", json_dsl::string());
            });
            endpoint.handle(move |client, params| {
                let from = PublicKey::from_hex(params.find("from").unwrap().as_str().unwrap())
                    .unwrap();
                match ConfigurationSchema::new(&b.view()).get_vote(&from).unwrap() {
                    Some(vote) => {
                        let vote_value = VoteValue { revoke: vote.revoke() };
                        client.json(&vote_value.to_json())
                    }
                    None => {
                        return client.error(ValueNotFound::new("Unable to find vote for public key"));
                    }
                }
            })
        });
        api.get("blocks", |endpoint| {
            let b = b.clone();
            let c = cfg.clone();

            endpoint.summary("Returns blockchain info array");
            endpoint.params(|params| {
                params.opt_typed("from", json_dsl::u64());
                params.opt_typed("count", json_dsl::u64())
            });

            endpoint.handle(move |client, params| {
                let from = params.find("from").map(|x| x.as_u64().unwrap()).map(|x| x + 1);
                let count = params.find("count").map(|x| x.as_u64().unwrap()).unwrap_or(100);

                let view = b.clone().view();
                let explorer = BlockchainExplorer::new(&view, c.clone());
                match explorer.blocks_range::<T>(count, from) {
                    Ok(blocks) => client.json(&blocks.to_json()),
                    Err(e) => client.error(e),
                }
            })
        });
        api.get("blocks/:height", |endpoint| {
            let b = b.clone();
            let c = cfg.clone();

            endpoint.summary("Returns block with given height");
            endpoint.params(|params| {
                params.req_typed("height", json_dsl::u64());
            });

            endpoint.handle(move |client, params| {
                let height = params.find("height").unwrap().as_u64().unwrap();

                let view = b.clone().view();
                let explorer = BlockchainExplorer::new(&view, c.clone());
                match explorer.block_info_with_height::<T>(height) {
                    Ok(Some(block)) => client.json(&block.to_json()),
                    Ok(None) => {
                        client.error(ValueNotFound::new("Unable to find block with given height"))
                    }
                    Err(e) => client.error(e),
                }
            })
        });
        api.get("transactions/:hash", |endpoint| {
            let b = b.clone();
            let c = cfg.clone();

            endpoint.summary("Returns transaction info with given hash");
            endpoint.params(|params| {
                params.req_typed("hash", json_dsl::string());
            });

            endpoint.handle(move |client, params| {
                let hash = params.find("hash").unwrap().as_str().unwrap();
                let view = b.clone().view();
                let explorer = BlockchainExplorer::new(&view, c.clone());
                match Hash::from_hex(hash) {
                    Ok(hash) => {
                        match explorer.tx_info::<T>(&hash) {
                            Ok(Some(tx_info)) => client.json(&tx_info.to_json()),
                            Ok(None) => {
                                client.error(ValueNotFound::new("Unable to found transaction \
                                                                 with given hash"))
                            }
                            Err(e) => client.error(e),
                        }
                    }
                    Err(_) => client.error(StorageError::new("Unable to decode transaction hash")),
                }
            })
        });
    }) // namespace blockchain
}
<commit_msg>Test node for configs<commit_after>mod explorer;
pub mod helpers;

extern crate time;
extern crate serde;
extern crate jsonway;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate exonum;
extern crate rustless;
extern crate valico;

extern crate clap;
extern crate env_logger;
extern crate log;
extern crate term;
extern crate colored;

use std::ops::Deref;
use std::marker::PhantomData;
use std::error;
use std::fmt;
use serde_json::value::from_value;

use serde::{Serialize, Serializer};
use serde::de;
use serde::de::{Visitor, Deserialize, Deserializer};
use rustless::json::ToJson;
use rustless::{Api, Nesting};
use valico::json_dsl;

use exonum::crypto::{Hash, PublicKey, SecretKey, HexValue, ToHex};
use exonum::storage::Error as StorageError;
use exonum::messages::RawTransaction;
use exonum::blockchain::{Blockchain, Schema};
use exonum::services::configuration::ConfigurationSchema;
use exonum::node::{Node, NodeConfig, NodeChannel, TxSender};
use exonum::services::configuration::{ConfigTx, TxConfigPropose, TxConfigVote};

pub use explorer::{TransactionInfo, BlockchainExplorer, BlockInfo};

pub type Channel = TxSender<NodeChannel>;

#[derive(Debug)]
pub struct ValueNotFound(String);

impl fmt::Display for ValueNotFound {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "ValueNotFound: {}", self.0)
    }
}

impl error::Error for ValueNotFound {
    fn description(&self) -> &str {
        self.0.as_ref()
    }
}

impl ValueNotFound {
    pub fn new<T: Into<String>>(s: T) -> ValueNotFound {
        ValueNotFound(s.into())
    }
}

#[derive(Clone, Debug)]
pub struct HexField<T: AsRef<[u8]> + Clone>(pub T);

impl<T> Deref for HexField<T>
    where T: AsRef<[u8]> + Clone
{
    type Target = T;

    fn deref(&self) -> &T {
        &self.0
    }
}

impl<T> Serialize for HexField<T>
    where T: AsRef<[u8]> + Clone
{
    fn serialize<S>(&self, ser: &mut S) -> Result<(), S::Error>
        where S: Serializer
    {
        ser.serialize_str(&self.0.as_ref().to_hex())
    }
}

struct HexVisitor<T>
    where T: AsRef<[u8]> + HexValue
{
    _p: PhantomData<T>,
}

impl<T> Visitor for HexVisitor<T>
    where T: AsRef<[u8]> + HexValue + Clone
{
    type Value = HexField<T>;

    fn visit_str<E>(&mut self, s: &str) -> Result<HexField<T>, E>
        where E: de::Error
    {
        let v = T::from_hex(s).map_err(|_| de::Error::custom("Invalid hex"))?;
        Ok(HexField(v))
    }
}

impl<T> Deserialize for HexField<T>
    where T: AsRef<[u8]> + HexValue + Clone
{
    fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
        where D: Deserializer
    {
        deserializer.deserialize_str(HexVisitor { _p: PhantomData })
    }
}

#[derive(Serialize)]
struct VoteValue {
    revoke: bool,
}

#[derive(Serialize)]
struct ConfigProposeInfo {
    from: PublicKey,
    height: u64,
    config: Vec<u8>,
    actual_from_height: u64,
}

#[derive(Deserialize)]
struct ConfigProposeRequest {
    pub config: Vec<u8>,
    pub actual_from_height: u64,
}

#[derive(Deserialize)]
struct ConfigVoteRequest {
    pub height: u64,
    pub hash_propose: Hash,
    pub seed: u64,
    pub revoke: bool,
}

impl ConfigProposeRequest {
    fn into_tx(&self, height: u64, pub_key: &PublicKey, sec_key: &SecretKey) -> TxConfigPropose {
        TxConfigPropose::new(pub_key,
                             height,
                             self.config.as_ref(),
                             self.actual_from_height,
                             &sec_key)
    }
}

impl ConfigVoteRequest {
    fn into_tx(&self, height: u64, pub_key: &PublicKey, sec_key: &SecretKey) -> TxConfigVote {
        TxConfigVote::new(pub_key,
                          height,
                          &self.hash_propose,
                          self.seed,
                          self.revoke,
                          &sec_key)
    }
}

impl From<TxConfigPropose> for ConfigProposeInfo {
    fn from(src: TxConfigPropose) -> ConfigProposeInfo {
        ConfigProposeInfo {
            from: *src.from(),
            height: src.height(),
            config: src.config().to_vec(),
            actual_from_height: src.actual_from_height(),
        }
    }
}

pub fn make_api<T>(api: &mut Api, b: Blockchain, channel: TxSender<NodeChannel>, cfg: NodeConfig)
    where T: TransactionInfo + From<RawTransaction>
{

    // let node = Node::new(b.clone(), cfg.clone());
    // let channel = node.channel();

    api.namespace("blockchain", move |api| {
        api.get("config/actual", |endpoint| {
            let b = b.clone();
            endpoint.summary("Returns actual configuration");
            endpoint.handle(move |client, _| {
                match Schema::new(&b.view()).get_actual_configuration() {
                    Ok(config) => client.json(&config.to_json()),
                    Err(e) => client.error(e),
                }
            })
        });
        api.put("config/propose", |endpoint| {
            let b = b.clone();
            let c = cfg.clone();
            let ch = channel.clone();
            endpoint.summary("Puts new ConfigPropose");
            endpoint.params(|params| {
                params.opt_typed("config", json_dsl::array_of(json_dsl::u64()));
                params.opt_typed("actual_from_height", json_dsl::u64());
            });
            endpoint.handle(move |client, params| {
                match from_value::<ConfigProposeRequest>(params.clone()) {
                    Ok(config_propose_request) => {
                        let config_propose =
                            config_propose_request.into_tx(Schema::new(&b.view()).last_height().unwrap(), &c.public_key, &c.secret_key);

                        let tx = ConfigTx::ConfigPropose(config_propose);
                        let tx_hash = HexValue::to_hex(&tx.hash());
                        match ch.send(tx) {
                            Ok(_) => {
                                let json = &jsonway::object(|json| json.set("tx_hash", tx_hash))
                                    .unwrap();
                                client.json(json)
                            }
                            Err(e) => {
                                client.error(e)
                            }
                        }
                    }
                    Err(_) => client.error(ValueNotFound::new("Can't parse ConfigPropose request")),
                }
            })
        });
        api.put("config/vote", |endpoint| {
            let b = b.clone();
            let c = cfg.clone();
            let ch = channel.clone();
            endpoint.summary("Puts new ConfigVote");
            endpoint.params(|params| {
                params.opt_typed("height", json_dsl::u64());
                params.opt_typed("hash_propose", json_dsl::string());
                params.opt_typed("seed", json_dsl::u64());
                params.opt_typed("revoke", json_dsl::boolean());
            });

            endpoint.handle(move |client, params| {
                match from_value::<ConfigVoteRequest>(params.clone()) {
                    Ok(config_vote_request) => {
                        let config_vote = config_vote_request.into_tx(Schema::new(&b.view()).last_height().unwrap(), &c.public_key, &c.secret_key);

                        let tx = ConfigTx::ConfigVote(config_vote);
                        let tx_hash = HexValue::to_hex(&tx.hash());
                        match ch.send(tx) {
                            Ok(_) => {
                                let json = &jsonway::object(|json| json.set("tx_hash", tx_hash))
                                    .unwrap();
                                client.json(json)
                            }
                            Err(e) => client.error(e),
                        }
                    }
                    Err(_) => client.error(ValueNotFound::new("Can't parse ConfigVote request")),
                }
            })
        });
        api.get("config/propose/:hash", |endpoint| {
            let b = b.clone();
            endpoint.summary("Returns config propose by hash");
            endpoint.params(|params| {
                params.opt_typed("hash", json_dsl::string());
            });
            endpoint.handle(move |client, params| {
                let hash_propose = Hash::from_hex(params.find("hash").unwrap().as_str().unwrap())
                    .unwrap();
                match ConfigurationSchema::new(&b.view()).get_config_propose(&hash_propose).unwrap() {
                    Some(config_propose) => {
                        let info = ConfigProposeInfo::from(config_propose);
                        return client.json(&info.to_json());
                    }
                    None => {
                        return client.error(ValueNotFound::new("Unable to find ConfigPropose for hash"))
                    }
                }
            })
        });
        api.get("config/vote/:from", |endpoint| {
            let b = b.clone();
            endpoint.summary("Returns config votes array");
            endpoint.params(|params| {
                params.opt_typed("from", json_dsl::string());
            });
            endpoint.handle(move |client, params| {
                let from = PublicKey::from_hex(params.find("from").unwrap().as_str().unwrap())
                    .unwrap();
                match ConfigurationSchema::new(&b.view()).get_vote(&from).unwrap() {
                    Some(vote) => {
                        let vote_value = VoteValue { revoke: vote.revoke() };
                        client.json(&vote_value.to_json())
                    }
                    None => {
                        return client.error(ValueNotFound::new("Unable to find vote for public key"));
                    }
                }
            })
        });
        api.get("blocks", |endpoint| {
            let b = b.clone();
            let c = cfg.clone();

            endpoint.summary("Returns blockchain info array");
            endpoint.params(|params| {
                params.opt_typed("from", json_dsl::u64());
                params.opt_typed("count", json_dsl::u64())
            });

            endpoint.handle(move |client, params| {
                let from = params.find("from").map(|x| x.as_u64().unwrap()).map(|x| x + 1);
                let count = params.find("count").map(|x| x.as_u64().unwrap()).unwrap_or(100);

                let view = b.clone().view();
                let explorer = BlockchainExplorer::new(&view, c.clone());
                match explorer.blocks_range::<T>(count, from) {
                    Ok(blocks) => client.json(&blocks.to_json()),
                    Err(e) => client.error(e),
                }
            })
        });
        api.get("blocks/:height", |endpoint| {
            let b = b.clone();
            let c = cfg.clone();

            endpoint.summary("Returns block with given height");
            endpoint.params(|params| {
                params.req_typed("height", json_dsl::u64());
            });

            endpoint.handle(move |client, params| {
                let height = params.find("height").unwrap().as_u64().unwrap();

                let view = b.clone().view();
                let explorer = BlockchainExplorer::new(&view, c.clone());
                match explorer.block_info_with_height::<T>(height) {
                    Ok(Some(block)) => client.json(&block.to_json()),
                    Ok(None) => {
                        client.error(ValueNotFound::new("Unable to find block with given height"))
                    }
                    Err(e) => client.error(e),
                }
            })
        });
        api.get("transactions/:hash", |endpoint| {
            let b = b.clone();
            let c = cfg.clone();

            endpoint.summary("Returns transaction info with given hash");
            endpoint.params(|params| {
                params.req_typed("hash", json_dsl::string());
            });

            endpoint.handle(move |client, params| {
                let hash = params.find("hash").unwrap().as_str().unwrap();
                let view = b.clone().view();
                let explorer = BlockchainExplorer::new(&view, c.clone());
                match Hash::from_hex(hash) {
                    Ok(hash) => {
                        match explorer.tx_info::<T>(&hash) {
                            Ok(Some(tx_info)) => client.json(&tx_info.to_json()),
                            Ok(None) => {
                                client.error(ValueNotFound::new("Unable to found transaction \
                                                                 with given hash"))
                            }
                            Err(e) => client.error(e),
                        }
                    }
                    Err(_) => client.error(StorageError::new("Unable to decode transaction hash")),
                }
            })
        });
    }) // namespace blockchain
}
<|endoftext|>


<commit_before>use std::io::{Cursor, Read};
use std::path::Path;
use std::pin::Pin;
use std::str::FromStr;

use bytes::Bytes;
use futures::{Future, FutureExt, TryStreamExt};
use hyper::{
    client::{connect::Connect, Client},
    header::CONTENT_TYPE,
    Method, Request, Uri,
};
use hyper_tls::HttpsConnector;
use multipart::client::lazy::Multipart;
use telegram_bot_raw::{
    Body as TelegramBody, HttpRequest, HttpResponse, Method as TelegramMethod, MultipartValue, Text,
};

use super::Connector;
use crate::errors::{Error, ErrorKind};

#[derive(Debug)]
pub struct HyperConnector<C>(Client<C>);

enum MultipartTemporaryValue {
    Text(Text),
    Data { file_name: Text, data: Bytes },
}

impl<C> HyperConnector<C> {
    pub fn new(client: Client<C>) -> Self {
        HyperConnector(client)
    }
}

impl<C: Connect + std::fmt::Debug + 'static> Connector for HyperConnector<C> {
    fn request(
        &self,
        token: &str,
        req: HttpRequest,
    ) -> Pin<Box<dyn Future<Output = Result<HttpResponse, Error>> + Send>> {
        let uri = Uri::from_str(&req.url.url(token));
        let client = self.0.clone();

        let future = async move {
            let uri = uri?;

            let method = match req.method {
                TelegramMethod::Get => Method::GET,
                TelegramMethod::Post => Method::POST,
            };

            let mut http_request = Request::builder();
            http_request.method(method).uri(uri);

            let request = match req.body {
                TelegramBody::Empty => http_request.body(Into::<hyper::Body>::into(vec![])),
                TelegramBody::Json(body) => {
                    let content_type = "application/json".parse()?;
                    http_request
                        .headers_mut()
                        .map(move |headers| headers.insert(CONTENT_TYPE, content_type));
                    http_request.body(Into::<hyper::Body>::into(body))
                }
                TelegramBody::Multipart(parts) => {
                    let mut fields = Vec::new();
                    for (key, value) in parts {
                        match value {
                            MultipartValue::Text(text) => {
                                fields.push((key, MultipartTemporaryValue::Text(text)))
                            }
                            MultipartValue::Path { file_name, path } => {
                                let file_name = match file_name {
                                    Some(file_name) => file_name,
                                    None => {
                                        match AsRef::<Path>::as_ref(&path)
                                            .file_name()
                                            .and_then(|s| s.to_str())
                                            .map(Into::into)
                                        {
                                            None => {
                                                return Err(
                                                    ErrorKind::InvalidMultipartFilename.into()
                                                )
                                            }
                                            Some(file_name) => file_name,
                                        }
                                    }
                                };

                                let data = tokio::fs::read(path).await?;
                                fields.push((
                                    key,
                                    MultipartTemporaryValue::Data {
                                        file_name,
                                        data: data.into(),
                                    },
                                ))
                            }
                            MultipartValue::Data { file_name, data } => fields
                                .push((key, MultipartTemporaryValue::Data { file_name, data })),
                        }
                    }

                    let mut prepared = {
                        let mut part = Multipart::new();
                        for (key, value) in &fields {
                            match value {
                                MultipartTemporaryValue::Text(text) => {
                                    part.add_text(*key, text.as_str());
                                }
                                MultipartTemporaryValue::Data { file_name, data } => {
                                    part.add_stream(
                                        *key,
                                        Cursor::new(data),
                                        Some(file_name.as_str()),
                                        None,
                                    );
                                }
                            }
                        }
                        part.prepare().map_err(|err| err.error)
                    }?;

                    let boundary = prepared.boundary();

                    let content_type =
                        format!("multipart/form-data;boundary={bound}", bound = boundary)
                            .parse()?;
                    http_request.headers_mut().map(move |headers| {
                        headers.insert(CONTENT_TYPE, content_type);
                    });

                    let mut bytes = Vec::new();
                    prepared.read_to_end(&mut bytes)?;
                    http_request.body(bytes.into())
                }
                body => panic!("Unknown body type {:?}", body),
            }?;

            let response = client.request(request).await?;
            let whole_chunk = response.into_body().try_concat().await;

            let body = whole_chunk
                .iter()
                .fold(vec![], |mut acc, chunk| -> Vec<u8> {
                    acc.extend_from_slice(&chunk);
                    acc
                });

            Ok::<HttpResponse, Error>(HttpResponse { body: Some(body) })
        };

        future.boxed()
    }
}

pub fn default_connector() -> Result<Box<dyn Connector>, Error> {
    let connector = HttpsConnector::new().map_err(|err| {
        ::std::io::Error::new(::std::io::ErrorKind::Other, format!("tls error: {}", err))
    })?;
    Ok(Box::new(HyperConnector::new(
        Client::builder().build(connector),
    )))
}
<commit_msg>refactor: review fix<commit_after>use std::io::{Cursor, Read};
use std::path::Path;
use std::pin::Pin;
use std::str::FromStr;

use bytes::Bytes;
use futures::{Future, FutureExt, TryStreamExt};
use hyper::{
    client::{connect::Connect, Client},
    header::CONTENT_TYPE,
    Method, Request, Uri,
};
use hyper_tls::HttpsConnector;
use multipart::client::lazy::Multipart;
use telegram_bot_raw::{
    Body as TelegramBody, HttpRequest, HttpResponse, Method as TelegramMethod, MultipartValue, Text,
};

use super::Connector;
use crate::errors::{Error, ErrorKind};

#[derive(Debug)]
pub struct HyperConnector<C>(Client<C>);

enum MultipartTemporaryValue {
    Text(Text),
    Data { file_name: Text, data: Bytes },
}

impl<C> HyperConnector<C> {
    pub fn new(client: Client<C>) -> Self {
        HyperConnector(client)
    }
}

impl<C: Connect + std::fmt::Debug + 'static> Connector for HyperConnector<C> {
    fn request(
        &self,
        token: &str,
        req: HttpRequest,
    ) -> Pin<Box<dyn Future<Output = Result<HttpResponse, Error>> + Send>> {
        let uri = Uri::from_str(&req.url.url(token));
        let client = self.0.clone();

        let future = async move {
            let uri = uri?;

            let method = match req.method {
                TelegramMethod::Get => Method::GET,
                TelegramMethod::Post => Method::POST,
            };

            let mut http_request = Request::builder();
            http_request.method(method).uri(uri);

            let request = match req.body {
                TelegramBody::Empty => http_request.body(Into::<hyper::Body>::into(vec![])),
                TelegramBody::Json(body) => {
                    let content_type = "application/json".parse()?;
                    http_request
                        .headers_mut()
                        .map(move |headers| headers.insert(CONTENT_TYPE, content_type));
                    http_request.body(Into::<hyper::Body>::into(body))
                }
                TelegramBody::Multipart(parts) => {
                    let mut fields = Vec::new();
                    for (key, value) in parts {
                        match value {
                            MultipartValue::Text(text) => {
                                fields.push((key, MultipartTemporaryValue::Text(text)))
                            }
                            MultipartValue::Path { file_name, path } => {
                                let file_name = file_name
                                    .or_else(|| {
                                        AsRef::<Path>::as_ref(&path)
                                            .file_name()
                                            .and_then(|s| s.to_str())
                                            .map(Into::into)
                                    })
                                    .ok_or(ErrorKind::InvalidMultipartFilename)?;

                                let data = tokio::fs::read(path).await?;
                                fields.push((
                                    key,
                                    MultipartTemporaryValue::Data {
                                        file_name,
                                        data: data.into(),
                                    },
                                ))
                            }
                            MultipartValue::Data { file_name, data } => fields
                                .push((key, MultipartTemporaryValue::Data { file_name, data })),
                        }
                    }

                    let mut prepared = {
                        let mut part = Multipart::new();
                        for (key, value) in &fields {
                            match value {
                                MultipartTemporaryValue::Text(text) => {
                                    part.add_text(*key, text.as_str());
                                }
                                MultipartTemporaryValue::Data { file_name, data } => {
                                    part.add_stream(
                                        *key,
                                        Cursor::new(data),
                                        Some(file_name.as_str()),
                                        None,
                                    );
                                }
                            }
                        }
                        part.prepare().map_err(|err| err.error)
                    }?;

                    let boundary = prepared.boundary();

                    let content_type =
                        format!("multipart/form-data;boundary={bound}", bound = boundary)
                            .parse()?;
                    http_request.headers_mut().map(move |headers| {
                        headers.insert(CONTENT_TYPE, content_type);
                    });

                    let mut bytes = Vec::new();
                    prepared.read_to_end(&mut bytes)?;
                    http_request.body(bytes.into())
                }
                body => panic!("Unknown body type {:?}", body),
            }?;

            let response = client.request(request).await?;
            let whole_chunk = response.into_body().try_concat().await;

            let body = whole_chunk
                .iter()
                .fold(vec![], |mut acc, chunk| -> Vec<u8> {
                    acc.extend_from_slice(&chunk);
                    acc
                });

            Ok::<HttpResponse, Error>(HttpResponse { body: Some(body) })
        };

        future.boxed()
    }
}

pub fn default_connector() -> Result<Box<dyn Connector>, Error> {
    let connector = HttpsConnector::new().map_err(|err| {
        ::std::io::Error::new(::std::io::ErrorKind::Other, format!("tls error: {}", err))
    })?;
    Ok(Box::new(HyperConnector::new(
        Client::builder().build(connector),
    )))
}
<|endoftext|>


<commit_before>#![feature(slice_patterns)]
use std::env;
use std::fs::File;
use std::io::Read;
use std::collections::HashMap;

#[derive(Clone)]
enum Expr {
  Int(i64),
  Str(String)
}

// XXX this should probably be split up. The VM has the instructions, a "Thread" has locals, stack, and arguments.
struct VM<'a> {
  instructions: Vec<&'a str>,
  locals: HashMap<&'a str, Expr>,
  labels: HashMap<&'a str, usize>,
}

fn parse_labels<'a>(instructions: &Vec<&'a str>) -> HashMap<&'a str, usize> {
  let mut map = HashMap::new();
  for (i, instr) in instructions.iter().enumerate() {
    if instr.starts_with("$label") {
      match instr.split(' ').nth(1) {
        Some(label) => map.insert(label, i),
        None => panic!("VM error: missing label"),
      };
    }
  };
  map
}

impl<'a> VM<'a> {
  pub fn new(instructions: Vec<&'a str>) -> VM {
    VM {
      labels: parse_labels(&instructions),
      instructions: instructions,
      locals: HashMap::new(),
    }
  }

  pub fn run(&mut self) {
    let ip = self.labels.get("main").unwrap_or(&0).clone();
    self.run_call(ip, &mut Vec::new());
  }

  // TODO locals
  pub fn run_call(&mut self, start_ip: usize, stack: &mut Vec<Expr>) -> Option<Expr> {
    let mut ip = start_ip;
    let len = self.instructions.len();

    let mut carry = false; // comparison carry

    let mut arguments = Vec::new();

    while ip < len {
      let instr = self.instructions[ip];
      ip += 1;
      if instr.starts_with("#") || instr.trim() == "" {
        continue;
      }
      match instr.split(" ").collect::<Vec<_>>().as_slice() {
        &["$label", _] => (),
        &["push", "int", n] => stack.push(Expr::Int(n.parse::<i64>().unwrap())),
        &["push", "str", n] => stack.push(Expr::Str(String::from(n))),
        &["push", "arg"] => arguments.push(stack.pop().expect("Nothing on the stack to push to the arguments")),

        &["dup"] => {
          let expr = stack.pop().expect("Nothing on the stack to pop");
          stack.push(expr.clone());
          stack.push(expr);
        },

        // how = "always" | "carry"
        &["jump", how, n] => {
          let new_ip = if n.starts_with("$") {
            self.labels.get(n.trim_matches('$')).expect("VM error: no such label").clone()
          } else {
            n.parse::<usize>().unwrap()
          };

          if new_ip < len {
            // only check carry if we are conditionally jumping
            if how == "always" || (how == "carry" && carry) {
              ip = new_ip;
            }
          } else {
            panic!("VM error: trying to jump past end of bytecode ({} > {})", new_ip, len);
          }
        },

        &["carry", "set", val] => carry = val == "true",
        &["carry", "invert"] => carry = !carry,

        &["call", "primitive", "say"] => if let Some(Expr::Str(arg)) = stack.pop() {
          println!("hey {}", arg);
        } else {
          panic!("VM error: incorrect arguments to `say`");
        },

        &["call", "primitive", "typeof"] => match stack.pop() {
            Some(Expr::Str(_)) => stack.push(Expr::Str(String::from("str"))),
            Some(Expr::Int(_)) => stack.push(Expr::Str(String::from("int"))),
            None => panic!("VM error: no argument supplied to `typeof`"),
        },

        &["call", "primitive", "convert"] => if let
            (Some(Expr::Str(from)), Some(Expr::Str(to)))
            =
            (stack.pop(), stack.pop()) {

            match (from.as_ref(), to.as_ref()) {
                ("int", "str") => if let Some(Expr::Int(arg)) = stack.pop() {
                  stack.push(Expr::Str(arg.to_string()));
                } else {
                  panic!("VM error: cannot convert int to string");
                },
                _ => panic!("VM error: cannot convert from {} to {}", from, to),
            }
        }
        else {
            panic!("VM error: bad arguments to primitive call `convert`");
        },

        // TODO resolve name in scope, check type, apply
        &["call", name] => {
          let new_ip = self.labels.get(name).expect("VM error: no such label").clone();
          let ret = self.run_call(new_ip, &mut arguments);
          arguments = Vec::new();
          match ret {
            Some(val) => stack.push(val),
            None => (),
          };
        },

        &["ret"] => {
          return None;
        },

        &["ret", "val"] => {
          if stack.len() != 1 {
            panic!("VM error: trying to return a value but the stack size is not 1 ({})", stack.len());
          }
          return stack.pop();
        },

        // NOTE: it's stack[*-1] OP stack[*-2]
        // which means if have stack=[1, 2]
        // you'll have 2 OP 1
        /// XXX this should JUST be a call!
        &["math", op] => if let
            (Some(Expr::Int(arg1)), Some(Expr::Int(arg2)))
            =
            (stack.pop(), stack.pop()) {
          let value = match op {
            "+" => arg1 + arg2,
            "-" => arg1 + arg2,
            //"/" => TODO. this should have very specific rules...
            "*" => arg1 * arg2,
            _ => panic!("VM error: unrecognized `op` operator: {}", op),
          };
          stack.push(Expr::Int(value));
        } else {
          panic!("VM error: not enough arguments to `math`");
        },

        // same note about "2 OP 1"
        // XXX same note about how this should be a `call`
        &["cmp", op] => carry = match (stack.pop(), stack.pop()) {
            (Some(Expr::Int(arg1)), Some(Expr::Int(arg2))) => match op {
                "<" => arg1 < arg2,
                ">" => arg1 > arg2,
                "<=" => arg1 <= arg2,
                ">=" => arg1 >= arg2,
                "=" => arg1 == arg2,
                _ => panic!("VM error: unrecognized `cmp` operator for int: {}", op),
            },
            (Some(Expr::Str(arg1)), Some(Expr::Str(arg2))) => match op {
                "=" => arg1 == arg2,
                _ => panic!("VM error: unrecognized `cmp` operator for str: {}", op),
            },
            _ => panic!("VM error: not enough arguments to `cmp`"),
        },

        // TODO this *shouldn't* be an identifier
        //      the backend should transform SSAF to tons of load/store
        //      (probably)
        //      (see backend#1)
        &["local", "load", name] => match self.locals.get(name) {
          Some(expr) => stack.push(expr.clone()),
          None       => panic!("VM error: unknown local variable {}", name),
        },

        &["local", "store", name] => if let Some(arg) = stack.pop() {
          // XXX check
          let _ = self.locals.insert(name, arg);
        } else {
          panic!("VM error: not enough arguments for `local store`");
        },

        &[instr, ..] => panic!("VM error: no such instruction {}", instr),
        &[..] => panic!("VM error: Unrecognized instruction!"),
      }
    }

    None
  }
}

fn main() {
	if let Some(path) = env::args().nth(1) {
		println!("Loading file {}", path);

    match File::open(&path) {
      Ok(mut file) => {
        let mut content = String::new();
        if let Ok(_) = file.read_to_string(&mut content) {
          // remove trailing newlines, split by line
          let instructions = content.trim_matches('\n').split('\n').collect();
          let mut vm = VM::new(instructions);
          vm.run();
        }
      },
      Err(err) => {
        panic!("Can't read file {} (error: {})", path, err);
      }
    }
	} else {
    panic!("Please give a file argument");
  }

  //     let path = Path::new("chry.fa");
  // let mut file = BufferedReader::new(File::open(&path));
  // for line in file.lines() {
  //  print!("{}", line.unwrap());
  //      }
}
<commit_msg>Make locals local to run_call<commit_after>#![feature(slice_patterns)]
use std::env;
use std::fs::File;
use std::io::Read;
use std::collections::HashMap;

#[derive(Clone)]
enum Expr {
  Int(i64),
  Str(String)
}

// XXX this should probably be split up. The VM has the instructions, a "Thread" has locals, stack, and arguments.
struct VM<'a> {
  instructions: Vec<&'a str>,
  labels: HashMap<&'a str, usize>,
}

fn parse_labels<'a>(instructions: &Vec<&'a str>) -> HashMap<&'a str, usize> {
  let mut map = HashMap::new();
  for (i, instr) in instructions.iter().enumerate() {
    if instr.starts_with("$label") {
      match instr.split(' ').nth(1) {
        Some(label) => map.insert(label, i),
        None => panic!("VM error: missing label"),
      };
    }
  };
  map
}

impl<'a> VM<'a> {
  pub fn new(instructions: Vec<&'a str>) -> VM {
    VM {
      labels: parse_labels(&instructions),
      instructions: instructions,
    }
  }

  pub fn run(&mut self) {
    let ip = self.labels.get("main").unwrap_or(&0).clone();
    self.run_call(ip, &mut Vec::new());
  }

  // TODO locals
  pub fn run_call(&mut self, start_ip: usize, stack: &mut Vec<Expr>) -> Option<Expr> {
    let mut ip = start_ip;
    let len = self.instructions.len();
    let locals: &mut HashMap<&'a str, Expr> = &mut HashMap::new();

    let mut carry = false; // comparison carry

    let mut arguments = Vec::new();

    while ip < len {
      let instr = self.instructions[ip];
      ip += 1;
      if instr.starts_with("#") || instr.trim() == "" {
        continue;
      }
      match instr.split(" ").collect::<Vec<_>>().as_slice() {
        &["$label", _] => (),
        &["push", "int", n] => stack.push(Expr::Int(n.parse::<i64>().unwrap())),
        &["push", "str", n] => stack.push(Expr::Str(String::from(n))),
        &["push", "arg"] => arguments.push(stack.pop().expect("Nothing on the stack to push to the arguments")),

        &["dup"] => {
          let expr = stack.pop().expect("Nothing on the stack to pop");
          stack.push(expr.clone());
          stack.push(expr);
        },

        // how = "always" | "carry"
        &["jump", how, n] => {
          let new_ip = if n.starts_with("$") {
            self.labels.get(n.trim_matches('$')).expect("VM error: no such label").clone()
          } else {
            n.parse::<usize>().unwrap()
          };

          if new_ip < len {
            // only check carry if we are conditionally jumping
            if how == "always" || (how == "carry" && carry) {
              ip = new_ip;
            }
          } else {
            panic!("VM error: trying to jump past end of bytecode ({} > {})", new_ip, len);
          }
        },

        &["carry", "set", val] => carry = val == "true",
        &["carry", "invert"] => carry = !carry,

        &["call", "primitive", "say"] => if let Some(Expr::Str(arg)) = stack.pop() {
          println!("hey {}", arg);
        } else {
          panic!("VM error: incorrect arguments to `say`");
        },

        &["call", "primitive", "typeof"] => match stack.pop() {
            Some(Expr::Str(_)) => stack.push(Expr::Str(String::from("str"))),
            Some(Expr::Int(_)) => stack.push(Expr::Str(String::from("int"))),
            None => panic!("VM error: no argument supplied to `typeof`"),
        },

        &["call", "primitive", "convert"] => if let
            (Some(Expr::Str(from)), Some(Expr::Str(to)))
            =
            (stack.pop(), stack.pop()) {

            match (from.as_ref(), to.as_ref()) {
                ("int", "str") => if let Some(Expr::Int(arg)) = stack.pop() {
                  stack.push(Expr::Str(arg.to_string()));
                } else {
                  panic!("VM error: cannot convert int to string");
                },
                _ => panic!("VM error: cannot convert from {} to {}", from, to),
            }
        }
        else {
            panic!("VM error: bad arguments to primitive call `convert`");
        },

        // TODO resolve name in scope, check type, apply
        &["call", name] => {
          let new_ip = self.labels.get(name).expect("VM error: no such label").clone();
          let ret = self.run_call(new_ip, &mut arguments);
          arguments = Vec::new();
          match ret {
            Some(val) => stack.push(val),
            None => (),
          };
        },

        &["ret"] => {
          return None;
        },

        &["ret", "val"] => {
          if stack.len() != 1 {
            panic!("VM error: trying to return a value but the stack size is not 1 ({})", stack.len());
          }
          return stack.pop();
        },

        // NOTE: it's stack[*-1] OP stack[*-2]
        // which means if have stack=[1, 2]
        // you'll have 2 OP 1
        /// XXX this should JUST be a call!
        &["math", op] => if let
            (Some(Expr::Int(arg1)), Some(Expr::Int(arg2)))
            =
            (stack.pop(), stack.pop()) {
          let value = match op {
            "+" => arg1 + arg2,
            "-" => arg1 - arg2,
            //"/" => TODO. this should have very specific rules...
            "*" => arg1 * arg2,
            _ => panic!("VM error: unrecognized `op` operator: {}", op),
          };
          stack.push(Expr::Int(value));
        } else {
          panic!("VM error: not enough arguments to `math`");
        },

        // same note about "2 OP 1"
        // XXX same note about how this should be a `call`
        &["cmp", op] => carry = match (stack.pop(), stack.pop()) {
            (Some(Expr::Int(arg1)), Some(Expr::Int(arg2))) => match op {
                "<" => arg1 < arg2,
                ">" => arg1 > arg2,
                "<=" => arg1 <= arg2,
                ">=" => arg1 >= arg2,
                "=" => arg1 == arg2,
                _ => panic!("VM error: unrecognized `cmp` operator for int: {}", op),
            },
            (Some(Expr::Str(arg1)), Some(Expr::Str(arg2))) => match op {
                "=" => arg1 == arg2,
                _ => panic!("VM error: unrecognized `cmp` operator for str: {}", op),
            },
            _ => panic!("VM error: not enough arguments to `cmp`"),
        },

        // TODO this *shouldn't* be an identifier
        //      the backend should transform SSAF to tons of load/store
        //      (probably)
        //      (see backend#1)
        &["local", "load", name] => match locals.get(name) {
          Some(expr) => stack.push(expr.clone()),
          None       => panic!("VM error: unknown local variable {}", name),
        },

        &["local", "store", name] => if let Some(arg) = stack.pop() {
          // XXX check
          let _ = locals.insert(name, arg);
        } else {
          panic!("VM error: not enough arguments for `local store`");
        },

        &[instr, ..] => panic!("VM error: no such instruction {}", instr),
        &[..] => panic!("VM error: Unrecognized instruction!"),
      }
    }

    None
  }
}

fn main() {
	if let Some(path) = env::args().nth(1) {
		println!("Loading file {}", path);

    match File::open(&path) {
      Ok(mut file) => {
        let mut content = String::new();
        if let Ok(_) = file.read_to_string(&mut content) {
          // remove trailing newlines, split by line
          let instructions = content.trim_matches('\n').split('\n').collect();
          let mut vm = VM::new(instructions);
          vm.run();
        }
      },
      Err(err) => {
        panic!("Can't read file {} (error: {})", path, err);
      }
    }
	} else {
    panic!("Please give a file argument");
  }

  //     let path = Path::new("chry.fa");
  // let mut file = BufferedReader::new(File::open(&path));
  // for line in file.lines() {
  //  print!("{}", line.unwrap());
  //      }
}
<|endoftext|>


<commit_before>// Copyright 2013 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.

//! Sampling from random distributions

// Some implementations use the Ziggurat method
// https://en.wikipedia.org/wiki/Ziggurat_algorithm
//
// The version used here is ZIGNOR [Doornik 2005, "An Improved
// Ziggurat Method to Generate Normal Random Samples"] which is slower
// (about double, it generates an extra random number) than the
// canonical version [Marsaglia & Tsang 2000, "The Ziggurat Method for
// Generating Random Variables"], but more robust. If one wanted, one
// could implement VIZIGNOR the ZIGNOR paper for more speed.

use num;
use rand::{Rng,Rand};

mod ziggurat_tables;

// inlining should mean there is no performance penalty for this
#[inline]
fn ziggurat<R:Rng>(rng: &mut R,
                   center_u: bool,
                   X: ziggurat_tables::ZigTable,
                   F: ziggurat_tables::ZigTable,
                   F_DIFF: ziggurat_tables::ZigTable,
                   pdf: &'static fn(f64) -> f64, // probability density function
                   zero_case: &'static fn(&mut R, f64) -> f64) -> f64 {
    loop {
        let u = if center_u {2.0 * rng.gen() - 1.0} else {rng.gen()};
        let i: uint = rng.gen::<uint>() & 0xff;
        let x = u * X[i];

        let test_x = if center_u {num::abs(x)} else {x};

        // algebraically equivalent to |u| < X[i+1]/X[i] (or u < X[i+1]/X[i])
        if test_x < X[i + 1] {
            return x;
        }
        if i == 0 {
            return zero_case(rng, u);
        }
        // algebraically equivalent to f1 + DRanU()*(f0 - f1) < 1
        if F[i+1] + F_DIFF[i+1] * rng.gen() < pdf(x) {
            return x;
        }
    }
}

/// A wrapper around an `f64` to generate N(0, 1) random numbers (a.k.a.  a
/// standard normal, or Gaussian). Multiplying the generated values by the
/// desired standard deviation `sigma` then adding the desired mean `mu` will
/// give N(mu, sigma^2) distributed random numbers.
///
/// Note that this has to be unwrapped before use as an `f64` (using either
/// `*` or `cast::transmute` is safe).
///
/// # Example
///
/// ```
/// use std::rand::distributions::StandardNormal;
///
/// fn main() {
///     let normal = 2.0 + (*rand::random::<StandardNormal>()) * 3.0;
///     println!("{} is from a N(2, 9) distribution", normal)
/// }
/// ```
pub struct StandardNormal(f64);

impl Rand for StandardNormal {
    fn rand<R:Rng>(rng: &mut R) -> StandardNormal {
        #[inline]
        fn pdf(x: f64) -> f64 {
            ((-x*x/2.0) as f64).exp()
        }
        #[inline]
        fn zero_case<R:Rng>(rng: &mut R, u: f64) -> f64 {
            // compute a random number in the tail by hand

            // strange initial conditions, because the loop is not
            // do-while, so the condition should be true on the first
            // run, they get overwritten anyway (0 < 1, so these are
            // good).
            let mut x = 1.0f64;
            let mut y = 0.0f64;

            // FIXME #7755: infinities?
            while -2.0 * y < x * x {
                x = rng.gen::<f64>().ln() / ziggurat_tables::ZIG_NORM_R;
                y = rng.gen::<f64>().ln();
            }

            if u < 0.0 { x - ziggurat_tables::ZIG_NORM_R } else { ziggurat_tables::ZIG_NORM_R - x }
        }

        StandardNormal(ziggurat(
            rng,
            true, // this is symmetric
            &ziggurat_tables::ZIG_NORM_X,
            &ziggurat_tables::ZIG_NORM_F, &ziggurat_tables::ZIG_NORM_F_DIFF,
            pdf, zero_case))
    }
}

/// A wrapper around an `f64` to generate Exp(1) random numbers. Dividing by
/// the desired rate `lambda` will give Exp(lambda) distributed random
/// numbers.
///
/// Note that this has to be unwrapped before use as an `f64` (using either
/// `*` or `cast::transmute` is safe).
///
/// # Example
///
/// ```
/// use std::rand::distributions::Exp1;
///
/// fn main() {
///     let exp2 = (*rand::random::<Exp1>()) * 0.5;
///     println!("{} is from a Exp(2) distribution", exp2);
/// }
/// ```
pub struct Exp1(f64);

// This could be done via `-rng.gen::<f64>().ln()` but that is slower.
impl Rand for Exp1 {
    #[inline]
    fn rand<R:Rng>(rng: &mut R) -> Exp1 {
        #[inline]
        fn pdf(x: f64) -> f64 {
            (-x).exp()
        }
        #[inline]
        fn zero_case<R:Rng>(rng: &mut R, _u: f64) -> f64 {
            ziggurat_tables::ZIG_EXP_R - rng.gen::<f64>().ln()
        }

        Exp1(ziggurat(rng, false,
                      &ziggurat_tables::ZIG_EXP_X,
                      &ziggurat_tables::ZIG_EXP_F, &ziggurat_tables::ZIG_EXP_F_DIFF,
                      pdf, zero_case))
    }
}
<commit_msg>std::rand: add the Sample and IndependentSample traits.<commit_after>// Copyright 2013 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.

//! Sampling from random distributions

// Some implementations use the Ziggurat method
// https://en.wikipedia.org/wiki/Ziggurat_algorithm
//
// The version used here is ZIGNOR [Doornik 2005, "An Improved
// Ziggurat Method to Generate Normal Random Samples"] which is slower
// (about double, it generates an extra random number) than the
// canonical version [Marsaglia & Tsang 2000, "The Ziggurat Method for
// Generating Random Variables"], but more robust. If one wanted, one
// could implement VIZIGNOR the ZIGNOR paper for more speed.

use num;
use rand::{Rng,Rand};

/// Things that can be used to create a random instance of `Support`.
pub trait Sample<Support> {
    /// Generate a random value of `Support`, using `rng` as the
    /// source of randomness.
    fn sample<R: Rng>(&mut self, rng: &mut R) -> Support;
}

/// `Sample`s that do not require keeping track of state, so each
/// sample is (statistically) independent of all others, assuming the
/// `Rng` used has this property.
// XXX maybe having this separate is overkill (the only reason is to
// take &self rather than &mut self)? or maybe this should be the
// trait called `Sample` and the other should be `DependentSample`.
pub trait IndependentSample<Support>: Sample<Support> {
    /// Generate a random value.
    fn ind_sample<R: Rng>(&self, &mut R) -> Support;
}

mod ziggurat_tables;

// inlining should mean there is no performance penalty for this
#[inline]
fn ziggurat<R:Rng>(rng: &mut R,
                   center_u: bool,
                   X: ziggurat_tables::ZigTable,
                   F: ziggurat_tables::ZigTable,
                   F_DIFF: ziggurat_tables::ZigTable,
                   pdf: &'static fn(f64) -> f64, // probability density function
                   zero_case: &'static fn(&mut R, f64) -> f64) -> f64 {
    loop {
        let u = if center_u {2.0 * rng.gen() - 1.0} else {rng.gen()};
        let i: uint = rng.gen::<uint>() & 0xff;
        let x = u * X[i];

        let test_x = if center_u {num::abs(x)} else {x};

        // algebraically equivalent to |u| < X[i+1]/X[i] (or u < X[i+1]/X[i])
        if test_x < X[i + 1] {
            return x;
        }
        if i == 0 {
            return zero_case(rng, u);
        }
        // algebraically equivalent to f1 + DRanU()*(f0 - f1) < 1
        if F[i+1] + F_DIFF[i+1] * rng.gen() < pdf(x) {
            return x;
        }
    }
}

/// A wrapper around an `f64` to generate N(0, 1) random numbers (a.k.a.  a
/// standard normal, or Gaussian). Multiplying the generated values by the
/// desired standard deviation `sigma` then adding the desired mean `mu` will
/// give N(mu, sigma^2) distributed random numbers.
///
/// Note that this has to be unwrapped before use as an `f64` (using either
/// `*` or `cast::transmute` is safe).
///
/// # Example
///
/// ```
/// use std::rand::distributions::StandardNormal;
///
/// fn main() {
///     let normal = 2.0 + (*rand::random::<StandardNormal>()) * 3.0;
///     println!("{} is from a N(2, 9) distribution", normal)
/// }
/// ```
pub struct StandardNormal(f64);

impl Rand for StandardNormal {
    fn rand<R:Rng>(rng: &mut R) -> StandardNormal {
        #[inline]
        fn pdf(x: f64) -> f64 {
            ((-x*x/2.0) as f64).exp()
        }
        #[inline]
        fn zero_case<R:Rng>(rng: &mut R, u: f64) -> f64 {
            // compute a random number in the tail by hand

            // strange initial conditions, because the loop is not
            // do-while, so the condition should be true on the first
            // run, they get overwritten anyway (0 < 1, so these are
            // good).
            let mut x = 1.0f64;
            let mut y = 0.0f64;

            // FIXME #7755: infinities?
            while -2.0 * y < x * x {
                x = rng.gen::<f64>().ln() / ziggurat_tables::ZIG_NORM_R;
                y = rng.gen::<f64>().ln();
            }

            if u < 0.0 { x - ziggurat_tables::ZIG_NORM_R } else { ziggurat_tables::ZIG_NORM_R - x }
        }

        StandardNormal(ziggurat(
            rng,
            true, // this is symmetric
            &ziggurat_tables::ZIG_NORM_X,
            &ziggurat_tables::ZIG_NORM_F, &ziggurat_tables::ZIG_NORM_F_DIFF,
            pdf, zero_case))
    }
}

/// A wrapper around an `f64` to generate Exp(1) random numbers. Dividing by
/// the desired rate `lambda` will give Exp(lambda) distributed random
/// numbers.
///
/// Note that this has to be unwrapped before use as an `f64` (using either
/// `*` or `cast::transmute` is safe).
///
/// # Example
///
/// ```
/// use std::rand::distributions::Exp1;
///
/// fn main() {
///     let exp2 = (*rand::random::<Exp1>()) * 0.5;
///     println!("{} is from a Exp(2) distribution", exp2);
/// }
/// ```
pub struct Exp1(f64);

// This could be done via `-rng.gen::<f64>().ln()` but that is slower.
impl Rand for Exp1 {
    #[inline]
    fn rand<R:Rng>(rng: &mut R) -> Exp1 {
        #[inline]
        fn pdf(x: f64) -> f64 {
            (-x).exp()
        }
        #[inline]
        fn zero_case<R:Rng>(rng: &mut R, _u: f64) -> f64 {
            ziggurat_tables::ZIG_EXP_R - rng.gen::<f64>().ln()
        }

        Exp1(ziggurat(rng, false,
                      &ziggurat_tables::ZIG_EXP_X,
                      &ziggurat_tables::ZIG_EXP_F, &ziggurat_tables::ZIG_EXP_F_DIFF,
                      pdf, zero_case))
    }
}
<|endoftext|>


<commit_before>// A port of the simplistic benchmark from
//
//    http://github.com/PaulKeeble/ScalaVErlangAgents
//
// I *think* it's the same, more or less.

#![crate_name = "msgsend"]

#![feature(core)]

extern crate time;
extern crate zmq;

use std::os;
use std::thread;
use std::sync::mpsc::{channel, Sender, Receiver};

fn server(mut pull_socket: zmq::Socket, mut push_socket: zmq::Socket, mut workers: u64) {
    let mut count = 0;
    let mut msg = zmq::Message::new().unwrap();

    while workers != 0 {
        match pull_socket.recv(&mut msg, 0) {
            Err(e) => panic!(e),
            Ok(()) => {
                let s = msg.as_str().unwrap();
                if s.is_empty() {
                    workers -= 1;
                } else {
                    count += s.parse().unwrap();
                }
            }
        }
    }

    match push_socket.send_str(count.to_string().as_slice(), 0) {
        Ok(()) => { }
        Err(e) => panic!(e),
    }
}

fn spawn_server(ctx: &mut zmq::Context, workers: u64) -> Sender<()> {
    let mut pull_socket = ctx.socket(zmq::PULL).unwrap();
    let mut push_socket = ctx.socket(zmq::PUSH).unwrap();

    pull_socket.bind("inproc://server-pull").unwrap();
    push_socket.bind("inproc://server-push").unwrap();

    // Spawn the server.
    let (ready_tx, ready_rx) = channel();
    let (start_tx, start_rx) = channel();

    thread::spawn(move|| {
        // Let the main thread know we're ready.
        ready_tx.send(()).unwrap();

        // Wait until we need to start.
        start_rx.recv().unwrap();

        server(pull_socket, push_socket, workers);
    });

    // Wait for the server to start.
    ready_rx.recv().unwrap();

    start_tx
}

fn worker(mut push_socket: zmq::Socket, count: u64) {
    for _ in 0 .. count {
        push_socket.send_str(100.to_string().as_slice(), 0).unwrap();
    }

    // Let the server know we're done.
    push_socket.send_str("", 0).unwrap();
}

fn spawn_worker(ctx: &mut zmq::Context, count: u64) -> Receiver<()> {
    let mut push_socket = ctx.socket(zmq::PUSH).unwrap();

    push_socket.connect("inproc://server-pull").unwrap();
    //push_socket.connect("tcp://127.0.0.1:3456").unwrap();

    // Spawn the worker.
    let (tx, rx) = channel();
    thread::spawn(move|| {
        // Let the main thread we're ready.
        tx.send(()).unwrap();

        worker(push_socket, count);

        tx.send(()).unwrap();
    });

    // Wait for the worker to start.
    rx.recv().unwrap();

    rx
}

fn run(ctx: &mut zmq::Context, size: u64, workers: u64) {
    let start_ch = spawn_server(ctx, workers);

    // Create some command/control sockets.
    let mut push_socket = ctx.socket(zmq::PUSH).unwrap();
    let mut pull_socket = ctx.socket(zmq::PULL).unwrap();

    push_socket.connect("inproc://server-pull").unwrap();
    pull_socket.connect("inproc://server-push").unwrap();
    //push_socket.connect("tcp://127.0.0.1:3456").unwrap();
    //pull_socket.connect("tcp://127.0.0.1:3457").unwrap();

    // Spawn all the workers.
    let mut worker_results = Vec::new();
    for _ in 0 .. workers {
        worker_results.push(spawn_worker(ctx, size / workers));
    }

    let start = time::precise_time_s();

    start_ch.send(()).unwrap();

    // Block until all the workers finish.
    for po in worker_results {
        po.recv().unwrap();
    }

    // Receive the final count.
    let result: i32 = match pull_socket.recv_msg(0) {
        Ok(msg) => {
            let msg_str: &str = msg.as_str().unwrap();
            msg_str.parse().unwrap()
        },
        Err(e) => panic!(e),
    };

    let end = time::precise_time_s();
    let elapsed = end - start;

    println!("Count is {}", result);
    println!("Test took {} seconds", elapsed);
    let thruput = ((size / workers * workers) as f64) / elapsed;
    println!("Throughput={} per sec", thruput);
}

fn main() {
    let args = os::args();

    let args = if os::getenv("RUST_BENCH").is_some() {
        vec!("".to_string(), "1000000".to_string(), "10000".to_string())
    } else if args.len() <= 1 {
        vec!("".to_string(), "10000".to_string(), "4".to_string())
    } else {
        args
    };

    let size = args[1].as_slice().parse().unwrap();
    let workers = args[2].as_slice().parse().unwrap();

    let mut ctx = zmq::Context::new();

    run(&mut ctx, size, workers);
}
<commit_msg>os -> env<commit_after>// A port of the simplistic benchmark from
//
//    http://github.com/PaulKeeble/ScalaVErlangAgents
//
// I *think* it's the same, more or less.

#![crate_name = "msgsend"]

#![feature(core, env)]

extern crate time;
extern crate zmq;

use std::env;
use std::thread;
use std::sync::mpsc::{channel, Sender, Receiver};

fn server(mut pull_socket: zmq::Socket, mut push_socket: zmq::Socket, mut workers: u64) {
    let mut count = 0;
    let mut msg = zmq::Message::new().unwrap();

    while workers != 0 {
        match pull_socket.recv(&mut msg, 0) {
            Err(e) => panic!(e),
            Ok(()) => {
                let s = msg.as_str().unwrap();
                if s.is_empty() {
                    workers -= 1;
                } else {
                    count += s.parse().unwrap();
                }
            }
        }
    }

    match push_socket.send_str(count.to_string().as_slice(), 0) {
        Ok(()) => { }
        Err(e) => panic!(e),
    }
}

fn spawn_server(ctx: &mut zmq::Context, workers: u64) -> Sender<()> {
    let mut pull_socket = ctx.socket(zmq::PULL).unwrap();
    let mut push_socket = ctx.socket(zmq::PUSH).unwrap();

    pull_socket.bind("inproc://server-pull").unwrap();
    push_socket.bind("inproc://server-push").unwrap();

    // Spawn the server.
    let (ready_tx, ready_rx) = channel();
    let (start_tx, start_rx) = channel();

    thread::spawn(move|| {
        // Let the main thread know we're ready.
        ready_tx.send(()).unwrap();

        // Wait until we need to start.
        start_rx.recv().unwrap();

        server(pull_socket, push_socket, workers);
    });

    // Wait for the server to start.
    ready_rx.recv().unwrap();

    start_tx
}

fn worker(mut push_socket: zmq::Socket, count: u64) {
    for _ in 0 .. count {
        push_socket.send_str(100.to_string().as_slice(), 0).unwrap();
    }

    // Let the server know we're done.
    push_socket.send_str("", 0).unwrap();
}

fn spawn_worker(ctx: &mut zmq::Context, count: u64) -> Receiver<()> {
    let mut push_socket = ctx.socket(zmq::PUSH).unwrap();

    push_socket.connect("inproc://server-pull").unwrap();
    //push_socket.connect("tcp://127.0.0.1:3456").unwrap();

    // Spawn the worker.
    let (tx, rx) = channel();
    thread::spawn(move|| {
        // Let the main thread we're ready.
        tx.send(()).unwrap();

        worker(push_socket, count);

        tx.send(()).unwrap();
    });

    // Wait for the worker to start.
    rx.recv().unwrap();

    rx
}

fn run(ctx: &mut zmq::Context, size: u64, workers: u64) {
    let start_ch = spawn_server(ctx, workers);

    // Create some command/control sockets.
    let mut push_socket = ctx.socket(zmq::PUSH).unwrap();
    let mut pull_socket = ctx.socket(zmq::PULL).unwrap();

    push_socket.connect("inproc://server-pull").unwrap();
    pull_socket.connect("inproc://server-push").unwrap();
    //push_socket.connect("tcp://127.0.0.1:3456").unwrap();
    //pull_socket.connect("tcp://127.0.0.1:3457").unwrap();

    // Spawn all the workers.
    let mut worker_results = Vec::new();
    for _ in 0 .. workers {
        worker_results.push(spawn_worker(ctx, size / workers));
    }

    let start = time::precise_time_s();

    start_ch.send(()).unwrap();

    // Block until all the workers finish.
    for po in worker_results {
        po.recv().unwrap();
    }

    // Receive the final count.
    let result: i32 = match pull_socket.recv_msg(0) {
        Ok(msg) => {
            let msg_str: &str = msg.as_str().unwrap();
            msg_str.parse().unwrap()
        },
        Err(e) => panic!(e),
    };

    let end = time::precise_time_s();
    let elapsed = end - start;

    println!("Count is {}", result);
    println!("Test took {} seconds", elapsed);
    let thruput = ((size / workers * workers) as f64) / elapsed;
    println!("Throughput={} per sec", thruput);
}

fn main() {
    let args: Vec<String> = env::args().collect();

    let args = if env::var("RUST_BENCH").is_ok() {
        vec!("".to_string(), "1000000".to_string(), "10000".to_string())
    } else if args.len() <= 1 {
        vec!("".to_string(), "10000".to_string(), "4".to_string())
    } else {
        args
    };

    let size = args[1].as_slice().parse().unwrap();
    let workers = args[2].as_slice().parse().unwrap();

    let mut ctx = zmq::Context::new();

    run(&mut ctx, size, workers);
}
<|endoftext|>