<commit_before>use pact_mock_server_ffi::message_with_metadata;
use pact_mock_server_ffi::write_pact_file;
use pact_mock_server_ffi::write_message_pact_file;
use libc::c_char;
use pact_mock_server_ffi::message_reify;
use pact_mock_server_ffi::message_with_contents;
use pact_mock_server_ffi::message_given;
use pact_mock_server_ffi::new_message;
use pact_mock_server_ffi::message_expects_to_receive;
use pact_mock_server_ffi::new_message_pact;
use std::ffi::{CStr, CString};
use std::panic::catch_unwind;

use bytes::Bytes;
use expectest::prelude::*;
use maplit::*;
use reqwest::blocking::Client;
use reqwest::header::CONTENT_TYPE;

use pact_mock_server_ffi::{
  cleanup_mock_server,
  create_mock_server,
  handles::InteractionPart,
  mock_server_mismatches,
  new_interaction,
  new_pact,
  upon_receiving,
  with_request,
  with_body,
  with_header,
  with_multipart_file,
  with_query_parameter,
  response_status,
  create_mock_server_for_pact
};
use pact_models::bodies::OptionalBody;

#[test]
fn post_to_mock_server_with_misatches() {
  let pact_json = include_str!("post-pact.json");
  let pact_json_c = CString::new(pact_json).expect("Could not construct C string from json");
  let address = CString::new("127.0.0.1:0").unwrap();
  let port = create_mock_server(pact_json_c.as_ptr(), address.as_ptr(), false);
  expect!(port).to(be_greater_than(0));

  let _result = catch_unwind(|| {
    let client = Client::default();
    client.post(format!("http://127.0.0.1:{}/path", port).as_str())
      .header(CONTENT_TYPE, "application/json")
      .body(r#"{"foo":"no-very-bar"}"#)
      .send()
  });

  let mismatches = unsafe {
    CStr::from_ptr(mock_server_mismatches(port)).to_string_lossy().into_owned()
  };

  cleanup_mock_server(port);

  expect!(mismatches).to(be_equal_to("[{\"method\":\"POST\",\"mismatches\":[{\"actual\":\"\\\"no-very-bar\\\"\",\"expected\":\"\\\"bar\\\"\",\"mismatch\":\"Expected \'bar\' to be equal to \'no-very-bar\'\",\"path\":\"$.foo\",\"type\":\"BodyMismatch\"}],\"path\":\"/path\",\"type\":\"request-mismatch\"}]"));
}

#[test]
fn create_header_with_multiple_values() {
  let consumer_name = CString::new("consumer").unwrap();
  let provider_name = CString::new("provider").unwrap();
  let pact_handle = new_pact(consumer_name.as_ptr(), provider_name.as_ptr());
  let description = CString::new("create_header_with_multiple_values").unwrap();
  let interaction = new_interaction(pact_handle, description.as_ptr());
  let name = CString::new("accept").unwrap();
  let value_1 = CString::new("application/hal+json").unwrap();
  let value_2 = CString::new("application/json").unwrap();
  with_header(interaction.clone(), InteractionPart::Request, name.as_ptr(), 1, value_2.as_ptr());
  with_header(interaction.clone(), InteractionPart::Request, name.as_ptr(), 0, value_1.as_ptr());
  interaction.with_interaction(&|_, i| {
    expect!(i.request.headers.as_ref()).to(be_some().value(&hashmap!{
      "accept".to_string() => vec!["application/hal+json".to_string(), "application/json".to_string()]
    }));
  });
}

#[test]
fn create_query_parameter_with_multiple_values() {
  let consumer_name = CString::new("consumer").unwrap();
  let provider_name = CString::new("provider").unwrap();
  let pact_handle = new_pact(consumer_name.as_ptr(), provider_name.as_ptr());
  let description = CString::new("create_query_parameter_with_multiple_values").unwrap();
  let interaction = new_interaction(pact_handle, description.as_ptr());
  let name = CString::new("q").unwrap();
  let value_1 = CString::new("1").unwrap();
  let value_2 = CString::new("2").unwrap();
  let value_3 = CString::new("3").unwrap();
  with_query_parameter(interaction.clone(), name.as_ptr(), 2, value_3.as_ptr());
  with_query_parameter(interaction.clone(), name.as_ptr(), 0, value_1.as_ptr());
  with_query_parameter(interaction.clone(), name.as_ptr(), 1, value_2.as_ptr());
  interaction.with_interaction(&|_, i| {
    expect!(i.request.query.as_ref()).to(be_some().value(&hashmap!{
      "q".to_string() => vec!["1".to_string(), "2".to_string(), "3".to_string()]
    }));
  });
}

#[test]
fn create_multipart_file() {
  let consumer_name = CString::new("consumer").unwrap();
  let provider_name = CString::new("provider").unwrap();
  let pact_handle = new_pact(consumer_name.as_ptr(), provider_name.as_ptr());
  let description = CString::new("create_multipart_file").unwrap();
  let interaction = new_interaction(pact_handle, description.as_ptr());
  let content_type = CString::new("application/json").unwrap();
  let file = CString::new("tests/multipart-test-file.json").unwrap();
  let part_name = CString::new("file").unwrap();

  with_multipart_file(interaction.clone(), InteractionPart::Request, content_type.as_ptr(), file.as_ptr(), part_name.as_ptr());

  interaction.with_interaction(&|_, i| {
    let boundary = match &i.request.headers {
      Some(hashmap) => {
        hashmap.get("Content-Type")
          .map(|vec| vec[0].as_str())
          // Sorry for awful mime parsing..
          .map(|content_type: &str| content_type.split("boundary=").collect::<Vec<_>>())
          .map(|split| split[1])
          .unwrap_or("")
      },
      None => ""
    };

    expect!(i.request.headers.as_ref()).to(be_some().value(&hashmap!{
      "Content-Type".to_string() => vec![format!("multipart/form-data; boundary={}", boundary)],
    }));

    let actual_req_body_str = match &i.request.body {
      OptionalBody::Present(body, _) => body.clone(),
      _ => Bytes::new(),
    };

    let expected_req_body = Bytes::from(format!(
      "--{boundary}\r\nContent-Disposition: form-data; name=\"file\"; filename=\"multipart-test-file.json\"\r\nContent-Type: application/json\r\n\r\ntrue\r\n--{boundary}--\r\n",
      boundary = boundary
    ));

    expect!(actual_req_body_str).to(be_equal_to(expected_req_body));
  });
}

#[test]
fn http_consumer_feature_test() {
  let consumer_name = CString::new("http-consumer").unwrap();
  let provider_name = CString::new("http-provider").unwrap();
  let pact_handle = new_pact(consumer_name.as_ptr(), provider_name.as_ptr());
  let description = CString::new("request_with_matchers").unwrap();
  let interaction = new_interaction(pact_handle.clone(), description.as_ptr());
  let special_header = CString::new("My-Special-Content-Type").unwrap();
  let content_type = CString::new("Content-Type").unwrap();
  let authorization = CString::new("Authorization").unwrap();
  let path_matcher = CString::new("{\"value\":\"/request/1234\",\"pact:matcher:type\":\"regex\", \"regex\":\"\\/request\\/[0-9]+\"}").unwrap();
  let value_header_with_matcher = CString::new("{\"value\":\"application/json\",\"pact:matcher:type\":\"dummy\"}").unwrap();
  let auth_header_with_matcher = CString::new("{\"value\":\"Bearer 1234\",\"pact:matcher:type\":\"regex\", \"regex\":\"Bearer [0-9]+\"}").unwrap();
  let query_param_matcher = CString::new("{\"value\":\"bar\",\"pact:matcher:type\":\"regex\", \"regex\":\"(bar|baz|bat)\"}").unwrap();
  let request_body_with_matchers = CString::new("{\"id\": {\"value\":1,\"pact:matcher:type\":\"type\"}}").unwrap();
  let response_body_with_matchers = CString::new("{\"created\": {\"value\":\"maybe\",\"pact:matcher:type\":\"regex\", \"regex\":\"(yes|no|maybe)\"}}").unwrap();
  let address = CString::new("127.0.0.1:0").unwrap();
  let file_path = CString::new("/tmp/pact").unwrap();
  let description = CString::new("a request to test the FFI interface").unwrap();
  let method = CString::new("POST").unwrap();
  let query =  CString::new("foo").unwrap();
  let header = CString::new("application/json").unwrap();

  upon_receiving(interaction.clone(), description.as_ptr());
  with_request(interaction.clone(), method  .as_ptr(), path_matcher.as_ptr());
  with_header(interaction.clone(), InteractionPart::Request, content_type.as_ptr(), 0, value_header_with_matcher.as_ptr());
  with_header(interaction.clone(), InteractionPart::Request, authorization.as_ptr(), 0, auth_header_with_matcher.as_ptr());
  with_query_parameter(interaction.clone(), query.as_ptr(), 0, query_param_matcher.as_ptr());
  with_body(interaction.clone(), InteractionPart::Request, header.as_ptr(), request_body_with_matchers.as_ptr());
  // will respond with...
  with_header(interaction.clone(), InteractionPart::Response, content_type.as_ptr(), 0, value_header_with_matcher.as_ptr());
  with_header(interaction.clone(), InteractionPart::Response, special_header.as_ptr(), 0, value_header_with_matcher.as_ptr());
  with_body(interaction.clone(), InteractionPart::Response, CString::new("application/json").unwrap().as_ptr(), response_body_with_matchers.as_ptr());
  response_status(interaction.clone(), 200);
  let port = create_mock_server_for_pact(pact_handle.clone(), address.as_ptr(), false);

  expect!(port).to(be_greater_than(0));

  let _ = catch_unwind(|| {
    let client = Client::default();
    let result = client.post(format!("http://127.0.0.1:{}/request/9999?foo=baz", port).as_str())
      .header("Content-Type", "application/json")
      .header("Authorization", "Bearer 9999")
      .body(r#"{"id": 7}"#)
      .send();

    match result {
      Ok(res) => {
        expect!(res.status()).to(be_eq(200));
        expect!(res.headers().get("My-Special-Content-Type").unwrap()).to(be_eq("application/json"));
        let json: serde_json::Value = res.json().unwrap_or_default();
        expect!(json.get("created").unwrap().as_str().unwrap()).to(be_eq("maybe"));
      },
      Err(_) => {
        panic!("expected 200 response but request failed");
      }
    };
  });

  let mismatches = unsafe {
    CStr::from_ptr(mock_server_mismatches(port)).to_string_lossy().into_owned()
  };

  write_pact_file(port, file_path.as_ptr(), true);
  cleanup_mock_server(port);

  expect!(mismatches).to(be_equal_to("[]"));
}

#[test]
fn message_consumer_feature_test() {
  let consumer_name = CString::new("message-consumer").unwrap();
  let provider_name = CString::new("message-provider").unwrap();
  let description = CString::new("message_request_with_matchers").unwrap();
  let content_type = CString::new("application/json").unwrap();
  let metadata_key = CString::new("message-queue-name").unwrap();
  let metadata_val = CString::new("message-queue-val").unwrap();
  let request_body_with_matchers = CString::new("{\"id\": {\"value\":1,\"pact:matcher:type\":\"type\"}}").unwrap();
  let file_path = CString::new("/tmp/pact").unwrap();

  let message_pact_handle = new_message_pact(consumer_name.as_ptr(), provider_name.as_ptr());
  let message_handle = new_message(message_pact_handle.clone(), description.as_ptr());
  let given = CString::new("a functioning FFI interface").unwrap();
  message_given(message_handle.clone(), given.as_ptr());
  let descr = CString::new("a request to test the FFI interface").unwrap();
  message_expects_to_receive(message_handle.clone(), descr.as_ptr());
  message_with_contents(message_handle.clone(), content_type.as_ptr(), request_body_with_matchers.as_ptr(), request_body_with_matchers.as_bytes().len());
  message_with_metadata(message_handle.clone(), metadata_key.as_ptr(), metadata_val.as_ptr());
  let res: *const c_char = message_reify(message_handle.clone());
  let c_str: &CStr = unsafe { CStr::from_ptr(res) };
  expect!(c_str.to_str()).to(be_ok().value("{\"contents\":{\"id\":1},\"description\":\"a request to test the FFI interface\",\"metadata\":{\"contentType\":\"application/json\",\"message-queue-name\":\"message-queue-val\"},\"providerStates\":[{\"name\":\"a functioning FFI interface\"}]}".to_string()));
  let res = write_message_pact_file(message_pact_handle.clone(), file_path.as_ptr(), true);
  expect!(res).to(be_eq(0));
}
<commit_msg>fix: ok so maybe let's see if this works on linux 🤷<commit_after>use pact_mock_server_ffi::message_with_metadata;
use pact_mock_server_ffi::write_pact_file;
use pact_mock_server_ffi::write_message_pact_file;
use libc::c_char;
use pact_mock_server_ffi::message_reify;
use pact_mock_server_ffi::message_with_contents;
use pact_mock_server_ffi::message_given;
use pact_mock_server_ffi::new_message;
use pact_mock_server_ffi::message_expects_to_receive;
use pact_mock_server_ffi::new_message_pact;
use std::ffi::{CStr, CString};
use std::panic::catch_unwind;

use bytes::Bytes;
use expectest::prelude::*;
use maplit::*;
use reqwest::blocking::Client;
use reqwest::header::CONTENT_TYPE;

use pact_mock_server_ffi::{
  cleanup_mock_server,
  create_mock_server,
  handles::InteractionPart,
  mock_server_mismatches,
  new_interaction,
  new_pact,
  upon_receiving,
  with_request,
  with_body,
  with_header,
  with_multipart_file,
  with_query_parameter,
  response_status,
  create_mock_server_for_pact
};
use pact_models::bodies::OptionalBody;

#[test]
fn post_to_mock_server_with_misatches() {
  let pact_json = include_str!("post-pact.json");
  let pact_json_c = CString::new(pact_json).expect("Could not construct C string from json");
  let address = CString::new("127.0.0.1:0").unwrap();
  let port = create_mock_server(pact_json_c.as_ptr(), address.as_ptr(), false);
  expect!(port).to(be_greater_than(0));

  let _result = catch_unwind(|| {
    let client = Client::default();
    client.post(format!("http://127.0.0.1:{}/path", port).as_str())
      .header(CONTENT_TYPE, "application/json")
      .body(r#"{"foo":"no-very-bar"}"#)
      .send()
  });

  let mismatches = unsafe {
    CStr::from_ptr(mock_server_mismatches(port)).to_string_lossy().into_owned()
  };

  cleanup_mock_server(port);

  expect!(mismatches).to(be_equal_to("[{\"method\":\"POST\",\"mismatches\":[{\"actual\":\"\\\"no-very-bar\\\"\",\"expected\":\"\\\"bar\\\"\",\"mismatch\":\"Expected \'bar\' to be equal to \'no-very-bar\'\",\"path\":\"$.foo\",\"type\":\"BodyMismatch\"}],\"path\":\"/path\",\"type\":\"request-mismatch\"}]"));
}

#[test]
fn create_header_with_multiple_values() {
  let consumer_name = CString::new("consumer").unwrap();
  let provider_name = CString::new("provider").unwrap();
  let pact_handle = new_pact(consumer_name.as_ptr(), provider_name.as_ptr());
  let description = CString::new("create_header_with_multiple_values").unwrap();
  let interaction = new_interaction(pact_handle, description.as_ptr());
  let name = CString::new("accept").unwrap();
  let value_1 = CString::new("application/hal+json").unwrap();
  let value_2 = CString::new("application/json").unwrap();
  with_header(interaction.clone(), InteractionPart::Request, name.as_ptr(), 1, value_2.as_ptr());
  with_header(interaction.clone(), InteractionPart::Request, name.as_ptr(), 0, value_1.as_ptr());
  interaction.with_interaction(&|_, i| {
    expect!(i.request.headers.as_ref()).to(be_some().value(&hashmap!{
      "accept".to_string() => vec!["application/hal+json".to_string(), "application/json".to_string()]
    }));
  });
}

#[test]
fn create_query_parameter_with_multiple_values() {
  let consumer_name = CString::new("consumer").unwrap();
  let provider_name = CString::new("provider").unwrap();
  let pact_handle = new_pact(consumer_name.as_ptr(), provider_name.as_ptr());
  let description = CString::new("create_query_parameter_with_multiple_values").unwrap();
  let interaction = new_interaction(pact_handle, description.as_ptr());
  let name = CString::new("q").unwrap();
  let value_1 = CString::new("1").unwrap();
  let value_2 = CString::new("2").unwrap();
  let value_3 = CString::new("3").unwrap();
  with_query_parameter(interaction.clone(), name.as_ptr(), 2, value_3.as_ptr());
  with_query_parameter(interaction.clone(), name.as_ptr(), 0, value_1.as_ptr());
  with_query_parameter(interaction.clone(), name.as_ptr(), 1, value_2.as_ptr());
  interaction.with_interaction(&|_, i| {
    expect!(i.request.query.as_ref()).to(be_some().value(&hashmap!{
      "q".to_string() => vec!["1".to_string(), "2".to_string(), "3".to_string()]
    }));
  });
}

#[test]
fn create_multipart_file() {
  let consumer_name = CString::new("consumer").unwrap();
  let provider_name = CString::new("provider").unwrap();
  let pact_handle = new_pact(consumer_name.as_ptr(), provider_name.as_ptr());
  let description = CString::new("create_multipart_file").unwrap();
  let interaction = new_interaction(pact_handle, description.as_ptr());
  let content_type = CString::new("application/json").unwrap();
  let file = CString::new("tests/multipart-test-file.json").unwrap();
  let part_name = CString::new("file").unwrap();

  with_multipart_file(interaction.clone(), InteractionPart::Request, content_type.as_ptr(), file.as_ptr(), part_name.as_ptr());

  interaction.with_interaction(&|_, i| {
    let boundary = match &i.request.headers {
      Some(hashmap) => {
        hashmap.get("Content-Type")
          .map(|vec| vec[0].as_str())
          // Sorry for awful mime parsing..
          .map(|content_type: &str| content_type.split("boundary=").collect::<Vec<_>>())
          .map(|split| split[1])
          .unwrap_or("")
      },
      None => ""
    };

    expect!(i.request.headers.as_ref()).to(be_some().value(&hashmap!{
      "Content-Type".to_string() => vec![format!("multipart/form-data; boundary={}", boundary)],
    }));

    let actual_req_body_str = match &i.request.body {
      OptionalBody::Present(body, _) => body.clone(),
      _ => Bytes::new(),
    };

    let expected_req_body = Bytes::from(format!(
      "--{boundary}\r\nContent-Disposition: form-data; name=\"file\"; filename=\"multipart-test-file.json\"\r\nContent-Type: application/json\r\n\r\ntrue\r\n--{boundary}--\r\n",
      boundary = boundary
    ));

    expect!(actual_req_body_str).to(be_equal_to(expected_req_body));
  });
}

#[test]
fn http_consumer_feature_test() {
  let consumer_name = CString::new("http-consumer").unwrap();
  let provider_name = CString::new("http-provider").unwrap();
  let pact_handle = new_pact(consumer_name.as_ptr(), provider_name.as_ptr());
  let description = CString::new("request_with_matchers").unwrap();
  let interaction = new_interaction(pact_handle.clone(), description.as_ptr());
  let special_header = CString::new("My-Special-Content-Type").unwrap();
  let content_type = CString::new("Content-Type").unwrap();
  let authorization = CString::new("Authorization").unwrap();
  let path_matcher = CString::new("{\"value\":\"/request/1234\",\"pact:matcher:type\":\"regex\", \"regex\":\"\\/request\\/[0-9]+\"}").unwrap();
  let value_header_with_matcher = CString::new("{\"value\":\"application/json\",\"pact:matcher:type\":\"dummy\"}").unwrap();
  let auth_header_with_matcher = CString::new("{\"value\":\"Bearer 1234\",\"pact:matcher:type\":\"regex\", \"regex\":\"Bearer [0-9]+\"}").unwrap();
  let query_param_matcher = CString::new("{\"value\":\"bar\",\"pact:matcher:type\":\"regex\", \"regex\":\"(bar|baz|bat)\"}").unwrap();
  let request_body_with_matchers = CString::new("{\"id\": {\"value\":1,\"pact:matcher:type\":\"type\"}}").unwrap();
  let response_body_with_matchers = CString::new("{\"created\": {\"value\":\"maybe\",\"pact:matcher:type\":\"regex\", \"regex\":\"(yes|no|maybe)\"}}").unwrap();
  let address = CString::new("127.0.0.1:0").unwrap();
  let file_path = CString::new("/tmp/pact").unwrap();
  let description = CString::new("a request to test the FFI interface").unwrap();
  let method = CString::new("POST").unwrap();
  let query =  CString::new("foo").unwrap();
  let header = CString::new("application/json").unwrap();

  upon_receiving(interaction.clone(), description.as_ptr());
  with_request(interaction.clone(), method  .as_ptr(), path_matcher.as_ptr());
  with_header(interaction.clone(), InteractionPart::Request, content_type.as_ptr(), 0, value_header_with_matcher.as_ptr());
  with_header(interaction.clone(), InteractionPart::Request, authorization.as_ptr(), 0, auth_header_with_matcher.as_ptr());
  with_query_parameter(interaction.clone(), query.as_ptr(), 0, query_param_matcher.as_ptr());
  with_body(interaction.clone(), InteractionPart::Request, header.as_ptr(), request_body_with_matchers.as_ptr());
  // will respond with...
  with_header(interaction.clone(), InteractionPart::Response, content_type.as_ptr(), 0, value_header_with_matcher.as_ptr());
  with_header(interaction.clone(), InteractionPart::Response, special_header.as_ptr(), 0, value_header_with_matcher.as_ptr());
  with_body(interaction.clone(), InteractionPart::Response, header.as_ptr(), response_body_with_matchers.as_ptr());
  response_status(interaction.clone(), 200);
  let port = create_mock_server_for_pact(pact_handle.clone(), address.as_ptr(), false);

  expect!(port).to(be_greater_than(0));

  let _ = catch_unwind(|| {
    let client = Client::default();
    let result = client.post(format!("http://127.0.0.1:{}/request/9999?foo=baz", port).as_str())
      .header("Content-Type", "application/json")
      .header("Authorization", "Bearer 9999")
      .body(r#"{"id": 7}"#)
      .send();

    match result {
      Ok(res) => {
        expect!(res.status()).to(be_eq(200));
        expect!(res.headers().get("My-Special-Content-Type").unwrap()).to(be_eq("application/json"));
        let json: serde_json::Value = res.json().unwrap_or_default();
        expect!(json.get("created").unwrap().as_str().unwrap()).to(be_eq("maybe"));
      },
      Err(_) => {
        panic!("expected 200 response but request failed");
      }
    };
  });

  let mismatches = unsafe {
    CStr::from_ptr(mock_server_mismatches(port)).to_string_lossy().into_owned()
  };

  write_pact_file(port, file_path.as_ptr(), true);
  cleanup_mock_server(port);

  expect!(mismatches).to(be_equal_to("[]"));
}

#[test]
fn message_consumer_feature_test() {
  let consumer_name = CString::new("message-consumer").unwrap();
  let provider_name = CString::new("message-provider").unwrap();
  let description = CString::new("message_request_with_matchers").unwrap();
  let content_type = CString::new("application/json").unwrap();
  let metadata_key = CString::new("message-queue-name").unwrap();
  let metadata_val = CString::new("message-queue-val").unwrap();
  let request_body_with_matchers = CString::new("{\"id\": {\"value\":1,\"pact:matcher:type\":\"type\"}}").unwrap();
  let file_path = CString::new("/tmp/pact").unwrap();
  let given = CString::new("a functioning FFI interface").unwrap();
  let receive_description = CString::new("a request to test the FFI interface").unwrap();

  let message_pact_handle = new_message_pact(consumer_name.as_ptr(), provider_name.as_ptr());
  let message_handle = new_message(message_pact_handle.clone(), description.as_ptr());
  message_given(message_handle.clone(), given.as_ptr());
  message_expects_to_receive(message_handle.clone(), receive_description.as_ptr());
  message_with_contents(message_handle.clone(), content_type.as_ptr(), request_body_with_matchers.as_ptr(), request_body_with_matchers.as_bytes().len());
  message_with_metadata(message_handle.clone(), metadata_key.as_ptr(), metadata_val.as_ptr());
  let res: *const c_char = message_reify(message_handle.clone());
  let reified: &CStr = unsafe { CStr::from_ptr(res) };
  expect!(reified.to_str().to_owned()).to(be_ok().value("{\"contents\":{\"id\":1},\"description\":\"a request to test the FFI interface\",\"metadata\":{\"contentType\":\"application/json\",\"message-queue-name\":\"message-queue-val\"},\"providerStates\":[{\"name\":\"a functioning FFI interface\"}]}".to_string()));
  let res = write_message_pact_file(message_pact_handle.clone(), file_path.as_ptr(), true);
  expect!(res).to(be_eq(0));
}
<|endoftext|>


<commit_before>//! Generic B-Tree implementation.
//!
//! This module defines a `Btree<K, V>` type which provides similar functionality to
//! `BtreeMap<K, V>`, but with some important differences in the implementation:
//!
//! 1. Memory is allocated from a `NodePool<K, V>` instead of the global heap.
//! 2. The footprint of a BTree is only 4 bytes.
//! 3. A BTree doesn't implement `Drop`, leaving it to the pool to manage memory.
//!
//! The node pool is intended to be used as a LIFO allocator. After building up a larger data
//! structure with many list references, the whole thing can be discarded quickly by clearing the
//! pool.

use std::marker::PhantomData;

// A Node reference is a direct index to an element of the pool.
type NodeRef = u32;

/// A B-tree data structure which nodes are allocated from a pool.
pub struct BTree<K, V> {
    index: NodeRef,
    unused1: PhantomData<K>,
    unused2: PhantomData<V>,
}

/// An enum representing a B-tree node.
/// Keys and values are required to implement Default.
enum Node<K, V> {
    Inner {
        size: u8,
        keys: [K; 7],
        nodes: [NodeRef; 8],
    },
    Leaf {
        size: u8,
        keys: [K; 7],
        values: [V; 7],
    },
}

/// Memory pool for nodes.
struct NodePool<K, V> {
    // The array containing the nodes.
    data: Vec<Node<K, V>>,

    // A free list
    freelist: Vec<NodeRef>,
}

impl<K: Default, V: Default> NodePool<K, V> {
    /// Create a new NodePool.
    pub fn new() -> NodePool<K, V> {
        NodePool {
            data: Vec::new(),
            freelist: Vec::new(),
        }
    }

    /// Get a B-tree node.
    pub fn get(&self, index: u32) -> Option<&Node<K, V>> {
        unimplemented!()
    }
}

impl<K: Default, V: Default> BTree<K, V> {
    /// Search for `key` and return a `Cursor` that either points at `key` or the position
    /// where it would be inserted.
    pub fn search(&mut self, key: K) -> Cursor<K, V> {
        unimplemented!()
    }
}

pub struct Cursor<'a, K: 'a, V: 'a> {
    pool: &'a mut NodePool<K, V>,
    height: usize,
    path: [(NodeRef, u8); 16],
}

impl<'a, K: Default, V: Default> Cursor<'a, K, V> {
    /// The key at the cursor position. Returns `None` when the cursor points off the end.
    pub fn key(&self) -> Option<K> {
        unimplemented!()
    }

    /// The value at the cursor position. Returns `None` when the cursor points off the end.
    pub fn value(&self) -> Option<&V> {
        unimplemented!()
    }

    /// Move to the next element.
    /// Returns `false` if that moves the cursor off the end.
    pub fn next(&mut self) -> bool {
        unimplemented!()
    }

    /// Move to the previous element.
    /// Returns `false` if this moves the cursor before the beginning.
    pub fn prev(&mut self) -> bool {
        unimplemented!()
    }

    /// Insert a `(key, value)` pair at the cursor position.
    /// It is an error to insert a key that would be out of order at this position.
    pub fn insert(&mut self, key: K, value: V) {
        unimplemented!()
    }

    /// Remove the current element.
    pub fn remove(&mut self) {
        unimplemented!()
    }
}
<commit_msg>Remove the dead B-Tree interface.<commit_after><|endoftext|>


<commit_before>extern crate clap;
extern crate iron;
extern crate router;

// GET  /bucket/<BUCKET_NAME>/doc - list
// GET  /bucket/<BUCKET_NAME>/doc/<ID> - get
// POST /bucket/<BUCKET_NAME>/doc/<ID> - upsert
// POST /bucket/<BUCKET_NAME>/doc/add/<ID> - add
// POST /bucket/<BUCKET_NAME>/doc/append/<ID> - append
// POST /bucket/<BUCKET_NAME>/doc/prepend/<ID> - append
// POST /bucket/<BUCKET_NAME>/doc/replace/<ID> - replace
// POST /bucket/<BUCKET_NAME>/doc/set/<ID> - set

pub fn start_web(args: &clap::ArgMatches) {
    let port: u16 = args.value_of("rest-port").unwrap().to_string().parse::<u16>().unwrap();
    println!("Starting REST Interface on port {}.", port);

    // let mut router = Router::new();
}
<commit_msg>REST structure<commit_after>extern crate clap;
extern crate iron;
extern crate router;

// GET  /bucket/<BUCKET_NAME>/doc - list
// GET  /bucket/<BUCKET_NAME>/doc/<ID> - get
// POST /bucket/<BUCKET_NAME>/doc/<ID> - upsert
// POST /bucket/<BUCKET_NAME>/doc/<ID>/add - add
// POST /bucket/<BUCKET_NAME>/doc/<ID>/append - append
// POST /bucket/<BUCKET_NAME>/doc/<ID>/prepend - append
// POST /bucket/<BUCKET_NAME>/doc/<ID>/replace - replace
// POST /bucket/<BUCKET_NAME>/doc/<ID>/set - set

pub fn start_web(args: &clap::ArgMatches) {
    let port: u16 = args.value_of("rest-port").unwrap().to_string().parse::<u16>().unwrap();
    println!("Starting REST Interface on port {}.", port);

    // let mut router = Router::new();
}
<|endoftext|>


<commit_before>use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
use std::path::PathBuf;
use std::ptr;
use winapi::shared::minwindef::{DWORD, MAX_PATH};
use winapi::um::{
    errhandlingapi::GetLastError,
    libloaderapi::{
       GetModuleFileNameW, GetModuleHandleExW, GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
        GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
    }
};
use winapi::shared::winerror::ERROR_INSUFFICIENT_BUFFER;

pub(crate) fn get_executable_path() -> Option<PathBuf> {
    fn get_executable_path(len: usize) -> Option<PathBuf> {
        let mut buf = Vec::with_capacity(len);
        unsafe {
            let ret = GetModuleFileNameW(ptr::null_mut(), buf.as_mut_ptr(), len as DWORD) as usize;
            if ret == 0 {
                None
            } else if ret < len {
                // Success, we need to trim trailing null bytes from the vec.
                buf.set_len(ret);
                let s = OsString::from_wide(&buf);
                Some(s.into())
            } else {
                // The buffer might not be big enough so we need to check errno.
                let errno = GetLastError();
                if errno == ERROR_INSUFFICIENT_BUFFER {
                    get_executable_path(len * 2)
                } else {
                    None
                }
            }
        }
    }

    get_executable_path(MAX_PATH)
}

pub(crate) fn get_dylib_path() -> Option<PathBuf> {
    fn get_dylib_path(len: usize) -> Option<PathBuf> {
        let mut buf = Vec::with_capacity(len);
        unsafe {
            let mut handle_module = core::ptr::null_mut();
            if GetModuleHandleExW(
                GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
                    | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
                get_dylib_path as *const _,
                &mut handle_module,
            ) == 0 {
                None
            } else {
                let ret =
                    GetModuleFileNameW(handle_module, buf.as_mut_ptr(), len as DWORD) as usize;
                if ret == 0 {
                    None
                } else if ret < len {
                    // Success, we need to trim trailing null bytes from the vec.
                    buf.set_len(ret);
                    let s = OsString::from_wide(&buf);
                    Some(s.into())
                } else {
                    // The buffer might not be big enough so we need to check errno.
                    let errno = GetLastError();
                    if errno == ERROR_INSUFFICIENT_BUFFER {
                        get_dylib_path(len * 2)
                    } else {
                        None
                    }
                }
            }
        }
    }

    get_dylib_path(MAX_PATH)
}
<commit_msg>Reformat source according to rustfmt<commit_after>use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
use std::path::PathBuf;
use std::ptr;
use winapi::shared::minwindef::{DWORD, MAX_PATH};
use winapi::shared::winerror::ERROR_INSUFFICIENT_BUFFER;
use winapi::um::{
    errhandlingapi::GetLastError,
    libloaderapi::{
        GetModuleFileNameW, GetModuleHandleExW, GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
        GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
    },
};

pub(crate) fn get_executable_path() -> Option<PathBuf> {
    fn get_executable_path(len: usize) -> Option<PathBuf> {
        let mut buf = Vec::with_capacity(len);
        unsafe {
            let ret = GetModuleFileNameW(ptr::null_mut(), buf.as_mut_ptr(), len as DWORD) as usize;
            if ret == 0 {
                None
            } else if ret < len {
                // Success, we need to trim trailing null bytes from the vec.
                buf.set_len(ret);
                let s = OsString::from_wide(&buf);
                Some(s.into())
            } else {
                // The buffer might not be big enough so we need to check errno.
                let errno = GetLastError();
                if errno == ERROR_INSUFFICIENT_BUFFER {
                    get_executable_path(len * 2)
                } else {
                    None
                }
            }
        }
    }

    get_executable_path(MAX_PATH)
}

pub(crate) fn get_dylib_path() -> Option<PathBuf> {
    fn get_dylib_path(len: usize) -> Option<PathBuf> {
        let mut buf = Vec::with_capacity(len);
        unsafe {
            let mut handle_module = core::ptr::null_mut();
            if GetModuleHandleExW(
                GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
                    | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
                get_dylib_path as *const _,
                &mut handle_module,
            ) == 0
            {
                None
            } else {
                let ret =
                    GetModuleFileNameW(handle_module, buf.as_mut_ptr(), len as DWORD) as usize;
                if ret == 0 {
                    None
                } else if ret < len {
                    // Success, we need to trim trailing null bytes from the vec.
                    buf.set_len(ret);
                    let s = OsString::from_wide(&buf);
                    Some(s.into())
                } else {
                    // The buffer might not be big enough so we need to check errno.
                    let errno = GetLastError();
                    if errno == ERROR_INSUFFICIENT_BUFFER {
                        get_dylib_path(len * 2)
                    } else {
                        None
                    }
                }
            }
        }
    }

    get_dylib_path(MAX_PATH)
}
<|endoftext|>


<commit_before>use std::mem;
use std::ptr;
use std::string::String;
use std::ffi::OsStr;
use std::os::raw::{c_void, c_char};
use std::os::windows::ffi::OsStrExt;

use crypt32::{CertOpenStore, CertCloseStore, CertAddEncodedCertificateToStore, CertFreeCertificateContext, CertGetCertificateChain, CertFreeCertificateChain, CertVerifyCertificateChainPolicy};
use winapi::minwindef::DWORD;
use winapi::wincrypt::{PCCERT_CHAIN_CONTEXT, CERT_STORE_PROV_MEMORY, HCERTSTORE, CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, PCCERT_CONTEXT, X509_ASN_ENCODING, CERT_STORE_ADD_ALWAYS, CERT_CHAIN_PARA, CERT_CHAIN_POLICY_PARA, CERT_CHAIN_POLICY_STATUS, CERT_CHAIN_POLICY_SSL, szOID_PKIX_KP_SERVER_AUTH, szOID_SERVER_GATED_CRYPTO, szOID_SGC_NETSCAPE};
use winapi::winnt::LPWSTR;

pub fn validate_cert_chain(encoded_certs: Vec<&[u8]>, hostname: &str) -> bool {
    let context = fail_on_error!(build_cert_context(encoded_certs));
    let chain = fail_on_error!(build_chain(context));
    verify_chain_against_policy(chain, hostname)
}

macro_rules! as_cchar_vec {
    ($e:expr) => {
        {
            let mut bytes = String::from($e).into_bytes();
            bytes.push(0);
            bytes.iter().map(|&b| b as c_char).collect()
        }
    }
}

// Sadly, winapi-rs doesn't yet have a structure we need, so I'll need to build it here.
#[repr(C)]
struct SSL_EXTRA_CERT_CHAIN_POLICY_PARA {
    cbSize: DWORD,
    dwAuthType: DWORD,
    fdwChecks: DWORD,
    pwszServerName: LPWSTR
}

impl Copy for SSL_EXTRA_CERT_CHAIN_POLICY_PARA {}
impl Clone for SSL_EXTRA_CERT_CHAIN_POLICY_PARA { fn clone(&self) -> SSL_EXTRA_CERT_CHAIN_POLICY_PARA {*self} }

// Private implementations. We need to do a lot of work here to make sure things
// behave properly. In particular, we need wrapper types.
struct CertStore(HCERTSTORE);

impl Drop for CertStore {
    fn drop(&mut self) {
        unsafe {
            CertCloseStore(self.0 as *mut _, 0);
        }
    }
}

struct CertContext(PCCERT_CONTEXT);

impl Drop for CertContext {
    fn drop(&mut self) {
        unsafe {
            CertFreeCertificateContext(self.0 as *mut _);
        }
    }
}

struct CertChainContext(PCCERT_CHAIN_CONTEXT);

impl Drop for CertChainContext {
    fn drop(&mut self) {
        unsafe {
            CertFreeCertificateChain(self.0 as *mut _);
        }
    }
}

// Verify that a given certificate chain meets the security policy.
fn verify_chain_against_policy(chain_context: CertChainContext, hostname: &str) -> bool {
    // To begin, we need to create the policy. The policy is simple: suitable for
    // SSL, suitable for this host. First, we need the hostname as a null-terminated array of wchar_t.
    // This bizarre one-liner does that.
    let mut encoded_host = OsStr::new(hostname).encode_wide().chain(Some(0).into_iter()).collect::<Vec<_>>();

    // Then we need some structs to tell Windows what policy we want.
    let mut extra_policy = SSL_EXTRA_CERT_CHAIN_POLICY_PARA {
        cbSize: mem::size_of::<SSL_EXTRA_CERT_CHAIN_POLICY_PARA>() as u32,
        dwAuthType: 2,  // AUTHTYPE_SERVER
        fdwChecks: 0,
        pwszServerName: encoded_host.as_mut_ptr(),  // This is safe: the function won't modify the buffer.
    };
    let mut policy = CERT_CHAIN_POLICY_PARA {
        cbSize: mem::size_of::<CERT_CHAIN_POLICY_PARA>() as u32,
        dwFlags: 0,
        pvExtraPolicyPara: &mut extra_policy as *mut _ as *mut c_void,
    };

    // Finally we need a structure where Windows can tell us the result.
    let mut result = CERT_CHAIN_POLICY_STATUS {
        cbSize: mem::size_of::<CERT_CHAIN_POLICY_STATUS>() as u32,
        dwError: 0,
        lChainIndex: 0,
        lElementIndex: 0,
        pvExtraPolicyStatus: ptr::null_mut(),
    };

    // We can now ask Windows to validate.
    unsafe {
        let verified = CertVerifyCertificateChainPolicy(
            CERT_CHAIN_POLICY_SSL as *const i8,
            chain_context.0,
            &mut policy,
            &mut result,
        );

        if verified == 0 {
            return false;
        }
    }
    // This is probably overbroad: look at the errors in https://msdn.microsoft.com/en-us/library/windows/desktop/aa377188(v=vs.85).aspx
    // and re-evaluate.
    result.dwError == 0
}

// Builds a certificate chain context. This tells Windows to build a chain, but
// doesn't validate that it's acceptable for the host in question.
fn build_chain(cert_context: CertContext) -> Result<CertChainContext, &'static str> {
    // Define acceptable certificate uses. In this case, we would like to just use SERVER_AUTH, but
    // Chrome uses SERVER_GATED_CRYPTO and SGC_NETSCAPE because...well, who knows, but let's do that
    // anyway.
    //
    // We get mutable pointers to these strings and then a mutable pointer to the array, but only
    // because Windows isn't good enough with saying that things are const. These won't change.
    // Annoyingly, though, we have to convince Rust to do it.
    let mut server_auth: Vec<c_char> = as_cchar_vec!(szOID_PKIX_KP_SERVER_AUTH);
    let mut server_gated_crypto: Vec<c_char> = as_cchar_vec!(szOID_SERVER_GATED_CRYPTO);
    let mut sgc_netscape: Vec<c_char> = as_cchar_vec!(szOID_SGC_NETSCAPE);
    let mut usage = [
        server_auth.as_mut_ptr(),
        server_gated_crypto.as_mut_ptr(),
        sgc_netscape.as_mut_ptr(),
    ];

    let mut chain_parameters: CERT_CHAIN_PARA = unsafe{ mem::zeroed() };
    chain_parameters.RequestedUsage.dwType = 1;  // USAGE_MATCH_TYPE_OR
    chain_parameters.RequestedUsage.Usage.cUsageIdentifier = usage.len() as u32;
    chain_parameters.RequestedUsage.Usage.rgpszUsageIdentifier = usage.as_mut_ptr();
    chain_parameters.cbSize = mem::size_of::<CERT_CHAIN_PARA>() as u32;

    let mut chain_context_ptr = ptr::null();
    unsafe {
        let got_chain = CertGetCertificateChain(
            ptr::null_mut(),  // default engine
            cert_context.0,  // leaf certificate
            ptr::null_mut(),  // use the default system time
            (*(cert_context.0)).hCertStore,  // where to find intermediate certs
            &mut chain_parameters,  // The chain building constraints
            0,  // no flags
            ptr::null_mut(),  // reserved
            &mut chain_context_ptr
        );
        if got_chain == 0 {
            return Err("Unable to build certificate chain");
        }
        if chain_context_ptr.is_null() {
            return Err("Unable to build certificate chain");
        }
    }
    let context = CertChainContext(chain_context_ptr as PCCERT_CHAIN_CONTEXT);
    Ok(context)
}

// Builds the certificate chain provided into a certificate store.
fn build_cert_context(encoded_certs: Vec<&[u8]>) -> Result<CertContext, &'static str> {
    // Build a backing store, in-memory.
    let store_ptr = unsafe {
        let backing_store = CertOpenStore(
            CERT_STORE_PROV_MEMORY as *const i8,
            0,
            0,
            CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG,
            ptr::null(),
        );
        if backing_store.is_null() {
            return Err("Unable to open in memory store.")
        }
        backing_store
    };
    let store = CertStore(store_ptr);
    let mut primary_cert_ptr = ptr::null();

    // Then, add the leaf cert. We want to hold on to a reference to it, because
    // that's what we'll end up returning.
    unsafe {
        let ok = CertAddEncodedCertificateToStore(
            store.0,
            X509_ASN_ENCODING,
            encoded_certs[0].as_ptr(),
            encoded_certs[0].len() as u32,
            CERT_STORE_ADD_ALWAYS,
            &mut primary_cert_ptr,
        );
        if ok == 0 {
            return Err("Unable to parse leaf certificate");
        }
    }
    let primary_cert = CertContext(primary_cert_ptr);

    // Now, for every other cert, add it to the store. Don't bother
    // keeping a reference.
    for cert in &encoded_certs[1..] {
        unsafe {
            let ok = CertAddEncodedCertificateToStore(
                store.0,
                X509_ASN_ENCODING,
                cert.as_ptr(),
                cert.len() as u32,
                CERT_STORE_ADD_ALWAYS,
                ptr::null_mut(),
            );
            if ok == 0 {
                return Err("Unable to parse intermediate certificate");
            }
        }
    }

    Ok(primary_cert)
}

#[cfg(test)]
mod test {
    use windows::validate_cert_chain;
    use test::{expired_chain, certifi_chain, self_signed_chain};

    #[test]
    fn can_validate_good_chain() {
        let chain = certifi_chain();
        let valid = validate_cert_chain(chain, "certifi.io");
        assert_eq!(valid, true);
    }

    #[test]
    fn fails_on_bad_hostname() {
        let chain = certifi_chain();
        let valid = validate_cert_chain(chain, "lukasa.co.uk");
        assert_eq!(valid, false);
    }

    #[test]
    fn fails_on_bad_cert() {
        let mut good_chain = certifi_chain();
        let originals = good_chain.split_first_mut().unwrap();
        let leaf = originals.0;
        let intermediates = originals.1;

        // Deliberately truncate the leaf cert.
        let mut certs = vec![&leaf[1..50]];
        certs.extend(intermediates.iter());
        let valid = validate_cert_chain(certs, "certifi.io");
        assert_eq!(valid, false);
    }

    #[test]
    fn fails_on_expired_cert() {
        let chain = expired_chain();
        let valid = validate_cert_chain(chain, "expired.badssl.com");
        assert_eq!(valid, false);
    }

    #[test]
    fn test_fails_on_self_signed() {
        let chain = self_signed_chain();
        let valid = validate_cert_chain(chain, "self-signed.badssl.com");
        assert_eq!(valid, false);
    }
}<commit_msg>Fixup overlong Windows import lines.<commit_after>use std::mem;
use std::ptr;
use std::string::String;
use std::ffi::OsStr;
use std::os::raw::{c_void, c_char};
use std::os::windows::ffi::OsStrExt;

use crypt32::{CertOpenStore, CertCloseStore, CertAddEncodedCertificateToStore,
              CertFreeCertificateContext, CertGetCertificateChain,
              CertFreeCertificateChain, CertVerifyCertificateChainPolicy};
use winapi::minwindef::DWORD;
use winapi::wincrypt::{PCCERT_CHAIN_CONTEXT, CERT_STORE_PROV_MEMORY, HCERTSTORE,
                       CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, PCCERT_CONTEXT,
                       X509_ASN_ENCODING, CERT_STORE_ADD_ALWAYS, CERT_CHAIN_PARA,
                       CERT_CHAIN_POLICY_PARA, CERT_CHAIN_POLICY_STATUS,
                       CERT_CHAIN_POLICY_SSL, szOID_PKIX_KP_SERVER_AUTH,
                       szOID_SERVER_GATED_CRYPTO, szOID_SGC_NETSCAPE};
use winapi::winnt::LPWSTR;

pub fn validate_cert_chain(encoded_certs: Vec<&[u8]>, hostname: &str) -> bool {
    let context = fail_on_error!(build_cert_context(encoded_certs));
    let chain = fail_on_error!(build_chain(context));
    verify_chain_against_policy(chain, hostname)
}

macro_rules! as_cchar_vec {
    ($e:expr) => {
        {
            let mut bytes = String::from($e).into_bytes();
            bytes.push(0);
            bytes.iter().map(|&b| b as c_char).collect()
        }
    }
}

// Sadly, winapi-rs doesn't yet have a structure we need, so I'll need to build it here.
#[repr(C)]
struct SSL_EXTRA_CERT_CHAIN_POLICY_PARA {
    cbSize: DWORD,
    dwAuthType: DWORD,
    fdwChecks: DWORD,
    pwszServerName: LPWSTR
}

impl Copy for SSL_EXTRA_CERT_CHAIN_POLICY_PARA {}
impl Clone for SSL_EXTRA_CERT_CHAIN_POLICY_PARA { fn clone(&self) -> SSL_EXTRA_CERT_CHAIN_POLICY_PARA {*self} }

// Private implementations. We need to do a lot of work here to make sure things
// behave properly. In particular, we need wrapper types.
struct CertStore(HCERTSTORE);

impl Drop for CertStore {
    fn drop(&mut self) {
        unsafe {
            CertCloseStore(self.0 as *mut _, 0);
        }
    }
}

struct CertContext(PCCERT_CONTEXT);

impl Drop for CertContext {
    fn drop(&mut self) {
        unsafe {
            CertFreeCertificateContext(self.0 as *mut _);
        }
    }
}

struct CertChainContext(PCCERT_CHAIN_CONTEXT);

impl Drop for CertChainContext {
    fn drop(&mut self) {
        unsafe {
            CertFreeCertificateChain(self.0 as *mut _);
        }
    }
}

// Verify that a given certificate chain meets the security policy.
fn verify_chain_against_policy(chain_context: CertChainContext, hostname: &str) -> bool {
    // To begin, we need to create the policy. The policy is simple: suitable for
    // SSL, suitable for this host. First, we need the hostname as a null-terminated array of wchar_t.
    // This bizarre one-liner does that.
    let mut encoded_host = OsStr::new(hostname).encode_wide().chain(Some(0).into_iter()).collect::<Vec<_>>();

    // Then we need some structs to tell Windows what policy we want.
    let mut extra_policy = SSL_EXTRA_CERT_CHAIN_POLICY_PARA {
        cbSize: mem::size_of::<SSL_EXTRA_CERT_CHAIN_POLICY_PARA>() as u32,
        dwAuthType: 2,  // AUTHTYPE_SERVER
        fdwChecks: 0,
        pwszServerName: encoded_host.as_mut_ptr(),  // This is safe: the function won't modify the buffer.
    };
    let mut policy = CERT_CHAIN_POLICY_PARA {
        cbSize: mem::size_of::<CERT_CHAIN_POLICY_PARA>() as u32,
        dwFlags: 0,
        pvExtraPolicyPara: &mut extra_policy as *mut _ as *mut c_void,
    };

    // Finally we need a structure where Windows can tell us the result.
    let mut result = CERT_CHAIN_POLICY_STATUS {
        cbSize: mem::size_of::<CERT_CHAIN_POLICY_STATUS>() as u32,
        dwError: 0,
        lChainIndex: 0,
        lElementIndex: 0,
        pvExtraPolicyStatus: ptr::null_mut(),
    };

    // We can now ask Windows to validate.
    unsafe {
        let verified = CertVerifyCertificateChainPolicy(
            CERT_CHAIN_POLICY_SSL as *const i8,
            chain_context.0,
            &mut policy,
            &mut result,
        );

        if verified == 0 {
            return false;
        }
    }
    // This is probably overbroad: look at the errors in https://msdn.microsoft.com/en-us/library/windows/desktop/aa377188(v=vs.85).aspx
    // and re-evaluate.
    result.dwError == 0
}

// Builds a certificate chain context. This tells Windows to build a chain, but
// doesn't validate that it's acceptable for the host in question.
fn build_chain(cert_context: CertContext) -> Result<CertChainContext, &'static str> {
    // Define acceptable certificate uses. In this case, we would like to just use SERVER_AUTH, but
    // Chrome uses SERVER_GATED_CRYPTO and SGC_NETSCAPE because...well, who knows, but let's do that
    // anyway.
    //
    // We get mutable pointers to these strings and then a mutable pointer to the array, but only
    // because Windows isn't good enough with saying that things are const. These won't change.
    // Annoyingly, though, we have to convince Rust to do it.
    let mut server_auth: Vec<c_char> = as_cchar_vec!(szOID_PKIX_KP_SERVER_AUTH);
    let mut server_gated_crypto: Vec<c_char> = as_cchar_vec!(szOID_SERVER_GATED_CRYPTO);
    let mut sgc_netscape: Vec<c_char> = as_cchar_vec!(szOID_SGC_NETSCAPE);
    let mut usage = [
        server_auth.as_mut_ptr(),
        server_gated_crypto.as_mut_ptr(),
        sgc_netscape.as_mut_ptr(),
    ];

    let mut chain_parameters: CERT_CHAIN_PARA = unsafe{ mem::zeroed() };
    chain_parameters.RequestedUsage.dwType = 1;  // USAGE_MATCH_TYPE_OR
    chain_parameters.RequestedUsage.Usage.cUsageIdentifier = usage.len() as u32;
    chain_parameters.RequestedUsage.Usage.rgpszUsageIdentifier = usage.as_mut_ptr();
    chain_parameters.cbSize = mem::size_of::<CERT_CHAIN_PARA>() as u32;

    let mut chain_context_ptr = ptr::null();
    unsafe {
        let got_chain = CertGetCertificateChain(
            ptr::null_mut(),  // default engine
            cert_context.0,  // leaf certificate
            ptr::null_mut(),  // use the default system time
            (*(cert_context.0)).hCertStore,  // where to find intermediate certs
            &mut chain_parameters,  // The chain building constraints
            0,  // no flags
            ptr::null_mut(),  // reserved
            &mut chain_context_ptr
        );
        if got_chain == 0 {
            return Err("Unable to build certificate chain");
        }
        if chain_context_ptr.is_null() {
            return Err("Unable to build certificate chain");
        }
    }
    let context = CertChainContext(chain_context_ptr as PCCERT_CHAIN_CONTEXT);
    Ok(context)
}

// Builds the certificate chain provided into a certificate store.
fn build_cert_context(encoded_certs: Vec<&[u8]>) -> Result<CertContext, &'static str> {
    // Build a backing store, in-memory.
    let store_ptr = unsafe {
        let backing_store = CertOpenStore(
            CERT_STORE_PROV_MEMORY as *const i8,
            0,
            0,
            CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG,
            ptr::null(),
        );
        if backing_store.is_null() {
            return Err("Unable to open in memory store.")
        }
        backing_store
    };
    let store = CertStore(store_ptr);
    let mut primary_cert_ptr = ptr::null();

    // Then, add the leaf cert. We want to hold on to a reference to it, because
    // that's what we'll end up returning.
    unsafe {
        let ok = CertAddEncodedCertificateToStore(
            store.0,
            X509_ASN_ENCODING,
            encoded_certs[0].as_ptr(),
            encoded_certs[0].len() as u32,
            CERT_STORE_ADD_ALWAYS,
            &mut primary_cert_ptr,
        );
        if ok == 0 {
            return Err("Unable to parse leaf certificate");
        }
    }
    let primary_cert = CertContext(primary_cert_ptr);

    // Now, for every other cert, add it to the store. Don't bother
    // keeping a reference.
    for cert in &encoded_certs[1..] {
        unsafe {
            let ok = CertAddEncodedCertificateToStore(
                store.0,
                X509_ASN_ENCODING,
                cert.as_ptr(),
                cert.len() as u32,
                CERT_STORE_ADD_ALWAYS,
                ptr::null_mut(),
            );
            if ok == 0 {
                return Err("Unable to parse intermediate certificate");
            }
        }
    }

    Ok(primary_cert)
}

#[cfg(test)]
mod test {
    use windows::validate_cert_chain;
    use test::{expired_chain, certifi_chain, self_signed_chain};

    #[test]
    fn can_validate_good_chain() {
        let chain = certifi_chain();
        let valid = validate_cert_chain(chain, "certifi.io");
        assert_eq!(valid, true);
    }

    #[test]
    fn fails_on_bad_hostname() {
        let chain = certifi_chain();
        let valid = validate_cert_chain(chain, "lukasa.co.uk");
        assert_eq!(valid, false);
    }

    #[test]
    fn fails_on_bad_cert() {
        let mut good_chain = certifi_chain();
        let originals = good_chain.split_first_mut().unwrap();
        let leaf = originals.0;
        let intermediates = originals.1;

        // Deliberately truncate the leaf cert.
        let mut certs = vec![&leaf[1..50]];
        certs.extend(intermediates.iter());
        let valid = validate_cert_chain(certs, "certifi.io");
        assert_eq!(valid, false);
    }

    #[test]
    fn fails_on_expired_cert() {
        let chain = expired_chain();
        let valid = validate_cert_chain(chain, "expired.badssl.com");
        assert_eq!(valid, false);
    }

    #[test]
    fn test_fails_on_self_signed() {
        let chain = self_signed_chain();
        let valid = validate_cert_chain(chain, "self-signed.badssl.com");
        assert_eq!(valid, false);
    }
}<|endoftext|>


<commit_before>//! Definition of the `JoinAll` combinator, waiting for all of a list of futures
//! to finish.

use std::prelude::v1::*;

use std::fmt;
use std::mem;

use {Future, IntoFuture, Poll, Async};

#[derive(Debug)]
enum ElemState<T> where T: Future {
    Pending(T),
    Done(T::Item),
}

/// A future which takes a list of futures and resolves with a vector of the
/// completed values.
///
/// This future is created with the `join_all` method.
#[must_use = "futures do nothing unless polled"]
pub struct JoinAll<I>
    where I: IntoIterator,
          I::Item: IntoFuture,
{
    elems: Vec<ElemState<<I::Item as IntoFuture>::Future>>,
}

impl<I> fmt::Debug for JoinAll<I>
    where I: IntoIterator,
          I::Item: IntoFuture,
          <<I as IntoIterator>::Item as IntoFuture>::Future: fmt::Debug,
          <<I as IntoIterator>::Item as IntoFuture>::Item: fmt::Debug,
{
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("JoinAll")
            .field("elems", &self.elems)
            .finish()
    }
}

/// Creates a future which represents a collection of the results of the futures
/// given.
///
/// The returned future will drive execution for all of its underlying futures,
/// collecting the results into a destination `Vec<T>` in the same order as they
/// were provided. If any future returns an error then all other futures will be
/// canceled and an error will be returned immediately. If all futures complete
/// successfully, however, then the returned future will succeed with a `Vec` of
/// all the successful results.
///
/// # Examples
///
/// ```
/// use futures::future::*;
///
/// let f = join_all(vec![
///     ok::<u32, u32>(1),
///     ok::<u32, u32>(2),
///     ok::<u32, u32>(3),
/// ]);
/// let f = f.map(|x| {
///     assert_eq!(x, [1, 2, 3]);
/// });
///
/// let f = join_all(vec![
///     Box::new(ok::<u32, u32>(1)),
///     Box::new(err::<u32, u32>(2)),
///     Box::new(ok::<u32, u32>(3)),
/// ]);
/// let f = f.then(|x| {
///     assert_eq!(x, Err(2));
///     x
/// });
/// ```
pub fn join_all<I>(i: I) -> JoinAll<I>
    where I: IntoIterator,
          I::Item: IntoFuture,
{
    let elems = i.into_iter().map(|f| {
        ElemState::Pending(f.into_future())
    }).collect();
    JoinAll { elems: elems }
}

impl<I> Future for JoinAll<I>
    where I: IntoIterator,
          I::Item: IntoFuture,
{
    type Item = Vec<<I::Item as IntoFuture>::Item>;
    type Error = <I::Item as IntoFuture>::Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        let mut all_done = true;

        for idx in 0 .. self.elems.len() {
            let done_val = match self.elems[idx] {
                ElemState::Pending(ref mut t) => {
                    match t.poll() {
                        Ok(Async::Ready(v)) => Ok(v),
                        Ok(Async::NotReady) => {
                            all_done = false;
                            continue
                        }
                        Err(e) => Err(e),
                    }
                }
                ElemState::Done(ref mut _v) => continue,
            };

            match done_val {
                Ok(v) => self.elems[idx] = ElemState::Done(v),
                Err(e) => {
                    // On completion drop all our associated resources
                    // ASAP.
                    self.elems = Vec::new();
                    return Err(e)
                }
            }
        }

        if all_done {
            let elems = mem::replace(&mut self.elems, Vec::new());
            let result = elems.into_iter().map(|e| {
                match e {
                    ElemState::Done(t) => t,
                    _ => unreachable!(),
                }
            }).collect();
            Ok(Async::Ready(result))
        } else {
            Ok(Async::NotReady)
        }
    }
}
<commit_msg>JoinAll<I> -> JoinAll<Item><commit_after>//! Definition of the `JoinAll` combinator, waiting for all of a list of futures
//! to finish.

use std::prelude::v1::*;

use std::fmt;
use std::mem;

use {Future, IntoFuture, Poll, Async};

#[derive(Debug)]
enum ElemState<T> where T: Future {
    Pending(T),
    Done(T::Item),
}

/// A future which takes a list of futures and resolves with a vector of the
/// completed values.
///
/// This future is created with the `join_all` method.
#[must_use = "futures do nothing unless polled"]
pub struct JoinAll<Item>
    where Item: IntoFuture,
{
    elems: Vec<ElemState<<Item as IntoFuture>::Future>>,
}

impl<I> fmt::Debug for JoinAll<I>
    where I: IntoIterator,
          I::Item: IntoFuture,
          <<I as IntoIterator>::Item as IntoFuture>::Future: fmt::Debug,
          <<I as IntoIterator>::Item as IntoFuture>::Item: fmt::Debug,
{
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("JoinAll")
            .field("elems", &self.elems)
            .finish()
    }
}

/// Creates a future which represents a collection of the results of the futures
/// given.
///
/// The returned future will drive execution for all of its underlying futures,
/// collecting the results into a destination `Vec<T>` in the same order as they
/// were provided. If any future returns an error then all other futures will be
/// canceled and an error will be returned immediately. If all futures complete
/// successfully, however, then the returned future will succeed with a `Vec` of
/// all the successful results.
///
/// # Examples
///
/// ```
/// use futures::future::*;
///
/// let f = join_all(vec![
///     ok::<u32, u32>(1),
///     ok::<u32, u32>(2),
///     ok::<u32, u32>(3),
/// ]);
/// let f = f.map(|x| {
///     assert_eq!(x, [1, 2, 3]);
/// });
///
/// let f = join_all(vec![
///     Box::new(ok::<u32, u32>(1)),
///     Box::new(err::<u32, u32>(2)),
///     Box::new(ok::<u32, u32>(3)),
/// ]);
/// let f = f.then(|x| {
///     assert_eq!(x, Err(2));
///     x
/// });
/// ```
pub fn join_all<I>(i: I) -> JoinAll<I::Item>
    where I: IntoIterator,
          I::Item: IntoFuture,
{
    let elems = i.into_iter().map(|f| {
        ElemState::Pending(f.into_future())
    }).collect();
    JoinAll { elems: elems }
}

impl<Item> Future for JoinAll<Item>
    where Item: IntoFuture,
{
    type Item = Vec<<Item as IntoFuture>::Item>;
    type Error = <Item as IntoFuture>::Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        let mut all_done = true;

        for idx in 0 .. self.elems.len() {
            let done_val = match self.elems[idx] {
                ElemState::Pending(ref mut t) => {
                    match t.poll() {
                        Ok(Async::Ready(v)) => Ok(v),
                        Ok(Async::NotReady) => {
                            all_done = false;
                            continue
                        }
                        Err(e) => Err(e),
                    }
                }
                ElemState::Done(ref mut _v) => continue,
            };

            match done_val {
                Ok(v) => self.elems[idx] = ElemState::Done(v),
                Err(e) => {
                    // On completion drop all our associated resources
                    // ASAP.
                    self.elems = Vec::new();
                    return Err(e)
                }
            }
        }

        if all_done {
            let elems = mem::replace(&mut self.elems, Vec::new());
            let result = elems.into_iter().map(|e| {
                match e {
                    ElemState::Done(t) => t,
                    _ => unreachable!(),
                }
            }).collect();
            Ok(Async::Ready(result))
        } else {
            Ok(Async::NotReady)
        }
    }
}
<|endoftext|>


<commit_before>// TODO: COPYRIGHT, USE & AUTHORS
use super::ga_core::GASolution;
use super::ga_population::{GAPopulation, GAPopulationSortBasis, GAPopulationSortOrder};
use super::ga_random;
use std::cmp;

/// Selector Trait
///
/// Interface to Selection Schemes
pub trait GASelector<'a, T: GASolution>
{
    /// Assign the population on which to operate
    fn assign(&mut self, population: &'a mut GAPopulation<T>);
    /// Update internal state.
    /// Some selectors implement an empty update().
    fn update(&mut self) {}

    fn select(&self) -> &T;
}

pub trait GAScoreTypeBasedSelection<T: GASolution>
{
    fn score(&self, individual: &T) -> f32;

    fn population_sort_basis(&self) -> GAPopulationSortBasis;

    fn max_score(&self, population: &GAPopulation<T>) -> f32
    {
        // TODO: Knowing that best is at 0 is knowing too much.
        self.score(population.individual(0, self.population_sort_basis()))
    }

    fn min_score(&self, population: &GAPopulation<T>) -> f32
    {
        // TODO: Knowing that worst is at size()-1 is knowing too much.
        self.score(population.individual(population.size()-1, self.population_sort_basis()))
    }
}

pub struct GARawScoreBasedSelection;

impl<T: GASolution> GAScoreTypeBasedSelection<T> for GARawScoreBasedSelection
{
    fn score(&self, individual: &T) -> f32
    {
        individual.score()
    }

    fn population_sort_basis(&self) -> GAPopulationSortBasis
    {
        GAPopulationSortBasis::Raw
    }
}

pub struct GAScaledScoreBasedSelection;

impl<T: GASolution> GAScoreTypeBasedSelection<T> for GAScaledScoreBasedSelection
{
    fn score(&self, individual: &T) -> f32
    {
        individual.fitness()
    }

    fn population_sort_basis(&self) -> GAPopulationSortBasis
    {
        GAPopulationSortBasis::Scaled
    }
}

// GASolution-s live as long as the population. Lifetime 'a is bound to the
// population borrowed by the 'population' member, as well as to the enclosed
// GASolution-s, because they share the same memory.
pub struct GARankSelector<'a, T: 'a + GASolution>
{
    // Q: How to make this member common to all GASelectors?
    // Q: Will 'mut' make things more complex? When the rank selector is in
    //    scope and borrowing the population, no other objects will be able
    //    to borrow it, not even in non-mut mode according to the rules.
    // Selectors modify populations (they sort them, for instance), so the
    // reference must be 'mut'.
    population: &'a mut GAPopulation<T>,

    // TODO: Check correct lifetime.
    score_selection: &'a GAScoreTypeBasedSelection<T>
}

impl<'a, T: GASolution> GARankSelector<'a, T>
{
    pub fn new(p: &'a mut GAPopulation<T>, s: &'a GAScoreTypeBasedSelection<T>) -> GARankSelector<'a, T>
    {
        GARankSelector
        {
            population: p,
            score_selection: s
        }
    }
}

// TODO: DOC.
impl<'a, T: GASolution> GASelector<'a, T> for GARankSelector<'a, T>
{
    fn assign(&mut self, population: &'a mut GAPopulation<T>)
    {
        self.population = population;
    }

    fn update(&mut self)
    {
        self.population.sort();
    }

    fn select(&self) -> &T
    {
        // TODO: Confirm assumption that population has 1 individual at least.
        // Number of individuals that share best score.
        let mut best_count = 1;

        // This is not a move, but a copy.
        let population_sort_basis = self.score_selection.population_sort_basis();

        // TODO: Use best() when implemented.
        // Q: Should individual() (ith_best()) return an optional or
        //    does it guarantee that it will always return something valid?
        //    What if the population is still empty?
        let best_score: f32 = self.score_selection.score(self.population.individual(0, population_sort_basis));

        // Skip 0th best.
        for i in 1..self.population.size()
        {
            if self.score_selection.score(self.population.individual(i, population_sort_basis)) != best_score
            {
                break;
            }

            best_count = best_count + 1;
        }

        // Select any individual from those that share best score.
        self.population.individual(ga_random::ga_random_range(0, best_count), population_sort_basis)
    }
}

pub struct GAUniformSelector<'a, T: 'a + GASolution>
{
    population: &'a mut GAPopulation<T>
}

impl<'a, T: GASolution> GAUniformSelector<'a, T>
{
    pub fn new(p: &'a mut GAPopulation<T>) -> GAUniformSelector<'a, T>
    {
        GAUniformSelector
        {
            population: p
        }
    }
}

impl<'a, T: GASolution> GASelector<'a, T> for GAUniformSelector<'a, T>
{
    fn assign(&mut self, population: &'a mut GAPopulation<T>)
    {
        self.population = population;
    }

    fn update(&mut self)
    {
        // Need to sort first, because GAPopulation.individual() draws individuals
        // from the sorted lists.
        self.population.sort();
    }

    // Select any individual at random.
    fn select(&self) -> &T
    {
        // Since selection is at random, it doesn't matter where the individual
        // is drawn from, the Raw/score-sorted or the Scaled/fitness-sorted list.
        self.population.individual(
            ga_random::ga_random_range(0, self.population.size()),
            GAPopulationSortBasis::Raw)
    }
}

pub struct GARouletteWheelSelector<'a, T: 'a + GASolution>
{
    population: &'a mut GAPopulation<T>,

    // TODO: Check correct lifetime.
    score_selection: &'a GAScoreTypeBasedSelection<T>,

    wheel_proportions: Vec<f32>,

    // TODO: Remove if not useful.
    wheel_is_dirty: bool
}

impl<'a, T: GASolution> GARouletteWheelSelector<'a, T>
{
    // TODO: Check s's lifetime.
    pub fn new(p: &'a mut GAPopulation<T>,
               s: &'a GAScoreTypeBasedSelection<T>) -> GARouletteWheelSelector<'a, T>
    {
        // TODO: Comment doesn't look correct.
        // vec![] borrows references (invocation of size() is through *p, or so the
        // compiler says); since p has already been borrowed as a mutable reference
        // (no data races allowed), p.size() can't be passed to vec![].
        let wheel_size = p.size();

        GARouletteWheelSelector
        {
            population: p,
            score_selection: s,
            wheel_proportions: vec![0.0; wheel_size],
            wheel_is_dirty: false
        }
    }
}

impl<'a, T: GASolution> GASelector<'a, T> for GARouletteWheelSelector<'a, T>
{
    fn assign(&mut self, population: &'a mut GAPopulation<T>)
    {
        self.population = population;

        if self.population.size() != self.wheel_proportions.len()
        {
            self.wheel_proportions.resize(self.population.size(), 0.0);
        }

        self.wheel_is_dirty = true;
    }

    fn update(&mut self)
    {
        // TODO: Can a population grow? If it can, need to resize the wheel.

        let wheel_slots = self.wheel_proportions.len();
        let max_score = self.score_selection.max_score(self.population);
        let min_score = self.score_selection.min_score(self.population);

        if max_score == min_score
        {
            // Upper bound is excluded.
            for i in 0 .. wheel_slots
            {
                self.wheel_proportions[i] = ((i+1) as f32)/(wheel_slots as f32);
            }
        }
        else if (max_score > 0.0 && min_score >= 0.0)
                 || (max_score <= 0.0 && min_score < 0.0)
        {
            // This is not a move, but a copy.
            let population_sort_basis = self.score_selection.population_sort_basis();

            self.population.sort();

            match self.population.order()
            {
                GAPopulationSortOrder::HighIsBest
                =>  {
                        self.wheel_proportions[0]
                          = self.score_selection.score(
                              self.population.individual(0, population_sort_basis));

                        for i in 1 .. wheel_slots
                        {
                            self.wheel_proportions[i]
                              = self.score_selection.score(
                                  self.population.individual(i, population_sort_basis))
                                + self.wheel_proportions[i-1];
                        }

                        for i in 0 .. wheel_slots
                        {
                            self.wheel_proportions[i]
                              /= self.wheel_proportions[wheel_slots-1];
                        }
                    },
                GAPopulationSortOrder::LowIsBest
                =>  {
                        self.wheel_proportions[0]
                          = -self.score_selection.score(
                               self.population.individual(0, population_sort_basis))
                            + max_score + min_score;

                        for i in 1 .. wheel_slots
                        {
                            self.wheel_proportions[i]
                              = -self.score_selection.score(
                                   self.population.individual(i, population_sort_basis))
                                + max_score + min_score
                                + self.wheel_proportions[i-1];
                        }

                        for i in 0 .. wheel_slots
                        {
                            self.wheel_proportions[i]
                              /= self.wheel_proportions[wheel_slots-1];
                        }
                    }
            }
        }
        else
        {
            // TODO: Raise error.
        }
    }

    fn select(&self) -> &T
    {
        // TODO: Cache this value? Or Vec already caches it?
        let wheel_slots = self.wheel_proportions.len();
        let cutoff = ga_random::ga_random_float();
        let mut lower = 0;
        let mut upper = wheel_slots-1;
        let mut i;

        while upper >= lower
        {
            i = lower + (upper-lower)/2;

            assert!(i >= 0 && i < wheel_slots);

            if self.wheel_proportions[i] > cutoff
            {
                upper = i-1;
            }
            else
            {
                lower = i+1;
            }
        }

        lower = cmp::min(wheel_slots-1, lower);
        lower = cmp::max(0, lower);

        self.population.individual(lower, self.score_selection.population_sort_basis())
    }
}

pub struct GATournamentSelector<'a, T: 'a + GASolution>
{
    // TODO: Check correct lifetime.
    score_selection: &'a GAScoreTypeBasedSelection<T>,

    roulette_wheel_selector: GARouletteWheelSelector<'a, T>,

    population_sort_order: GAPopulationSortOrder
}

impl<'a, T: GASolution> GATournamentSelector<'a, T>
{
    // TODO: Check s's lifetime.
    pub fn new(p: &'a mut GAPopulation<T>,
               s: &'a GAScoreTypeBasedSelection<T>) -> GATournamentSelector<'a, T>
    {
        GATournamentSelector
        {
            score_selection: s,
            population_sort_order: p.order(),
            roulette_wheel_selector: GARouletteWheelSelector::new(p, s)
        }
    }
}

impl<'a, T: GASolution> GASelector<'a, T> for GATournamentSelector<'a, T>
{
    fn assign(&mut self, population: &'a mut GAPopulation<T>)
    {
        self.population_sort_order = population.order();

        self.roulette_wheel_selector.assign(population);
    }

    fn update(&mut self)
    {
        self.roulette_wheel_selector.update();
    }

    fn select(&self) -> &T
    {
        let low_score_individual;
        let high_score_individual;
        let individual1;
        let individual2;

        individual1 = self.roulette_wheel_selector.select();

        individual2 = self.roulette_wheel_selector.select();

        if self.score_selection.score(individual1)
           >= self.score_selection.score(individual2)
        {
            low_score_individual = individual2;
            high_score_individual = individual1;
        }
        else
        {
            low_score_individual = individual1;
            high_score_individual = individual2;
        }

        match self.population_sort_order
        {
            GAPopulationSortOrder::HighIsBest => high_score_individual,
            GAPopulationSortOrder::LowIsBest  => low_score_individual
        }
    }
}
<commit_msg>Sort population 1st thing in GARouletteWheelSelector.update().<commit_after>// TODO: COPYRIGHT, USE & AUTHORS
use super::ga_core::GASolution;
use super::ga_population::{GAPopulation, GAPopulationSortBasis, GAPopulationSortOrder};
use super::ga_random;
use std::cmp;

/// Selector Trait
///
/// Interface to Selection Schemes
pub trait GASelector<'a, T: GASolution>
{
    /// Assign the population on which to operate
    fn assign(&mut self, population: &'a mut GAPopulation<T>);
    /// Update internal state.
    /// Some selectors implement an empty update().
    fn update(&mut self) {}

    fn select(&self) -> &T;
}

pub trait GAScoreTypeBasedSelection<T: GASolution>
{
    fn score(&self, individual: &T) -> f32;

    fn population_sort_basis(&self) -> GAPopulationSortBasis;

    fn max_score(&self, population: &GAPopulation<T>) -> f32
    {
        // TODO: Knowing that best is at 0 is knowing too much.
        self.score(population.individual(0, self.population_sort_basis()))
    }

    fn min_score(&self, population: &GAPopulation<T>) -> f32
    {
        // TODO: Knowing that worst is at size()-1 is knowing too much.
        self.score(population.individual(population.size()-1, self.population_sort_basis()))
    }
}

pub struct GARawScoreBasedSelection;

impl<T: GASolution> GAScoreTypeBasedSelection<T> for GARawScoreBasedSelection
{
    fn score(&self, individual: &T) -> f32
    {
        individual.score()
    }

    fn population_sort_basis(&self) -> GAPopulationSortBasis
    {
        GAPopulationSortBasis::Raw
    }
}

pub struct GAScaledScoreBasedSelection;

impl<T: GASolution> GAScoreTypeBasedSelection<T> for GAScaledScoreBasedSelection
{
    fn score(&self, individual: &T) -> f32
    {
        individual.fitness()
    }

    fn population_sort_basis(&self) -> GAPopulationSortBasis
    {
        GAPopulationSortBasis::Scaled
    }
}

// GASolution-s live as long as the population. Lifetime 'a is bound to the
// population borrowed by the 'population' member, as well as to the enclosed
// GASolution-s, because they share the same memory.
pub struct GARankSelector<'a, T: 'a + GASolution>
{
    // Q: How to make this member common to all GASelectors?
    // Q: Will 'mut' make things more complex? When the rank selector is in
    //    scope and borrowing the population, no other objects will be able
    //    to borrow it, not even in non-mut mode according to the rules.
    // Selectors modify populations (they sort them, for instance), so the
    // reference must be 'mut'.
    population: &'a mut GAPopulation<T>,

    // TODO: Check correct lifetime.
    score_selection: &'a GAScoreTypeBasedSelection<T>
}

impl<'a, T: GASolution> GARankSelector<'a, T>
{
    pub fn new(p: &'a mut GAPopulation<T>, s: &'a GAScoreTypeBasedSelection<T>) -> GARankSelector<'a, T>
    {
        GARankSelector
        {
            population: p,
            score_selection: s
        }
    }
}

// TODO: DOC.
impl<'a, T: GASolution> GASelector<'a, T> for GARankSelector<'a, T>
{
    fn assign(&mut self, population: &'a mut GAPopulation<T>)
    {
        self.population = population;
    }

    fn update(&mut self)
    {
        self.population.sort();
    }

    fn select(&self) -> &T
    {
        // TODO: Confirm assumption that population has 1 individual at least.
        // Number of individuals that share best score.
        let mut best_count = 1;

        // This is not a move, but a copy.
        let population_sort_basis = self.score_selection.population_sort_basis();

        // TODO: Use best() when implemented.
        // Q: Should individual() (ith_best()) return an optional or
        //    does it guarantee that it will always return something valid?
        //    What if the population is still empty?
        let best_score: f32 = self.score_selection.score(self.population.individual(0, population_sort_basis));

        // Skip 0th best.
        for i in 1..self.population.size()
        {
            if self.score_selection.score(self.population.individual(i, population_sort_basis)) != best_score
            {
                break;
            }

            best_count = best_count + 1;
        }

        // Select any individual from those that share best score.
        self.population.individual(ga_random::ga_random_range(0, best_count), population_sort_basis)
    }
}

pub struct GAUniformSelector<'a, T: 'a + GASolution>
{
    population: &'a mut GAPopulation<T>
}

impl<'a, T: GASolution> GAUniformSelector<'a, T>
{
    pub fn new(p: &'a mut GAPopulation<T>) -> GAUniformSelector<'a, T>
    {
        GAUniformSelector
        {
            population: p
        }
    }
}

impl<'a, T: GASolution> GASelector<'a, T> for GAUniformSelector<'a, T>
{
    fn assign(&mut self, population: &'a mut GAPopulation<T>)
    {
        self.population = population;
    }

    fn update(&mut self)
    {
        // Need to sort first, because GAPopulation.individual() draws individuals
        // from the sorted lists.
        self.population.sort();
    }

    // Select any individual at random.
    fn select(&self) -> &T
    {
        // Since selection is at random, it doesn't matter where the individual
        // is drawn from, the Raw/score-sorted or the Scaled/fitness-sorted list.
        self.population.individual(
            ga_random::ga_random_range(0, self.population.size()),
            GAPopulationSortBasis::Raw)
    }
}

pub struct GARouletteWheelSelector<'a, T: 'a + GASolution>
{
    population: &'a mut GAPopulation<T>,

    // TODO: Check correct lifetime.
    score_selection: &'a GAScoreTypeBasedSelection<T>,

    wheel_proportions: Vec<f32>,

    // TODO: Remove if not useful.
    wheel_is_dirty: bool
}

impl<'a, T: GASolution> GARouletteWheelSelector<'a, T>
{
    // TODO: Check s's lifetime.
    pub fn new(p: &'a mut GAPopulation<T>,
               s: &'a GAScoreTypeBasedSelection<T>) -> GARouletteWheelSelector<'a, T>
    {
        // TODO: Comment doesn't look correct.
        // vec![] borrows references (invocation of size() is through *p, or so the
        // compiler says); since p has already been borrowed as a mutable reference
        // (no data races allowed), p.size() can't be passed to vec![].
        let wheel_size = p.size();

        GARouletteWheelSelector
        {
            population: p,
            score_selection: s,
            wheel_proportions: vec![0.0; wheel_size],
            wheel_is_dirty: false
        }
    }
}

impl<'a, T: GASolution> GASelector<'a, T> for GARouletteWheelSelector<'a, T>
{
    fn assign(&mut self, population: &'a mut GAPopulation<T>)
    {
        self.population = population;

        if self.population.size() != self.wheel_proportions.len()
        {
            self.wheel_proportions.resize(self.population.size(), 0.0);
        }

        self.wheel_is_dirty = true;
    }

    fn update(&mut self)
    {
        // TODO: Can a population grow? If it can, need to resize the wheel.

        self.population.sort();

        let wheel_slots = self.wheel_proportions.len();
        let max_score = self.score_selection.max_score(self.population);
        let min_score = self.score_selection.min_score(self.population);

        if max_score == min_score
        {
            // Upper bound is excluded.
            for i in 0 .. wheel_slots
            {
                self.wheel_proportions[i] = ((i+1) as f32)/(wheel_slots as f32);
            }
        }
        else if (max_score > 0.0 && min_score >= 0.0)
                 || (max_score <= 0.0 && min_score < 0.0)
        {
            // This is not a move, but a copy.
            let population_sort_basis = self.score_selection.population_sort_basis();

            match self.population.order()
            {
                GAPopulationSortOrder::HighIsBest
                =>  {
                        self.wheel_proportions[0]
                          = self.score_selection.score(
                              self.population.individual(0, population_sort_basis));

                        for i in 1 .. wheel_slots
                        {
                            self.wheel_proportions[i]
                              = self.score_selection.score(
                                  self.population.individual(i, population_sort_basis))
                                + self.wheel_proportions[i-1];
                        }

                        for i in 0 .. wheel_slots
                        {
                            self.wheel_proportions[i]
                              /= self.wheel_proportions[wheel_slots-1];
                        }
                    },
                GAPopulationSortOrder::LowIsBest
                =>  {
                        self.wheel_proportions[0]
                          = -self.score_selection.score(
                               self.population.individual(0, population_sort_basis))
                            + max_score + min_score;

                        for i in 1 .. wheel_slots
                        {
                            self.wheel_proportions[i]
                              = -self.score_selection.score(
                                   self.population.individual(i, population_sort_basis))
                                + max_score + min_score
                                + self.wheel_proportions[i-1];
                        }

                        for i in 0 .. wheel_slots
                        {
                            self.wheel_proportions[i]
                              /= self.wheel_proportions[wheel_slots-1];
                        }
                    }
            }
        }
        else
        {
            // TODO: Raise error.
        }
    }

    fn select(&self) -> &T
    {
        // TODO: Cache this value? Or Vec already caches it?
        let wheel_slots = self.wheel_proportions.len();
        let cutoff = ga_random::ga_random_float();
        let mut lower = 0;
        let mut upper = wheel_slots-1;
        let mut i;

        while upper >= lower
        {
            i = lower + (upper-lower)/2;

            assert!(i >= 0 && i < wheel_slots);

            if self.wheel_proportions[i] > cutoff
            {
                upper = i-1;
            }
            else
            {
                lower = i+1;
            }
        }

        lower = cmp::min(wheel_slots-1, lower);
        lower = cmp::max(0, lower);

        self.population.individual(lower, self.score_selection.population_sort_basis())
    }
}

pub struct GATournamentSelector<'a, T: 'a + GASolution>
{
    // TODO: Check correct lifetime.
    score_selection: &'a GAScoreTypeBasedSelection<T>,

    roulette_wheel_selector: GARouletteWheelSelector<'a, T>,

    population_sort_order: GAPopulationSortOrder
}

impl<'a, T: GASolution> GATournamentSelector<'a, T>
{
    // TODO: Check s's lifetime.
    pub fn new(p: &'a mut GAPopulation<T>,
               s: &'a GAScoreTypeBasedSelection<T>) -> GATournamentSelector<'a, T>
    {
        GATournamentSelector
        {
            score_selection: s,
            population_sort_order: p.order(),
            roulette_wheel_selector: GARouletteWheelSelector::new(p, s)
        }
    }
}

impl<'a, T: GASolution> GASelector<'a, T> for GATournamentSelector<'a, T>
{
    fn assign(&mut self, population: &'a mut GAPopulation<T>)
    {
        self.population_sort_order = population.order();

        self.roulette_wheel_selector.assign(population);
    }

    fn update(&mut self)
    {
        self.roulette_wheel_selector.update();
    }

    fn select(&self) -> &T
    {
        let low_score_individual;
        let high_score_individual;
        let individual1;
        let individual2;

        individual1 = self.roulette_wheel_selector.select();

        individual2 = self.roulette_wheel_selector.select();

        if self.score_selection.score(individual1)
           >= self.score_selection.score(individual2)
        {
            low_score_individual = individual2;
            high_score_individual = individual1;
        }
        else
        {
            low_score_individual = individual1;
            high_score_individual = individual2;
        }

        match self.population_sort_order
        {
            GAPopulationSortOrder::HighIsBest => high_score_individual,
            GAPopulationSortOrder::LowIsBest  => low_score_individual
        }
    }
}
<|endoftext|>