<commit_before>use std::io::{self, Read, Error, ErrorKind};
use std::borrow::Cow;

use hyper;
use hyper::{client, Client, Url };
use hyper::net::HttpsConnector;
use hyper_native_tls::NativeTlsClient;

use std::time::Duration;
use rustc_serialize::json::Json;
use product;
use configs::{Configs, ApiConfigs, ProxyConfigs};

const HOST_URL: &'static str = "https://www.versioneye.com";

fn to_product_url(lang: &str, prod_key: &str, version: &str) -> String {
    format!("{}/{}/{}/{}", HOST_URL, lang, prod_key, version)
}

fn configs_to_url(api_confs: &ApiConfigs, resource_path: &str)
    -> Result<hyper::Url, hyper::error::ParseError> {
    let url_str = match api_confs.port {
        None => {
            format!(
                "{}://{}/{}/{}",
                api_confs.scheme.clone().unwrap(), api_confs.host.clone().unwrap(),
                api_confs.path.clone().unwrap(), resource_path,
            )

        },
        Some(port) => format!(
            "{}://{}:{}/{}/{}",
            api_confs.scheme.clone().unwrap(), api_confs.host.clone().unwrap(),
            port, api_confs.path.clone().unwrap(), resource_path
        )
    };

    Url::parse(url_str.as_str())
}

fn request_json<'a>(uri: &Url, proxy_confs: &'a ProxyConfigs) -> Option<String> {
    let ssl = NativeTlsClient::new().unwrap();
    let connector = HttpsConnector::new(ssl);

    //use proxy only iff user has defined proxy host and port
    let mut client = if proxy_confs.is_complete() {
        let host = Cow::from(proxy_confs.host.clone().unwrap());
        let port = proxy_confs.port.clone().unwrap();
        let scheme = proxy_confs.scheme.clone().unwrap_or("http".to_string());

        let ssl_proxy = NativeTlsClient::new().unwrap();
        let proxy = client::ProxyConfig::new (
            scheme.as_str(), host, port, connector, ssl_proxy
        );

        Client::with_proxy_config(proxy)
    } else {
        Client::with_connector(connector)
    };

    client.set_read_timeout(Some(Duration::new(5,0)));

    let mut res = client.get(uri.as_str()).send().expect("Failed to fetch results from the url");
    let mut body = String::new();
    res.read_to_string(&mut body).expect("Failed to read response body");

    Some(body)
}

pub fn fetch_product_details_by_sha(confs: &Configs, file_sha: &str)
    -> Result<product::ProductMatch, Error> {

    let sha_res = fetch_product_by_sha(&confs, &file_sha);
    match sha_res {
        Ok(m) => {
            let sha = m.sha.expect("No product sha from SHA result");
            let product = m.product.expect("No product info from SHA result");
            match fetch_product( &confs, &product.language, &product.prod_key, &product.version ) {
                Ok(mut m) => {
                    m.sha = Some(sha);
                    Ok(m)
                },
                Err(e) => {
                    println!("Failed to fetch product details for sha: {}", file_sha);
                    Err(e)
                }
            }

        },
        Err(e) => Err(e)
    }
}

pub fn fetch_product_by_sha(confs: &Configs, sha: &str)
    -> Result<product::ProductMatch, io::Error> {
    let api_confs = confs.api.clone();
    let resource_path = format!("products/sha/{}", sha.clone() );
    let mut resource_url = match configs_to_url(&api_confs, resource_path.as_str()) {
        Ok(the_url) => the_url,
        Err(_)      => {
            return Err(
                Error::new(
                    ErrorKind::InvalidData,
                    "The values of API configs make up non-valid URL"
                )
            )
        }
    };

    //attach query params
    resource_url
        .query_pairs_mut()
        .clear()
        .append_pair("api_key", api_confs.key.clone().unwrap().as_str());

    let json_txt = request_json( &resource_url, &confs.proxy );
    process_sha_response(json_txt)
}

pub fn encode_prod_key<'b>(prod_key: &'b str) -> String {
    let encoded_prod_key = prod_key.to_string();
    encoded_prod_key
        .replace(".", "~")
        .replace("/", ":")
        .trim().to_string()

}

pub fn encode_language<'b>(lang: &'b str) -> String {
    let encoded_lang = lang.to_string();
    encoded_lang.replace(".", "").trim().to_lowercase().to_string()
}

pub fn fetch_product<'a>(
    confs: &Configs, lang: &str, prod_key: &str, version: &str
) -> Result<product::ProductMatch, io::Error> {

    let api_confs = confs.api.clone();
    let encoded_prod_key = encode_prod_key(&prod_key);
    let encoded_lang = encode_language(lang);
    let resource_path = format!("products/{}/{}", encoded_lang.clone(), encoded_prod_key.clone());
    let prod_url = to_product_url(encoded_lang.clone().as_str(), encoded_prod_key.clone().as_str(), version);

    let mut resource_url = match configs_to_url(&api_confs, resource_path.as_str()) {
        Ok(the_url) => the_url,
        Err(_)      => {
            return Err(
                Error::new(
                    ErrorKind::InvalidData,
                    "The values of API configs make up non-valid URL"
                )
            )
        }
    };

    //attach query params
    resource_url
        .query_pairs_mut()
        .clear()
        .append_pair("prod_version", version)
        .append_pair("api_key", api_confs.key.clone().unwrap().as_str());

    let json_txt = request_json( &resource_url, &confs.proxy );
    process_product_response(json_txt, Some(prod_url))
}

//TODO: log all error details
//-- helper functions
pub fn process_sha_response(json_text: Option<String> ) -> Result<product::ProductMatch, io::Error> {
    if json_text.is_none() {
        return Err(
            Error::new(ErrorKind::Other, "No response from API")
        )
    }

    let json_res = Json::from_str( &json_text.clone().unwrap());
    if json_res.is_err() {
        return Err(
            Error::new(
                ErrorKind::Other,
                "Failed to parse response from SHA endpoint - server failed to process request"
            )
        )
    }

    let json_obj = json_res.unwrap();
    //if response includes error field in HTTP200 response
    if let Some(_) = json_obj.find("error") {
        return Err(
            Error::new(
                ErrorKind::Other,
                "Hit API request limit - upgrade your subscription"
            )
        )
    }

    if !json_obj.is_array() {
        return Err(Error::new( ErrorKind::Other, "The structure of SHA response has been changed"));
    }

    let product_doc = match json_obj.as_array() {
        Some(products) if products.len() > 0 => products[0].clone(),
        _ => return Err(Error::new(ErrorKind::Other, "Empty response - API may be not responding"))
    };

    let the_prod = product::Product {
        name: "".to_string(),
        language: product_doc["language"].as_string().expect("No field `language`").to_string(),
        prod_key: product_doc["prod_key"].as_string().expect("No field `prod_key`").to_string(),
        version: product_doc["version"].as_string().expect("No field `version`").to_string(),
        prod_type: Some( product_doc["prod_type"].as_string().expect("No field `prod_types`").to_string() )
    };

    let the_sha = product::ProductSHA {
        packaging: product_doc["packaging"].as_string().unwrap_or("unknown").to_string(),
        method: product_doc["sha_method"].as_string().expect("No field `sha_method`").to_string(),
        value: product_doc["sha_value"].as_string().expect("No field `sha_value`").to_string(),
        filepath: None
    };

    Ok(product::ProductMatch::new(the_prod, the_sha))
}

// converts the response of product endpoint into ProductMatch struct
pub fn process_product_response(
    json_text: Option<String>, prod_url: Option<String>
) -> Result<product::ProductMatch, io::Error> {

    if json_text.is_none() {
        return Err(
            Error::new(
                ErrorKind::Other,
                "API returned empty response string - server may not responding"
            )
        )
    }

    let json_res = Json::from_str( &json_text.clone().unwrap());
    if json_res.is_err() {
        return Err(
            Error::new(
                ErrorKind::Other,
                "Failed to parse response from the Product endpoint - server failed to process request"
            )
        )
    }

    let json_obj = json_res.unwrap();
    if !json_obj.is_object() {
        return Err(Error::new(ErrorKind::Other, "Response had no product details"));
    }

    //if response includes error field in HTTP200 response
    // NB! it may include other errors than limit, but @Rob asked to see custom Limit error message
    if let Some(_) = json_obj.find("error") {
        return Err(
            Error::new(
                ErrorKind::Other,
                "Hit API request limit - upgrade your subscription"
            )
        )
    }

    let product_doc = json_obj.as_object().expect("Failed to fetch product document");
    let the_prod = product::Product {
        name: product_doc["name"].as_string().unwrap().to_string(),
        language: product_doc["language"].as_string().unwrap().to_string(),
        prod_key: product_doc["prod_key"].as_string().unwrap().to_string(),
        version: product_doc["version"].as_string().unwrap().to_string(),
        prod_type: Some( product_doc["prod_type"].as_string().unwrap_or("").to_string() )
    };

    let licenses = match product_doc["licenses"].as_array() {
        Some(arr) => arr.iter().fold(vec![], |mut acc, ref x| {
            acc.push(product::ProductLicense {
                name: x["name"].as_string().unwrap().to_string(),
                url: x["url"].as_string().unwrap_or("").to_string()
            });

            acc
        }),
        None      => vec![]
    };

    let n_vulns = match product_doc["security_vulnerabilities"].as_array() {
        Some(arr) => arr.len() as u32,
        None      => 0 as u32
    };

    let the_match = product::ProductMatch {
        sha: None,
        product: Some(the_prod),
        url: prod_url,
        licenses : licenses,
        n_vulns : n_vulns,
        error: None
    };

    Ok(the_match)
}
<commit_msg>Update error message.<commit_after>use std::io::{self, Read, Error, ErrorKind};
use std::borrow::Cow;

use hyper;
use hyper::{client, Client, Url };
use hyper::net::HttpsConnector;
use hyper_native_tls::NativeTlsClient;

use std::time::Duration;
use rustc_serialize::json::Json;
use product;
use configs::{Configs, ApiConfigs, ProxyConfigs};

const HOST_URL: &'static str = "https://www.versioneye.com";

fn to_product_url(lang: &str, prod_key: &str, version: &str) -> String {
    format!("{}/{}/{}/{}", HOST_URL, lang, prod_key, version)
}

fn configs_to_url(api_confs: &ApiConfigs, resource_path: &str)
    -> Result<hyper::Url, hyper::error::ParseError> {
    let url_str = match api_confs.port {
        None => {
            format!(
                "{}://{}/{}/{}",
                api_confs.scheme.clone().unwrap(), api_confs.host.clone().unwrap(),
                api_confs.path.clone().unwrap(), resource_path,
            )

        },
        Some(port) => format!(
            "{}://{}:{}/{}/{}",
            api_confs.scheme.clone().unwrap(), api_confs.host.clone().unwrap(),
            port, api_confs.path.clone().unwrap(), resource_path
        )
    };

    Url::parse(url_str.as_str())
}

fn request_json<'a>(uri: &Url, proxy_confs: &'a ProxyConfigs) -> Option<String> {
    let ssl = NativeTlsClient::new().unwrap();
    let connector = HttpsConnector::new(ssl);

    //use proxy only iff user has defined proxy host and port
    let mut client = if proxy_confs.is_complete() {
        let host = Cow::from(proxy_confs.host.clone().unwrap());
        let port = proxy_confs.port.clone().unwrap();
        let scheme = proxy_confs.scheme.clone().unwrap_or("http".to_string());

        let ssl_proxy = NativeTlsClient::new().unwrap();
        let proxy = client::ProxyConfig::new (
            scheme.as_str(), host, port, connector, ssl_proxy
        );

        Client::with_proxy_config(proxy)
    } else {
        Client::with_connector(connector)
    };

    client.set_read_timeout(Some(Duration::new(5,0)));

    let mut res = client.get(uri.as_str()).send().expect("Failed to fetch results from the url");
    let mut body = String::new();
    res.read_to_string(&mut body).expect("Failed to read response body");

    Some(body)
}

pub fn fetch_product_details_by_sha(confs: &Configs, file_sha: &str)
    -> Result<product::ProductMatch, Error> {

    let sha_res = fetch_product_by_sha(&confs, &file_sha);
    match sha_res {
        Ok(m) => {
            let sha = m.sha.expect("No product sha from SHA result");
            let product = m.product.expect("No product info from SHA result");
            match fetch_product( &confs, &product.language, &product.prod_key, &product.version ) {
                Ok(mut m) => {
                    m.sha = Some(sha);
                    Ok(m)
                },
                Err(e) => {
                    println!("Failed to fetch product details for sha: {}", file_sha);
                    Err(e)
                }
            }

        },
        Err(e) => Err(e)
    }
}

pub fn fetch_product_by_sha(confs: &Configs, sha: &str)
    -> Result<product::ProductMatch, io::Error> {
    let api_confs = confs.api.clone();
    let resource_path = format!("products/sha/{}", sha.clone() );
    let mut resource_url = match configs_to_url(&api_confs, resource_path.as_str()) {
        Ok(the_url) => the_url,
        Err(_)      => {
            return Err(
                Error::new(
                    ErrorKind::InvalidData,
                    "The values of API configs make up non-valid URL"
                )
            )
        }
    };

    //attach query params
    resource_url
        .query_pairs_mut()
        .clear()
        .append_pair("api_key", api_confs.key.clone().unwrap().as_str());

    let json_txt = request_json( &resource_url, &confs.proxy );
    process_sha_response(json_txt)
}

pub fn encode_prod_key<'b>(prod_key: &'b str) -> String {
    let encoded_prod_key = prod_key.to_string();
    encoded_prod_key
        .replace(".", "~")
        .replace("/", ":")
        .trim().to_string()

}

pub fn encode_language<'b>(lang: &'b str) -> String {
    let encoded_lang = lang.to_string();
    encoded_lang.replace(".", "").trim().to_lowercase().to_string()
}

pub fn fetch_product<'a>(
    confs: &Configs, lang: &str, prod_key: &str, version: &str
) -> Result<product::ProductMatch, io::Error> {

    let api_confs = confs.api.clone();
    let encoded_prod_key = encode_prod_key(&prod_key);
    let encoded_lang = encode_language(lang);
    let resource_path = format!("products/{}/{}", encoded_lang.clone(), encoded_prod_key.clone());
    let prod_url = to_product_url(encoded_lang.clone().as_str(), encoded_prod_key.clone().as_str(), version);

    let mut resource_url = match configs_to_url(&api_confs, resource_path.as_str()) {
        Ok(the_url) => the_url,
        Err(_)      => {
            return Err(
                Error::new(
                    ErrorKind::InvalidData,
                    "The values of API configs make up non-valid URL"
                )
            )
        }
    };

    //attach query params
    resource_url
        .query_pairs_mut()
        .clear()
        .append_pair("prod_version", version)
        .append_pair("api_key", api_confs.key.clone().unwrap().as_str());

    let json_txt = request_json( &resource_url, &confs.proxy );
    process_product_response(json_txt, Some(prod_url))
}

//TODO: log all error details
//-- helper functions
pub fn process_sha_response(json_text: Option<String> ) -> Result<product::ProductMatch, io::Error> {
    if json_text.is_none() {
        return Err(
            Error::new(ErrorKind::Other, "No response from API")
        )
    }

    let json_res = Json::from_str( &json_text.clone().unwrap());
    if json_res.is_err() {
        return Err(
            Error::new(
                ErrorKind::Other,
                "Failed to parse response from SHA endpoint - server failed to process request"
            )
        )
    }

    let json_obj = json_res.unwrap();
    //if response includes error field in HTTP200 response
    if let Some(_) = json_obj.find("error") {
        return Err(
            Error::new(
                ErrorKind::Other,
                "API rate limit reached. Go to https://www.versioneye.com and upgrade your subscription to a higher plan."
            )
        )
    }

    if !json_obj.is_array() {
        return Err(Error::new( ErrorKind::Other, "The structure of SHA response has been changed"));
    }

    let product_doc = match json_obj.as_array() {
        Some(products) if products.len() > 0 => products[0].clone(),
        _ => return Err(Error::new(ErrorKind::Other, "Empty response - API may be not responding"))
    };

    let the_prod = product::Product {
        name: "".to_string(),
        language: product_doc["language"].as_string().expect("No field `language`").to_string(),
        prod_key: product_doc["prod_key"].as_string().expect("No field `prod_key`").to_string(),
        version: product_doc["version"].as_string().expect("No field `version`").to_string(),
        prod_type: Some( product_doc["prod_type"].as_string().expect("No field `prod_types`").to_string() )
    };

    let the_sha = product::ProductSHA {
        packaging: product_doc["packaging"].as_string().unwrap_or("unknown").to_string(),
        method: product_doc["sha_method"].as_string().expect("No field `sha_method`").to_string(),
        value: product_doc["sha_value"].as_string().expect("No field `sha_value`").to_string(),
        filepath: None
    };

    Ok(product::ProductMatch::new(the_prod, the_sha))
}

// converts the response of product endpoint into ProductMatch struct
pub fn process_product_response(
    json_text: Option<String>, prod_url: Option<String>
) -> Result<product::ProductMatch, io::Error> {

    if json_text.is_none() {
        return Err(
            Error::new(
                ErrorKind::Other,
                "API returned empty response string - server may not responding"
            )
        )
    }

    let json_res = Json::from_str( &json_text.clone().unwrap());
    if json_res.is_err() {
        return Err(
            Error::new(
                ErrorKind::Other,
                "Failed to parse response from the Product endpoint - server failed to process request"
            )
        )
    }

    let json_obj = json_res.unwrap();
    if !json_obj.is_object() {
        return Err(Error::new(ErrorKind::Other, "Response had no product details"));
    }

    //if response includes error field in HTTP200 response
    // NB! it may include other errors than limit, but @Rob asked to see custom Limit error message
    if let Some(_) = json_obj.find("error") {
        return Err(
            Error::new(
                ErrorKind::Other,
                "Hit API request limit - upgrade your subscription"
            )
        )
    }

    let product_doc = json_obj.as_object().expect("Failed to fetch product document");
    let the_prod = product::Product {
        name: product_doc["name"].as_string().unwrap().to_string(),
        language: product_doc["language"].as_string().unwrap().to_string(),
        prod_key: product_doc["prod_key"].as_string().unwrap().to_string(),
        version: product_doc["version"].as_string().unwrap().to_string(),
        prod_type: Some( product_doc["prod_type"].as_string().unwrap_or("").to_string() )
    };

    let licenses = match product_doc["licenses"].as_array() {
        Some(arr) => arr.iter().fold(vec![], |mut acc, ref x| {
            acc.push(product::ProductLicense {
                name: x["name"].as_string().unwrap().to_string(),
                url: x["url"].as_string().unwrap_or("").to_string()
            });

            acc
        }),
        None      => vec![]
    };

    let n_vulns = match product_doc["security_vulnerabilities"].as_array() {
        Some(arr) => arr.len() as u32,
        None      => 0 as u32
    };

    let the_match = product::ProductMatch {
        sha: None,
        product: Some(the_prod),
        url: prod_url,
        licenses : licenses,
        n_vulns : n_vulns,
        error: None
    };

    Ok(the_match)
}
<|endoftext|>


<commit_before>//! Application utilities

use std;
use clap;
use log;
use simplelog;

/// Adds a `clap` argument named `"log-level"` with short name `"l"`,
/// and value name `"LOG_LEVEL"`, suitable for passing a log level filter:
///
/// ```ignore
/// ./myapp -l Debug
/// ```
pub fn clap_arg_log_level <'a, 'b> (app : clap::App <'a, 'b>)
  -> clap::App <'a, 'b>
{
  app.arg (clap::Arg::with_name ("log-level")
    .short ("l")
    .value_name ("LOG_LEVEL")
    .help ("Log level filter"))
}

/// Get the value of the `LOG_LEVEL` option and attempt to parse it as
/// a `simplelog::LevelFilter`
pub fn clap_opt_log_level <'a> (opts : &clap::ArgMatches <'a>)
  -> Result <Option <simplelog::LevelFilter>, log::ParseLevelError>
{
  use std::str::FromStr;
  use simplelog::LevelFilter;
  match opts.value_of ("log-level") {
    None => Ok (None),
    Some (log_level) => {
      let log_level = LevelFilter::from_str (log_level)?;
      Ok (Some (log_level))
    }
  }
}

/// Initialize `simplelog` terminal logger to stdout with the given log level.
///
/// This sets the "target level" to `Error` (show module paths for all levels)
/// and sets the "thread level" to `Off` (don't show thread numbers for any
/// levels).
pub fn init_simple_termlogger (log_level : simplelog::LevelFilter) {
  simplelog::TermLogger::init (
    log_level,
    simplelog::ConfigBuilder::new()
      .set_target_level (simplelog::LevelFilter::Error) // module path
      .set_thread_level (simplelog::LevelFilter::Off)   // no thread numbers
      .build(),
    simplelog::TerminalMode::Stdout
  ).unwrap();
}

/// Gets the `file_name()` of the `std::env::current_exe()`
#[inline]
pub fn exe_name() -> std::ffi::OsString {
  std::env::current_exe().unwrap().file_name().unwrap().to_owned()
}
<commit_msg>doc comment<commit_after>//! Application utilities

use std;
use clap;
use log;
use simplelog;

/// Adds a `clap` argument named `"log-level"` with short name `"l"`,
/// and value name `"LOG_LEVEL"`, suitable for passing a log level filter:
///
/// ```text
/// $ ./myapp -l Debug
/// ```
pub fn clap_arg_log_level <'a, 'b> (app : clap::App <'a, 'b>)
  -> clap::App <'a, 'b>
{
  app.arg (clap::Arg::with_name ("log-level")
    .short ("l")
    .value_name ("LOG_LEVEL")
    .help ("Log level filter"))
}

/// Get the value of the `LOG_LEVEL` option and attempt to parse it as
/// a `simplelog::LevelFilter`
pub fn clap_opt_log_level <'a> (opts : &clap::ArgMatches <'a>)
  -> Result <Option <simplelog::LevelFilter>, log::ParseLevelError>
{
  use std::str::FromStr;
  use simplelog::LevelFilter;
  match opts.value_of ("log-level") {
    None => Ok (None),
    Some (log_level) => {
      let log_level = LevelFilter::from_str (log_level)?;
      Ok (Some (log_level))
    }
  }
}

/// Initialize `simplelog` terminal logger to stdout with the given log level.
///
/// This sets the "target level" to `Error` (show module paths for all levels)
/// and sets the "thread level" to `Off` (don't show thread numbers for any
/// levels).
pub fn init_simple_termlogger (log_level : simplelog::LevelFilter) {
  simplelog::TermLogger::init (
    log_level,
    simplelog::ConfigBuilder::new()
      .set_target_level (simplelog::LevelFilter::Error) // module path
      .set_thread_level (simplelog::LevelFilter::Off)   // no thread numbers
      .build(),
    simplelog::TerminalMode::Stdout
  ).unwrap();
}

/// Gets the `file_name()` of the `std::env::current_exe()`
#[inline]
pub fn exe_name() -> std::ffi::OsString {
  std::env::current_exe().unwrap().file_name().unwrap().to_owned()
}
<|endoftext|>


<commit_before>use clap::{
    arg_enum, crate_version, value_t, value_t_or_exit, App, AppSettings, Arg, ArgMatches,
    SubCommand,
};

arg_enum! {
    #[derive(Copy, Clone, Debug, PartialEq)]
    pub enum Format {
        List,
        Json,
    }
}

impl Default for Format {
    fn default() -> Self { Format::List }
}

arg_enum! {
    #[derive(Copy, Clone, Debug, PartialEq)]
    pub enum Color {
        Auto,
        Never,
        Always
    }
}

impl Default for Color {
    fn default() -> Self { Color::Auto }
}

/// Options from CLI arguments
#[derive(Debug, PartialEq, Default)]
pub struct Options {
    pub format: Format,
    pub color: Color,
    pub features: Vec<String>,
    pub ignore: Vec<String>,
    pub exclude: Vec<String>,
    pub manifest_path: Option<String>,
    pub quiet: bool,
    pub verbose: u64,
    pub exit_code: i32,
    pub packages: Vec<String>,
    pub root: Option<String>,
    pub depth: Option<i32>,
    pub root_deps_only: bool,
    pub workspace: bool,
    pub aggressive: bool,
    pub workspace_only: bool,
    pub offline: bool,
}

impl Options {
    pub fn all_features(&self) -> bool { self.features.is_empty() }

    pub fn no_default_features(&self) -> bool {
        !(self.features.is_empty() || self.features.contains(&"default".to_owned()))
    }

    pub fn locked(&self) -> bool { false }

    pub fn frozen(&self) -> bool { false }
}

impl<'a> From<&ArgMatches<'a>> for Options {
    fn from(m: &ArgMatches<'a>) -> Self {
        let mut opts = Options {
            format: value_t_or_exit!(m.value_of("format"), Format),
            color: value_t_or_exit!(m.value_of("color"), Color),
            features: m
                .values_of("features")
                .map(|vals| {
                    vals.flat_map(|x| x.split_ascii_whitespace().collect::<Vec<_>>())
                        .map(ToOwned::to_owned)
                        .collect()
                })
                .unwrap_or_else(Vec::new),
            ignore: m
                .values_of("ignore")
                .map(|vals| {
                    vals.flat_map(|x| x.split_ascii_whitespace().collect::<Vec<_>>())
                        .map(ToOwned::to_owned)
                        .collect()
                })
                .unwrap_or_else(Vec::new),
            exclude: m
                .values_of("exclude")
                .map(|vals| {
                    vals.flat_map(|x| x.split_ascii_whitespace().collect::<Vec<_>>())
                        .map(ToOwned::to_owned)
                        .collect()
                })
                .unwrap_or_else(Vec::new),
            manifest_path: m.value_of("manifest-path").map(ToOwned::to_owned),
            quiet: m.is_present("quiet"),
            verbose: m.occurrences_of("verbose"),
            exit_code: value_t!(m, "exit-code", i32).ok().unwrap_or(0),
            packages: m
                .values_of("packages")
                .map(|vals| {
                    vals.flat_map(|x| x.split_ascii_whitespace().collect::<Vec<_>>())
                        .map(ToOwned::to_owned)
                        .collect::<Vec<_>>()
                })
                .unwrap_or_else(Vec::new),
            root: m.value_of("root").map(ToOwned::to_owned),
            depth: value_t!(m, "depth", i32).ok(),
            root_deps_only: m.is_present("root-deps-only"),
            workspace_only: m.is_present("ignore-external-rel"),
            workspace: m.is_present("workspace"),
            aggressive: m.is_present("aggressive"),
            offline: m.is_present("offline"),
        };

        if m.is_present("root-deps-only") {
            opts.depth = Some(1);
        }

        if m.is_present("ignore-external-rel") {
            opts.depth = Some(1);
            opts.root_deps_only = true;
        }

        opts
    }
}

fn build() -> App<'static, 'static> {
    App::new("cargo-outdated")
        .bin_name("cargo")
        .setting(AppSettings::SubcommandRequired)
        .subcommand(
            SubCommand::with_name("outdated")
                .setting(AppSettings::UnifiedHelpMessage)
                .about("Displays information about project dependency versions")
                .version(crate_version!())
                .arg(
                    Arg::with_name("aggressive")
                        .short("a")
                        .long("aggresssive")
                        .help("Ignores channels for latest updates"),
                )
                .arg(
                    Arg::with_name("quiet")
                        .short("q")
                        .long("quiet")
                        .help("Suppresses warnings"),
                )
                .arg(
                    Arg::with_name("root-deps-only")
                        .short("R")
                        .long("root-deps-only")
                        .help("Only check root dependencies (Equivalent to --depth=1)"),
                )
                .arg(
                    Arg::with_name("ignore-external-rel")
                        .short("e")
                        .long("ignore-external-rel")
                        .help("Ignore relative dependencies external to workspace and check root dependencies only."),
                )
                .arg(
                    Arg::with_name("workspace")
                        .short("w")
                        .long("workspace")
                        .help("Checks updates for all workspace members rather than only the root package"),
                )
                .arg(
                    Arg::with_name("offline")
                        .short("o")
                        .long("offline")
                        .help("Run without accessing the network (useful for testing w/ local registries)"),
                )
                .arg(
                    Arg::with_name("format")
                        .long("format")
                        .default_value("list")
                        .case_insensitive(true)
                        .possible_values(&Format::variants())
                        .value_name("FORMAT")
                        .help("Output formatting"),
                )
                .arg(
                    Arg::with_name("ignore")
                        .short("i")
                        .long("ignore")
                        .help("Dependencies to not print in the output (comma separated or one per '--ignore' argument)")
                        .value_delimiter(",")
                        .number_of_values(1)
                        .multiple(true)
                        .value_name("DEPENDENCIES"),
                )
                .arg(
                    Arg::with_name("exclude")
                        .short("x")
                        .long("exclude")
                        .help("Dependencies to exclude from building (comma separated or one per '--exclude' argument)")
                        .value_delimiter(",")
                        .multiple(true)
                        .number_of_values(1)
                        .value_name("DEPENDENCIES"),
                )
                .arg(
                    Arg::with_name("verbose")
                        .short("v")
                        .long("verbose")
                        .multiple(true)
                        .help("Use verbose output")
                )
                .arg(
                    Arg::with_name("color")
                        .long("color")
                        .possible_values(&Color::variants())
                        .default_value("auto")
                        .value_name("COLOR")
                        .case_insensitive(true)
                        .help("Output coloring")
                )
                .arg(
                    Arg::with_name("depth")
                        .short("d")
                        .long("depth")
                        .value_name("NUM")
                        .help("How deep in the dependency chain to search (Defaults to all dependencies when omitted)")
                )
                .arg(
                    Arg::with_name("exit-code")
                        .long("exit-code")
                        .help("The exit code to return on new versions found")
                        .default_value("0")
                        .value_name("NUM"))
                .arg(
                    Arg::with_name("manifest-path")
                        .short("m")
                        .long("manifest-path")
                        .help("Path to the Cargo.toml file to use (Defaults to Cargo.toml in project root)")
                        .value_name("PATH"))
                .arg(
                    Arg::with_name("root")
                        .short("r")
                        .long("root")
                        .help("Package to treat as the root package")
                        .value_name("ROOT"))
                .arg(
                    Arg::with_name("packages")
                        .short("p")
                        .long("packages")
                        .help("Packages to inspect for updates (comma separated or one per '--packages' argument)")
                        .value_delimiter(",")
                        .number_of_values(1)
                        .multiple(true)
                        .value_name("PKGS"))
                .arg(
                    Arg::with_name("features")
                        .long("features")
                        .value_delimiter(",")
                        .help("Space-separated list of features")
                        .multiple(true)
                        .number_of_values(1)
                        .value_name("FEATURES"))
            )
}

pub fn parse() -> Options {
    let matches = build().get_matches();

    Options::from(matches.subcommand_matches("outdated").unwrap())
}

#[cfg(test)]
mod test {
    use super::*;

    fn options(args: &[&str]) -> Options {
        let mut argv = vec!["cargo", "outdated"];
        argv.extend(args);
        let m = build().get_matches_from(argv);
        Options::from(m.subcommand_matches("outdated").unwrap())
    }

    fn options_fail(args: &[&str]) -> clap::Result<ArgMatches<'static>> {
        let mut argv = vec!["cargo", "outdated"];
        argv.extend(args);
        build().get_matches_from_safe(argv)
    }

    #[test]
    fn default() {
        let opts = options(&[]);
        assert_eq!(Options::default(), opts)
    }

    #[test]
    fn root_only() {
        let opts = options(&["--root-deps-only"]);
        assert_eq!(
            Options {
                depth: Some(1),
                root_deps_only: true,
                ..Options::default()
            },
            opts
        )
    }

    #[test]
    fn workspace_only() {
        let opts = options(&["--ignore-external-rel"]);
        assert_eq!(
            Options {
                workspace_only: true,
                depth: Some(1),
                root_deps_only: true,
                ..Options::default()
            },
            opts
        )
    }

    #[test]
    fn features() {
        let opts1 = options(&["--features=one,two,three"]);
        let opts2 = options(&["--features", "one,two,three"]);
        let opts3 = options(&["--features", "one two three"]);
        let opts4 = options(&[
            "--features",
            "one",
            "--features",
            "two",
            "--features",
            "three",
        ]);
        let opts5 = options(&["--features", "one", "--features", "two,three"]);

        let correct = Options {
            features: vec!["one".into(), "two".into(), "three".into()],
            ..Options::default()
        };

        assert_eq!(correct, opts1);
        assert_eq!(correct, opts2);
        assert_eq!(correct, opts3);
        assert_eq!(correct, opts4);
        assert_eq!(correct, opts5);
    }

    #[test]
    fn features_fail() {
        let res = options_fail(&["--features", "one", "two"]);
        assert!(res.is_err());
        assert_eq!(
            res.as_ref().unwrap_err().kind,
            clap::ErrorKind::UnknownArgument,
            "{:?}",
            res.as_ref().unwrap_err().kind
        );
    }

    #[test]
    fn exclude() {
        let opts1 = options(&["--exclude=one,two,three"]);
        let opts2 = options(&["--exclude", "one,two,three"]);
        let opts3 = options(&["--exclude", "one two three"]);
        let opts4 = options(&["--exclude", "one", "--exclude", "two", "--exclude", "three"]);
        let opts5 = options(&["--exclude", "one", "--exclude", "two,three"]);
        let correct = Options {
            exclude: vec!["one".into(), "two".into(), "three".into()],
            ..Options::default()
        };

        assert_eq!(correct, opts1);
        assert_eq!(correct, opts2);
        assert_eq!(correct, opts3);
        assert_eq!(correct, opts4);
        assert_eq!(correct, opts5);
    }

    #[test]
    fn exclude_fail() {
        let res = options_fail(&["--exclude", "one", "two"]);
        assert!(res.is_err());
        assert_eq!(
            res.as_ref().unwrap_err().kind,
            clap::ErrorKind::UnknownArgument,
            "{:?}",
            res.as_ref().unwrap_err().kind
        );
    }

    #[test]
    fn ignore() {
        let opts1 = options(&["--ignore=one,two,three"]);
        let opts2 = options(&["--ignore", "one,two,three"]);
        let opts3 = options(&["--ignore", "one two three"]);
        let opts4 = options(&["--ignore", "one", "--ignore", "two", "--ignore", "three"]);
        let opts5 = options(&["--ignore", "one", "--ignore", "two,three"]);
        let correct = Options {
            ignore: vec!["one".into(), "two".into(), "three".into()],
            ..Options::default()
        };

        assert_eq!(correct, opts1);
        assert_eq!(correct, opts2);
        assert_eq!(correct, opts3);
        assert_eq!(correct, opts4);
        assert_eq!(correct, opts5);
    }

    #[test]
    fn ignore_fail() {
        let res = options_fail(&["--ignore", "one", "two"]);
        assert!(res.is_err());
        assert_eq!(
            res.as_ref().unwrap_err().kind,
            clap::ErrorKind::UnknownArgument,
            "{:?}",
            res.as_ref().unwrap_err().kind
        );
    }

    #[test]
    fn verbose() {
        let opts1 = options(&["--verbose", "--verbose", "--verbose"]);
        let correct = Options {
            verbose: 3,
            ..Options::default()
        };

        assert_eq!(correct, opts1);
    }

    #[test]
    fn packages() {
        let opts1 = options(&["--packages", "one,two"]);
        let opts2 = options(&["--packages", "one two"]);
        let opts3 = options(&["--packages", "one", "--packages", "two"]);
        let correct = Options {
            packages: vec!["one".into(), "two".into()],
            ..Options::default()
        };

        assert_eq!(correct, opts1);
        assert_eq!(correct, opts2);
        assert_eq!(correct, opts3);
    }

    #[test]
    fn packages_fail() {
        let res = options_fail(&["--packages", "one", "two"]);
        assert!(res.is_err());
        assert_eq!(
            res.as_ref().unwrap_err().kind,
            clap::ErrorKind::UnknownArgument,
            "{:?}",
            res.as_ref().unwrap_err().kind
        );
    }

    #[test]
    fn format_case() {
        let opts1 = options(&["--format", "JsOn"]);
        let correct = Options {
            format: Format::Json,
            ..Options::default()
        };

        assert_eq!(correct, opts1);
    }

    #[test]
    fn format_unknown() {
        let res = options_fail(&["--format", "foobar"]);
        assert!(res.is_err());
        assert_eq!(
            res.as_ref().unwrap_err().kind,
            clap::ErrorKind::InvalidValue,
            "{:?}",
            res.as_ref().unwrap_err().kind
        );
    }

    #[test]
    fn color_case() {
        let opts1 = options(&["--color", "NeVeR"]);
        let correct = Options {
            color: Color::Never,
            ..Options::default()
        };

        assert_eq!(correct, opts1);
    }

    #[test]
    fn color_unknown() {
        let res = options_fail(&["--color", "foobar"]);
        assert!(res.is_err());
        assert_eq!(
            res.as_ref().unwrap_err().kind,
            clap::ErrorKind::InvalidValue,
            "{:?}",
            res.as_ref().unwrap_err().kind
        );
    }
}
<commit_msg>fix: Typo in `--aggressive` arg<commit_after>use clap::{
    arg_enum, crate_version, value_t, value_t_or_exit, App, AppSettings, Arg, ArgMatches,
    SubCommand,
};

arg_enum! {
    #[derive(Copy, Clone, Debug, PartialEq)]
    pub enum Format {
        List,
        Json,
    }
}

impl Default for Format {
    fn default() -> Self { Format::List }
}

arg_enum! {
    #[derive(Copy, Clone, Debug, PartialEq)]
    pub enum Color {
        Auto,
        Never,
        Always
    }
}

impl Default for Color {
    fn default() -> Self { Color::Auto }
}

/// Options from CLI arguments
#[derive(Debug, PartialEq, Default)]
pub struct Options {
    pub format: Format,
    pub color: Color,
    pub features: Vec<String>,
    pub ignore: Vec<String>,
    pub exclude: Vec<String>,
    pub manifest_path: Option<String>,
    pub quiet: bool,
    pub verbose: u64,
    pub exit_code: i32,
    pub packages: Vec<String>,
    pub root: Option<String>,
    pub depth: Option<i32>,
    pub root_deps_only: bool,
    pub workspace: bool,
    pub aggressive: bool,
    pub workspace_only: bool,
    pub offline: bool,
}

impl Options {
    pub fn all_features(&self) -> bool { self.features.is_empty() }

    pub fn no_default_features(&self) -> bool {
        !(self.features.is_empty() || self.features.contains(&"default".to_owned()))
    }

    pub fn locked(&self) -> bool { false }

    pub fn frozen(&self) -> bool { false }
}

impl<'a> From<&ArgMatches<'a>> for Options {
    fn from(m: &ArgMatches<'a>) -> Self {
        let mut opts = Options {
            format: value_t_or_exit!(m.value_of("format"), Format),
            color: value_t_or_exit!(m.value_of("color"), Color),
            features: m
                .values_of("features")
                .map(|vals| {
                    vals.flat_map(|x| x.split_ascii_whitespace().collect::<Vec<_>>())
                        .map(ToOwned::to_owned)
                        .collect()
                })
                .unwrap_or_else(Vec::new),
            ignore: m
                .values_of("ignore")
                .map(|vals| {
                    vals.flat_map(|x| x.split_ascii_whitespace().collect::<Vec<_>>())
                        .map(ToOwned::to_owned)
                        .collect()
                })
                .unwrap_or_else(Vec::new),
            exclude: m
                .values_of("exclude")
                .map(|vals| {
                    vals.flat_map(|x| x.split_ascii_whitespace().collect::<Vec<_>>())
                        .map(ToOwned::to_owned)
                        .collect()
                })
                .unwrap_or_else(Vec::new),
            manifest_path: m.value_of("manifest-path").map(ToOwned::to_owned),
            quiet: m.is_present("quiet"),
            verbose: m.occurrences_of("verbose"),
            exit_code: value_t!(m, "exit-code", i32).ok().unwrap_or(0),
            packages: m
                .values_of("packages")
                .map(|vals| {
                    vals.flat_map(|x| x.split_ascii_whitespace().collect::<Vec<_>>())
                        .map(ToOwned::to_owned)
                        .collect::<Vec<_>>()
                })
                .unwrap_or_else(Vec::new),
            root: m.value_of("root").map(ToOwned::to_owned),
            depth: value_t!(m, "depth", i32).ok(),
            root_deps_only: m.is_present("root-deps-only"),
            workspace_only: m.is_present("ignore-external-rel"),
            workspace: m.is_present("workspace"),
            aggressive: m.is_present("aggressive"),
            offline: m.is_present("offline"),
        };

        if m.is_present("root-deps-only") {
            opts.depth = Some(1);
        }

        if m.is_present("ignore-external-rel") {
            opts.depth = Some(1);
            opts.root_deps_only = true;
        }

        opts
    }
}

fn build() -> App<'static, 'static> {
    App::new("cargo-outdated")
        .bin_name("cargo")
        .setting(AppSettings::SubcommandRequired)
        .subcommand(
            SubCommand::with_name("outdated")
                .setting(AppSettings::UnifiedHelpMessage)
                .about("Displays information about project dependency versions")
                .version(crate_version!())
                .arg(
                    Arg::with_name("aggressive")
                        .short("a")
                        .long("aggressive")
                        .help("Ignores channels for latest updates"),
                )
                .arg(
                    Arg::with_name("quiet")
                        .short("q")
                        .long("quiet")
                        .help("Suppresses warnings"),
                )
                .arg(
                    Arg::with_name("root-deps-only")
                        .short("R")
                        .long("root-deps-only")
                        .help("Only check root dependencies (Equivalent to --depth=1)"),
                )
                .arg(
                    Arg::with_name("ignore-external-rel")
                        .short("e")
                        .long("ignore-external-rel")
                        .help("Ignore relative dependencies external to workspace and check root dependencies only."),
                )
                .arg(
                    Arg::with_name("workspace")
                        .short("w")
                        .long("workspace")
                        .help("Checks updates for all workspace members rather than only the root package"),
                )
                .arg(
                    Arg::with_name("offline")
                        .short("o")
                        .long("offline")
                        .help("Run without accessing the network (useful for testing w/ local registries)"),
                )
                .arg(
                    Arg::with_name("format")
                        .long("format")
                        .default_value("list")
                        .case_insensitive(true)
                        .possible_values(&Format::variants())
                        .value_name("FORMAT")
                        .help("Output formatting"),
                )
                .arg(
                    Arg::with_name("ignore")
                        .short("i")
                        .long("ignore")
                        .help("Dependencies to not print in the output (comma separated or one per '--ignore' argument)")
                        .value_delimiter(",")
                        .number_of_values(1)
                        .multiple(true)
                        .value_name("DEPENDENCIES"),
                )
                .arg(
                    Arg::with_name("exclude")
                        .short("x")
                        .long("exclude")
                        .help("Dependencies to exclude from building (comma separated or one per '--exclude' argument)")
                        .value_delimiter(",")
                        .multiple(true)
                        .number_of_values(1)
                        .value_name("DEPENDENCIES"),
                )
                .arg(
                    Arg::with_name("verbose")
                        .short("v")
                        .long("verbose")
                        .multiple(true)
                        .help("Use verbose output")
                )
                .arg(
                    Arg::with_name("color")
                        .long("color")
                        .possible_values(&Color::variants())
                        .default_value("auto")
                        .value_name("COLOR")
                        .case_insensitive(true)
                        .help("Output coloring")
                )
                .arg(
                    Arg::with_name("depth")
                        .short("d")
                        .long("depth")
                        .value_name("NUM")
                        .help("How deep in the dependency chain to search (Defaults to all dependencies when omitted)")
                )
                .arg(
                    Arg::with_name("exit-code")
                        .long("exit-code")
                        .help("The exit code to return on new versions found")
                        .default_value("0")
                        .value_name("NUM"))
                .arg(
                    Arg::with_name("manifest-path")
                        .short("m")
                        .long("manifest-path")
                        .help("Path to the Cargo.toml file to use (Defaults to Cargo.toml in project root)")
                        .value_name("PATH"))
                .arg(
                    Arg::with_name("root")
                        .short("r")
                        .long("root")
                        .help("Package to treat as the root package")
                        .value_name("ROOT"))
                .arg(
                    Arg::with_name("packages")
                        .short("p")
                        .long("packages")
                        .help("Packages to inspect for updates (comma separated or one per '--packages' argument)")
                        .value_delimiter(",")
                        .number_of_values(1)
                        .multiple(true)
                        .value_name("PKGS"))
                .arg(
                    Arg::with_name("features")
                        .long("features")
                        .value_delimiter(",")
                        .help("Space-separated list of features")
                        .multiple(true)
                        .number_of_values(1)
                        .value_name("FEATURES"))
            )
}

pub fn parse() -> Options {
    let matches = build().get_matches();

    Options::from(matches.subcommand_matches("outdated").unwrap())
}

#[cfg(test)]
mod test {
    use super::*;

    fn options(args: &[&str]) -> Options {
        let mut argv = vec!["cargo", "outdated"];
        argv.extend(args);
        let m = build().get_matches_from(argv);
        Options::from(m.subcommand_matches("outdated").unwrap())
    }

    fn options_fail(args: &[&str]) -> clap::Result<ArgMatches<'static>> {
        let mut argv = vec!["cargo", "outdated"];
        argv.extend(args);
        build().get_matches_from_safe(argv)
    }

    #[test]
    fn default() {
        let opts = options(&[]);
        assert_eq!(Options::default(), opts)
    }

    #[test]
    fn root_only() {
        let opts = options(&["--root-deps-only"]);
        assert_eq!(
            Options {
                depth: Some(1),
                root_deps_only: true,
                ..Options::default()
            },
            opts
        )
    }

    #[test]
    fn workspace_only() {
        let opts = options(&["--ignore-external-rel"]);
        assert_eq!(
            Options {
                workspace_only: true,
                depth: Some(1),
                root_deps_only: true,
                ..Options::default()
            },
            opts
        )
    }

    #[test]
    fn features() {
        let opts1 = options(&["--features=one,two,three"]);
        let opts2 = options(&["--features", "one,two,three"]);
        let opts3 = options(&["--features", "one two three"]);
        let opts4 = options(&[
            "--features",
            "one",
            "--features",
            "two",
            "--features",
            "three",
        ]);
        let opts5 = options(&["--features", "one", "--features", "two,three"]);

        let correct = Options {
            features: vec!["one".into(), "two".into(), "three".into()],
            ..Options::default()
        };

        assert_eq!(correct, opts1);
        assert_eq!(correct, opts2);
        assert_eq!(correct, opts3);
        assert_eq!(correct, opts4);
        assert_eq!(correct, opts5);
    }

    #[test]
    fn features_fail() {
        let res = options_fail(&["--features", "one", "two"]);
        assert!(res.is_err());
        assert_eq!(
            res.as_ref().unwrap_err().kind,
            clap::ErrorKind::UnknownArgument,
            "{:?}",
            res.as_ref().unwrap_err().kind
        );
    }

    #[test]
    fn exclude() {
        let opts1 = options(&["--exclude=one,two,three"]);
        let opts2 = options(&["--exclude", "one,two,three"]);
        let opts3 = options(&["--exclude", "one two three"]);
        let opts4 = options(&["--exclude", "one", "--exclude", "two", "--exclude", "three"]);
        let opts5 = options(&["--exclude", "one", "--exclude", "two,three"]);
        let correct = Options {
            exclude: vec!["one".into(), "two".into(), "three".into()],
            ..Options::default()
        };

        assert_eq!(correct, opts1);
        assert_eq!(correct, opts2);
        assert_eq!(correct, opts3);
        assert_eq!(correct, opts4);
        assert_eq!(correct, opts5);
    }

    #[test]
    fn exclude_fail() {
        let res = options_fail(&["--exclude", "one", "two"]);
        assert!(res.is_err());
        assert_eq!(
            res.as_ref().unwrap_err().kind,
            clap::ErrorKind::UnknownArgument,
            "{:?}",
            res.as_ref().unwrap_err().kind
        );
    }

    #[test]
    fn ignore() {
        let opts1 = options(&["--ignore=one,two,three"]);
        let opts2 = options(&["--ignore", "one,two,three"]);
        let opts3 = options(&["--ignore", "one two three"]);
        let opts4 = options(&["--ignore", "one", "--ignore", "two", "--ignore", "three"]);
        let opts5 = options(&["--ignore", "one", "--ignore", "two,three"]);
        let correct = Options {
            ignore: vec!["one".into(), "two".into(), "three".into()],
            ..Options::default()
        };

        assert_eq!(correct, opts1);
        assert_eq!(correct, opts2);
        assert_eq!(correct, opts3);
        assert_eq!(correct, opts4);
        assert_eq!(correct, opts5);
    }

    #[test]
    fn ignore_fail() {
        let res = options_fail(&["--ignore", "one", "two"]);
        assert!(res.is_err());
        assert_eq!(
            res.as_ref().unwrap_err().kind,
            clap::ErrorKind::UnknownArgument,
            "{:?}",
            res.as_ref().unwrap_err().kind
        );
    }

    #[test]
    fn verbose() {
        let opts1 = options(&["--verbose", "--verbose", "--verbose"]);
        let correct = Options {
            verbose: 3,
            ..Options::default()
        };

        assert_eq!(correct, opts1);
    }

    #[test]
    fn packages() {
        let opts1 = options(&["--packages", "one,two"]);
        let opts2 = options(&["--packages", "one two"]);
        let opts3 = options(&["--packages", "one", "--packages", "two"]);
        let correct = Options {
            packages: vec!["one".into(), "two".into()],
            ..Options::default()
        };

        assert_eq!(correct, opts1);
        assert_eq!(correct, opts2);
        assert_eq!(correct, opts3);
    }

    #[test]
    fn packages_fail() {
        let res = options_fail(&["--packages", "one", "two"]);
        assert!(res.is_err());
        assert_eq!(
            res.as_ref().unwrap_err().kind,
            clap::ErrorKind::UnknownArgument,
            "{:?}",
            res.as_ref().unwrap_err().kind
        );
    }

    #[test]
    fn format_case() {
        let opts1 = options(&["--format", "JsOn"]);
        let correct = Options {
            format: Format::Json,
            ..Options::default()
        };

        assert_eq!(correct, opts1);
    }

    #[test]
    fn format_unknown() {
        let res = options_fail(&["--format", "foobar"]);
        assert!(res.is_err());
        assert_eq!(
            res.as_ref().unwrap_err().kind,
            clap::ErrorKind::InvalidValue,
            "{:?}",
            res.as_ref().unwrap_err().kind
        );
    }

    #[test]
    fn color_case() {
        let opts1 = options(&["--color", "NeVeR"]);
        let correct = Options {
            color: Color::Never,
            ..Options::default()
        };

        assert_eq!(correct, opts1);
    }

    #[test]
    fn color_unknown() {
        let res = options_fail(&["--color", "foobar"]);
        assert!(res.is_err());
        assert_eq!(
            res.as_ref().unwrap_err().kind,
            clap::ErrorKind::InvalidValue,
            "{:?}",
            res.as_ref().unwrap_err().kind
        );
    }
}
<|endoftext|>


<commit_before>#[derive(Debug, Clone, PartialEq)]
pub enum Command {
    Value(Val),
    BeginBlock,
    EndBlock,
    EmptyBlock,
    Pack,
    Size,
    Length,
    Dup,
    Not,
    If,
    Define,
    ApplyFunction,
    Read,
    Swap,
    Split,
    Get,
    DupGet,
    Move,
    Grab,
    DupGrab,
    Include,
    Drop,
    Type,
    ToFloat,
    ToInt,
    ToBool,
    Eq,
    Neq,
    GreaterThan,
    GreaterEquals,
    LessThan,
    LessEquals,
    Write,
    Print,
    Exit,
    Or,
    And,
    Xor,
    Add,
    Sub,
    Mul,
    Pow,
    Div,
    Rem,
}

use self::Command::*;
use crate::value::Value as Val;

impl Command {
    pub fn from_str(cmd: &str) -> Self {
        match &*cmd.to_lowercase() {
            "{" | "[" | "do" => BeginBlock,
            "}" | "]" | "end" => EndBlock,
            "{}" | "[]" | "nop" => EmptyBlock,
            "inc" | "include" => Include,
            "@" | "pack" => Pack,
            "size" => Size,
            "len" => Length,
            ";" | "dup" => Dup,
            "!" | "not" => Not,
            "?" | "if" => If,
            ":=" | "def" => Define,
            "()" | "apply" => ApplyFunction,
            "<-" | "read" => Read,
            "$" | "swap" => Swap,
            "\\/" | "\\\\/" | "split" => Split,
            "." | "get" => Get,
            ".:" | "dupget" => DupGet,
            "<>" | "move" => Move,
            "¤" | "grab" => Grab,
            ":" | "dupgrab" => DupGrab,
            "~" | "drop" => Drop,
            "t" | "type" => Type,
            "#" | "float" => ToFloat,
            "i" | "int" => ToInt,
            "b" | "bool" => ToBool,
            "==" | "=" | "eq" => Eq,
            "!=" | "~=" | "neq" => Neq,
            ">" => GreaterThan,
            ">=" => GreaterEquals,
            "<" => LessThan,
            "<=" => LessEquals,
            "->" | "wrte" => Write,
            "_" | "prnt" => Print,
            "x" | "exit" => Exit,
            "|" | "or" => Or,
            "&" | "and" => And,
            "^" | "xor" => Xor,
            "+" | "add" => Add,
            "-" | "sub" => Sub,
            "*" | "mul" => Mul,
            "**" | "pow" => Pow,
            "/" | "div" => Div,
            "%" | "rem" => Rem,
            _ => Value(Val::parse(cmd)),
        }
    }
}
<commit_msg>Rename commands<commit_after>#[derive(Debug, Clone, PartialEq)]
pub enum Command {
    Value(Val),
    BeginBlock,
    EndBlock,
    EmptyBlock,
    Pack,
    Size,
    Length,
    Dup,
    Not,
    If,
    Define,
    ApplyFunction,
    Read,
    Swap,
    Split,
    Get,
    DupGet,
    Move,
    Grab,
    DupGrab,
    Include,
    Drop,
    Type,
    ToFloat,
    ToInt,
    ToBool,
    Eq,
    Neq,
    GreaterThan,
    GreaterEquals,
    LessThan,
    LessEquals,
    Write,
    Print,
    Exit,
    Or,
    And,
    Xor,
    Add,
    Sub,
    Mul,
    Pow,
    Div,
    Rem,
}

use self::Command::*;
use crate::value::Value as Val;

impl Command {
    pub fn from_str(cmd: &str) -> Self {
        if let Some(cmd) = Self::from_str_pure(cmd) {
            cmd
        } else {
            Value(Val::parse(cmd))
        }
    }
    pub fn from_str_pure(cmd: &str) -> Option<Self> {
        Some(match &*cmd.to_lowercase() {
            "{" | "[" => BeginBlock,
            "}" | "]" => EndBlock,
            "{}" | "[]" => EmptyBlock,
            "inc" | "include" => Include,
            "@" | "pack" => Pack,
            "size" => Size,
            "len" => Length,
            ";" | "dup" => Dup,
            "!" | "not" => Not,
            "?" | "if" => If,
            ":=" | "def" => Define,
            "()" | "apply" => ApplyFunction,
            "<-" | "read" => Read,
            "$" | "swap" => Swap,
            "\\/" | "\\\\/" | "split" => Split,
            "." | "get" => Get,
            ".:" | "dupget" => DupGet,
            "<>" | "move" => Move,
            "#" | "grab" => Grab,
            ":" | "dupgrab" => DupGrab,
            "~" | "drop" => Drop,
            "t" | "type" => Type,
            "f" | "float" => ToFloat,
            "i" | "int" => ToInt,
            "b" | "bool" => ToBool,
            "==" | "=" | "eq" => Eq,
            "!=" | "~=" | "neq" => Neq,
            ">" => GreaterThan,
            ">=" => GreaterEquals,
            "<" => LessThan,
            "<=" => LessEquals,
            "->" | "wrte" => Write,
            "_" | "prnt" => Print,
            "x" | "exit" => Exit,
            "|" | "or" => Or,
            "&" | "and" => And,
            "^" | "xor" => Xor,
            "+" | "add" => Add,
            "-" | "sub" => Sub,
            "*" | "mul" => Mul,
            "**" | "pow" => Pow,
            "/" | "div" => Div,
            "%" | "rem" => Rem,
            _ => return None,
        })
    }
}
<|endoftext|>


<commit_before>//! ELF executables

use alloc::String;

use goblin::elf::section_header::SHT_SYMTAB;

#[cfg(target_arch = "x86")]
pub use goblin::elf32::{header, program_header, section_header, sym};

#[cfg(target_arch = "x86_64")]
pub use goblin::elf64::{header, program_header, section_header, sym};

/// An ELF executable
pub struct Elf<'a> {
    pub data: &'a [u8],
    header: &'a header::Header
}

impl<'a> Elf<'a> {
    /// Create a ELF executable from data
    pub fn from(data: &'a [u8]) -> Result<Elf<'a>, String> {
        if data.len() < header::SIZEOF_EHDR {
            Err(format!("Elf: Not enough data: {} < {}", data.len(), header::SIZEOF_EHDR))
        } else if &data[..header::SELFMAG] != header::ELFMAG {
            Err(format!("Elf: Invalid magic: {:?} != {:?}", &data[..4], header::ELFMAG))
        } else if data.get(header::EI_CLASS) != Some(&header::ELFCLASS) {
            Err(format!("Elf: Invalid architecture: {:?} != {:?}", data.get(header::EI_CLASS), header::ELFCLASS))
        } else {
            Ok(Elf {
                data: data,
                header: unsafe { &*(data.as_ptr() as usize as *const header::Header) }
            })
        }
    }

    pub fn sections(&'a self) -> ElfSections<'a> {
        ElfSections {
            data: self.data,
            header: self.header,
            i: 0
        }
    }

    pub fn segments(&'a self) -> ElfSegments<'a> {
        ElfSegments {
            data: self.data,
            header: self.header,
            i: 0
        }
    }

    pub fn symbols(&'a self) -> Option<ElfSymbols<'a>> {
        let mut symtab_opt = None;
        for section in self.sections() {
            if section.sh_type == SHT_SYMTAB {
                symtab_opt = Some(section);
                break;
            }
        }

        if let Some(symtab) = symtab_opt {
            Some(ElfSymbols {
                data: self.data,
                header: self.header,
                symtab: symtab,
                i: 0
            })
        } else {
            None
        }
    }

    /// Get the entry field of the header
    pub fn entry(&self) -> usize {
        self.header.e_entry as usize
    }
}

pub struct ElfSections<'a> {
    data: &'a [u8],
    header: &'a header::Header,
    i: usize
}

impl<'a> Iterator for ElfSections<'a> {
    type Item = &'a section_header::SectionHeader;
    fn next(&mut self) -> Option<Self::Item> {
        if self.i < self.header.e_shnum as usize {
            let item = unsafe {
                &* ((
                        self.data.as_ptr() as usize
                        + self.header.e_shoff as usize
                        + self.i * self.header.e_shentsize as usize
                    ) as *const section_header::SectionHeader)
            };
            self.i += 1;
            Some(item)
        } else {
            None
        }
    }
}

pub struct ElfSegments<'a> {
    data: &'a [u8],
    header: &'a header::Header,
    i: usize
}

impl<'a> Iterator for ElfSegments<'a> {
    type Item = &'a program_header::ProgramHeader;
    fn next(&mut self) -> Option<Self::Item> {
        if self.i < self.header.e_phnum as usize {
            let item = unsafe {
                &* ((
                        self.data.as_ptr() as usize
                        + self.header.e_phoff as usize
                        + self.i * self.header.e_phentsize as usize
                    ) as *const program_header::ProgramHeader)
            };
            self.i += 1;
            Some(item)
        } else {
            None
        }
    }
}

pub struct ElfSymbols<'a> {
    data: &'a [u8],
    header: &'a header::Header,
    symtab: &'a section_header::SectionHeader,
    i: usize
}

impl<'a> Iterator for ElfSymbols<'a> {
    type Item = &'a sym::Sym;
    fn next(&mut self) -> Option<Self::Item> {
        if self.i < (self.symtab.sh_size as usize) / sym::SIZEOF_SYM {
            let item = unsafe {
                &* ((
                        self.data.as_ptr() as usize
                        + self.symtab.sh_offset as usize
                        + self.i * sym::SIZEOF_SYM
                    ) as *const sym::Sym)
            };
            self.i += 1;
            Some(item)
        } else {
            None
        }
    }
}
<commit_msg>elf.rs: do not inline constant from goblin library<commit_after>//! ELF executables

use alloc::String;

use goblin::elf::section_header::SHT_SYMTAB;

#[cfg(target_arch = "x86")]
pub use goblin::elf32::{header, program_header, section_header, sym};

#[cfg(target_arch = "x86_64")]
pub use goblin::elf64::{header, program_header, section_header, sym};

/// An ELF executable
pub struct Elf<'a> {
    pub data: &'a [u8],
    header: &'a header::Header
}

impl<'a> Elf<'a> {
    /// Create a ELF executable from data
    pub fn from(data: &'a [u8]) -> Result<Elf<'a>, String> {
        if data.len() < header::SIZEOF_EHDR {
            Err(format!("Elf: Not enough data: {} < {}", data.len(), header::SIZEOF_EHDR))
        } else if &data[..header::SELFMAG] != header::ELFMAG {
            Err(format!("Elf: Invalid magic: {:?} != {:?}", &data[..header::SELFMAG], header::ELFMAG))
        } else if data.get(header::EI_CLASS) != Some(&header::ELFCLASS) {
            Err(format!("Elf: Invalid architecture: {:?} != {:?}", data.get(header::EI_CLASS), header::ELFCLASS))
        } else {
            Ok(Elf {
                data: data,
                header: unsafe { &*(data.as_ptr() as usize as *const header::Header) }
            })
        }
    }

    pub fn sections(&'a self) -> ElfSections<'a> {
        ElfSections {
            data: self.data,
            header: self.header,
            i: 0
        }
    }

    pub fn segments(&'a self) -> ElfSegments<'a> {
        ElfSegments {
            data: self.data,
            header: self.header,
            i: 0
        }
    }

    pub fn symbols(&'a self) -> Option<ElfSymbols<'a>> {
        let mut symtab_opt = None;
        for section in self.sections() {
            if section.sh_type == SHT_SYMTAB {
                symtab_opt = Some(section);
                break;
            }
        }

        if let Some(symtab) = symtab_opt {
            Some(ElfSymbols {
                data: self.data,
                header: self.header,
                symtab: symtab,
                i: 0
            })
        } else {
            None
        }
    }

    /// Get the entry field of the header
    pub fn entry(&self) -> usize {
        self.header.e_entry as usize
    }
}

pub struct ElfSections<'a> {
    data: &'a [u8],
    header: &'a header::Header,
    i: usize
}

impl<'a> Iterator for ElfSections<'a> {
    type Item = &'a section_header::SectionHeader;
    fn next(&mut self) -> Option<Self::Item> {
        if self.i < self.header.e_shnum as usize {
            let item = unsafe {
                &* ((
                        self.data.as_ptr() as usize
                        + self.header.e_shoff as usize
                        + self.i * self.header.e_shentsize as usize
                    ) as *const section_header::SectionHeader)
            };
            self.i += 1;
            Some(item)
        } else {
            None
        }
    }
}

pub struct ElfSegments<'a> {
    data: &'a [u8],
    header: &'a header::Header,
    i: usize
}

impl<'a> Iterator for ElfSegments<'a> {
    type Item = &'a program_header::ProgramHeader;
    fn next(&mut self) -> Option<Self::Item> {
        if self.i < self.header.e_phnum as usize {
            let item = unsafe {
                &* ((
                        self.data.as_ptr() as usize
                        + self.header.e_phoff as usize
                        + self.i * self.header.e_phentsize as usize
                    ) as *const program_header::ProgramHeader)
            };
            self.i += 1;
            Some(item)
        } else {
            None
        }
    }
}

pub struct ElfSymbols<'a> {
    data: &'a [u8],
    header: &'a header::Header,
    symtab: &'a section_header::SectionHeader,
    i: usize
}

impl<'a> Iterator for ElfSymbols<'a> {
    type Item = &'a sym::Sym;
    fn next(&mut self) -> Option<Self::Item> {
        if self.i < (self.symtab.sh_size as usize) / sym::SIZEOF_SYM {
            let item = unsafe {
                &* ((
                        self.data.as_ptr() as usize
                        + self.symtab.sh_offset as usize
                        + self.i * sym::SIZEOF_SYM
                    ) as *const sym::Sym)
            };
            self.i += 1;
            Some(item)
        } else {
            None
        }
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#![feature(append)]
#![feature(arc_unique)]
#![feature(box_syntax)]
#![feature(filling_drop)]
#![feature(hashmap_hasher)]
#![feature(heap_api)]
#![feature(mpsc_select)]
#![feature(plugin)]
#![feature(raw)]
#![feature(slice_chars)]
#![feature(str_char)]
#![feature(unsafe_no_drop_flag)]

#![deny(unsafe_code)]
#![allow(unrooted_must_root)]

#![plugin(string_cache_plugin)]
#![plugin(plugins)]

#[macro_use]
extern crate log;

#[macro_use]
extern crate bitflags;

#[macro_use]
#[no_link]
extern crate plugins as servo_plugins;
extern crate net_traits;
#[macro_use]
extern crate profile_traits;

#[macro_use]
extern crate util;

extern crate rustc_serialize;
extern crate azure;
extern crate canvas_traits;
extern crate clock_ticks;
extern crate cssparser;
extern crate encoding;
extern crate fnv;
extern crate euclid;
extern crate gfx;
extern crate gfx_traits;
extern crate layout_traits;
extern crate libc;
extern crate msg;
extern crate script;
extern crate script_traits;
extern crate selectors;
extern crate smallvec;
extern crate string_cache;
extern crate style;
extern crate url;

// Listed first because of macro definitions
pub mod layout_debug;

pub mod animation;
pub mod block;
pub mod construct;
pub mod context;
pub mod data;
pub mod display_list_builder;
pub mod floats;
pub mod flow;
pub mod flow_list;
pub mod flow_ref;
pub mod fragment;
pub mod generated_content;
pub mod layout_task;
pub mod incremental;
pub mod inline;
pub mod list_item;
pub mod model;
pub mod multicol;
pub mod opaque_node;
pub mod parallel;
pub mod sequential;
pub mod table_wrapper;
pub mod table;
pub mod table_caption;
pub mod table_colgroup;
pub mod table_rowgroup;
pub mod table_row;
pub mod table_cell;
pub mod text;
pub mod traversal;
pub mod wrapper;

pub mod css {
    pub mod matching;
    pub mod node_style;
}
<commit_msg>Remove the unused unrooted_must_root permission from layout.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#![feature(append)]
#![feature(arc_unique)]
#![feature(box_syntax)]
#![feature(filling_drop)]
#![feature(hashmap_hasher)]
#![feature(heap_api)]
#![feature(mpsc_select)]
#![feature(plugin)]
#![feature(raw)]
#![feature(slice_chars)]
#![feature(str_char)]
#![feature(unsafe_no_drop_flag)]

#![deny(unsafe_code)]

#![plugin(string_cache_plugin)]
#![plugin(plugins)]

#[macro_use]
extern crate log;

#[macro_use]
extern crate bitflags;

#[macro_use]
#[no_link]
extern crate plugins as servo_plugins;
extern crate net_traits;
#[macro_use]
extern crate profile_traits;

#[macro_use]
extern crate util;

extern crate rustc_serialize;
extern crate azure;
extern crate canvas_traits;
extern crate clock_ticks;
extern crate cssparser;
extern crate encoding;
extern crate fnv;
extern crate euclid;
extern crate gfx;
extern crate gfx_traits;
extern crate layout_traits;
extern crate libc;
extern crate msg;
extern crate script;
extern crate script_traits;
extern crate selectors;
extern crate smallvec;
extern crate string_cache;
extern crate style;
extern crate url;

// Listed first because of macro definitions
pub mod layout_debug;

pub mod animation;
pub mod block;
pub mod construct;
pub mod context;
pub mod data;
pub mod display_list_builder;
pub mod floats;
pub mod flow;
pub mod flow_list;
pub mod flow_ref;
pub mod fragment;
pub mod generated_content;
pub mod layout_task;
pub mod incremental;
pub mod inline;
pub mod list_item;
pub mod model;
pub mod multicol;
pub mod opaque_node;
pub mod parallel;
pub mod sequential;
pub mod table_wrapper;
pub mod table;
pub mod table_caption;
pub mod table_colgroup;
pub mod table_rowgroup;
pub mod table_row;
pub mod table_cell;
pub mod text;
pub mod traversal;
pub mod wrapper;

pub mod css {
    pub mod matching;
    pub mod node_style;
}
<|endoftext|>


<commit_before>use std::{mem, ptr};
use libc::{self, c_char};
use std::slice;
use std::cell::RefCell;
use std::collections::VecMap;

use super::get_ladspa_descriptor;

// essentially ladspa.h API translated to rust.
mod ladspa {
    use libc::{c_void, c_char};

    pub type Data = f32;
    pub type Properties = i32;
    pub type PortDescriptor = i32;
    pub type PortRangeHintDescriptor = i32;

    pub type Handle = *mut c_void;

    #[repr(C)]
    #[derive(Copy)]
    pub struct PortRangeHint {
        pub hint_descriptor: PortRangeHintDescriptor,
        pub lower_bound: Data,
        pub upper_bound: Data,
    }

    #[repr(C)]
    #[allow(missing_copy_implementations)] // Remove this for a fun warning/suggestion cycle!
    pub struct Descriptor {
        pub unique_id: u64,
        pub label: *const c_char,
        pub properties: Properties,
        pub name: *const c_char,
        pub maker: *const c_char,
        pub copyright: *const c_char,
        pub port_count: u64,
        pub port_descriptors: *mut PortDescriptor,
        pub port_names: *mut *const c_char,
        pub port_range_hints: *mut PortRangeHint,
        pub implementation_data: *mut c_void,
        pub instantiate: extern "C" fn(descriptor: *const Descriptor, sample_rate: u64) -> Handle,
        pub connect_port: extern "C" fn(instance: Handle, port: u64, data_location: *mut Data),
        pub activate: extern "C" fn(instance: Handle),
        pub run: extern "C" fn(instance: Handle, sample_count: u64),
        pub run_adding: extern "C" fn(instance: Handle, sample_count: u64),
        pub set_run_adding_gain: extern "C" fn(instance: Handle, gain: Data),
        pub deactivate: extern "C" fn(instance: Handle),
        pub cleanup: extern "C" fn(instance: Handle),
    }
}

unsafe fn alloc<T>(num: u64) -> *mut T {
    let ptr: *mut T = mem::transmute(libc::malloc(num * mem::size_of::<T>() as u64));
    if ptr == ptr::null_mut() {
        panic!("malloc returned null!");
    }
    ptr
}

unsafe fn free<T>(x: *const T) {
    libc::free(mem::transmute(x));
}

unsafe fn make_c_str(s: &'static str) -> *const c_char {
    let c_str: *mut c_char = alloc::<c_char>(s.len() as u64 + 1);
    ptr::copy_memory(c_str, mem::transmute(s.as_ptr()), s.len());
    slice::from_raw_parts_mut(c_str, s.len() + 1)[s.len()] = 0; // add the null terminator
    c_str
}

static mut init_done: bool = false;
static mut num_descriptors: u64 = 0;
static mut descriptors: Option<*mut *mut ladspa::Descriptor> = None;
static MAX_DESCRIPTORS: u64 = 32;

// It seems that ladspa_descriptor is deleted during link time optimization unless we
// call it from somewhere.
#[allow(dead_code)]
unsafe fn _lto_workaround() {
    ladspa_descriptor(0);
}

#[no_mangle]
/// This is exported so that your plugin is recognised by LADSPA hosts. Don't worry about it.
pub unsafe extern "C" fn ladspa_descriptor(index: u64) -> *mut ladspa::Descriptor {
    if !init_done {
        libc::atexit(global_destruct);
        descriptors = Some(alloc(MAX_DESCRIPTORS));
        init_done = true;
    }

    // If it's already been generated, return the cached copy.
    if index < num_descriptors {
        return *descriptors.unwrap().offset(index as isize);
    }

    match get_ladspa_descriptor(index) {
        Some(plugin) => {
            let desc: &mut ladspa::Descriptor = mem::transmute(alloc::<ladspa::Descriptor>(1));

            // the following is ok because none of the fields in the descriptor have
            // destructors. if they did rust would try to drop null pointers on write.

            // copy data fields
            desc.unique_id = plugin.unique_id;
            desc.label = make_c_str(plugin.label);
            desc.properties = plugin.properties.bits();
            desc.name = make_c_str(plugin.name);
            desc.maker = make_c_str(plugin.maker);
            desc.copyright = make_c_str(plugin.copyright);
            desc.port_count = plugin.ports.len() as u64;
            desc.port_descriptors = alloc::<ladspa::PortDescriptor>(desc.port_count);
            desc.port_names = alloc::<*const c_char>(desc.port_count);
            desc.port_range_hints = alloc::<ladspa::PortRangeHint>(desc.port_count);
            for i in 0..desc.port_count as usize {
                *desc.port_descriptors.offset(i as isize)
                    = plugin.ports[i].desc as i32;

                *desc.port_names.offset(i as isize)
                    = make_c_str(plugin.ports[i].name);

                let port = &plugin.ports[i];
                *desc.port_range_hints.offset(i as isize)
                    = ladspa::PortRangeHint {
                        hint_descriptor: port.hint.map(|x| x.bits()).unwrap_or(0) |
                            port.default.map(|x| x as i32).unwrap_or(0) |
                            port.lower_bound.map(|_| 1).unwrap_or(0) |
                            port.upper_bound.map(|_| 2).unwrap_or(0),
                        lower_bound: port.lower_bound.unwrap_or(0_f32),
                        upper_bound: port.upper_bound.unwrap_or(0_f32),
                    };
            }

            // implementation_data holds the original rustic descriptor
            desc.implementation_data = mem::transmute(alloc::<super::PluginDescriptor>(1));
            ptr::write(mem::transmute::<_, *mut super::PluginDescriptor>
                    (desc.implementation_data), plugin);

            // attach functions
            desc.instantiate = instantiate;
            desc.connect_port = connect_port;
            desc.run = run;
            desc.cleanup = cleanup;
            //desc.run_adding = run_adding;
            //desc.set_run_adding_gain = set_run_adding_gain;
            desc.activate = activate;
            desc.deactivate = deactivate;

            // store in global descriptor table
            let ptr = mem::transmute(desc);
            *descriptors.unwrap().offset(num_descriptors as isize) = ptr;
            num_descriptors += 1;
            if num_descriptors >= MAX_DESCRIPTORS {
                panic!("The program tried to define more than the max supported number of descriptors currently supported - this usually means you forgot to return None at some point in get_ladspa_descriptor.");
            }

            ptr
        }
        None => ptr::null_mut()
    }
}

// these next two should free everything allocated in ladspa_descriptor - checked with valgrind.
extern "C" fn global_destruct() {
    unsafe {
        if !init_done {
            return;
        }
        for i in 0..num_descriptors {
            free_descriptor(*descriptors.unwrap().offset(i as isize));
        }
        free(descriptors.unwrap());
    }
}

unsafe fn free_descriptor(ptr: *mut ladspa::Descriptor) {
    let desc: &mut ladspa::Descriptor = mem::transmute(ptr);
    free(desc.label);
    free(desc.name);
    free(desc.maker);
    free(desc.copyright);
    free(desc.port_descriptors);
    for i in 0..desc.port_count {
        free(*desc.port_names.offset(i as isize));
    }
    free(desc.port_names);
    free(desc.port_range_hints);
    let rust_desc: *mut super::PluginDescriptor = mem::transmute(desc.implementation_data);
    drop(ptr::read(rust_desc));
    free(desc.implementation_data);
    free(ptr);
}

// The handle that is given to ladspa.
struct Handle<'a> {
    descriptor: &'static super::PluginDescriptor,
    plugin: Box<super::Plugin + 'static>,
    port_map: VecMap<super::PortConnection<'a>>,
    ports: Vec<&'a super::PortConnection<'a>>,
}

extern "C" fn instantiate(descriptor: *const ladspa::Descriptor, sample_rate: u64) -> ladspa::Handle {
    unsafe {
        let desc: &mut ladspa::Descriptor = mem::transmute(descriptor);

        let rust_desc: &super::PluginDescriptor = mem::transmute(desc.implementation_data);
        let rust_plugin = (rust_desc.new)(rust_desc, sample_rate);
        let port_map: VecMap<super::PortConnection> = VecMap::new();
        let ports: Vec<&super::PortConnection> = Vec::new();

        let heap_handle: *mut Handle = alloc::<Handle>(1);
        ptr::write(mem::transmute(&(*heap_handle).descriptor), rust_desc);
        ptr::write(mem::transmute(&(*heap_handle).plugin), rust_plugin);
        ptr::write(mem::transmute(&(*heap_handle).port_map), port_map);
        ptr::write(mem::transmute(&(*heap_handle).ports), ports);
        mem::transmute(heap_handle)
    }
}

extern "C" fn connect_port(instance: ladspa::Handle, port_num: u64, data_location: *mut ladspa::Data) {
    unsafe {
        let handle: &mut Handle = mem::transmute(instance);

        let port = handle.descriptor.ports[port_num as usize];

        // Create appropriate pointers to port data. Mutable locations are wrapped in refcells.
        let data = match port.desc {
            super::PortDescriptor::AudioInput => {
                super::PortData::AudioInput( // Initially create a size 0 slice because we don't know how big
                    slice::from_raw_parts(mem::transmute(data_location), 0)) // it will be yet.
            },
            super::PortDescriptor::AudioOutput => {
                super::PortData::AudioOutput(RefCell::new( // Same here.
                    slice::from_raw_parts_mut(mem::transmute(data_location), 0)))
            },
            super::PortDescriptor::ControlInput => {
                super::PortData::ControlInput(mem::transmute(data_location))
            },
            super::PortDescriptor::ControlOutput => {
                super::PortData::ControlOutput(RefCell::new(mem::transmute(data_location)))
            },
            super::PortDescriptor::Invalid => panic!("Invalid port descriptor!"),
        };

        let conn = super::PortConnection {
            port: port,
            data: data,
        };
        handle.port_map.insert(port_num as usize, conn);

        // Depends on the assumption that ports will be recreated whenever port_map changes
        let handle_ptr: *mut Handle = mem::transmute(instance);
        if handle.port_map.len() == handle.descriptor.ports.len() {
            (*handle_ptr).ports = (0..handle.port_map.len()).map(|i| &handle.port_map[i]).collect();
        }
    }
}

extern "C" fn run(instance: ladspa::Handle, sample_count: u64) {
    unsafe {
        let handle: &mut Handle = mem::transmute(instance);
        for (_, port) in handle.port_map.iter_mut() {
            match port.data {
                super::PortData::AudioOutput(ref mut data) => {
                    let ptr = mem::transmute(data.borrow().as_ptr());
                    *data.borrow_mut() = slice::from_raw_parts_mut(ptr, sample_count as usize);
                },
                super::PortData::AudioInput(ref mut data) => {
                    let ptr = mem::transmute(data.as_ptr());
                    *data = slice::from_raw_parts(ptr, sample_count as usize);
                },
                _ => { }
            }
        }
        handle.plugin.run(sample_count as usize, handle.ports.as_slice());
    }
}

extern "C" fn activate(instance: ladspa::Handle) {
    unsafe{
        let handle: &mut Handle = mem::transmute(instance);
        handle.plugin.activate();
    }
}
extern "C" fn deactivate(instance: ladspa::Handle) {
    unsafe {
        let handle: &mut Handle = mem::transmute(instance);
        handle.plugin.deactivate();
    }
}

/*
extern "C" fn run_adding(instance: ladspa::Handle, sample_count: u64) {
}
extern "C" fn set_run_adding_gain(instance: ladspa::Handle, gain: ladspa::Data) {
}
*/

extern "C" fn cleanup(instance: ladspa::Handle) {
    unsafe {
        let handle: *mut Handle = mem::transmute(instance);
        drop(ptr::read(handle));
        free(instance);
    }
}

<commit_msg>Properly assign null pointers to run_adding related functions (not yet supported).<commit_after>use std::{mem, ptr};
use libc::{self, c_char};
use std::slice;
use std::cell::RefCell;
use std::collections::VecMap;

use super::get_ladspa_descriptor;

// essentially ladspa.h API translated to rust.
mod ladspa {
    use libc::{c_void, c_char};

    pub type Data = f32;
    pub type Properties = i32;
    pub type PortDescriptor = i32;
    pub type PortRangeHintDescriptor = i32;

    pub type Handle = *mut c_void;

    #[repr(C)]
    #[derive(Copy)]
    pub struct PortRangeHint {
        pub hint_descriptor: PortRangeHintDescriptor,
        pub lower_bound: Data,
        pub upper_bound: Data,
    }

    #[repr(C)]
    #[allow(missing_copy_implementations)] // Remove this for a fun warning/suggestion cycle!
    pub struct Descriptor {
        pub unique_id: u64,
        pub label: *const c_char,
        pub properties: Properties,
        pub name: *const c_char,
        pub maker: *const c_char,
        pub copyright: *const c_char,
        pub port_count: u64,
        pub port_descriptors: *mut PortDescriptor,
        pub port_names: *mut *const c_char,
        pub port_range_hints: *mut PortRangeHint,
        pub implementation_data: *mut c_void,
        pub instantiate: extern "C" fn(descriptor: *const Descriptor, sample_rate: u64) -> Handle,
        pub connect_port: extern "C" fn(instance: Handle, port: u64, data_location: *mut Data),
        pub activate: extern "C" fn(instance: Handle),
        pub run: extern "C" fn(instance: Handle, sample_count: u64),
        pub run_adding: extern "C" fn(instance: Handle, sample_count: u64),
        pub set_run_adding_gain: extern "C" fn(instance: Handle, gain: Data),
        pub deactivate: extern "C" fn(instance: Handle),
        pub cleanup: extern "C" fn(instance: Handle),
    }
}

unsafe fn alloc<T>(num: u64) -> *mut T {
    let ptr: *mut T = mem::transmute(libc::malloc(num * mem::size_of::<T>() as u64));
    if ptr == ptr::null_mut() {
        panic!("malloc returned null!");
    }
    ptr
}

unsafe fn free<T>(x: *const T) {
    libc::free(mem::transmute(x));
}

unsafe fn make_c_str(s: &'static str) -> *const c_char {
    let c_str: *mut c_char = alloc::<c_char>(s.len() as u64 + 1);
    ptr::copy_memory(c_str, mem::transmute(s.as_ptr()), s.len());
    slice::from_raw_parts_mut(c_str, s.len() + 1)[s.len()] = 0; // add the null terminator
    c_str
}

static mut init_done: bool = false;
static mut num_descriptors: u64 = 0;
static mut descriptors: Option<*mut *mut ladspa::Descriptor> = None;
static MAX_DESCRIPTORS: u64 = 32;

// It seems that ladspa_descriptor is deleted during link time optimization unless we
// call it from somewhere.
#[allow(dead_code)]
unsafe fn _lto_workaround() {
    ladspa_descriptor(0);
}

#[no_mangle]
/// This is exported so that your plugin is recognised by LADSPA hosts. Don't worry about it.
pub unsafe extern "C" fn ladspa_descriptor(index: u64) -> *mut ladspa::Descriptor {
    if !init_done {
        libc::atexit(global_destruct);
        descriptors = Some(alloc(MAX_DESCRIPTORS));
        init_done = true;
    }

    // If it's already been generated, return the cached copy.
    if index < num_descriptors {
        return *descriptors.unwrap().offset(index as isize);
    }

    match get_ladspa_descriptor(index) {
        Some(plugin) => {
            let desc: &mut ladspa::Descriptor = mem::transmute(alloc::<ladspa::Descriptor>(1));

            // the following is ok because none of the fields in the descriptor have
            // destructors. if they did rust would try to drop null pointers on write.

            // copy data fields
            desc.unique_id = plugin.unique_id;
            desc.label = make_c_str(plugin.label);
            desc.properties = plugin.properties.bits();
            desc.name = make_c_str(plugin.name);
            desc.maker = make_c_str(plugin.maker);
            desc.copyright = make_c_str(plugin.copyright);
            desc.port_count = plugin.ports.len() as u64;
            desc.port_descriptors = alloc::<ladspa::PortDescriptor>(desc.port_count);
            desc.port_names = alloc::<*const c_char>(desc.port_count);
            desc.port_range_hints = alloc::<ladspa::PortRangeHint>(desc.port_count);
            for i in 0..desc.port_count as usize {
                *desc.port_descriptors.offset(i as isize)
                    = plugin.ports[i].desc as i32;

                *desc.port_names.offset(i as isize)
                    = make_c_str(plugin.ports[i].name);

                let port = &plugin.ports[i];
                *desc.port_range_hints.offset(i as isize)
                    = ladspa::PortRangeHint {
                        hint_descriptor: port.hint.map(|x| x.bits()).unwrap_or(0) |
                            port.default.map(|x| x as i32).unwrap_or(0) |
                            port.lower_bound.map(|_| 1).unwrap_or(0) |
                            port.upper_bound.map(|_| 2).unwrap_or(0),
                        lower_bound: port.lower_bound.unwrap_or(0_f32),
                        upper_bound: port.upper_bound.unwrap_or(0_f32),
                    };
            }

            // implementation_data holds the original rustic descriptor
            desc.implementation_data = mem::transmute(alloc::<super::PluginDescriptor>(1));
            ptr::write(mem::transmute::<_, *mut super::PluginDescriptor>
                    (desc.implementation_data), plugin);

            // attach functions
            desc.instantiate = instantiate;
            desc.connect_port = connect_port;
            desc.run = run;
            desc.cleanup = cleanup;
            // u8 is arbitrary, just need some type here
            desc.run_adding = mem::transmute(ptr::null::<*const u8>());
            desc.set_run_adding_gain = mem::transmute(ptr::null::<*const u8>());
            desc.activate = activate;
            desc.deactivate = deactivate;

            // store in global descriptor table
            let ptr = mem::transmute(desc);
            *descriptors.unwrap().offset(num_descriptors as isize) = ptr;
            num_descriptors += 1;
            if num_descriptors >= MAX_DESCRIPTORS {
                panic!("The program tried to define more than the max supported number of descriptors currently supported - this usually means you forgot to return None at some point in get_ladspa_descriptor.");
            }

            ptr
        }
        None => ptr::null_mut()
    }
}

// these next two should free everything allocated in ladspa_descriptor - checked with valgrind.
extern "C" fn global_destruct() {
    unsafe {
        if !init_done {
            return;
        }
        for i in 0..num_descriptors {
            free_descriptor(*descriptors.unwrap().offset(i as isize));
        }
        free(descriptors.unwrap());
    }
}

unsafe fn free_descriptor(ptr: *mut ladspa::Descriptor) {
    let desc: &mut ladspa::Descriptor = mem::transmute(ptr);
    free(desc.label);
    free(desc.name);
    free(desc.maker);
    free(desc.copyright);
    free(desc.port_descriptors);
    for i in 0..desc.port_count {
        free(*desc.port_names.offset(i as isize));
    }
    free(desc.port_names);
    free(desc.port_range_hints);
    let rust_desc: *mut super::PluginDescriptor = mem::transmute(desc.implementation_data);
    drop(ptr::read(rust_desc));
    free(desc.implementation_data);
    free(ptr);
}

// The handle that is given to ladspa.
struct Handle<'a> {
    descriptor: &'static super::PluginDescriptor,
    plugin: Box<super::Plugin + 'static>,
    port_map: VecMap<super::PortConnection<'a>>,
    ports: Vec<&'a super::PortConnection<'a>>,
}

extern "C" fn instantiate(descriptor: *const ladspa::Descriptor, sample_rate: u64) -> ladspa::Handle {
    unsafe {
        let desc: &mut ladspa::Descriptor = mem::transmute(descriptor);

        let rust_desc: &super::PluginDescriptor = mem::transmute(desc.implementation_data);
        let rust_plugin = (rust_desc.new)(rust_desc, sample_rate);
        let port_map: VecMap<super::PortConnection> = VecMap::new();
        let ports: Vec<&super::PortConnection> = Vec::new();

        let heap_handle: *mut Handle = alloc::<Handle>(1);
        ptr::write(mem::transmute(&(*heap_handle).descriptor), rust_desc);
        ptr::write(mem::transmute(&(*heap_handle).plugin), rust_plugin);
        ptr::write(mem::transmute(&(*heap_handle).port_map), port_map);
        ptr::write(mem::transmute(&(*heap_handle).ports), ports);
        mem::transmute(heap_handle)
    }
}

extern "C" fn connect_port(instance: ladspa::Handle, port_num: u64, data_location: *mut ladspa::Data) {
    unsafe {
        let handle: &mut Handle = mem::transmute(instance);

        let port = handle.descriptor.ports[port_num as usize];

        // Create appropriate pointers to port data. Mutable locations are wrapped in refcells.
        let data = match port.desc {
            super::PortDescriptor::AudioInput => {
                super::PortData::AudioInput( // Initially create a size 0 slice because we don't know how big
                    slice::from_raw_parts(mem::transmute(data_location), 0)) // it will be yet.
            },
            super::PortDescriptor::AudioOutput => {
                super::PortData::AudioOutput(RefCell::new( // Same here.
                    slice::from_raw_parts_mut(mem::transmute(data_location), 0)))
            },
            super::PortDescriptor::ControlInput => {
                super::PortData::ControlInput(mem::transmute(data_location))
            },
            super::PortDescriptor::ControlOutput => {
                super::PortData::ControlOutput(RefCell::new(mem::transmute(data_location)))
            },
            super::PortDescriptor::Invalid => panic!("Invalid port descriptor!"),
        };

        let conn = super::PortConnection {
            port: port,
            data: data,
        };
        handle.port_map.insert(port_num as usize, conn);

        // Depends on the assumption that ports will be recreated whenever port_map changes
        let handle_ptr: *mut Handle = mem::transmute(instance);
        if handle.port_map.len() == handle.descriptor.ports.len() {
            (*handle_ptr).ports = (0..handle.port_map.len()).map(|i| &handle.port_map[i]).collect();
        }
    }
}

extern "C" fn run(instance: ladspa::Handle, sample_count: u64) {
    unsafe {
        let handle: &mut Handle = mem::transmute(instance);
        for (_, port) in handle.port_map.iter_mut() {
            match port.data {
                super::PortData::AudioOutput(ref mut data) => {
                    let ptr = mem::transmute(data.borrow().as_ptr());
                    *data.borrow_mut() = slice::from_raw_parts_mut(ptr, sample_count as usize);
                },
                super::PortData::AudioInput(ref mut data) => {
                    let ptr = mem::transmute(data.as_ptr());
                    *data = slice::from_raw_parts(ptr, sample_count as usize);
                },
                _ => { }
            }
        }
        handle.plugin.run(sample_count as usize, handle.ports.as_slice());
    }
}

extern "C" fn activate(instance: ladspa::Handle) {
    unsafe{
        let handle: &mut Handle = mem::transmute(instance);
        handle.plugin.activate();
    }
}
extern "C" fn deactivate(instance: ladspa::Handle) {
    unsafe {
        let handle: &mut Handle = mem::transmute(instance);
        handle.plugin.deactivate();
    }
}

/*
extern "C" fn run_adding(instance: ladspa::Handle, sample_count: u64) {
}
extern "C" fn set_run_adding_gain(instance: ladspa::Handle, gain: ladspa::Data) {
}
*/

extern "C" fn cleanup(instance: ladspa::Handle) {
    unsafe {
        let handle: *mut Handle = mem::transmute(instance);
        drop(ptr::read(handle));
        free(instance);
    }
}

<|endoftext|>