<commit_before>//! Exported verifier functions

use std::env;
use std::str;
use std::str::FromStr;
use std::sync::Arc;

use clap::{AppSettings, ArgMatches, ErrorKind};
use log::{debug, LevelFilter};
use simplelog::{Config, TerminalMode, TermLogger};

use pact_matching::models::http_utils::HttpAuth;
use pact_matching::s;
use pact_models::PactSpecification;
use pact_verifier::*;
use pact_verifier::callback_executors::HttpRequestProviderStateExecutor;

use super::args;

fn pact_source(matches: &ArgMatches) -> Vec<PactSource> {
  let mut sources = vec![];
  if let Some(values) = matches.values_of("file") {
    sources.extend(values.map(|v| PactSource::File(s!(v))).collect::<Vec<PactSource>>());
  };
  if let Some(values) = matches.values_of("dir") {
    sources.extend(values.map(|v| PactSource::Dir(s!(v))).collect::<Vec<PactSource>>());
  };
  if let Some(values) = matches.values_of("url") {
    sources.extend(values.map(|v| {
      if matches.is_present("user") {
        PactSource::URL(s!(v), matches.value_of("user").map(|user| {
          HttpAuth::User(user.to_string(), matches.value_of("password").map(|p| p.to_string()))
        }))
      } else if matches.is_present("token") {
        PactSource::URL(s!(v), matches.value_of("token").map(|token| HttpAuth::Token(token.to_string())))
      } else {
        PactSource::URL(s!(v), None)
      }
    }).collect::<Vec<PactSource>>());
  };
  if let Some(values) = matches.values_of("broker-url") {
    sources.extend(values.map(|v| {
      if matches.is_present("user") || matches.is_present("token") {
        let name = matches.value_of("provider-name").unwrap().to_string();
        let pending = matches.is_present("enable-pending");
        let wip = matches.value_of("include-wip-pacts-since").map(|wip| wip.to_string());
        let provider_tags = matches.values_of("provider-tags")
        .map_or_else(Vec::new, |tags| tags.map(|tag| tag.to_string()).collect());

        let selectors = if matches.is_present("consumer-version-selectors") {
          matches.values_of("consumer-version-selectors")
            .map_or_else(Vec::new, |s| json_to_selectors(s.collect::<Vec<_>>()));
        } else if matches.is_present("consumer-version-tags") {
          matches.values_of("consumer-version-tags")
            .map_or_else(Vec::new, |tags| consumer_tags_to_selectors(tags.collect::<Vec<_>>()));
        } else {
          vec![]
        };

        if matches.is_present("token") {
          PactSource::BrokerWithDynamicConfiguration {
            provider_name: name,
            broker_url: v.into(),
            enable_pending: pending,
            include_wip_pacts_since: wip,
            provider_tags,
            selectors: selectors,
            auth: matches.value_of("token").map(|token| HttpAuth::Token(token.to_string())),
            links: vec![]
          }
        } else {
        let auth = matches.value_of("user").map(|user| {
          HttpAuth::User(user.to_string(), matches.value_of("password").map(|p| p.to_string()))
        });
          PactSource::BrokerWithDynamicConfiguration {
            provider_name: name,
            broker_url: v.into(),
            enable_pending: pending,
            include_wip_pacts_since: wip,
            provider_tags,
            selectors: selectors,
            auth,
            links: vec![]
          }
        }
      } else {
        PactSource::BrokerUrl(s!(matches.value_of("provider-name").unwrap()), s!(v), None, vec![])
      }
    }).collect::<Vec<PactSource>>());
  };
  sources
}

fn consumer_tags_to_selectors(tags: Vec<&str>) -> Vec<pact_verifier::ConsumerVersionSelector> {
  tags.iter().map(|t| {
    pact_verifier::ConsumerVersionSelector {
      consumer: None,
      fallback_tag: None,
      tag: t.to_string(),
      latest: Some(true),
    }
  }).collect()
}

fn json_to_selectors(tags: Vec<&str>) -> Vec<pact_verifier::ConsumerVersionSelector> {
  tags.iter().map(|t| serde_json::from_str(t))
  .flatten()
  .collect()
}

fn interaction_filter(matches: &ArgMatches) -> FilterInfo {
  if matches.is_present("filter-description") &&
      (matches.is_present("filter-state") || matches.is_present("filter-no-state")) {
      if matches.is_present("filter-state") {
          FilterInfo::DescriptionAndState(s!(matches.value_of("filter-description").unwrap()),
              s!(matches.value_of("filter-state").unwrap()))
      } else {
          FilterInfo::DescriptionAndState(s!(matches.value_of("filter-description").unwrap()),
              s!(""))
      }
  } else if matches.is_present("filter-description") {
      FilterInfo::Description(s!(matches.value_of("filter-description").unwrap()))
  } else if matches.is_present("filter-state") {
      FilterInfo::State(s!(matches.value_of("filter-state").unwrap()))
  } else if matches.is_present("filter-no-state") {
      FilterInfo::State(s!(""))
  } else {
      FilterInfo::None
  }
}

/// Handles the command line arguments from the running process
pub async fn handle_cli() -> Result<(), i32> {
  let args: Vec<String> = env::args().collect();
  let program = args[0].clone();
  let version = format!("v{}", clap::crate_version!()).as_str().to_owned();
  let app = args::setup_app(program, &version);
  let matches = app
                  .setting(AppSettings::ArgRequiredElseHelp)
                  .setting(AppSettings::ColoredHelp)
                  .get_matches_safe();

  match matches {
    Ok(results) => handle_matches(&results).await,
    Err(ref err) => {
      match err.kind {
          ErrorKind::HelpDisplayed => {
              println!("{}", err.message);
              Ok(())
          },
          ErrorKind::VersionDisplayed => {
              print_version();
              println!();
              Ok(())
          },
          _ => {
              err.exit()
          }
      }
    }
  }
}

// TODO: it's possible to introspect the clap::Error and return it or wrapped error type
// so that the caller could have more control over the error output.
//
// Currently, clap prints things out as if it were a CLI call
#[allow(dead_code, missing_docs)]
pub async fn handle_args(args: Vec<String>) -> Result<(), i32> {
  let program = "pact_verifier_cli".to_string();
  let version = format!("v{}", clap::crate_version!()).as_str().to_owned();
  let app = args::setup_app(program, &version);
  let matches = app
                  .setting(AppSettings::NoBinaryName)
                  .setting(AppSettings::ColorNever)
                  .get_matches_from_safe(args);

  match matches {
    Ok(results) => handle_matches(&results).await,
    Err(ref err) => {
      log::error!("error verifying Pact: {:?} {:?}", err.message, err);
      Err(1)
    }
  }
}

async fn handle_matches(matches: &clap::ArgMatches<'_>) -> Result<(), i32> {
    let level = matches.value_of("loglevel").unwrap_or("warn");
    let log_level = match level {
        "none" => LevelFilter::Off,
        _ => LevelFilter::from_str(level).unwrap()
    };
    TermLogger::init(log_level, Config::default(), TerminalMode::Mixed).unwrap_or_default();
    let provider = ProviderInfo {
      host: s!(matches.value_of("hostname").unwrap_or("localhost")),
      port: matches.value_of("port").map(|port| port.parse::<u16>().unwrap()),
      path: matches.value_of("base-path").unwrap_or("/").into(),
      protocol: s!(matches.value_of("scheme").unwrap_or("http")),
      .. ProviderInfo::default()
    };
    let source = pact_source(matches);
    let filter = interaction_filter(matches);
    let provider_state_executor = Arc::new(HttpRequestProviderStateExecutor {
      state_change_url: matches.value_of("state-change-url").map(|s| s.to_string()),
      state_change_body: !matches.is_present("state-change-as-query"),
      state_change_teardown: matches.is_present("state-change-teardown")
    });

    let options = VerificationOptions {
      publish: matches.is_present("publish"),
      provider_version: matches.value_of("provider-version").map(|v| v.to_string()),
      build_url: matches.value_of("build-url").map(|v| v.to_string()),
      request_filter: None::<Arc<NullRequestFilterExecutor>>,
      provider_tags: matches.values_of("provider-tags").map_or_else(Vec::new, |tags| tags.map(|tag| tag.to_string()).collect()),
      disable_ssl_verification: matches.is_present("disable-ssl-verification"),
      request_timeout: matches.value_of("request-timeout").map(|t| t.parse::<u64>().unwrap_or(5000)).unwrap_or(5000),
      .. VerificationOptions::default()
    };

    for s in &source {
      debug!("Pact source to verify = {}", s);
    };

    if verify_provider_async(
        provider,
        source,
        filter,
        matches.values_of_lossy("filter-consumer").unwrap_or_default(),
        options,
        &provider_state_executor
    ).await {
        Ok(())
    } else {
        Err(1)
    }
}

fn print_version() {
  println!("\npact verifier version     : v{}", clap::crate_version!());
  println!("pact specification version: v{}", PactSpecification::V3.version_str());
}
<commit_msg>fix: consumer version selectors<commit_after>//! Exported verifier functions

use std::env;
use std::str;
use std::str::FromStr;
use std::sync::Arc;

use clap::{AppSettings, ArgMatches, ErrorKind};
use log::{debug, LevelFilter};
use simplelog::{Config, TerminalMode, TermLogger};

use pact_matching::models::http_utils::HttpAuth;
use pact_matching::s;
use pact_models::PactSpecification;
use pact_verifier::*;
use pact_verifier::callback_executors::HttpRequestProviderStateExecutor;

use super::args;

fn pact_source(matches: &ArgMatches) -> Vec<PactSource> {
  let mut sources = vec![];
  if let Some(values) = matches.values_of("file") {
    sources.extend(values.map(|v| PactSource::File(s!(v))).collect::<Vec<PactSource>>());
  };
  if let Some(values) = matches.values_of("dir") {
    sources.extend(values.map(|v| PactSource::Dir(s!(v))).collect::<Vec<PactSource>>());
  };
  if let Some(values) = matches.values_of("url") {
    sources.extend(values.map(|v| {
      if matches.is_present("user") {
        PactSource::URL(s!(v), matches.value_of("user").map(|user| {
          HttpAuth::User(user.to_string(), matches.value_of("password").map(|p| p.to_string()))
        }))
      } else if matches.is_present("token") {
        PactSource::URL(s!(v), matches.value_of("token").map(|token| HttpAuth::Token(token.to_string())))
      } else {
        PactSource::URL(s!(v), None)
      }
    }).collect::<Vec<PactSource>>());
  };
  if let Some(values) = matches.values_of("broker-url") {
    sources.extend(values.map(|v| {
      if matches.is_present("user") || matches.is_present("token") {
        let name = matches.value_of("provider-name").unwrap().to_string();
        let pending = matches.is_present("enable-pending");
        let wip = matches.value_of("include-wip-pacts-since").map(|wip| wip.to_string());
        let provider_tags = matches.values_of("provider-tags")
        .map_or_else(Vec::new, |tags| tags.map(|tag| tag.to_string()).collect());

        let selectors = if matches.is_present("consumer-version-selectors") {
          matches.values_of("consumer-version-selectors")
            .map_or_else(Vec::new, |s| json_to_selectors(s.collect::<Vec<_>>()))
        } else if matches.is_present("consumer-version-tags") {
          matches.values_of("consumer-version-tags")
            .map_or_else(Vec::new, |tags| consumer_tags_to_selectors(tags.collect::<Vec<_>>()))
        } else {
          vec![]
        };

        if matches.is_present("token") {
          PactSource::BrokerWithDynamicConfiguration {
            provider_name: name,
            broker_url: v.into(),
            enable_pending: pending,
            include_wip_pacts_since: wip,
            provider_tags,
            selectors: selectors,
            auth: matches.value_of("token").map(|token| HttpAuth::Token(token.to_string())),
            links: vec![]
          }
        } else {
        let auth = matches.value_of("user").map(|user| {
          HttpAuth::User(user.to_string(), matches.value_of("password").map(|p| p.to_string()))
        });
          PactSource::BrokerWithDynamicConfiguration {
            provider_name: name,
            broker_url: v.into(),
            enable_pending: pending,
            include_wip_pacts_since: wip,
            provider_tags,
            selectors: selectors,
            auth,
            links: vec![]
          }
        }
      } else {
        PactSource::BrokerUrl(s!(matches.value_of("provider-name").unwrap()), s!(v), None, vec![])
      }
    }).collect::<Vec<PactSource>>());
  };
  sources
}

fn consumer_tags_to_selectors(tags: Vec<&str>) -> Vec<pact_verifier::ConsumerVersionSelector> {
  tags.iter().map(|t| {
    pact_verifier::ConsumerVersionSelector {
      consumer: None,
      fallback_tag: None,
      tag: t.to_string(),
      latest: Some(true),
    }
  }).collect()
}

fn json_to_selectors(tags: Vec<&str>) -> Vec<pact_verifier::ConsumerVersionSelector> {
  tags.iter().map(|t| serde_json::from_str(t))
  .flatten()
  .collect()
}

fn interaction_filter(matches: &ArgMatches) -> FilterInfo {
  if matches.is_present("filter-description") &&
      (matches.is_present("filter-state") || matches.is_present("filter-no-state")) {
      if matches.is_present("filter-state") {
          FilterInfo::DescriptionAndState(s!(matches.value_of("filter-description").unwrap()),
              s!(matches.value_of("filter-state").unwrap()))
      } else {
          FilterInfo::DescriptionAndState(s!(matches.value_of("filter-description").unwrap()),
              s!(""))
      }
  } else if matches.is_present("filter-description") {
      FilterInfo::Description(s!(matches.value_of("filter-description").unwrap()))
  } else if matches.is_present("filter-state") {
      FilterInfo::State(s!(matches.value_of("filter-state").unwrap()))
  } else if matches.is_present("filter-no-state") {
      FilterInfo::State(s!(""))
  } else {
      FilterInfo::None
  }
}

/// Handles the command line arguments from the running process
pub async fn handle_cli() -> Result<(), i32> {
  let args: Vec<String> = env::args().collect();
  let program = args[0].clone();
  let version = format!("v{}", clap::crate_version!()).as_str().to_owned();
  let app = args::setup_app(program, &version);
  let matches = app
                  .setting(AppSettings::ArgRequiredElseHelp)
                  .setting(AppSettings::ColoredHelp)
                  .get_matches_safe();

  match matches {
    Ok(results) => handle_matches(&results).await,
    Err(ref err) => {
      match err.kind {
          ErrorKind::HelpDisplayed => {
              println!("{}", err.message);
              Ok(())
          },
          ErrorKind::VersionDisplayed => {
              print_version();
              println!();
              Ok(())
          },
          _ => {
              err.exit()
          }
      }
    }
  }
}

// TODO: it's possible to introspect the clap::Error and return it or wrapped error type
// so that the caller could have more control over the error output.
//
// Currently, clap prints things out as if it were a CLI call
#[allow(dead_code, missing_docs)]
pub async fn handle_args(args: Vec<String>) -> Result<(), i32> {
  let program = "pact_verifier_cli".to_string();
  let version = format!("v{}", clap::crate_version!()).as_str().to_owned();
  let app = args::setup_app(program, &version);
  let matches = app
                  .setting(AppSettings::NoBinaryName)
                  .setting(AppSettings::ColorNever)
                  .get_matches_from_safe(args);

  match matches {
    Ok(results) => handle_matches(&results).await,
    Err(ref err) => {
      log::error!("error verifying Pact: {:?} {:?}", err.message, err);
      Err(1)
    }
  }
}

async fn handle_matches(matches: &clap::ArgMatches<'_>) -> Result<(), i32> {
    let level = matches.value_of("loglevel").unwrap_or("warn");
    let log_level = match level {
        "none" => LevelFilter::Off,
        _ => LevelFilter::from_str(level).unwrap()
    };
    TermLogger::init(log_level, Config::default(), TerminalMode::Mixed).unwrap_or_default();
    let provider = ProviderInfo {
      host: s!(matches.value_of("hostname").unwrap_or("localhost")),
      port: matches.value_of("port").map(|port| port.parse::<u16>().unwrap()),
      path: matches.value_of("base-path").unwrap_or("/").into(),
      protocol: s!(matches.value_of("scheme").unwrap_or("http")),
      .. ProviderInfo::default()
    };
    let source = pact_source(matches);
    let filter = interaction_filter(matches);
    let provider_state_executor = Arc::new(HttpRequestProviderStateExecutor {
      state_change_url: matches.value_of("state-change-url").map(|s| s.to_string()),
      state_change_body: !matches.is_present("state-change-as-query"),
      state_change_teardown: matches.is_present("state-change-teardown")
    });

    let options = VerificationOptions {
      publish: matches.is_present("publish"),
      provider_version: matches.value_of("provider-version").map(|v| v.to_string()),
      build_url: matches.value_of("build-url").map(|v| v.to_string()),
      request_filter: None::<Arc<NullRequestFilterExecutor>>,
      provider_tags: matches.values_of("provider-tags").map_or_else(Vec::new, |tags| tags.map(|tag| tag.to_string()).collect()),
      disable_ssl_verification: matches.is_present("disable-ssl-verification"),
      request_timeout: matches.value_of("request-timeout").map(|t| t.parse::<u64>().unwrap_or(5000)).unwrap_or(5000),
      .. VerificationOptions::default()
    };

    for s in &source {
      debug!("Pact source to verify = {}", s);
    };

    if verify_provider_async(
        provider,
        source,
        filter,
        matches.values_of_lossy("filter-consumer").unwrap_or_default(),
        options,
        &provider_state_executor
    ).await {
        Ok(())
    } else {
        Err(1)
    }
}

fn print_version() {
  println!("\npact verifier version     : v{}", clap::crate_version!());
  println!("pact specification version: v{}", PactSpecification::V3.version_str());
}
<|endoftext|>


<commit_before>use rocket::{self, Rocket, State };
use rocket::logger::LoggingLevel;
use rocket_contrib::JSON;
use rocket::response::{Response, Responder};
use rocket::request::{self, FromRequest, Request};
use rocket::Outcome;
use rocket::http::{Status, Cookie, Cookies};
use rocket::config::{Environment, Config};
use adapters::json;
use entities::*;
use business::db::Db;
use business::error::{Error, RepoError, ParameterError};
use infrastructure::error::AppError;
use serde_json::ser::to_string;
use business::sort::SortByAverageRating;
use business::{usecase, filter, geo};
use business::filter::InBBox;
use business::duplicates::{self, DuplicateType};
use std::{result,thread,env};
use r2d2::{self, Pool};
use regex::Regex;
use super::mail;
use super::cfg;

static MAX_INVISIBLE_RESULTS : usize = 5;
static COOKIE_USER_KEY       : &str  = "user_id";

lazy_static! {
    static ref CONFIG: cfg::Config = {
        match env::current_dir() {
            Ok(cwd) => {
                let path = cwd.as_path().join("config.toml");
                match cfg::Config::load(path) {
                    Ok(cfg) => cfg,
                    Err(e) => {
                        warn!("could not read configuration file 'config.toml': {}", e);
                        cfg::Config::default()
                    }
                }
            }
            Err(e) => {
                warn!("could not determine current working directory: {}", e);
                cfg::Config::default()
            }
        }
    };
}

mod neo4j;
#[cfg(test)]
mod mockdb;
#[cfg(test)]
mod tests;

#[cfg(not(test))]
type DbPool = neo4j::ConnectionPool;
#[cfg(test)]
type DbPool = mockdb::ConnectionPool;

type Result<T> = result::Result<JSON<T>, AppError>;

fn extract_ids(s: &str) -> Vec<String> {
    s.split(',')
        .map(|x| x.to_owned())
        .filter(|id| id != "")
        .collect::<Vec<String>>()
}

#[derive(Debug)]
struct User(String);

impl<'a, 'r> FromRequest<'a, 'r> for User {
    type Error = ();

    fn from_request(request: &'a Request<'r>) -> request::Outcome<User, ()> {
        let user = request.cookies()
            .get_private(COOKIE_USER_KEY)
            .and_then(|cookie| cookie.value().parse().ok())
            .map(|id| User(id));
        match user {
            Some(user) => Outcome::Success(user),
            None => Outcome::Failure((Status::Unauthorized, ()))
        }
    }
}

fn notify(subject: &str, body: &str) {
    match mail::create(&CONFIG.notification.send_to, subject, body) {
        Ok(mail) => {
            thread::spawn(move ||{
                if let Err(err) = mail::send(&mail) {
                    warn!("Could not send mail: {}", err);
                }
            });
        }
        Err(e) => {
            warn!("could not create notifiction mail: {}", e);
        }
    }
}

#[get("/entries/<ids>")]
fn get_entry(db: State<DbPool>, ids: String) -> Result<Vec<json::Entry>> {
    let ids = extract_ids(&ids);
    let entries = usecase::get_entries(&*db.get()?, &ids)?;
    let tags = usecase::get_tags_by_entry_ids(&*db.get()?, &ids)?;
    let ratings = usecase::get_ratings_by_entry_ids(&*db.get()?, &ids)?;
    Ok(JSON(entries
        .into_iter()
        .map(|e|{
            let t = tags.get(&e.id).cloned().unwrap_or_else(|| vec![]);
            let r = ratings.get(&e.id).cloned().unwrap_or_else(|| vec![]);
            json::Entry::from_entry_with_tags_and_ratings(e,t,r)
        })
        .collect::<Vec<json::Entry>>()))
}

#[get("/duplicates")]
fn get_duplicates(db: State<DbPool>) -> Result<Vec<(String, String, DuplicateType)>> {
    let entries = db.get()?.all_entries()?;
    let ids = duplicates::find_duplicates(&entries);
    Ok(JSON(ids))
}

#[post("/entries", format = "application/json", data = "<e>")]
fn post_entry(db: State<DbPool>, e: JSON<usecase::NewEntry>) -> result::Result<String, AppError> {
    let e = e.into_inner();
    let id = usecase::create_new_entry(&mut *db.get()?, e.clone())?;
    notify(&format!("Neuer Eintrag: {}", e.title),&format!("{:?}",e));
    Ok(id)
}

#[put("/entries/<id>", format = "application/json", data = "<e>")]
fn put_entry(db: State<DbPool>, id: String, e: JSON<usecase::UpdateEntry>) -> Result<String> {
    let e = e.into_inner();
    usecase::update_entry(&mut *db.get()?, e.clone())?;
    notify(&format!("Veränderter Eintrag: {}", e.title),&format!("{:?}",e));
    Ok(JSON(id))
}

#[get("/tags")]
fn get_tags(db: State<DbPool>) -> Result<Vec<String>> {
    let tags = usecase::get_tag_ids(&*db.get()?)?;
    Ok(JSON(tags))
}

#[get("/categories")]
fn get_categories(db: State<DbPool>) -> Result<Vec<Category>> {
    let categories = db.get()?.all_categories()?;
    Ok(JSON(categories))
}

#[get("/categories/<id>")]
fn get_category(db: State<DbPool>, id: String) -> Result<String> {
    let ids = extract_ids(&id);
    let categories = db.get()?.all_categories()?;
    let res = match ids.len() {
        0 => to_string(&categories),

        1 => {
            let id = ids[0].clone();
            let e = categories
                .into_iter()
                .find(|x| x.id == id)
                .ok_or(RepoError::NotFound)?;
            to_string(&e)
        }
        _ => {
            to_string(&categories.into_iter()
                .filter(|e| ids.iter().any(|id| e.id == id.clone()))
                .collect::<Vec<Category>>())
        }
    }?;
    Ok(JSON(res))
}

#[derive(FromForm)]
struct SearchQuery {
    bbox: String,
    categories: Option<String>,
    text: Option<String>,
    tags: Option<String>,
}

lazy_static! {
    static ref HASH_TAG_REGEX: Regex = Regex::new(r"#(?P<tag>\w+((-\w+)*)?)").unwrap();
}

fn extract_hash_tags(text: &str) -> Vec<String> {
    let mut res: Vec<String> = vec![];
    for cap in HASH_TAG_REGEX.captures_iter(text) {
        res.push(cap["tag"].into());
    }
    res
}

fn remove_hash_tags(text: &str) -> String {
    HASH_TAG_REGEX
        .replace_all(text, "")
        .into_owned()
        .replace("  ", " ")
        .trim()
        .into()
}

#[get("/search?<search>")]
fn get_search(db: State<DbPool>, search: SearchQuery) -> Result<json::SearchResult> {

    let entries = db.get()?.all_entries()?;

    let bbox = geo::extract_bbox(&search.bbox)
        .map_err(Error::Parameter)
        .map_err(AppError::Business)?;
    let bbox_center = geo::center(&bbox[0], &bbox[1]);

    let mut entries: Vec<&Entry> = entries.iter().collect();

    if let Some(cat_str) = search.categories {
        let cat_ids = extract_ids(&cat_str);
        entries = entries
            .into_iter()
            .filter(&*filter::entries_by_category_ids(&cat_ids))
            .collect();
    }

    let mut tags = vec![];

    if let Some(ref txt) = search.text {
        tags = extract_hash_tags(txt);
    }

    if let Some(tags_str) = search.tags {
        for t in extract_ids(&tags_str) {
            tags.push(t);
        }
    }

    // search tags even without preceding #:
    if let Some(ref txt) = search.text {
        for t in to_words(txt){
            tags.push(t);
        }
    }

    let triples = db.get()?.all_triples()?;

    let text = match search.text {
        Some(txt) => remove_hash_tags(&txt),
        None => "".into()
    };

    let entries : Vec<&Entry> = entries
        .into_iter()
        .filter(&*filter::entries_by_tags_or_search_text(&text, &tags, &triples))
        .collect();

    let mut entries : Vec<Entry> = entries.into_iter().cloned().collect();

    let all_ratings = db.get()?.all_ratings()?;

    entries.sort_by_avg_rating(&all_ratings, &triples);

    let visible_results: Vec<_> = entries
        .iter()
        .filter(|x| x.in_bbox(&bbox))
        .map(|x| &x.id)
        .cloned()
        .collect();

    let invisible_results = entries
        .iter()
        .filter(|e| !visible_results.iter().any(|v| *v == e.id))
        .take(MAX_INVISIBLE_RESULTS)
        .map(|x| &x.id)
        .cloned()
        .collect::<Vec<_>>();

    Ok(JSON(json::SearchResult {
        visible: visible_results,
        invisible: invisible_results,
    }))
}

fn to_words(txt: &str) -> Vec<String> {
    txt.to_lowercase().split(' ').map(|x| x.to_string()).collect()
}

#[post("/login", format = "application/json", data = "<login>")]
fn login(db: State<DbPool>, mut cookies: Cookies, login: JSON<usecase::Login>) -> Result<()> {
    let id = usecase::login(&mut*db.get()?, login.into_inner())?;
    cookies.add_private(Cookie::new(COOKIE_USER_KEY, id));
    Ok(JSON(()))
}

#[post("/logout", format = "application/json")]
fn logout(mut cookies: Cookies) -> Result<()> {
    cookies.remove_private(Cookie::named(COOKIE_USER_KEY));
    Ok(JSON(()))
}

#[get("/users/<id>", format = "application/json")]
fn get_user(db: State<DbPool>, user: User, id: String) -> result::Result<JSON<json::User>,AppError> {
    let (username, email) = usecase::get_user(&mut*db.get()?, &user.0, &id)?;
    Ok(JSON(json::User{ username, email }))
}

#[post("/users", format = "application/json", data = "<u>")]
fn post_user(db: State<DbPool>, u: JSON<usecase::NewUser>) -> result::Result<(),AppError> {
    usecase::create_new_user(&mut*db.get()?, u.into_inner())?;
    Ok(())
}

#[post("/ratings", format = "application/json", data = "<u>")]
fn post_rating(db: State<DbPool>, u: JSON<usecase::RateEntry>) -> result::Result<(),AppError> {
    usecase::rate_entry(&mut*db.get()?, u.into_inner())?;
    Ok(())
}

#[get("/ratings/<id>")]
fn get_ratings(db: State<DbPool>, id: String)-> Result<Vec<json::Rating>>{
    let ratings = usecase::get_ratings(&*db.get()?,&extract_ids(&id))?;
    let r_ids : Vec<String> = ratings
        .iter()
        .map(|r|r.id.clone())
        .collect();
    let comments = usecase::get_comments_by_rating_ids(&*db.get()?,&r_ids)?;
    let triples = db.get()?.all_triples()?;
    let result = ratings
        .into_iter()
        .map(|x|json::Rating{
            id       : x.id.clone(),
            created  : x.created,
            title    : x.title,
            user     : usecase::get_user_id_for_rating_id(&triples,&x.id),
            value    : x.value,
            context  : x.context,
            source   : x.source.unwrap_or("no-source".into()),
            comments : comments.get(&x.id).cloned().unwrap_or_else(|| vec![])
                .into_iter()
                .map(|c|json::Comment{
                    id: c.id.clone(),
                    created: c.created,
                    text: c.text,
                    user: usecase::get_user_id_for_comment_id(&triples,&c.id)
                 })
                .collect()
        })
        .collect();
    Ok(JSON(result))
}

#[get("/count/entries")]
fn get_count_entries(db: State<DbPool>) -> Result<usize> {
    let entries = db.get()?.all_entries()?;
    Ok(JSON(entries.len()))
}

#[get("/count/tags")]
fn get_count_tags(db: State<DbPool>) -> Result<usize> {
    let tags = usecase::get_tag_ids(&*db.get()?)?;
    Ok(JSON(tags.len()))
}

#[get("/server/version")]
fn get_version() -> &'static str {
    env!("CARGO_PKG_VERSION")
}

fn rocket_instance<T: r2d2::ManageConnection>(cfg: Config, pool: Pool<T>) -> Rocket {

    rocket::custom(cfg, true)
        .manage(pool)
        .mount("/",
               routes![login,
                       logout,
                       get_entry,
                       post_entry,
                       post_user,
                       post_rating,
                       put_entry,
                       get_user,
                       get_categories,
                       get_tags,
                       get_ratings,
                       get_category,
                       get_search,
                       get_duplicates,
                       get_count_entries,
                       get_count_tags,
                       get_version])

}

pub fn run(db_url: &str, port: u16, enable_cors: bool) {

    if enable_cors {
        panic!("enable-cors is currently not available until\
        \nhttps://github.com/SergioBenitez/Rocket/pull/141\nis merged :(");
    }

    let cfg = Config::build(Environment::Production)
        .log_level(LoggingLevel::Normal)
        .address("127.0.0.1")
        .port(port)
        .finalize()
        .unwrap();

    let pool = neo4j::create_connection_pool(db_url).unwrap();

    rocket_instance(cfg, pool).launch();
}

impl<'r> Responder<'r> for AppError {
    fn respond_to(self, _: &rocket::Request) -> result::Result<Response<'r>, Status> {
        Err(match self {
            AppError::Business(ref err) => {
                match *err {
                    Error::Parameter(ref err) => {
                         match *err {
                            ParameterError::Credentials => Status::Unauthorized,
                            _ => Status::BadRequest,
                         }
                    }
                    Error::Repo(ref err) => {
                        match *err {
                            RepoError::NotFound => Status::NotFound,
                            _ => Status::InternalServerError,
                        }
                    }
                    Error::Pwhash(_) => Status::InternalServerError
                }
            }

            _ => Status::InternalServerError,
        })
    }
}
<commit_msg>fix(rating-source): return empty text when no source is given<commit_after>use rocket::{self, Rocket, State };
use rocket::logger::LoggingLevel;
use rocket_contrib::JSON;
use rocket::response::{Response, Responder};
use rocket::request::{self, FromRequest, Request};
use rocket::Outcome;
use rocket::http::{Status, Cookie, Cookies};
use rocket::config::{Environment, Config};
use adapters::json;
use entities::*;
use business::db::Db;
use business::error::{Error, RepoError, ParameterError};
use infrastructure::error::AppError;
use serde_json::ser::to_string;
use business::sort::SortByAverageRating;
use business::{usecase, filter, geo};
use business::filter::InBBox;
use business::duplicates::{self, DuplicateType};
use std::{result,thread,env};
use r2d2::{self, Pool};
use regex::Regex;
use super::mail;
use super::cfg;

static MAX_INVISIBLE_RESULTS : usize = 5;
static COOKIE_USER_KEY       : &str  = "user_id";

lazy_static! {
    static ref CONFIG: cfg::Config = {
        match env::current_dir() {
            Ok(cwd) => {
                let path = cwd.as_path().join("config.toml");
                match cfg::Config::load(path) {
                    Ok(cfg) => cfg,
                    Err(e) => {
                        warn!("could not read configuration file 'config.toml': {}", e);
                        cfg::Config::default()
                    }
                }
            }
            Err(e) => {
                warn!("could not determine current working directory: {}", e);
                cfg::Config::default()
            }
        }
    };
}

mod neo4j;
#[cfg(test)]
mod mockdb;
#[cfg(test)]
mod tests;

#[cfg(not(test))]
type DbPool = neo4j::ConnectionPool;
#[cfg(test)]
type DbPool = mockdb::ConnectionPool;

type Result<T> = result::Result<JSON<T>, AppError>;

fn extract_ids(s: &str) -> Vec<String> {
    s.split(',')
        .map(|x| x.to_owned())
        .filter(|id| id != "")
        .collect::<Vec<String>>()
}

#[derive(Debug)]
struct User(String);

impl<'a, 'r> FromRequest<'a, 'r> for User {
    type Error = ();

    fn from_request(request: &'a Request<'r>) -> request::Outcome<User, ()> {
        let user = request.cookies()
            .get_private(COOKIE_USER_KEY)
            .and_then(|cookie| cookie.value().parse().ok())
            .map(|id| User(id));
        match user {
            Some(user) => Outcome::Success(user),
            None => Outcome::Failure((Status::Unauthorized, ()))
        }
    }
}

fn notify(subject: &str, body: &str) {
    match mail::create(&CONFIG.notification.send_to, subject, body) {
        Ok(mail) => {
            thread::spawn(move ||{
                if let Err(err) = mail::send(&mail) {
                    warn!("Could not send mail: {}", err);
                }
            });
        }
        Err(e) => {
            warn!("could not create notifiction mail: {}", e);
        }
    }
}

#[get("/entries/<ids>")]
fn get_entry(db: State<DbPool>, ids: String) -> Result<Vec<json::Entry>> {
    let ids = extract_ids(&ids);
    let entries = usecase::get_entries(&*db.get()?, &ids)?;
    let tags = usecase::get_tags_by_entry_ids(&*db.get()?, &ids)?;
    let ratings = usecase::get_ratings_by_entry_ids(&*db.get()?, &ids)?;
    Ok(JSON(entries
        .into_iter()
        .map(|e|{
            let t = tags.get(&e.id).cloned().unwrap_or_else(|| vec![]);
            let r = ratings.get(&e.id).cloned().unwrap_or_else(|| vec![]);
            json::Entry::from_entry_with_tags_and_ratings(e,t,r)
        })
        .collect::<Vec<json::Entry>>()))
}

#[get("/duplicates")]
fn get_duplicates(db: State<DbPool>) -> Result<Vec<(String, String, DuplicateType)>> {
    let entries = db.get()?.all_entries()?;
    let ids = duplicates::find_duplicates(&entries);
    Ok(JSON(ids))
}

#[post("/entries", format = "application/json", data = "<e>")]
fn post_entry(db: State<DbPool>, e: JSON<usecase::NewEntry>) -> result::Result<String, AppError> {
    let e = e.into_inner();
    let id = usecase::create_new_entry(&mut *db.get()?, e.clone())?;
    notify(&format!("Neuer Eintrag: {}", e.title),&format!("{:?}",e));
    Ok(id)
}

#[put("/entries/<id>", format = "application/json", data = "<e>")]
fn put_entry(db: State<DbPool>, id: String, e: JSON<usecase::UpdateEntry>) -> Result<String> {
    let e = e.into_inner();
    usecase::update_entry(&mut *db.get()?, e.clone())?;
    notify(&format!("Veränderter Eintrag: {}", e.title),&format!("{:?}",e));
    Ok(JSON(id))
}

#[get("/tags")]
fn get_tags(db: State<DbPool>) -> Result<Vec<String>> {
    let tags = usecase::get_tag_ids(&*db.get()?)?;
    Ok(JSON(tags))
}

#[get("/categories")]
fn get_categories(db: State<DbPool>) -> Result<Vec<Category>> {
    let categories = db.get()?.all_categories()?;
    Ok(JSON(categories))
}

#[get("/categories/<id>")]
fn get_category(db: State<DbPool>, id: String) -> Result<String> {
    let ids = extract_ids(&id);
    let categories = db.get()?.all_categories()?;
    let res = match ids.len() {
        0 => to_string(&categories),

        1 => {
            let id = ids[0].clone();
            let e = categories
                .into_iter()
                .find(|x| x.id == id)
                .ok_or(RepoError::NotFound)?;
            to_string(&e)
        }
        _ => {
            to_string(&categories.into_iter()
                .filter(|e| ids.iter().any(|id| e.id == id.clone()))
                .collect::<Vec<Category>>())
        }
    }?;
    Ok(JSON(res))
}

#[derive(FromForm)]
struct SearchQuery {
    bbox: String,
    categories: Option<String>,
    text: Option<String>,
    tags: Option<String>,
}

lazy_static! {
    static ref HASH_TAG_REGEX: Regex = Regex::new(r"#(?P<tag>\w+((-\w+)*)?)").unwrap();
}

fn extract_hash_tags(text: &str) -> Vec<String> {
    let mut res: Vec<String> = vec![];
    for cap in HASH_TAG_REGEX.captures_iter(text) {
        res.push(cap["tag"].into());
    }
    res
}

fn remove_hash_tags(text: &str) -> String {
    HASH_TAG_REGEX
        .replace_all(text, "")
        .into_owned()
        .replace("  ", " ")
        .trim()
        .into()
}

#[get("/search?<search>")]
fn get_search(db: State<DbPool>, search: SearchQuery) -> Result<json::SearchResult> {

    let entries = db.get()?.all_entries()?;

    let bbox = geo::extract_bbox(&search.bbox)
        .map_err(Error::Parameter)
        .map_err(AppError::Business)?;
    let bbox_center = geo::center(&bbox[0], &bbox[1]);

    let mut entries: Vec<&Entry> = entries.iter().collect();

    if let Some(cat_str) = search.categories {
        let cat_ids = extract_ids(&cat_str);
        entries = entries
            .into_iter()
            .filter(&*filter::entries_by_category_ids(&cat_ids))
            .collect();
    }

    let mut tags = vec![];

    if let Some(ref txt) = search.text {
        tags = extract_hash_tags(txt);
    }

    if let Some(tags_str) = search.tags {
        for t in extract_ids(&tags_str) {
            tags.push(t);
        }
    }

    // search tags even without preceding #:
    if let Some(ref txt) = search.text {
        for t in to_words(txt){
            tags.push(t);
        }
    }

    let triples = db.get()?.all_triples()?;

    let text = match search.text {
        Some(txt) => remove_hash_tags(&txt),
        None => "".into()
    };

    let entries : Vec<&Entry> = entries
        .into_iter()
        .filter(&*filter::entries_by_tags_or_search_text(&text, &tags, &triples))
        .collect();

    let mut entries : Vec<Entry> = entries.into_iter().cloned().collect();

    let all_ratings = db.get()?.all_ratings()?;

    entries.sort_by_avg_rating(&all_ratings, &triples);

    let visible_results: Vec<_> = entries
        .iter()
        .filter(|x| x.in_bbox(&bbox))
        .map(|x| &x.id)
        .cloned()
        .collect();

    let invisible_results = entries
        .iter()
        .filter(|e| !visible_results.iter().any(|v| *v == e.id))
        .take(MAX_INVISIBLE_RESULTS)
        .map(|x| &x.id)
        .cloned()
        .collect::<Vec<_>>();

    Ok(JSON(json::SearchResult {
        visible: visible_results,
        invisible: invisible_results,
    }))
}

fn to_words(txt: &str) -> Vec<String> {
    txt.to_lowercase().split(' ').map(|x| x.to_string()).collect()
}

#[post("/login", format = "application/json", data = "<login>")]
fn login(db: State<DbPool>, mut cookies: Cookies, login: JSON<usecase::Login>) -> Result<()> {
    let id = usecase::login(&mut*db.get()?, login.into_inner())?;
    cookies.add_private(Cookie::new(COOKIE_USER_KEY, id));
    Ok(JSON(()))
}

#[post("/logout", format = "application/json")]
fn logout(mut cookies: Cookies) -> Result<()> {
    cookies.remove_private(Cookie::named(COOKIE_USER_KEY));
    Ok(JSON(()))
}

#[get("/users/<id>", format = "application/json")]
fn get_user(db: State<DbPool>, user: User, id: String) -> result::Result<JSON<json::User>,AppError> {
    let (username, email) = usecase::get_user(&mut*db.get()?, &user.0, &id)?;
    Ok(JSON(json::User{ username, email }))
}

#[post("/users", format = "application/json", data = "<u>")]
fn post_user(db: State<DbPool>, u: JSON<usecase::NewUser>) -> result::Result<(),AppError> {
    usecase::create_new_user(&mut*db.get()?, u.into_inner())?;
    Ok(())
}

#[post("/ratings", format = "application/json", data = "<u>")]
fn post_rating(db: State<DbPool>, u: JSON<usecase::RateEntry>) -> result::Result<(),AppError> {
    usecase::rate_entry(&mut*db.get()?, u.into_inner())?;
    Ok(())
}

#[get("/ratings/<id>")]
fn get_ratings(db: State<DbPool>, id: String)-> Result<Vec<json::Rating>>{
    let ratings = usecase::get_ratings(&*db.get()?,&extract_ids(&id))?;
    let r_ids : Vec<String> = ratings
        .iter()
        .map(|r|r.id.clone())
        .collect();
    let comments = usecase::get_comments_by_rating_ids(&*db.get()?,&r_ids)?;
    let triples = db.get()?.all_triples()?;
    let result = ratings
        .into_iter()
        .map(|x|json::Rating{
            id       : x.id.clone(),
            created  : x.created,
            title    : x.title,
            user     : usecase::get_user_id_for_rating_id(&triples,&x.id),
            value    : x.value,
            context  : x.context,
            source   : x.source.unwrap_or("".into()),
            comments : comments.get(&x.id).cloned().unwrap_or_else(|| vec![])
                .into_iter()
                .map(|c|json::Comment{
                    id: c.id.clone(),
                    created: c.created,
                    text: c.text,
                    user: usecase::get_user_id_for_comment_id(&triples,&c.id)
                 })
                .collect()
        })
        .collect();
    Ok(JSON(result))
}

#[get("/count/entries")]
fn get_count_entries(db: State<DbPool>) -> Result<usize> {
    let entries = db.get()?.all_entries()?;
    Ok(JSON(entries.len()))
}

#[get("/count/tags")]
fn get_count_tags(db: State<DbPool>) -> Result<usize> {
    let tags = usecase::get_tag_ids(&*db.get()?)?;
    Ok(JSON(tags.len()))
}

#[get("/server/version")]
fn get_version() -> &'static str {
    env!("CARGO_PKG_VERSION")
}

fn rocket_instance<T: r2d2::ManageConnection>(cfg: Config, pool: Pool<T>) -> Rocket {

    rocket::custom(cfg, true)
        .manage(pool)
        .mount("/",
               routes![login,
                       logout,
                       get_entry,
                       post_entry,
                       post_user,
                       post_rating,
                       put_entry,
                       get_user,
                       get_categories,
                       get_tags,
                       get_ratings,
                       get_category,
                       get_search,
                       get_duplicates,
                       get_count_entries,
                       get_count_tags,
                       get_version])

}

pub fn run(db_url: &str, port: u16, enable_cors: bool) {

    if enable_cors {
        panic!("enable-cors is currently not available until\
        \nhttps://github.com/SergioBenitez/Rocket/pull/141\nis merged :(");
    }

    let cfg = Config::build(Environment::Production)
        .log_level(LoggingLevel::Normal)
        .address("127.0.0.1")
        .port(port)
        .finalize()
        .unwrap();

    let pool = neo4j::create_connection_pool(db_url).unwrap();

    rocket_instance(cfg, pool).launch();
}

impl<'r> Responder<'r> for AppError {
    fn respond_to(self, _: &rocket::Request) -> result::Result<Response<'r>, Status> {
        Err(match self {
            AppError::Business(ref err) => {
                match *err {
                    Error::Parameter(ref err) => {
                         match *err {
                            ParameterError::Credentials => Status::Unauthorized,
                            _ => Status::BadRequest,
                         }
                    }
                    Error::Repo(ref err) => {
                        match *err {
                            RepoError::NotFound => Status::NotFound,
                            _ => Status::InternalServerError,
                        }
                    }
                    Error::Pwhash(_) => Status::InternalServerError
                }
            }

            _ => Status::InternalServerError,
        })
    }
}
<|endoftext|>


<commit_before>use rustc::hir::*;
use rustc::hir::intravisit::FnKind;
use rustc::lint::*;
use rustc::ty::{self, RegionKind, TypeFoldable};
use rustc::traits;
use rustc::middle::expr_use_visitor as euv;
use rustc::middle::mem_categorization as mc;
use syntax::ast::NodeId;
use syntax_pos::Span;
use syntax::errors::DiagnosticBuilder;
use utils::{get_trait_def_id, implements_trait, in_macro, is_copy, is_self, match_type, multispan_sugg, paths,
            snippet, snippet_opt, span_lint_and_then};
use utils::ptr::get_spans;
use std::collections::{HashMap, HashSet};
use std::borrow::Cow;

/// **What it does:** Checks for functions taking arguments by value, but not
/// consuming them in its
/// body.
///
/// **Why is this bad?** Taking arguments by reference is more flexible and can
/// sometimes avoid
/// unnecessary allocations.
///
/// **Known problems:** Hopefully none.
///
/// **Example:**
/// ```rust
/// fn foo(v: Vec<i32>) {
///     assert_eq!(v.len(), 42);
/// }
/// ```
declare_lint! {
    pub NEEDLESS_PASS_BY_VALUE,
    Warn,
    "functions taking arguments by value, but not consuming them in its body"
}

pub struct NeedlessPassByValue;

impl LintPass for NeedlessPassByValue {
    fn get_lints(&self) -> LintArray {
        lint_array![NEEDLESS_PASS_BY_VALUE]
    }
}

macro_rules! need {
    ($e: expr) => { if let Some(x) = $e { x } else { return; } };
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
    fn check_fn(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        kind: FnKind<'tcx>,
        decl: &'tcx FnDecl,
        body: &'tcx Body,
        span: Span,
        node_id: NodeId,
    ) {
        if in_macro(span) {
            return;
        }

        match kind {
            FnKind::ItemFn(.., attrs) => for a in attrs {
                if_let_chain!{[
                    a.meta_item_list().is_some(),
                    let Some(name) = a.name(),
                    name == "proc_macro_derive",
                ], {
                    return;
                }}
            },
            _ => return,
        }

        let borrow_trait = need!(get_trait_def_id(cx, &paths::BORROW_TRAIT));
        let fn_trait = need!(cx.tcx.lang_items().fn_trait());

        let sized_trait = need!(cx.tcx.lang_items().sized_trait());

        let fn_def_id = cx.tcx.hir.local_def_id(node_id);

        let preds = traits::elaborate_predicates(cx.tcx, cx.param_env.caller_bounds.to_vec())
            .filter(|p| !p.is_global())
            .collect::<Vec<_>>();
        let preds = preds
            .iter()
            .filter_map(|pred| if let ty::Predicate::Trait(ref poly_trait_ref) = *pred {
                Some(poly_trait_ref.skip_binder())
            } else {
                None
            })
            .filter(|t| t.def_id() != sized_trait && !t.has_escaping_regions())
            .collect::<Vec<_>>();

        // Collect moved variables and spans which will need dereferencings from the
        // function body.
        let MovedVariablesCtxt {
            moved_vars,
            spans_need_deref,
            ..
        } = {
            let mut ctx = MovedVariablesCtxt::new(cx);
            let region_scope_tree = &cx.tcx.region_scope_tree(fn_def_id);
            euv::ExprUseVisitor::new(&mut ctx, cx.tcx, cx.param_env, region_scope_tree, cx.tables).consume_body(body);
            ctx
        };

        let fn_sig = cx.tcx.fn_sig(fn_def_id);
        let fn_sig = cx.tcx.erase_late_bound_regions(&fn_sig);

        for (idx, ((input, &ty), arg)) in decl.inputs
            .iter()
            .zip(fn_sig.inputs())
            .zip(&body.arguments)
            .enumerate()
        {
            // * Exclude a type that is specifically bounded by `Borrow`.
            // * Exclude a type whose reference also fulfills its bound.
            //   (e.g. `std::borrow::Borrow`, `serde::Serialize`)
            let (implements_borrow_trait, all_borrowable_trait) = {
                let preds = preds
                    .iter()
                    .filter(|t| t.self_ty() == ty)
                    .collect::<Vec<_>>();

                (
                    preds.iter().any(|t| t.def_id() == borrow_trait),
                    !preds.is_empty() && preds.iter().all(|t| {
                        implements_trait(
                            cx,
                            cx.tcx.mk_imm_ref(&RegionKind::ReErased, ty),
                            t.def_id(),
                            &t.input_types().skip(1).collect::<Vec<_>>(),
                        )
                    }),
                )
            };

            if_let_chain! {[
                !is_self(arg),
                !ty.is_mutable_pointer(),
                !is_copy(cx, ty),
                !implements_trait(cx, ty, fn_trait, &[]),
                !implements_borrow_trait,
                !all_borrowable_trait,

                let PatKind::Binding(mode, canonical_id, ..) = arg.pat.node,
                !moved_vars.contains(&canonical_id),
            ], {
                if mode == BindingAnnotation::Mutable || mode == BindingAnnotation::RefMut {
                    continue;
                }

                // Dereference suggestion
                let sugg = |db: &mut DiagnosticBuilder| {
                    let deref_span = spans_need_deref.get(&canonical_id);
                    if_let_chain! {[
                        match_type(cx, ty, &paths::VEC),
                        let Some(clone_spans) =
                            get_spans(cx, Some(body.id()), idx, &[("clone", ".to_owned()")]),
                        let TyPath(QPath::Resolved(_, ref path)) = input.node,
                        let Some(elem_ty) = path.segments.iter()
                            .find(|seg| seg.name == "Vec")
                            .and_then(|ps| ps.parameters.as_ref())
                            .map(|params| &params.types[0]),
                    ], {
                        let slice_ty = format!("&[{}]", snippet(cx, elem_ty.span, "_"));
                        db.span_suggestion(input.span,
                                        "consider changing the type to",
                                        slice_ty);

                        for (span, suggestion) in clone_spans {
                            db.span_suggestion(
                                span,
                                &snippet_opt(cx, span)
                                    .map_or(
                                        "change the call to".into(),
                                        |x| Cow::from(format!("change `{}` to", x)),
                                    ),
                                suggestion.into()
                            );
                        }

                        // cannot be destructured, no need for `*` suggestion
                        assert!(deref_span.is_none());
                        return;
                    }}

                    if match_type(cx, ty, &paths::STRING) {
                        if let Some(clone_spans) =
                            get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) {
                            db.span_suggestion(input.span, "consider changing the type to", "&str".to_string());

                            for (span, suggestion) in clone_spans {
                                db.span_suggestion(
                                    span,
                                    &snippet_opt(cx, span)
                                        .map_or(
                                            "change the call to".into(),
                                            |x| Cow::from(format!("change `{}` to", x))
                                        ),
                                    suggestion.into(),
                                );
                            }

                            assert!(deref_span.is_none());
                            return;
                        }
                    }

                    let mut spans = vec![(input.span, format!("&{}", snippet(cx, input.span, "_")))];

                    // Suggests adding `*` to dereference the added reference.
                    if let Some(deref_span) = deref_span {
                        spans.extend(
                            deref_span
                                .iter()
                                .cloned()
                                .map(|span| (span, format!("*{}", snippet(cx, span, "<expr>")))),
                        );
                        spans.sort_by_key(|&(span, _)| span);
                    }
                    multispan_sugg(db, "consider taking a reference instead".to_string(), spans);
                };

                span_lint_and_then(
                    cx,
                    NEEDLESS_PASS_BY_VALUE,
                    input.span,
                    "this argument is passed by value, but not consumed in the function body",
                    sugg,
                );
            }}
        }
    }
}

struct MovedVariablesCtxt<'a, 'tcx: 'a> {
    cx: &'a LateContext<'a, 'tcx>,
    moved_vars: HashSet<NodeId>,
    /// Spans which need to be prefixed with `*` for dereferencing the
    /// suggested additional reference.
    spans_need_deref: HashMap<NodeId, HashSet<Span>>,
}

impl<'a, 'tcx> MovedVariablesCtxt<'a, 'tcx> {
    fn new(cx: &'a LateContext<'a, 'tcx>) -> Self {
        Self {
            cx: cx,
            moved_vars: HashSet::new(),
            spans_need_deref: HashMap::new(),
        }
    }

    fn move_common(&mut self, _consume_id: NodeId, _span: Span, cmt: mc::cmt<'tcx>) {
        let cmt = unwrap_downcast_or_interior(cmt);

        if let mc::Categorization::Local(vid) = cmt.cat {
            self.moved_vars.insert(vid);
        }
    }

    fn non_moving_pat(&mut self, matched_pat: &Pat, cmt: mc::cmt<'tcx>) {
        let cmt = unwrap_downcast_or_interior(cmt);

        if let mc::Categorization::Local(vid) = cmt.cat {
            let mut id = matched_pat.id;
            loop {
                let parent = self.cx.tcx.hir.get_parent_node(id);
                if id == parent {
                    // no parent
                    return;
                }
                id = parent;

                if let Some(node) = self.cx.tcx.hir.find(id) {
                    match node {
                        map::Node::NodeExpr(e) => {
                            // `match` and `if let`
                            if let ExprMatch(ref c, ..) = e.node {
                                self.spans_need_deref
                                    .entry(vid)
                                    .or_insert_with(HashSet::new)
                                    .insert(c.span);
                            }
                        },

                        map::Node::NodeStmt(s) => {
                            // `let <pat> = x;`
                            if_let_chain! {[
                                let StmtDecl(ref decl, _) = s.node,
                                let DeclLocal(ref local) = decl.node,
                            ], {
                                self.spans_need_deref
                                    .entry(vid)
                                    .or_insert_with(HashSet::new)
                                    .insert(local.init
                                        .as_ref()
                                        .map(|e| e.span)
                                        .expect("`let` stmt without init aren't caught by match_pat"));
                            }}
                        },

                        _ => {},
                    }
                }
            }
        }
    }
}

impl<'a, 'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt<'a, 'tcx> {
    fn consume(&mut self, consume_id: NodeId, consume_span: Span, cmt: mc::cmt<'tcx>, mode: euv::ConsumeMode) {
        if let euv::ConsumeMode::Move(_) = mode {
            self.move_common(consume_id, consume_span, cmt);
        }
    }

    fn matched_pat(&mut self, matched_pat: &Pat, cmt: mc::cmt<'tcx>, mode: euv::MatchMode) {
        if let euv::MatchMode::MovingMatch = mode {
            self.move_common(matched_pat.id, matched_pat.span, cmt);
        } else {
            self.non_moving_pat(matched_pat, cmt);
        }
    }

    fn consume_pat(&mut self, consume_pat: &Pat, cmt: mc::cmt<'tcx>, mode: euv::ConsumeMode) {
        if let euv::ConsumeMode::Move(_) = mode {
            self.move_common(consume_pat.id, consume_pat.span, cmt);
        }
    }

    fn borrow(&mut self, _: NodeId, _: Span, _: mc::cmt<'tcx>, _: ty::Region, _: ty::BorrowKind, _: euv::LoanCause) {}

    fn mutate(&mut self, _: NodeId, _: Span, _: mc::cmt<'tcx>, _: euv::MutateMode) {}

    fn decl_without_init(&mut self, _: NodeId, _: Span) {}
}

fn unwrap_downcast_or_interior(mut cmt: mc::cmt) -> mc::cmt {
    loop {
        match cmt.cat.clone() {
            mc::Categorization::Downcast(c, _) | mc::Categorization::Interior(c, _) => {
                cmt = c;
            },
            _ => return cmt,
        }
    }
}
<commit_msg>Exclude Fn traits<commit_after>use rustc::hir::*;
use rustc::hir::intravisit::FnKind;
use rustc::lint::*;
use rustc::ty::{self, RegionKind, TypeFoldable};
use rustc::traits;
use rustc::middle::expr_use_visitor as euv;
use rustc::middle::mem_categorization as mc;
use syntax::ast::NodeId;
use syntax_pos::Span;
use syntax::errors::DiagnosticBuilder;
use utils::{get_trait_def_id, implements_trait, in_macro, is_copy, is_self, match_type, multispan_sugg, paths,
            snippet, snippet_opt, span_lint_and_then};
use utils::ptr::get_spans;
use std::collections::{HashMap, HashSet};
use std::borrow::Cow;

/// **What it does:** Checks for functions taking arguments by value, but not
/// consuming them in its
/// body.
///
/// **Why is this bad?** Taking arguments by reference is more flexible and can
/// sometimes avoid
/// unnecessary allocations.
///
/// **Known problems:** Hopefully none.
///
/// **Example:**
/// ```rust
/// fn foo(v: Vec<i32>) {
///     assert_eq!(v.len(), 42);
/// }
/// ```
declare_lint! {
    pub NEEDLESS_PASS_BY_VALUE,
    Warn,
    "functions taking arguments by value, but not consuming them in its body"
}

pub struct NeedlessPassByValue;

impl LintPass for NeedlessPassByValue {
    fn get_lints(&self) -> LintArray {
        lint_array![NEEDLESS_PASS_BY_VALUE]
    }
}

macro_rules! need {
    ($e: expr) => { if let Some(x) = $e { x } else { return; } };
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
    fn check_fn(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        kind: FnKind<'tcx>,
        decl: &'tcx FnDecl,
        body: &'tcx Body,
        span: Span,
        node_id: NodeId,
    ) {
        if in_macro(span) {
            return;
        }

        match kind {
            FnKind::ItemFn(.., attrs) => for a in attrs {
                if_let_chain!{[
                    a.meta_item_list().is_some(),
                    let Some(name) = a.name(),
                    name == "proc_macro_derive",
                ], {
                    return;
                }}
            },
            _ => return,
        }

        // Allow `Borrow` or functions to be taken by value
        let borrow_trait = need!(get_trait_def_id(cx, &paths::BORROW_TRAIT));
        let fn_traits = [
            need!(cx.tcx.lang_items().fn_trait()),
            need!(cx.tcx.lang_items().fn_once_trait()),
            need!(cx.tcx.lang_items().fn_mut_trait()),
        ];

        let sized_trait = need!(cx.tcx.lang_items().sized_trait());

        let fn_def_id = cx.tcx.hir.local_def_id(node_id);

        let preds = traits::elaborate_predicates(cx.tcx, cx.param_env.caller_bounds.to_vec())
            .filter(|p| !p.is_global())
            .collect::<Vec<_>>();
        let preds = preds
            .iter()
            .filter_map(|pred| if let ty::Predicate::Trait(ref poly_trait_ref) = *pred {
                Some(poly_trait_ref.skip_binder())
            } else {
                None
            })
            .filter(|t| t.def_id() != sized_trait && !t.has_escaping_regions())
            .collect::<Vec<_>>();

        // Collect moved variables and spans which will need dereferencings from the
        // function body.
        let MovedVariablesCtxt {
            moved_vars,
            spans_need_deref,
            ..
        } = {
            let mut ctx = MovedVariablesCtxt::new(cx);
            let region_scope_tree = &cx.tcx.region_scope_tree(fn_def_id);
            euv::ExprUseVisitor::new(&mut ctx, cx.tcx, cx.param_env, region_scope_tree, cx.tables).consume_body(body);
            ctx
        };

        let fn_sig = cx.tcx.fn_sig(fn_def_id);
        let fn_sig = cx.tcx.erase_late_bound_regions(&fn_sig);

        for (idx, ((input, &ty), arg)) in decl.inputs
            .iter()
            .zip(fn_sig.inputs())
            .zip(&body.arguments)
            .enumerate()
        {
            // * Exclude a type that is specifically bounded by `Borrow`.
            // * Exclude a type whose reference also fulfills its bound.
            //   (e.g. `std::convert::AsRef`, `serde::Serialize`)
            let (implements_borrow_trait, all_borrowable_trait) = {
                let preds = preds
                    .iter()
                    .filter(|t| t.self_ty() == ty)
                    .collect::<Vec<_>>();

                (
                    preds.iter().any(|t| t.def_id() == borrow_trait),
                    !preds.is_empty() && preds.iter().all(|t| {
                        implements_trait(
                            cx,
                            cx.tcx.mk_imm_ref(&RegionKind::ReErased, ty),
                            t.def_id(),
                            &t.input_types().skip(1).collect::<Vec<_>>(),
                        )
                    }),
                )
            };

            if_let_chain! {[
                !is_self(arg),
                !ty.is_mutable_pointer(),
                !is_copy(cx, ty),
                !fn_traits.iter().any(|&t| implements_trait(cx, ty, t, &[])),
                !implements_borrow_trait,
                !all_borrowable_trait,

                let PatKind::Binding(mode, canonical_id, ..) = arg.pat.node,
                !moved_vars.contains(&canonical_id),
            ], {
                if mode == BindingAnnotation::Mutable || mode == BindingAnnotation::RefMut {
                    continue;
                }

                // Dereference suggestion
                let sugg = |db: &mut DiagnosticBuilder| {
                    let deref_span = spans_need_deref.get(&canonical_id);
                    if_let_chain! {[
                        match_type(cx, ty, &paths::VEC),
                        let Some(clone_spans) =
                            get_spans(cx, Some(body.id()), idx, &[("clone", ".to_owned()")]),
                        let TyPath(QPath::Resolved(_, ref path)) = input.node,
                        let Some(elem_ty) = path.segments.iter()
                            .find(|seg| seg.name == "Vec")
                            .and_then(|ps| ps.parameters.as_ref())
                            .map(|params| &params.types[0]),
                    ], {
                        let slice_ty = format!("&[{}]", snippet(cx, elem_ty.span, "_"));
                        db.span_suggestion(input.span,
                                        "consider changing the type to",
                                        slice_ty);

                        for (span, suggestion) in clone_spans {
                            db.span_suggestion(
                                span,
                                &snippet_opt(cx, span)
                                    .map_or(
                                        "change the call to".into(),
                                        |x| Cow::from(format!("change `{}` to", x)),
                                    ),
                                suggestion.into()
                            );
                        }

                        // cannot be destructured, no need for `*` suggestion
                        assert!(deref_span.is_none());
                        return;
                    }}

                    if match_type(cx, ty, &paths::STRING) {
                        if let Some(clone_spans) =
                            get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) {
                            db.span_suggestion(input.span, "consider changing the type to", "&str".to_string());

                            for (span, suggestion) in clone_spans {
                                db.span_suggestion(
                                    span,
                                    &snippet_opt(cx, span)
                                        .map_or(
                                            "change the call to".into(),
                                            |x| Cow::from(format!("change `{}` to", x))
                                        ),
                                    suggestion.into(),
                                );
                            }

                            assert!(deref_span.is_none());
                            return;
                        }
                    }

                    let mut spans = vec![(input.span, format!("&{}", snippet(cx, input.span, "_")))];

                    // Suggests adding `*` to dereference the added reference.
                    if let Some(deref_span) = deref_span {
                        spans.extend(
                            deref_span
                                .iter()
                                .cloned()
                                .map(|span| (span, format!("*{}", snippet(cx, span, "<expr>")))),
                        );
                        spans.sort_by_key(|&(span, _)| span);
                    }
                    multispan_sugg(db, "consider taking a reference instead".to_string(), spans);
                };

                span_lint_and_then(
                    cx,
                    NEEDLESS_PASS_BY_VALUE,
                    input.span,
                    "this argument is passed by value, but not consumed in the function body",
                    sugg,
                );
            }}
        }
    }
}

struct MovedVariablesCtxt<'a, 'tcx: 'a> {
    cx: &'a LateContext<'a, 'tcx>,
    moved_vars: HashSet<NodeId>,
    /// Spans which need to be prefixed with `*` for dereferencing the
    /// suggested additional reference.
    spans_need_deref: HashMap<NodeId, HashSet<Span>>,
}

impl<'a, 'tcx> MovedVariablesCtxt<'a, 'tcx> {
    fn new(cx: &'a LateContext<'a, 'tcx>) -> Self {
        Self {
            cx: cx,
            moved_vars: HashSet::new(),
            spans_need_deref: HashMap::new(),
        }
    }

    fn move_common(&mut self, _consume_id: NodeId, _span: Span, cmt: mc::cmt<'tcx>) {
        let cmt = unwrap_downcast_or_interior(cmt);

        if let mc::Categorization::Local(vid) = cmt.cat {
            self.moved_vars.insert(vid);
        }
    }

    fn non_moving_pat(&mut self, matched_pat: &Pat, cmt: mc::cmt<'tcx>) {
        let cmt = unwrap_downcast_or_interior(cmt);

        if let mc::Categorization::Local(vid) = cmt.cat {
            let mut id = matched_pat.id;
            loop {
                let parent = self.cx.tcx.hir.get_parent_node(id);
                if id == parent {
                    // no parent
                    return;
                }
                id = parent;

                if let Some(node) = self.cx.tcx.hir.find(id) {
                    match node {
                        map::Node::NodeExpr(e) => {
                            // `match` and `if let`
                            if let ExprMatch(ref c, ..) = e.node {
                                self.spans_need_deref
                                    .entry(vid)
                                    .or_insert_with(HashSet::new)
                                    .insert(c.span);
                            }
                        },

                        map::Node::NodeStmt(s) => {
                            // `let <pat> = x;`
                            if_let_chain! {[
                                let StmtDecl(ref decl, _) = s.node,
                                let DeclLocal(ref local) = decl.node,
                            ], {
                                self.spans_need_deref
                                    .entry(vid)
                                    .or_insert_with(HashSet::new)
                                    .insert(local.init
                                        .as_ref()
                                        .map(|e| e.span)
                                        .expect("`let` stmt without init aren't caught by match_pat"));
                            }}
                        },

                        _ => {},
                    }
                }
            }
        }
    }
}

impl<'a, 'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt<'a, 'tcx> {
    fn consume(&mut self, consume_id: NodeId, consume_span: Span, cmt: mc::cmt<'tcx>, mode: euv::ConsumeMode) {
        if let euv::ConsumeMode::Move(_) = mode {
            self.move_common(consume_id, consume_span, cmt);
        }
    }

    fn matched_pat(&mut self, matched_pat: &Pat, cmt: mc::cmt<'tcx>, mode: euv::MatchMode) {
        if let euv::MatchMode::MovingMatch = mode {
            self.move_common(matched_pat.id, matched_pat.span, cmt);
        } else {
            self.non_moving_pat(matched_pat, cmt);
        }
    }

    fn consume_pat(&mut self, consume_pat: &Pat, cmt: mc::cmt<'tcx>, mode: euv::ConsumeMode) {
        if let euv::ConsumeMode::Move(_) = mode {
            self.move_common(consume_pat.id, consume_pat.span, cmt);
        }
    }

    fn borrow(&mut self, _: NodeId, _: Span, _: mc::cmt<'tcx>, _: ty::Region, _: ty::BorrowKind, _: euv::LoanCause) {}

    fn mutate(&mut self, _: NodeId, _: Span, _: mc::cmt<'tcx>, _: euv::MutateMode) {}

    fn decl_without_init(&mut self, _: NodeId, _: Span) {}
}

fn unwrap_downcast_or_interior(mut cmt: mc::cmt) -> mc::cmt {
    loop {
        match cmt.cat.clone() {
            mc::Categorization::Downcast(c, _) | mc::Categorization::Interior(c, _) => {
                cmt = c;
            },
            _ => return cmt,
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2017 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

extern crate capnp;
extern crate rand;

use std::{mem, io};

use capnp::{message, serialize, serialize_packed};
use capnp::traits::Owned;

pub mod common;

pub mod carsales_capnp {
  include!(concat!(env!("OUT_DIR"), "/carsales_capnp.rs"));
}
pub mod carsales;

pub mod catrank_capnp {
  include!(concat!(env!("OUT_DIR"), "/catrank_capnp.rs"));
}
pub mod catrank;

pub mod eval_capnp {
  include!(concat!(env!("OUT_DIR"), "/eval_capnp.rs"));
}

pub mod eval;

trait TestCase {
    type Request: for<'a> Owned<'a>;
    type Response: for<'a> Owned<'a>;
    type Expectation;

    fn setup_request(&self, &mut common::FastRand, <Self::Request as Owned>::Builder) -> Self::Expectation;
    fn handle_request(&self, <Self::Request as Owned>::Reader, <Self::Response as Owned>::Builder)
                      -> ::capnp::Result<()>;
    fn check_response(&self, <Self::Response as Owned>::Reader, Self::Expectation) -> ::capnp::Result<()>;
}

trait Serialize {
    fn read_message<R>(
        &self,
        read: &mut R,
        options: message::ReaderOptions)
                       -> ::capnp::Result<message::Reader<::capnp::serialize::OwnedSegments>>
        where R: io::BufRead;

    fn write_message<W, A>(&self, write: &mut W, message: &message::Builder<A>) -> ::capnp::Result<()>
        where W: io::Write, A: message::Allocator;
}

struct NoCompression;

impl Serialize for NoCompression {
    fn read_message<R>(&self, read: &mut R,
                       options: message::ReaderOptions)
                       -> ::capnp::Result<message::Reader<::capnp::serialize::OwnedSegments>>
        where R: io::BufRead
    {
        serialize::read_message(read, options)
    }

    fn write_message<W, A>(&self, write: &mut W, message: &message::Builder<A>) -> ::capnp::Result<()>
        where W: io::Write, A: message::Allocator {
        serialize::write_message(write, message).map_err(|e| e.into())
    }
}

struct Packed;

impl Serialize for Packed {
    fn read_message<R>(&self, read: &mut R,
                       options: message::ReaderOptions)
                       -> ::capnp::Result<message::Reader<::capnp::serialize::OwnedSegments>>
        where R: io::BufRead
    {
        serialize_packed::read_message(read, options)
    }

    fn write_message<W, A>(&self, write: &mut W, message: &message::Builder<A>) -> ::capnp::Result<()>
        where W: io::Write, A: message::Allocator {
        serialize_packed::write_message(write, message).map_err(|e| e.into())
    }
}

trait Scratch<'a> {
    type Allocator: message::Allocator;

    fn get_builders(&'a mut self) -> (message::Builder<Self::Allocator>, message::Builder<Self::Allocator>);
}

const SCRATCH_SIZE: usize = 128 * 1024;

#[derive(Clone, Copy)]
pub struct NoScratch;

impl <'a> Scratch<'a> for NoScratch {
    type Allocator = message::HeapAllocator;

    fn get_builders(&'a mut self) -> (message::Builder<Self::Allocator>, message::Builder<Self::Allocator>) {
        (message::Builder::new_default(), message::Builder::new_default())
    }
}

pub struct UseScratch {
    _owned_space: ::std::vec::Vec<::std::vec::Vec<capnp::Word>>,
    scratch_space: ::std::vec::Vec<message::ScratchSpace<'static>>,
}

impl UseScratch {
    pub fn new() -> UseScratch {
        let mut owned = Vec::new();
        let mut scratch = Vec::new();
        for _ in 0..6 {
            let mut words = ::capnp::Word::allocate_zeroed_vec(SCRATCH_SIZE);
            scratch.push(message::ScratchSpace::new(
                unsafe { mem::transmute(&mut words[..]) }));
            owned.push(words);
        }
        UseScratch {
            _owned_space: owned,
            scratch_space: scratch,
        }
    }
}

impl <'a> Scratch<'a> for UseScratch {
    type Allocator = message::ScratchSpaceHeapAllocator<'a, 'a>;

    fn get_builders(&'a mut self) -> (message::Builder<Self::Allocator>, message::Builder<Self::Allocator>) {
        (message::Builder::new(message::ScratchSpaceHeapAllocator::new(
            unsafe { mem::transmute(&mut self.scratch_space[0]) })),
         message::Builder::new(message::ScratchSpaceHeapAllocator::new(
             unsafe { mem::transmute(&mut self.scratch_space[1]) })))

    }
}

fn pass_by_object<S, T>(testcase: T, mut reuse: S, iters: u64) -> ::capnp::Result<()>
    where S: for<'a> Scratch<'a>, T: TestCase,
{
    let mut rng = common::FastRand::new();
    for _ in 0..iters {
        let (mut message_req, mut message_res) = reuse.get_builders();

        let expected = testcase.setup_request(
            &mut rng,
            message_req.init_root());

        try!(testcase.handle_request(
            try!(message_req.get_root_as_reader()),
            message_res.init_root()));

        try!(testcase.check_response(
            try!(message_res.get_root_as_reader()),
            expected));
    }
    Ok(())
}

fn pass_by_bytes<C, S, T>(testcase: T, mut reuse: S, compression: C, iters: u64) -> ::capnp::Result<()>
    where C: Serialize, S: for<'a> Scratch<'a>, T: TestCase,
{
    let mut request_bytes: ::std::vec::Vec<u8> =
        ::std::iter::repeat(0u8).take(SCRATCH_SIZE * 8).collect();
    let mut response_bytes: ::std::vec::Vec<u8> =
        ::std::iter::repeat(0u8).take(SCRATCH_SIZE * 8).collect();
    let mut rng = common::FastRand::new();
    for _ in 0..iters {
        let (mut message_req, mut message_res) = reuse.get_builders();

        let expected = {
            let request = message_req.init_root();
            testcase.setup_request(&mut rng, request)
        };

        {
            let response = message_res.init_root();

            {
                let mut writer: &mut [u8] = &mut request_bytes;
                compression.write_message(&mut writer, &mut message_req).unwrap()
            }

            let mut request_bytes1: &[u8] = &request_bytes;
            let message_reader = compression.read_message(
                &mut request_bytes1,
                Default::default()).unwrap();

            let request_reader = message_reader.get_root().unwrap();
            try!(testcase.handle_request(request_reader, response));
        }

        {
            let mut writer: &mut [u8] = &mut response_bytes;
            compression.write_message(&mut writer, &mut message_res).unwrap()
        }

        let mut response_bytes1: &[u8] = &response_bytes;
        let message_reader = compression.read_message(
            &mut response_bytes1,
            Default::default()).unwrap();

        let response_reader = message_reader.get_root().unwrap();
        try!(testcase.check_response(response_reader, expected));
    }
    Ok(())
}

fn server<C, S, T, R, W>(testcase: T, mut reuse: S, compression: C, iters: u64, mut input: R, mut output: W)
                         -> ::capnp::Result<()>
    where C: Serialize, S: for<'a> Scratch<'a>, T: TestCase, R: io::Read, W: io::Write,
{
    let mut out_buffered = io::BufWriter::new(&mut output);
    let mut in_buffered = io::BufReader::new(&mut input);
    for _ in 0..iters {
        use std::io::Write;
        let (mut message_res, _) = reuse.get_builders();

        {
            let response = message_res.init_root();
            let message_reader = try!(compression.read_message(
                &mut in_buffered,
                capnp::message::DEFAULT_READER_OPTIONS));
            let request_reader = try!(message_reader.get_root());
            try!(testcase.handle_request(request_reader, response));
        }

        try!(compression.write_message(&mut out_buffered, &mut message_res));
        try!(out_buffered.flush());
    }
    Ok(())
}

fn sync_client<C, S, T>(testcase: T, mut reuse: S, compression: C, iters: u64)
                        -> ::capnp::Result<()>
    where C: Serialize, S: for<'a> Scratch<'a>, T: TestCase,
{
    let mut out_stream: ::std::fs::File = unsafe { ::std::os::unix::io::FromRawFd::from_raw_fd(1) };
    let mut in_stream: ::std::fs::File = unsafe { ::std::os::unix::io::FromRawFd::from_raw_fd(0) };
    let mut in_buffered = io::BufReader::new(&mut in_stream);
    let mut out_buffered = io::BufWriter::new(&mut out_stream);
    let mut rng = common::FastRand::new();
    for _ in 0..iters {
        use std::io::Write;
        let (mut message_req, _) = reuse.get_builders();

        let expected = {
            let request = message_req.init_root();
            testcase.setup_request(&mut rng, request)
        };
        try!(compression.write_message(&mut out_buffered, &mut message_req));
        try!(out_buffered.flush());

        let message_reader = compression.read_message(
            &mut in_buffered,
            Default::default()).unwrap();
        let response_reader = try!(message_reader.get_root());
        try!(testcase.check_response(response_reader, expected));
    }
    Ok(())
}

fn pass_by_pipe<C, S, T>(testcase: T, reuse: S, compression: C, iters: u64) -> ::capnp::Result<()>
    where C: Serialize, S: for<'a> Scratch<'a>, T: TestCase,
{
    use std::{process, env};

    let mut args: Vec<String> = env::args().collect();
    args[2] = "client".to_string();

    let mut command = process::Command::new(&args[0]);
    command.args(&args[1..args.len()]);
    command.stdin(process::Stdio::piped());
    command.stdout(process::Stdio::piped());
    command.stderr(process::Stdio::null());
    match command.spawn() {
        Ok(ref mut p) => {
            let child_std_out = p.stdout.take().unwrap();
            let child_std_in = p.stdin.take().unwrap();
            try!(server(testcase, reuse, compression, iters, child_std_out, child_std_in));
            println!("{}", p.wait().unwrap());
            Ok(())
        }
        Err(e) => {
            println!("could not start process: {}", e);
            Ok(())
        }
    }
}

pub enum Mode {
    Object,
    Bytes,
    Client,
    Server,
    Pipe,
}

impl Mode {
    pub fn parse(s: &str) -> ::capnp::Result<Mode> {
        match s {
            "object" => Ok(Mode::Object),
            "bytes" => Ok(Mode::Bytes),
            "client" => Ok(Mode::Client),
            "server" => Ok(Mode::Server),
            "pipe" => Ok(Mode::Pipe),
            s => Err(::capnp::Error::failed(format!("unrecognized mode: {}", s)))
        }
    }
}

fn do_testcase<C, S, T>(testcase: T, mode: Mode, reuse: S, compression: C, iters: u64) -> ::capnp::Result<()>
    where C: Serialize, S: for<'a> Scratch<'a>, T: TestCase,
{
    match mode {
        Mode::Object => pass_by_object(testcase, reuse, iters),
        Mode::Bytes => pass_by_bytes(testcase, reuse, compression, iters),
        Mode::Client => sync_client(testcase, reuse, compression, iters),
        Mode::Server => {
            let input: ::std::fs::File = unsafe { ::std::os::unix::io::FromRawFd::from_raw_fd(1) };
            let output: ::std::fs::File = unsafe { ::std::os::unix::io::FromRawFd::from_raw_fd(0) };
            server(testcase, reuse, compression, iters, input, output)
        }
        Mode::Pipe => pass_by_pipe(testcase, reuse, compression, iters),
    }
}

fn do_testcase1<C, S>(case: &str, mode: Mode, scratch: S, compression: C, iters: u64) -> ::capnp::Result<()>
    where C: Serialize, S: for<'a> Scratch<'a>,
{
    match case {
        "carsales" => do_testcase(carsales::CarSales, mode, scratch, compression, iters),
        "catrank" => do_testcase(catrank::CatRank, mode, scratch, compression, iters),
        "eval" => do_testcase(eval::Eval, mode, scratch, compression, iters),
        s => panic!("unrecognized test case: {}", s)
    }
}

fn do_testcase2<C>(case: &str, mode: Mode, scratch: &str, compression: C, iters: u64) -> ::capnp::Result<()>
    where C: Serialize,
{
    match scratch {
        "no-reuse" => do_testcase1(case, mode, NoScratch, compression, iters),
        "reuse" => do_testcase1(case, mode, UseScratch::new(), compression, iters),
        s => panic!("unrecognized reuse option: {}", s),
    }
}

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

    assert!(args.len() == 6,
            "USAGE: {} CASE MODE REUSE COMPRESSION ITERATION_COUNT",
            args[0]);

    let iters = match args[5].parse::<u64>() {
        Ok(n) => n,
        Err(_) => {
            panic!("Could not parse a u64 from: {}", args[5]);
        }
    };

    let mode = Mode::parse(&*args[2]).unwrap();

    let result = match &*args[4] {
        "none" => do_testcase2(&*args[1], mode, &*args[3], NoCompression, iters),
        "packed" => do_testcase2(&*args[1], mode, &*args[3], Packed, iters),
        s => panic!("unrecognized compression: {}", s)
    };

    match result {
        Ok(()) => (),
        Err(e) => {
            panic!("error: {:?}", e);
        }
    }
}
<commit_msg>clean up error handling in benchmark<commit_after>// Copyright (c) 2013-2017 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

extern crate capnp;
extern crate rand;

use std::{mem, io};

use capnp::{message, serialize, serialize_packed};
use capnp::traits::Owned;

pub mod common;

pub mod carsales_capnp {
  include!(concat!(env!("OUT_DIR"), "/carsales_capnp.rs"));
}
pub mod carsales;

pub mod catrank_capnp {
  include!(concat!(env!("OUT_DIR"), "/catrank_capnp.rs"));
}
pub mod catrank;

pub mod eval_capnp {
  include!(concat!(env!("OUT_DIR"), "/eval_capnp.rs"));
}

pub mod eval;

trait TestCase {
    type Request: for<'a> Owned<'a>;
    type Response: for<'a> Owned<'a>;
    type Expectation;

    fn setup_request(&self, &mut common::FastRand, <Self::Request as Owned>::Builder) -> Self::Expectation;
    fn handle_request(&self, <Self::Request as Owned>::Reader, <Self::Response as Owned>::Builder)
                      -> ::capnp::Result<()>;
    fn check_response(&self, <Self::Response as Owned>::Reader, Self::Expectation) -> ::capnp::Result<()>;
}

trait Serialize {
    fn read_message<R>(
        &self,
        read: &mut R,
        options: message::ReaderOptions)
                       -> ::capnp::Result<message::Reader<::capnp::serialize::OwnedSegments>>
        where R: io::BufRead;

    fn write_message<W, A>(&self, write: &mut W, message: &message::Builder<A>) -> ::capnp::Result<()>
        where W: io::Write, A: message::Allocator;
}

struct NoCompression;

impl Serialize for NoCompression {
    fn read_message<R>(&self, read: &mut R,
                       options: message::ReaderOptions)
                       -> ::capnp::Result<message::Reader<::capnp::serialize::OwnedSegments>>
        where R: io::BufRead
    {
        serialize::read_message(read, options)
    }

    fn write_message<W, A>(&self, write: &mut W, message: &message::Builder<A>) -> ::capnp::Result<()>
        where W: io::Write, A: message::Allocator {
        serialize::write_message(write, message).map_err(|e| e.into())
    }
}

struct Packed;

impl Serialize for Packed {
    fn read_message<R>(&self, read: &mut R,
                       options: message::ReaderOptions)
                       -> ::capnp::Result<message::Reader<::capnp::serialize::OwnedSegments>>
        where R: io::BufRead
    {
        serialize_packed::read_message(read, options)
    }

    fn write_message<W, A>(&self, write: &mut W, message: &message::Builder<A>) -> ::capnp::Result<()>
        where W: io::Write, A: message::Allocator {
        serialize_packed::write_message(write, message).map_err(|e| e.into())
    }
}

trait Scratch<'a> {
    type Allocator: message::Allocator;

    fn get_builders(&'a mut self) -> (message::Builder<Self::Allocator>, message::Builder<Self::Allocator>);
}

const SCRATCH_SIZE: usize = 128 * 1024;

#[derive(Clone, Copy)]
pub struct NoScratch;

impl <'a> Scratch<'a> for NoScratch {
    type Allocator = message::HeapAllocator;

    fn get_builders(&'a mut self) -> (message::Builder<Self::Allocator>, message::Builder<Self::Allocator>) {
        (message::Builder::new_default(), message::Builder::new_default())
    }
}

pub struct UseScratch {
    _owned_space: ::std::vec::Vec<::std::vec::Vec<capnp::Word>>,
    scratch_space: ::std::vec::Vec<message::ScratchSpace<'static>>,
}

impl UseScratch {
    pub fn new() -> UseScratch {
        let mut owned = Vec::new();
        let mut scratch = Vec::new();
        for _ in 0..6 {
            let mut words = ::capnp::Word::allocate_zeroed_vec(SCRATCH_SIZE);
            scratch.push(message::ScratchSpace::new(
                unsafe { mem::transmute(&mut words[..]) }));
            owned.push(words);
        }
        UseScratch {
            _owned_space: owned,
            scratch_space: scratch,
        }
    }
}

impl <'a> Scratch<'a> for UseScratch {
    type Allocator = message::ScratchSpaceHeapAllocator<'a, 'a>;

    fn get_builders(&'a mut self) -> (message::Builder<Self::Allocator>, message::Builder<Self::Allocator>) {
        (message::Builder::new(message::ScratchSpaceHeapAllocator::new(
            unsafe { mem::transmute(&mut self.scratch_space[0]) })),
         message::Builder::new(message::ScratchSpaceHeapAllocator::new(
             unsafe { mem::transmute(&mut self.scratch_space[1]) })))

    }
}

fn pass_by_object<S, T>(testcase: T, mut reuse: S, iters: u64) -> ::capnp::Result<()>
    where S: for<'a> Scratch<'a>, T: TestCase,
{
    let mut rng = common::FastRand::new();
    for _ in 0..iters {
        let (mut message_req, mut message_res) = reuse.get_builders();

        let expected = testcase.setup_request(
            &mut rng,
            message_req.init_root());

        try!(testcase.handle_request(
            try!(message_req.get_root_as_reader()),
            message_res.init_root()));

        try!(testcase.check_response(
            try!(message_res.get_root_as_reader()),
            expected));
    }
    Ok(())
}

fn pass_by_bytes<C, S, T>(testcase: T, mut reuse: S, compression: C, iters: u64) -> ::capnp::Result<()>
    where C: Serialize, S: for<'a> Scratch<'a>, T: TestCase,
{
    let mut request_bytes: ::std::vec::Vec<u8> =
        ::std::iter::repeat(0u8).take(SCRATCH_SIZE * 8).collect();
    let mut response_bytes: ::std::vec::Vec<u8> =
        ::std::iter::repeat(0u8).take(SCRATCH_SIZE * 8).collect();
    let mut rng = common::FastRand::new();
    for _ in 0..iters {
        let (mut message_req, mut message_res) = reuse.get_builders();

        let expected = {
            let request = message_req.init_root();
            testcase.setup_request(&mut rng, request)
        };

        {
            let response = message_res.init_root();

            {
                let mut writer: &mut [u8] = &mut request_bytes;
                try!(compression.write_message(&mut writer, &mut message_req));
            }

            let mut request_bytes1: &[u8] = &request_bytes;
            let message_reader = try!(compression.read_message(
                &mut request_bytes1,
                Default::default()));

            let request_reader = try!(message_reader.get_root());
            try!(testcase.handle_request(request_reader, response));
        }

        {
            let mut writer: &mut [u8] = &mut response_bytes;
            try!(compression.write_message(&mut writer, &mut message_res));
        }

        let mut response_bytes1: &[u8] = &response_bytes;
        let message_reader = try!(compression.read_message(
            &mut response_bytes1,
            Default::default()));

        let response_reader = try!(message_reader.get_root());
        try!(testcase.check_response(response_reader, expected));
    }
    Ok(())
}

fn server<C, S, T, R, W>(testcase: T, mut reuse: S, compression: C, iters: u64, mut input: R, mut output: W)
                         -> ::capnp::Result<()>
    where C: Serialize, S: for<'a> Scratch<'a>, T: TestCase, R: io::Read, W: io::Write,
{
    let mut out_buffered = io::BufWriter::new(&mut output);
    let mut in_buffered = io::BufReader::new(&mut input);
    for _ in 0..iters {
        use std::io::Write;
        let (mut message_res, _) = reuse.get_builders();

        {
            let response = message_res.init_root();
            let message_reader = try!(compression.read_message(
                &mut in_buffered,
                capnp::message::DEFAULT_READER_OPTIONS));
            let request_reader = try!(message_reader.get_root());
            try!(testcase.handle_request(request_reader, response));
        }

        try!(compression.write_message(&mut out_buffered, &mut message_res));
        try!(out_buffered.flush());
    }
    Ok(())
}

fn sync_client<C, S, T>(testcase: T, mut reuse: S, compression: C, iters: u64)
                        -> ::capnp::Result<()>
    where C: Serialize, S: for<'a> Scratch<'a>, T: TestCase,
{
    let mut out_stream: ::std::fs::File = unsafe { ::std::os::unix::io::FromRawFd::from_raw_fd(1) };
    let mut in_stream: ::std::fs::File = unsafe { ::std::os::unix::io::FromRawFd::from_raw_fd(0) };
    let mut in_buffered = io::BufReader::new(&mut in_stream);
    let mut out_buffered = io::BufWriter::new(&mut out_stream);
    let mut rng = common::FastRand::new();
    for _ in 0..iters {
        use std::io::Write;
        let (mut message_req, _) = reuse.get_builders();

        let expected = {
            let request = message_req.init_root();
            testcase.setup_request(&mut rng, request)
        };
        try!(compression.write_message(&mut out_buffered, &mut message_req));
        try!(out_buffered.flush());

        let message_reader = try!(compression.read_message(
            &mut in_buffered,
            Default::default()));
        let response_reader = try!(message_reader.get_root());
        try!(testcase.check_response(response_reader, expected));
    }
    Ok(())
}

fn pass_by_pipe<C, S, T>(testcase: T, reuse: S, compression: C, iters: u64) -> ::capnp::Result<()>
    where C: Serialize, S: for<'a> Scratch<'a>, T: TestCase,
{
    use std::{process, env};

    let mut args: Vec<String> = env::args().collect();
    args[2] = "client".to_string();

    let mut command = process::Command::new(&args[0]);
    command.args(&args[1..args.len()]);
    command.stdin(process::Stdio::piped());
    command.stdout(process::Stdio::piped());
    command.stderr(process::Stdio::null());
    match command.spawn() {
        Ok(ref mut p) => {
            let child_std_out = p.stdout.take().unwrap();
            let child_std_in = p.stdin.take().unwrap();
            try!(server(testcase, reuse, compression, iters, child_std_out, child_std_in));
            println!("{}", p.wait().unwrap());
            Ok(())
        }
        Err(e) => {
            println!("could not start process: {}", e);
            Ok(())
        }
    }
}

pub enum Mode {
    Object,
    Bytes,
    Client,
    Server,
    Pipe,
}

impl Mode {
    pub fn parse(s: &str) -> ::capnp::Result<Mode> {
        match s {
            "object" => Ok(Mode::Object),
            "bytes" => Ok(Mode::Bytes),
            "client" => Ok(Mode::Client),
            "server" => Ok(Mode::Server),
            "pipe" => Ok(Mode::Pipe),
            s => Err(::capnp::Error::failed(format!("unrecognized mode: {}", s))),
        }
    }
}

fn do_testcase<C, S, T>(testcase: T, mode: Mode, reuse: S, compression: C, iters: u64) -> ::capnp::Result<()>
    where C: Serialize, S: for<'a> Scratch<'a>, T: TestCase,
{
    match mode {
        Mode::Object => pass_by_object(testcase, reuse, iters),
        Mode::Bytes => pass_by_bytes(testcase, reuse, compression, iters),
        Mode::Client => sync_client(testcase, reuse, compression, iters),
        Mode::Server => {
            let input: ::std::fs::File = unsafe { ::std::os::unix::io::FromRawFd::from_raw_fd(1) };
            let output: ::std::fs::File = unsafe { ::std::os::unix::io::FromRawFd::from_raw_fd(0) };
            server(testcase, reuse, compression, iters, input, output)
        }
        Mode::Pipe => pass_by_pipe(testcase, reuse, compression, iters),
    }
}

fn do_testcase1<C, S>(case: &str, mode: Mode, scratch: S, compression: C, iters: u64) -> ::capnp::Result<()>
    where C: Serialize, S: for<'a> Scratch<'a>,
{
    match case {
        "carsales" => do_testcase(carsales::CarSales, mode, scratch, compression, iters),
        "catrank" => do_testcase(catrank::CatRank, mode, scratch, compression, iters),
        "eval" => do_testcase(eval::Eval, mode, scratch, compression, iters),
        s => Err(::capnp::Error::failed(format!("unrecognized test case: {}", s))),
    }
}

fn do_testcase2<C>(case: &str, mode: Mode, scratch: &str, compression: C, iters: u64) -> ::capnp::Result<()>
    where C: Serialize,
{
    match scratch {
        "no-reuse" => do_testcase1(case, mode, NoScratch, compression, iters),
        "reuse" => do_testcase1(case, mode, UseScratch::new(), compression, iters),
        s => Err(::capnp::Error::failed(format!("unrecognized reuse option: {}", s))),
    }
}

fn try_main() -> ::capnp::Result<()> {
    let args: Vec<String> = ::std::env::args().collect();

    assert!(args.len() == 6,
            "USAGE: {} CASE MODE REUSE COMPRESSION ITERATION_COUNT",
            args[0]);

    let iters = match args[5].parse::<u64>() {
        Ok(n) => n,
        Err(_) =>
            return Err(::capnp::Error::failed(format!("Could not parse a u64 from: {}", args[5]))),
    };

    let mode = try!(Mode::parse(&*args[2]));

    match &*args[4] {
        "none" => do_testcase2(&*args[1], mode, &*args[3], NoCompression, iters),
        "packed" => do_testcase2(&*args[1], mode, &*args[3], Packed, iters),
        s => Err(::capnp::Error::failed(format!("unrecognized compression: {}", s))),
    }
}

pub fn main() {
    match try_main() {
        Ok(()) => (),
        Err(e) => {
            panic!("error: {:?}", e);
        }
    }
}
<|endoftext|>