<commit_before>extern crate cql_ffi;

use std::slice;

use cql_ffi::*;

//fn print_schema_meta(const CassSchemaMeta* meta, int indent);

unsafe fn print_keyspace(session:&mut CassSession, keyspace:&str) {
    let schema = cass_session_get_schema(session);
    let keyspace_meta = cass_schema_get_keyspace(schema, str2ref(keyspace));
    if keyspace_meta.is_null() {
        println!("Unable to find \"{}\" keyspace in the schema metadata\n", keyspace);
    } else {
        print_schema_meta(&*keyspace_meta, 0)
    }
    cass_schema_free(schema);
}

unsafe fn print_table(session: &mut CassSession, keyspace:&str, table:&str) {
    let schema = cass_session_get_schema(session);
    let keyspace_meta = cass_schema_get_keyspace(schema, str2ref(keyspace));
    match keyspace_meta.is_null() {
        true => println!("Unable to find \"{}\" keyspace in the schema metadata\n", keyspace),
        false => {
            let table_meta = cass_schema_meta_get_entry(keyspace_meta, str2ref(table));
            match table_meta.is_null() {
                true => {
                    print_schema_meta(&*table_meta, 0);
                },
                false => println!("Unable to find \"{}\" table in the schema metadata\n", keyspace)
            }
        }
    }
    cass_schema_free(schema);
}

unsafe fn print_error(future:&mut CassFuture) {
    let message = cass_future_error_message(future);
    let message = slice::from_raw_buf(&message.data,message.length as usize);
    println!("Error: {:?}", message);
}

fn execute_query(session: &mut CassSession, query: &str) -> CassError {unsafe{
    let query=str2cass_string(query);

    let statement = cass_statement_new(query, 0);
    let future = &mut *cass_session_execute(session, statement);
    cass_future_wait(future);
    let rc = cass_future_error_code(future);
    match rc {
        CassError::CASS_OK => {},
        _ => print_error(future)
    }
    cass_future_free(future);
    cass_statement_free(statement);
    return rc;
}}

fn main() {unsafe{
    let  cluster = cass_cluster_new();
    let session = cass_session_new();
    cass_cluster_set_contact_points(cluster, str2ref("127.0.0.1"));
    let connect_future = cass_session_connect(session, cluster);
    match cass_future_error_code(connect_future) {
        CassError::CASS_OK => {
            execute_query(&mut*session, "CREATE KEYSPACE examples WITH replication = { 'class': 'SimpleStrategy', 'replication_factor': '3' };");
            print_keyspace(&mut*session, "examples");
            execute_query(&mut*session, "CREATE TABLE examples.schema_meta (key text, value bigint, PRIMARY KEY (key));");
            print_table(&mut*session, "examples", "schema_meta");
            /* Close the session */
            let close_future = cass_session_close(&mut*session);
            cass_future_wait(close_future);
            cass_future_free(close_future);
        },
        _ => {
        /* Handle error */
            let message = cass_future_error_message(connect_future);
            println!("Unable to connect: '{:?}'\n", message);
        }
    }
    cass_future_free(connect_future);
    cass_cluster_free(cluster);
    cass_session_free(session);
}}

fn print_schema_value(value:&CassValue) {unsafe{
//~ cass_int32_t i;
//~ cass_bool_t b;
//~ cass_double_t d;
//~ CassString s;
//~ CassUuid u;
//    char us[CASS_UUID_STRING_LENGTH];
    let cass_value_type = cass_value_type(value);
    match cass_value_type {
        CassValueType::CASS_VALUE_TYPE_INT  => {
            let cint = &mut 0i32;
            cass_value_get_int32(value, cint);
            println!("{}", cint);
        },
        CassValueType::CASS_VALUE_TYPE_BOOLEAN => {
            let mut cbool = 0u32;
            cass_value_get_bool(value, &mut cbool);
            println!("{:?}", if cbool > 0u32 {"true"} else {"false"});
        },
        CassValueType::CASS_VALUE_TYPE_DOUBLE => {
            let mut cdouble = 0f64;
            cass_value_get_double(value, &mut cdouble);
            println!("{:?}", cdouble);
        },
        CassValueType::CASS_VALUE_TYPE_TEXT|CassValueType::CASS_VALUE_TYPE_ASCII|CassValueType::CASS_VALUE_TYPE_VARCHAR => {
            let cstring = cassvalue2cassstring(value);
            println!("\"{:?}\"", cstring);
        },
        CassValueType::CASS_VALUE_TYPE_UUID => {
            let uuid = cassvalue2cassuuid(value);
            let uuid_str = cassuuid2string(uuid.unwrap());
            println!("{:?}", uuid_str);
        },
        CassValueType::CASS_VALUE_TYPE_LIST => {
            print_schema_list(value);
        },
        CassValueType::CASS_VALUE_TYPE_MAP => {
            print_schema_map(value)
        },
        _ => {
            println!("<unhandled type>");
        }
    }
}}

fn print_schema_list(value:&CassValue) {unsafe{
    let iterator = cass_iterator_from_collection(value);
    let mut is_first = true;
    //printf("[ ");
    while cass_iterator_next(iterator) > 0 {
//        if (!is_first) {println!(", ");}
        print_schema_value(&*cass_iterator_get_value(&mut*iterator));
        is_first = if cass_false > 0 {true} else {false};
    }
    //printf(" ]");
    cass_iterator_free(iterator);
}}

fn print_schema_map(value:&CassValue) {unsafe{
    let iterator = cass_iterator_from_map(value);
    let mut is_first = cass_true;
    //printf("{ ");
    while cass_iterator_next(iterator) > 0 {
        //if (!is_first) printf(", ");
        print_schema_value(&*cass_iterator_get_map_key(&mut*iterator));
        //printf(" : ");
        print_schema_value(&*cass_iterator_get_map_value(&mut*iterator));
        is_first = cass_false;
    }
    //printf(" }");
    cass_iterator_free(iterator);
}}

fn print_schema_meta_field(field:&CassSchemaMetaField, indent:i32) {unsafe{
    let name = cass_schema_meta_field_name(field);
    let value = cass_schema_meta_field_value(field);
    //print_indent(indent);
    println!("{:?}: ", name);
    print_schema_value(&*value);
    //printf("\n");
}}

fn print_schema_meta_fields(meta:&CassSchemaMeta, indent:i32) {unsafe{
    let fields = cass_iterator_fields_from_schema_meta(meta);
    while cass_iterator_next(fields) > 0 {
        print_schema_meta_field(&*cass_iterator_get_schema_meta_field(&mut*fields), indent);
    }
    cass_iterator_free(fields);
}}

fn print_schema_meta_entries(meta:&CassSchemaMeta, indent:i32) {unsafe{
    let entries = cass_iterator_from_schema_meta(meta);
    while cass_iterator_next(entries) > 0 {
        print_schema_meta(&*cass_iterator_get_schema_meta(entries), indent);
    }
    cass_iterator_free(entries);
}}

fn print_schema_meta(meta:&CassSchemaMeta, indent:i32) {unsafe{
    //print_indent(indent);
    match cass_schema_meta_type(meta) {
        CassSchemaMetaType::KEYSPACE => {
            let field = cass_schema_meta_get_field(meta, str2ref("keyspace_name"));
            if !field.is_null() {
                let name = cassvalue2cassstring(&*cass_schema_meta_field_value(field));
                println!("Keyspace \"{:?}\":", name);
            }
            print_schema_meta_fields(meta, indent + 1);
            //printf("\n");
            print_schema_meta_entries(meta, indent + 1);
        },
        CassSchemaMetaType::TABLE => {
            let field = cass_schema_meta_get_field(meta, str2ref("columnfamily_name"));
            let name = cassvalue2cassstring(&*cass_schema_meta_field_value(field));
            println!("Table \"{:?}\":", name);
            print_schema_meta_fields(meta, indent + 1);
            //printf("\n");
            print_schema_meta_entries(meta, indent + 1);
        },
        CassSchemaMetaType::COLUMN => {
            let field = cass_schema_meta_get_field(meta, str2ref("column_name"));
            if !field.is_null() {
                let name = cassvalue2cassstring(&*cass_schema_meta_field_value(field));
                println!("Column \"{:?}\":\n", name);
            }
            print_schema_meta_fields(meta, indent + 1);
            //printf("\n");
        }
    }
}}
<commit_msg>use enum namespace<commit_after>extern crate cql_ffi;

use std::slice;

use cql_ffi::*;

//fn print_schema_meta(const CassSchemaMeta* meta, int indent);

unsafe fn print_keyspace(session:&mut CassSession, keyspace:&str) {
    let schema = cass_session_get_schema(session);
    let keyspace_meta = cass_schema_get_keyspace(schema, str2ref(keyspace));
    if keyspace_meta.is_null() {
        println!("Unable to find \"{}\" keyspace in the schema metadata\n", keyspace);
    } else {
        print_schema_meta(&*keyspace_meta, 0)
    }
    cass_schema_free(schema);
}

unsafe fn print_table(session: &mut CassSession, keyspace:&str, table:&str) {
    let schema = cass_session_get_schema(session);
    let keyspace_meta = cass_schema_get_keyspace(schema, str2ref(keyspace));
    match keyspace_meta.is_null() {
        true => println!("Unable to find \"{}\" keyspace in the schema metadata\n", keyspace),
        false => {
            let table_meta = cass_schema_meta_get_entry(keyspace_meta, str2ref(table));
            match table_meta.is_null() {
                true => {
                    print_schema_meta(&*table_meta, 0);
                },
                false => println!("Unable to find \"{}\" table in the schema metadata\n", keyspace)
            }
        }
    }
    cass_schema_free(schema);
}

unsafe fn print_error(future:&mut CassFuture) {
    let message = cass_future_error_message(future);
    let message = slice::from_raw_buf(&message.data,message.length as usize);
    println!("Error: {:?}", message);
}

fn execute_query(session: &mut CassSession, query: &str) -> CassError {unsafe{
    let query=str2cass_string(query);

    let statement = cass_statement_new(query, 0);
    let future = &mut *cass_session_execute(session, statement);
    cass_future_wait(future);
    let rc = cass_future_error_code(future);
    match rc {
        CassError::CASS_OK => {},
        _ => print_error(future)
    }
    cass_future_free(future);
    cass_statement_free(statement);
    return rc;
}}

fn main() {unsafe{
    let  cluster = cass_cluster_new();
    let session = cass_session_new();
    cass_cluster_set_contact_points(cluster, str2ref("127.0.0.1"));
    let connect_future = cass_session_connect(session, cluster);
    match cass_future_error_code(connect_future) {
        CassError::CASS_OK => {
            execute_query(&mut*session, "CREATE KEYSPACE examples WITH replication = { 'class': 'SimpleStrategy', 'replication_factor': '3' };");
            print_keyspace(&mut*session, "examples");
            execute_query(&mut*session, "CREATE TABLE examples.schema_meta (key text, value bigint, PRIMARY KEY (key));");
            print_table(&mut*session, "examples", "schema_meta");
            /* Close the session */
            let close_future = cass_session_close(&mut*session);
            cass_future_wait(close_future);
            cass_future_free(close_future);
        },
        _ => {
        /* Handle error */
            let message = cass_future_error_message(connect_future);
            println!("Unable to connect: '{:?}'\n", message);
        }
    }
    cass_future_free(connect_future);
    cass_cluster_free(cluster);
    cass_session_free(session);
}}

fn print_schema_value(value:&CassValue) {unsafe{
    use cql_ffi::CassValueType::*;
    let cass_value_type = cass_value_type(value);
    match cass_value_type {
        CASS_VALUE_TYPE_INT  => {
            let cint = &mut 0i32;
            cass_value_get_int32(value, cint);
            println!("{}", cint);
        },
        CASS_VALUE_TYPE_BOOLEAN => {
            let mut cbool = 0u32;
            cass_value_get_bool(value, &mut cbool);
            println!("{:?}", if cbool > 0u32 {"true"} else {"false"});
        },
        CASS_VALUE_TYPE_DOUBLE => {
            let mut cdouble = 0f64;
            cass_value_get_double(value, &mut cdouble);
            println!("{:?}", cdouble);
        },
        CASS_VALUE_TYPE_TEXT|CASS_VALUE_TYPE_ASCII|CASS_VALUE_TYPE_VARCHAR => {
            let cstring = cassvalue2cassstring(value);
            println!("\"{:?}\"", cstring);
        },
        CASS_VALUE_TYPE_UUID => {
            let uuid = cassvalue2cassuuid(value);
            let uuid_str = cassuuid2string(uuid.unwrap());
            println!("{:?}", uuid_str);
        },
        CASS_VALUE_TYPE_LIST => {
            print_schema_list(value);
        },
        CASS_VALUE_TYPE_MAP => {
            print_schema_map(value)
        },
        _ => {
            println!("<unhandled type>");
        }
    }
}}

fn print_schema_list(value:&CassValue) {unsafe{
    let iterator = cass_iterator_from_collection(value);
    let mut is_first = true;
    //printf("[ ");
    while cass_iterator_next(iterator) > 0 {
//        if (!is_first) {println!(", ");}
        print_schema_value(&*cass_iterator_get_value(&mut*iterator));
        is_first = if cass_false > 0 {true} else {false};
    }
    //printf(" ]");
    cass_iterator_free(iterator);
}}

fn print_schema_map(value:&CassValue) {unsafe{
    let iterator = cass_iterator_from_map(value);
    let mut is_first = cass_true;
    //printf("{ ");
    while cass_iterator_next(iterator) > 0 {
        //if (!is_first) printf(", ");
        print_schema_value(&*cass_iterator_get_map_key(&mut*iterator));
        //printf(" : ");
        print_schema_value(&*cass_iterator_get_map_value(&mut*iterator));
        is_first = cass_false;
    }
    //printf(" }");
    cass_iterator_free(iterator);
}}

fn print_schema_meta_field(field:&CassSchemaMetaField, indent:i32) {unsafe{
    let name = cass_schema_meta_field_name(field);
    let value = cass_schema_meta_field_value(field);
    //print_indent(indent);
    println!("{:?}: ", name);
    print_schema_value(&*value);
    //printf("\n");
}}

fn print_schema_meta_fields(meta:&CassSchemaMeta, indent:i32) {unsafe{
    let fields = cass_iterator_fields_from_schema_meta(meta);
    while cass_iterator_next(fields) > 0 {
        print_schema_meta_field(&*cass_iterator_get_schema_meta_field(&mut*fields), indent);
    }
    cass_iterator_free(fields);
}}

fn print_schema_meta_entries(meta:&CassSchemaMeta, indent:i32) {unsafe{
    let entries = cass_iterator_from_schema_meta(meta);
    while cass_iterator_next(entries) > 0 {
        print_schema_meta(&*cass_iterator_get_schema_meta(entries), indent);
    }
    cass_iterator_free(entries);
}}

fn print_schema_meta(meta:&CassSchemaMeta, indent:i32) {unsafe{
    //print_indent(indent);
    match cass_schema_meta_type(meta) {
        CassSchemaMetaType::KEYSPACE => {
            let field = cass_schema_meta_get_field(meta, str2ref("keyspace_name"));
            if !field.is_null() {
                let name = cassvalue2cassstring(&*cass_schema_meta_field_value(field));
                println!("Keyspace \"{:?}\":", name);
            }
            print_schema_meta_fields(meta, indent + 1);
            //printf("\n");
            print_schema_meta_entries(meta, indent + 1);
        },
        CassSchemaMetaType::TABLE => {
            let field = cass_schema_meta_get_field(meta, str2ref("columnfamily_name"));
            let name = cassvalue2cassstring(&*cass_schema_meta_field_value(field));
            println!("Table \"{:?}\":", name);
            print_schema_meta_fields(meta, indent + 1);
            //printf("\n");
            print_schema_meta_entries(meta, indent + 1);
        },
        CassSchemaMetaType::COLUMN => {
            let field = cass_schema_meta_get_field(meta, str2ref("column_name"));
            if !field.is_null() {
                let name = cassvalue2cassstring(&*cass_schema_meta_field_value(field));
                println!("Column \"{:?}\":\n", name);
            }
            print_schema_meta_fields(meta, indent + 1);
            //printf("\n");
        }
    }
}}
<|endoftext|>


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

use rand::{thread_rng, Rng};
use std::cmp::min;
use std::net::SocketAddr;
use std::ops::Deref;
use std::str;
use std::sync::Arc;
use std::thread;
use std::time;

use cpupool;
use futures::{self, future, Future, Stream};
use futures::sync::mpsc;
use hyper;
use tokio_core::reactor;
use tokio_timer::Timer;

use p2p;

const PEER_MAX_COUNT: u32 = 25;
const PEER_PREFERRED_COUNT: u32 = 8;
const GIST_SEEDS_URL: &'static str = "";

pub struct Seeder {
	peer_store: Arc<p2p::PeerStore>,
	p2p: Arc<p2p::Server>,

	capabilities: p2p::Capabilities,
}

impl Seeder {
	pub fn new(capabilities: p2p::Capabilities,
	           peer_store: Arc<p2p::PeerStore>,
	           p2p: Arc<p2p::Server>)
	           -> Seeder {
		Seeder {
			peer_store: peer_store,
			p2p: p2p,
			capabilities: capabilities,
		}
	}

	pub fn connect_and_monitor(&self,
	                           h: reactor::Handle,
	                           seed_list: Box<Future<Item = Vec<SocketAddr>, Error = String>>) {
		// open a channel with a listener that connects every peer address sent below
		// max peer count
		let (tx, rx) = futures::sync::mpsc::unbounded();
		h.spawn(self.listen_for_addrs(h.clone(), rx));

		// check seeds and start monitoring connections
		let seeder = self.connect_to_seeds(tx.clone(), seed_list)
			.join(self.monitor_peers(tx.clone()));

		h.spawn(seeder.map(|_| ()).map_err(|_| ()));
	}

	fn monitor_peers(&self,
	                 tx: mpsc::UnboundedSender<SocketAddr>)
	                 -> Box<Future<Item = (), Error = String>> {
		let peer_store = self.peer_store.clone();
		let p2p_server = self.p2p.clone();

		// now spawn a new future to regularly check if we need to acquire more peers
		// and if so, gets them from db
		let mon_loop = Timer::default()
			.interval(time::Duration::from_secs(10))
			.for_each(move |_| {

				// maintenance step first, clean up p2p server peers and mark bans
				// if needed
				let disconnected = p2p_server.clean_peers();
				for p in disconnected {
					if p.is_banned() {
						debug!("Marking peer {} as banned.", p.info.addr);
						peer_store.update_state(p.info.addr, p2p::State::Banned);
					}
				}

				// we don't have enough peers, getting more from db
				if p2p_server.peer_count() < PEER_PREFERRED_COUNT {
					let mut peers = peer_store.find_peers(p2p::State::Healthy,
					                                      p2p::UNKNOWN,
					                                      (2 * PEER_MAX_COUNT) as usize);
					debug!("Got {} more peers from db, trying to connect.", peers.len());
					thread_rng().shuffle(&mut peers[..]);
					let sz = min(PEER_PREFERRED_COUNT as usize, peers.len());
					for p in &peers[0..sz] {
						tx.send(p.addr).unwrap();
					}
				}
				Ok(())
			})
			.map_err(|e| e.to_string());
		Box::new(mon_loop)
	}

	// Check if we have any pre-existing peer in db. If so, start with those,
	// otherwise use the seeds provided.
	fn connect_to_seeds(&self,
	                    tx: mpsc::UnboundedSender<SocketAddr>,
	                    seed_list: Box<Future<Item = Vec<SocketAddr>, Error = String>>)
	                    -> Box<Future<Item = (), Error = String>> {
		let peer_store = self.peer_store.clone();

		// a thread pool is required so we don't block the event loop with a
		// db query
		let thread_pool = cpupool::CpuPool::new(1);
		let seeder = thread_pool.spawn_fn(move || {
				// check if we have some peers in db
				Ok(peer_store.find_peers(p2p::State::Healthy,
				                         p2p::FULL_HIST,
				                         (2 * PEER_MAX_COUNT) as usize))
			})
			.and_then(|mut peers| {
				// if so, get their addresses, otherwise use our seeds
				if peers.len() > 0 {
					thread_rng().shuffle(&mut peers[..]);
					Box::new(future::ok(peers.iter().map(|p| p.addr).collect::<Vec<_>>()))
				} else {
					seed_list
				}
			})
			.and_then(move |peer_addrs| {
				// connect to this first set of addresses
				let sz = min(PEER_PREFERRED_COUNT as usize, peer_addrs.len());
				for addr in &peer_addrs[0..sz] {
					debug!("Connecting to seed: {}.", addr);
					tx.send(*addr).unwrap();
				}
				Ok(())
			});
		Box::new(seeder)
	}

	/// Builds a future to continuously listen on a channel receiver for new
	/// addresses to and initiate a connection if the max peer count isn't
	/// exceeded. A request for more peers is also automatically sent after
	/// connection.
	fn listen_for_addrs(&self,
	                    h: reactor::Handle,
	                    rx: mpsc::UnboundedReceiver<SocketAddr>)
	                    -> Box<Future<Item = (), Error = ()>> {
		let capab = self.capabilities;
		let p2p_server = self.p2p.clone();

		let listener = rx.for_each(move |peer_addr| {
			debug!("New peer address to connect to: {}.", peer_addr);
			let inner_h = h.clone();
			if p2p_server.peer_count() < PEER_MAX_COUNT {
				connect_and_req(capab, p2p_server.clone(), inner_h, peer_addr)
			} else {
				Box::new(future::ok(()))
			}
		});
		Box::new(listener)
	}
}

/// Extract the list of seeds from a pre-defined gist. Easy method until we
/// have a set of DNS names we can rely on.
pub fn gist_seeds(h: reactor::Handle) -> Box<Future<Item = Vec<SocketAddr>, Error = String>> {
	let url = hyper::Url::parse(&GIST_SEEDS_URL).unwrap();
	let seeds = future::ok(()).and_then(move |_| {
		let client = hyper::Client::new(&h);

		// http get, filtering out non 200 results
		client.get(url)
			.map_err(|e| e.to_string())
			.and_then(|res| {
				if *res.status() != hyper::Ok {
					return Err(format!("Gist request failed: {}", res.status()));
				}
				Ok(res)
			})
			.and_then(|res| {

				// collect all chunks and split around whitespace to get a list of SocketAddr
				res.body().collect().map_err(|e| e.to_string()).and_then(|chunks| {
					let res = chunks.iter().fold("".to_string(), |acc, ref chunk| {
						acc + str::from_utf8(&chunk[..]).unwrap()
					});
					let addrs =
						res.split_whitespace().map(|s| s.parse().unwrap()).collect::<Vec<_>>();
					Ok(addrs)
				})
			})
	});
	Box::new(seeds)
}

/// Convenience function when the seed list is immediately known. Mostly used
/// for tests.
pub fn predefined_seeds(addrs_str: Vec<String>)
                        -> Box<Future<Item = Vec<SocketAddr>, Error = String>> {
	let seeds = future::ok(())
		.and_then(move |_| Ok(addrs_str.iter().map(|s| s.parse().unwrap()).collect::<Vec<_>>()));
	Box::new(seeds)
}

fn connect_and_req(capab: p2p::Capabilities,
                   p2p: Arc<p2p::Server>,
                   h: reactor::Handle,
                   addr: SocketAddr)
                   -> Box<Future<Item = (), Error = ()>> {
	let fut = p2p.connect_peer(addr, h)
		.and_then(move |p| {
			if let Some(p) = p {
				p.send_peer_request(capab);
			}
			Ok(())
		})
		.map_err(|e| {
			error!("Peer request error {:?}", e);
			()
		});
	Box::new(fut)
}
<commit_msg>Fix Hyper URL to URI ref<commit_after>// Copyright 2016 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use rand::{thread_rng, Rng};
use std::cmp::min;
use std::net::SocketAddr;
use std::ops::Deref;
use std::str::{self, FromStr};
use std::sync::Arc;
use std::thread;
use std::time;

use cpupool;
use futures::{self, future, Future, Stream};
use futures::sync::mpsc;
use hyper;
use tokio_core::reactor;
use tokio_timer::Timer;

use p2p;

const PEER_MAX_COUNT: u32 = 25;
const PEER_PREFERRED_COUNT: u32 = 8;
const GIST_SEEDS_URL: &'static str = "";

pub struct Seeder {
	peer_store: Arc<p2p::PeerStore>,
	p2p: Arc<p2p::Server>,

	capabilities: p2p::Capabilities,
}

impl Seeder {
	pub fn new(capabilities: p2p::Capabilities,
	           peer_store: Arc<p2p::PeerStore>,
	           p2p: Arc<p2p::Server>)
	           -> Seeder {
		Seeder {
			peer_store: peer_store,
			p2p: p2p,
			capabilities: capabilities,
		}
	}

	pub fn connect_and_monitor(&self,
	                           h: reactor::Handle,
	                           seed_list: Box<Future<Item = Vec<SocketAddr>, Error = String>>) {
		// open a channel with a listener that connects every peer address sent below
		// max peer count
		let (tx, rx) = futures::sync::mpsc::unbounded();
		h.spawn(self.listen_for_addrs(h.clone(), rx));

		// check seeds and start monitoring connections
		let seeder = self.connect_to_seeds(tx.clone(), seed_list)
			.join(self.monitor_peers(tx.clone()));

		h.spawn(seeder.map(|_| ()).map_err(|_| ()));
	}

	fn monitor_peers(&self,
	                 tx: mpsc::UnboundedSender<SocketAddr>)
	                 -> Box<Future<Item = (), Error = String>> {
		let peer_store = self.peer_store.clone();
		let p2p_server = self.p2p.clone();

		// now spawn a new future to regularly check if we need to acquire more peers
		// and if so, gets them from db
		let mon_loop = Timer::default()
			.interval(time::Duration::from_secs(10))
			.for_each(move |_| {

				// maintenance step first, clean up p2p server peers and mark bans
				// if needed
				let disconnected = p2p_server.clean_peers();
				for p in disconnected {
					if p.is_banned() {
						debug!("Marking peer {} as banned.", p.info.addr);
						peer_store.update_state(p.info.addr, p2p::State::Banned);
					}
				}

				// we don't have enough peers, getting more from db
				if p2p_server.peer_count() < PEER_PREFERRED_COUNT {
					let mut peers = peer_store.find_peers(p2p::State::Healthy,
					                                      p2p::UNKNOWN,
					                                      (2 * PEER_MAX_COUNT) as usize);
					debug!("Got {} more peers from db, trying to connect.", peers.len());
					thread_rng().shuffle(&mut peers[..]);
					let sz = min(PEER_PREFERRED_COUNT as usize, peers.len());
					for p in &peers[0..sz] {
						tx.send(p.addr).unwrap();
					}
				}
				Ok(())
			})
			.map_err(|e| e.to_string());
		Box::new(mon_loop)
	}

	// Check if we have any pre-existing peer in db. If so, start with those,
	// otherwise use the seeds provided.
	fn connect_to_seeds(&self,
	                    tx: mpsc::UnboundedSender<SocketAddr>,
	                    seed_list: Box<Future<Item = Vec<SocketAddr>, Error = String>>)
	                    -> Box<Future<Item = (), Error = String>> {
		let peer_store = self.peer_store.clone();

		// a thread pool is required so we don't block the event loop with a
		// db query
		let thread_pool = cpupool::CpuPool::new(1);
		let seeder = thread_pool.spawn_fn(move || {
				// check if we have some peers in db
				Ok(peer_store.find_peers(p2p::State::Healthy,
				                         p2p::FULL_HIST,
				                         (2 * PEER_MAX_COUNT) as usize))
			})
			.and_then(|mut peers| {
				// if so, get their addresses, otherwise use our seeds
				if peers.len() > 0 {
					thread_rng().shuffle(&mut peers[..]);
					Box::new(future::ok(peers.iter().map(|p| p.addr).collect::<Vec<_>>()))
				} else {
					seed_list
				}
			})
			.and_then(move |peer_addrs| {
				// connect to this first set of addresses
				let sz = min(PEER_PREFERRED_COUNT as usize, peer_addrs.len());
				for addr in &peer_addrs[0..sz] {
					debug!("Connecting to seed: {}.", addr);
					tx.send(*addr).unwrap();
				}
				Ok(())
			});
		Box::new(seeder)
	}

	/// Builds a future to continuously listen on a channel receiver for new
	/// addresses to and initiate a connection if the max peer count isn't
	/// exceeded. A request for more peers is also automatically sent after
	/// connection.
	fn listen_for_addrs(&self,
	                    h: reactor::Handle,
	                    rx: mpsc::UnboundedReceiver<SocketAddr>)
	                    -> Box<Future<Item = (), Error = ()>> {
		let capab = self.capabilities;
		let p2p_server = self.p2p.clone();

		let listener = rx.for_each(move |peer_addr| {
			debug!("New peer address to connect to: {}.", peer_addr);
			let inner_h = h.clone();
			if p2p_server.peer_count() < PEER_MAX_COUNT {
				connect_and_req(capab, p2p_server.clone(), inner_h, peer_addr)
			} else {
				Box::new(future::ok(()))
			}
		});
		Box::new(listener)
	}
}

/// Extract the list of seeds from a pre-defined gist. Easy method until we
/// have a set of DNS names we can rely on.
pub fn gist_seeds(h: reactor::Handle) -> Box<Future<Item = Vec<SocketAddr>, Error = String>> {
	let url = hyper::Uri::from_str(&GIST_SEEDS_URL).unwrap();
	let seeds = future::ok(()).and_then(move |_| {
		let client = hyper::Client::new(&h);

		// http get, filtering out non 200 results
		client.get(url)
			.map_err(|e| e.to_string())
			.and_then(|res| {
				if *res.status() != hyper::Ok {
					return Err(format!("Gist request failed: {}", res.status()));
				}
				Ok(res)
			})
			.and_then(|res| {

				// collect all chunks and split around whitespace to get a list of SocketAddr
				res.body().collect().map_err(|e| e.to_string()).and_then(|chunks| {
					let res = chunks.iter().fold("".to_string(), |acc, ref chunk| {
						acc + str::from_utf8(&chunk[..]).unwrap()
					});
					let addrs =
						res.split_whitespace().map(|s| s.parse().unwrap()).collect::<Vec<_>>();
					Ok(addrs)
				})
			})
	});
	Box::new(seeds)
}

/// Convenience function when the seed list is immediately known. Mostly used
/// for tests.
pub fn predefined_seeds(addrs_str: Vec<String>)
                        -> Box<Future<Item = Vec<SocketAddr>, Error = String>> {
	let seeds = future::ok(())
		.and_then(move |_| Ok(addrs_str.iter().map(|s| s.parse().unwrap()).collect::<Vec<_>>()));
	Box::new(seeds)
}

fn connect_and_req(capab: p2p::Capabilities,
                   p2p: Arc<p2p::Server>,
                   h: reactor::Handle,
                   addr: SocketAddr)
                   -> Box<Future<Item = (), Error = ()>> {
	let fut = p2p.connect_peer(addr, h)
		.and_then(move |p| {
			if let Some(p) = p {
				p.send_peer_request(capab);
			}
			Ok(())
		})
		.map_err(|e| {
			error!("Peer request error {:?}", e);
			()
		});
	Box::new(fut)
}
<|endoftext|>


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

use rustc_serialize::json;

use std::collections::HashMap;
use regex;

use structure;

#[derive(Clone, PartialEq, RustcDecodable, RustcEncodable)]
pub enum StorageAction {
    StoreSingle,
    StoreVector,
    StoreMapSingle,
    StoreMapVector,
}

#[derive(Clone, PartialEq, RustcDecodable, RustcEncodable)]
pub struct StorageSpec {
    action: StorageAction,
    replace: String,
    variable: String,
    key: String
}

#[derive(Clone, PartialEq, RustcDecodable, RustcEncodable)]
pub struct RuleSpec {
    name: String,
    regex: String,
    replace: String,
    storage: Vec<StorageSpec>
}

#[derive(Clone, PartialEq, RustcDecodable, RustcEncodable)]
pub struct RuleSpecContainer {
    rules: Vec<RuleSpec>
}

impl RuleSpecContainer {
    fn validate_rules(rsc: &RuleSpecContainer) -> Result<(), String> {
        let valid_names = regex::Regex::new("^[_0-9a-zA-Z]+$").unwrap();
        for r in rsc.rules.iter() {
            match regex::Regex::new(&r.regex) {
                Ok(_) => (),
                Err(_) => {
                    return Err("Regular expression '".to_string() + &r.regex +
                               "' in rule '" + &r.name + "' is invalid.");
                }
            }
            for s in r.storage.iter() {
                if !valid_names.is_match(&s.variable) {
                    return Err("Variable name '".to_string() + &s.variable +
                               "' in rule '" + &r.name + "'is invalid. " +
                               "Please only use underscores, digits and " +
                               "ASCII letters.");
                }
                if !valid_names.is_match(&s.key) {
                    return Err("Key name '".to_string() + &s.key +
                               "' in rule '" + &r.name + "'is invalid. " +
                               "Please only use underscores, digits and " +
                               "ASCII letters.");
                }
            }
        }
        Ok(())
    }

    pub fn from_json(js: &str) -> Result<RuleSpecContainer, String> {
        let rsc = match json::decode::<RuleSpecContainer>(js) {
            Ok(x) => x,
            Err(e) => {
                return Err(
                    (format!("Error parsing rules JSON: {}", e)).to_string());
            }
        };
        match RuleSpecContainer::validate_rules(&rsc) {
            Ok(_) => (),
            Err(e) => { return Err(e); }
        }
        Ok(rsc)
    }
}

struct VarVariant {
    single:     String,
    vector:     Vec<String>,
    map_single: HashMap<String, String>,
    map_vector: HashMap<String, Vec<String>>,
}

pub struct RulesEngine {
    variables: HashMap<String, VarVariant>
}

impl RulesEngine {
    pub fn new() -> RulesEngine {
        RulesEngine {
            variables: HashMap::new()
        }
    }

    pub fn apply_rule(&mut self, rule: &RuleSpec,
                      content: &mut structure::Content) -> Result<(), String> {
        // We have validated regexps before, so this must work.
        let re = regex::Regex::new(&rule.regex).unwrap();
        // Match and perform operations.
        for mut chunk in content.chunks.iter_mut() {
            // Process each match for storage
            for cap in re.captures_iter(&chunk) {
                for s in rule.storage.iter() {
                    let processed_key = cap.expand(s.key.as_str());
                    let valid_key = regex::Regex::new(
                        "^[_0-9a-zA-Z]+$").unwrap();
                    if !valid_key.is_match(&processed_key) {
                        return Err("Captured key '".to_string() +
                                   &processed_key +
                                   "' obtained from text when applying rule '" +
                                   &rule.name +
                                   "' is invalid. Please only use " +
                                   "underscores, digits, and ASCII letters.");
                    }
                    let processed_value = cap.expand(s.replace.as_str());
                    if !self.variables.contains_key(&s.variable) {
                        self.variables.insert(s.variable.clone(), VarVariant {
                            single: String::new(),
                            vector: Vec::new(),
                            map_single: HashMap::new(),
                            map_vector: HashMap::new()
                        });
                    }
                    match s.action {
                        StorageAction::StoreSingle => {
                            self.variables.get_mut(&s.variable).unwrap(
                                ).single = processed_value;
                        },
                        StorageAction::StoreVector => {
                            self.variables.get_mut(&s.variable).unwrap(
                                ).vector.push(processed_value);
                        },
                        StorageAction::StoreMapSingle => {
                            self.variables.get_mut(&s.variable).unwrap(
                                ).map_single.insert(processed_key,
                                                    processed_value);
                        },
                        StorageAction::StoreMapVector => {
                            let must_init = !self.variables.get(&s.variable)
                                            .unwrap().map_vector
                                            .contains_key(&processed_key);
                            if must_init {
                                self.variables.get_mut(&s.variable).unwrap()
                                    .map_vector.insert(
                                        processed_key.clone(), Vec::new());
                            }
                            self.variables.get_mut(&s.variable).unwrap(
                                ).map_vector.get_mut(&processed_key).unwrap(
                                    ).push(processed_value);
                        },
                    }
                }
            }
            // Finally replace all matches
            let new_chunk = re.replace_all(&chunk, rule.replace.as_str());
            // Surely there's an easier way of doing this...
            // Why isn't there a String.swap()?
            chunk.clear();
            chunk.push_str(&new_chunk);
        }
        Ok(())
    }

    pub fn apply_rules(&mut self, rules: &RuleSpecContainer,
                       content: &mut structure::Content) -> Result<(), String> {
        for rule in rules.rules.iter() {
            match self.apply_rule(rule, content) {
                Ok(_) => (),
                Err(e) => { return Err(e); }
            }
        }
        Ok(())
    }

    pub fn substitute_vars(&self, content: &mut structure::Content)
    -> Result<(), String>  {
        let re_map_var = regex::Regex::new(
            r"\{\{\s*([_0-9a-zA-Z]+)\.([_0-9a-zA-Z]+)\s*\}\}").unwrap();
        let re_single_var = regex::Regex::new(
            r"\{\{\s*([_0-9a-zA-Z]+)\s*\}\}").unwrap();
        for mut chunk in content.chunks.iter_mut() {
            // Try the map variables first.
            while let Some(m) = re_map_var.captures(&chunk.clone()) {
                let var_name = m.at(1).unwrap();
                let var_key = m.at(2).unwrap();
                match self.variables.get(var_name) {
                    Some(var) => {
                        match var.map_single.get(var_key) {
                            Some(value) => {
                                let new_chunk = re_map_var.replace(
                                    &chunk, regex::NoExpand(value));
                                // Why isn't there a String.swap()?
                                chunk.clear();
                                chunk.push_str(&new_chunk);
                                continue;
                            },
                            None => ()
                        }
                        match var.map_vector.get(var_key) {
                            Some(vector) => {
                                let new_chunk = re_map_var.replace(
                                    &chunk, regex::NoExpand(&vector.join("\n")));
                                // Why isn't there a String.swap()?
                                chunk.clear();
                                chunk.push_str(&new_chunk);
                                continue;
                            },
                            None => ()
                        }
                        return Err("Variable '".to_string() +
                                   &var_name +
                                   "' does not contain key '" +
                                   &var_key + "' at the point of " +
                                   "variable substitution.");
                    },
                    None => {
                        return Err("Variable '".to_string() +
                                   &var_name +
                                   "' is not defined at the point of " +
                                   "variable substitution.");
                    }
                }
            }
            // Then try the non-map variables.
            while let Some(m) = re_single_var.captures(&chunk.clone()) {
                let var_name = m.at(1).unwrap();
                match self.variables.get(var_name) {
                    Some(var) => {
                        if !var.single.is_empty() {
                            let new_chunk = re_map_var.replace(
                                &chunk, regex::NoExpand(&var.single));
                            // Why isn't there a String.swap()?
                            chunk.clear();
                            chunk.push_str(&new_chunk);
                            continue;
                        }
                        if !var.vector.is_empty() {
                            let new_chunk = re_map_var.replace(
                                &chunk, regex::NoExpand(&var.vector.join("\n")));
                            // Why isn't there a String.swap()?
                            chunk.clear();
                            chunk.push_str(&new_chunk);
                            continue;
                        }
                        return Err("Variable '".to_string() +
                                   &var_name +
                                   "' is not a single value nor a vector " +
                                   "at the point of " +
                                   "variable substitution.");
                    },
                    None => {
                        return Err("Variable '".to_string() +
                                   &var_name +
                                   "' is not defined at the point of " +
                                   "variable substitution.");
                    }
                }
            }
        }
        Ok(())
    }
}
<commit_msg>Removed static check that should have been a runtime check<commit_after>// Copyright 2016 Dario Domizioli
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use rustc_serialize::json;

use std::collections::HashMap;
use regex;

use structure;

#[derive(Clone, PartialEq, RustcDecodable, RustcEncodable)]
pub enum StorageAction {
    StoreSingle,
    StoreVector,
    StoreMapSingle,
    StoreMapVector,
}

#[derive(Clone, PartialEq, RustcDecodable, RustcEncodable)]
pub struct StorageSpec {
    action: StorageAction,
    replace: String,
    variable: String,
    key: String
}

#[derive(Clone, PartialEq, RustcDecodable, RustcEncodable)]
pub struct RuleSpec {
    name: String,
    regex: String,
    replace: String,
    storage: Vec<StorageSpec>
}

#[derive(Clone, PartialEq, RustcDecodable, RustcEncodable)]
pub struct RuleSpecContainer {
    rules: Vec<RuleSpec>
}

impl RuleSpecContainer {
    fn validate_rules(rsc: &RuleSpecContainer) -> Result<(), String> {
        let valid_names = regex::Regex::new("^[_0-9a-zA-Z]+$").unwrap();
        for r in rsc.rules.iter() {
            match regex::Regex::new(&r.regex) {
                Ok(_) => (),
                Err(_) => {
                    return Err("Regular expression '".to_string() + &r.regex +
                               "' in rule '" + &r.name + "' is invalid.");
                }
            }
            for s in r.storage.iter() {
                if !valid_names.is_match(&s.variable) {
                    return Err("Variable name '".to_string() + &s.variable +
                               "' in rule '" + &r.name + "'is invalid. " +
                               "Please only use underscores, digits and " +
                               "ASCII letters.");
                }
            }
        }
        Ok(())
    }

    pub fn from_json(js: &str) -> Result<RuleSpecContainer, String> {
        let rsc = match json::decode::<RuleSpecContainer>(js) {
            Ok(x) => x,
            Err(e) => {
                return Err(
                    (format!("Error parsing rules JSON: {}", e)).to_string());
            }
        };
        match RuleSpecContainer::validate_rules(&rsc) {
            Ok(_) => (),
            Err(e) => { return Err(e); }
        }
        Ok(rsc)
    }
}

struct VarVariant {
    single:     String,
    vector:     Vec<String>,
    map_single: HashMap<String, String>,
    map_vector: HashMap<String, Vec<String>>,
}

pub struct RulesEngine {
    variables: HashMap<String, VarVariant>
}

impl RulesEngine {
    pub fn new() -> RulesEngine {
        RulesEngine {
            variables: HashMap::new()
        }
    }

    pub fn apply_rule(&mut self, rule: &RuleSpec,
                      content: &mut structure::Content) -> Result<(), String> {
        // We have validated regexps before, so this must work.
        let re = regex::Regex::new(&rule.regex).unwrap();
        // Match and perform operations.
        for mut chunk in content.chunks.iter_mut() {
            // Process each match for storage
            for cap in re.captures_iter(&chunk) {
                for s in rule.storage.iter() {
                    let processed_key = cap.expand(s.key.as_str());
                    let valid_key = regex::Regex::new(
                        "^[_0-9a-zA-Z]+$").unwrap();
                    if !valid_key.is_match(&processed_key) {
                        return Err("Captured key '".to_string() +
                                   &processed_key +
                                   "' obtained from text when applying rule '" +
                                   &rule.name +
                                   "' is invalid. Please only use " +
                                   "underscores, digits, and ASCII letters.");
                    }
                    let processed_value = cap.expand(s.replace.as_str());
                    if !self.variables.contains_key(&s.variable) {
                        self.variables.insert(s.variable.clone(), VarVariant {
                            single: String::new(),
                            vector: Vec::new(),
                            map_single: HashMap::new(),
                            map_vector: HashMap::new()
                        });
                    }
                    match s.action {
                        StorageAction::StoreSingle => {
                            self.variables.get_mut(&s.variable).unwrap(
                                ).single = processed_value;
                        },
                        StorageAction::StoreVector => {
                            self.variables.get_mut(&s.variable).unwrap(
                                ).vector.push(processed_value);
                        },
                        StorageAction::StoreMapSingle => {
                            self.variables.get_mut(&s.variable).unwrap(
                                ).map_single.insert(processed_key,
                                                    processed_value);
                        },
                        StorageAction::StoreMapVector => {
                            let must_init = !self.variables.get(&s.variable)
                                            .unwrap().map_vector
                                            .contains_key(&processed_key);
                            if must_init {
                                self.variables.get_mut(&s.variable).unwrap()
                                    .map_vector.insert(
                                        processed_key.clone(), Vec::new());
                            }
                            self.variables.get_mut(&s.variable).unwrap(
                                ).map_vector.get_mut(&processed_key).unwrap(
                                    ).push(processed_value);
                        },
                    }
                }
            }
            // Finally replace all matches
            let new_chunk = re.replace_all(&chunk, rule.replace.as_str());
            // Surely there's an easier way of doing this...
            // Why isn't there a String.swap()?
            chunk.clear();
            chunk.push_str(&new_chunk);
        }
        Ok(())
    }

    pub fn apply_rules(&mut self, rules: &RuleSpecContainer,
                       content: &mut structure::Content) -> Result<(), String> {
        for rule in rules.rules.iter() {
            match self.apply_rule(rule, content) {
                Ok(_) => (),
                Err(e) => { return Err(e); }
            }
        }
        Ok(())
    }

    pub fn substitute_vars(&self, content: &mut structure::Content)
    -> Result<(), String>  {
        let re_map_var = regex::Regex::new(
            r"\{\{\s*([_0-9a-zA-Z]+)\.([_0-9a-zA-Z]+)\s*\}\}").unwrap();
        let re_single_var = regex::Regex::new(
            r"\{\{\s*([_0-9a-zA-Z]+)\s*\}\}").unwrap();
        for mut chunk in content.chunks.iter_mut() {
            // Try the map variables first.
            while let Some(m) = re_map_var.captures(&chunk.clone()) {
                let var_name = m.at(1).unwrap();
                let var_key = m.at(2).unwrap();
                match self.variables.get(var_name) {
                    Some(var) => {
                        match var.map_single.get(var_key) {
                            Some(value) => {
                                let new_chunk = re_map_var.replace(
                                    &chunk, regex::NoExpand(value));
                                // Why isn't there a String.swap()?
                                chunk.clear();
                                chunk.push_str(&new_chunk);
                                continue;
                            },
                            None => ()
                        }
                        match var.map_vector.get(var_key) {
                            Some(vector) => {
                                let new_chunk = re_map_var.replace(
                                    &chunk, regex::NoExpand(&vector.join("\n")));
                                // Why isn't there a String.swap()?
                                chunk.clear();
                                chunk.push_str(&new_chunk);
                                continue;
                            },
                            None => ()
                        }
                        return Err("Variable '".to_string() +
                                   &var_name +
                                   "' does not contain key '" +
                                   &var_key + "' at the point of " +
                                   "variable substitution.");
                    },
                    None => {
                        return Err("Variable '".to_string() +
                                   &var_name +
                                   "' is not defined at the point of " +
                                   "variable substitution.");
                    }
                }
            }
            // Then try the non-map variables.
            while let Some(m) = re_single_var.captures(&chunk.clone()) {
                let var_name = m.at(1).unwrap();
                match self.variables.get(var_name) {
                    Some(var) => {
                        if !var.single.is_empty() {
                            let new_chunk = re_map_var.replace(
                                &chunk, regex::NoExpand(&var.single));
                            // Why isn't there a String.swap()?
                            chunk.clear();
                            chunk.push_str(&new_chunk);
                            continue;
                        }
                        if !var.vector.is_empty() {
                            let new_chunk = re_map_var.replace(
                                &chunk, regex::NoExpand(&var.vector.join("\n")));
                            // Why isn't there a String.swap()?
                            chunk.clear();
                            chunk.push_str(&new_chunk);
                            continue;
                        }
                        return Err("Variable '".to_string() +
                                   &var_name +
                                   "' is not a single value nor a vector " +
                                   "at the point of " +
                                   "variable substitution.");
                    },
                    None => {
                        return Err("Variable '".to_string() +
                                   &var_name +
                                   "' is not defined at the point of " +
                                   "variable substitution.");
                    }
                }
            }
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>use sdl2::render::{Renderer};
use sdl2::event::Event;
pub type BoxedScene = Box<Scene + 'static>;

pub trait Scene {
    fn render(&self, renderer: &mut Renderer);
    fn handle_event(&self, event: &Event);
}
<commit_msg>Started building a framework for stacks of scenes<commit_after>use sdl2::render::{Renderer};
use sdl2::event::Event;
pub type BoxedScene = Box<Scene + 'static>;

pub trait Scene {
    fn render(&self, renderer: &mut Renderer);
    fn handle_event(&self, event: &Event);
}

pub struct SceneStack {
    scenes: Vec<BoxedScene>
}

impl SceneStack {

    pub fn new() -> SceneStack {
        SceneStack {
            scenes: vec![]
        }
    }

    pub fn push_scene(&mut self, scene: BoxedScene) {
        self.scenes.push(scene)
    }

    pub fn swap_scene(&mut self, scene: BoxedScene) -> Option<BoxedScene> {
        let old_scene = self.scenes.pop();
        self.scenes.push(scene);
        old_scene
    }

    pub fn pop_scene(&mut self) -> Option<BoxedScene> {
        self.scenes.pop()
    }

    pub fn render(&self, renderer: &mut Renderer) {
    }

    pub fn handle_event(&self, event: &Event) {
    }
}
<|endoftext|>


<commit_before>//! Defines the scene struct which contains the various objects defining the scene.
//! This includes the geometry, instances of the geometry, the camera and so on.

use std::sync::Arc;

use linalg::{Transform, Point, Vector, Ray};
use film::{Camera, Colorf};
use geometry::{Sphere, Instance};
use material::{Matte, SpecularMetal, Material};
use geometry::{Geometry, Intersection};
use integrator::{Whitted, Integrator};
use light;
use light::Light;

/// The scene containing the objects and camera configuration we'd like to render,
/// shared immutably among the ray tracing threads
pub struct Scene {
    pub camera: Arc<Camera>,
    /// TODO: Only one instance for now
    pub instances: Arc<Vec<Instance>>,
    pub integrator: Arc<Box<Integrator + Send + Sync>>,
    /// TODO: Only one light for now
    pub light: Arc<Box<Light + Send + Sync>>,
    sphere: Arc<Box<Geometry + Send + Sync>>,
}

impl Scene {
    /// Create our (currently) hard-coded scene, passing in the render target
    /// dimensions so we can set the projection matrix for the camera
    pub fn new(w: usize, h: usize) -> Scene {
        let sphere = Arc::new(Box::new(Sphere::new(1.0)) as Box<Geometry + Send + Sync>);
        let instances = vec![Instance::new(sphere.clone(),
            Arc::new(Box::new(SpecularMetal::new(&Colorf::new(0.155, 0.116, 0.138),
                                                 &Colorf::new(4.828, 3.122, 2.146)))
                     as Box<Material + Send + Sync>), Transform::translate(&Vector::new(-1.0, 0.0, 0.0))),
            Instance::new(sphere.clone(),
            Arc::new(Box::new(Matte::new(&Colorf::new(0.0, 0.0, 1.0), 0.8))
                     as Box<Material + Send + Sync>), Transform::translate(&Vector::new(2.0, 0.0, 0.0)))];
        Scene {
            camera: Arc::new(Camera::new(Transform::look_at(&Point::new(0.0, 0.0, -10.0),
                &Point::new(0.0, 0.0, 0.0), &Vector::new(0.0, 1.0, 0.0)), 40.0, (w, h))),
            instances: Arc::new(instances),
            integrator: Arc::new(Box::new(Whitted::new(8)) as Box<Integrator + Send + Sync>),
            light: Arc::new(Box::new(light::Point::new(&Point::new(0.0, 1.5, -4.0), &Colorf::broadcast(30.0)))
                            as Box<Light + Send + Sync>),
            sphere: sphere.clone(),
        }
    }
    /// Test the ray for intersections against the objects in the scene.
    /// Returns Some(Intersection) if an intersection was found and None if not.
    pub fn intersect(&self, ray: &mut Ray) -> Option<Intersection> {
        // We can always return the next hit found since the ray's max_t value is updated
        // after an intersection is found. Thus if we find another hit we know that one
        // occured before any previous ones. TODO: Replace with a BVH
        self.instances.iter().fold(None, |p, ref i|
                                    match i.intersect(ray) {
                                        Some(h) => Some(h),
                                        None => p,
                                    })
    }
}

<commit_msg>Use planes to setup small pt scene<commit_after>//! Defines the scene struct which contains the various objects defining the scene.
//! This includes the geometry, instances of the geometry, the camera and so on.

use std::sync::Arc;

use linalg::{Transform, Point, Vector, Ray};
use film::{Camera, Colorf};
use geometry::{Sphere, Plane, Instance};
use material::{Matte, SpecularMetal, Material};
use geometry::{Geometry, Intersection};
use integrator::{Whitted, Integrator};
use light;
use light::Light;

/// The scene containing the objects and camera configuration we'd like to render,
/// shared immutably among the ray tracing threads
pub struct Scene {
    pub camera: Arc<Camera>,
    /// TODO: Only one instance for now
    pub instances: Arc<Vec<Instance>>,
    pub integrator: Arc<Box<Integrator + Send + Sync>>,
    /// TODO: Only one light for now
    pub light: Arc<Box<Light + Send + Sync>>,
    sphere: Arc<Box<Geometry + Send + Sync>>,
}

impl Scene {
    /// Create our (currently) hard-coded scene, passing in the render target
    /// dimensions so we can set the projection matrix for the camera
    pub fn new(w: usize, h: usize) -> Scene {
        let sphere = Arc::new(Box::new(Sphere::new(1.9)) as Box<Geometry + Send + Sync>);
        let plane = Arc::new(Box::new(Plane) as Box<Geometry + Send + Sync>);
        let white_wall = Arc::new(Box::new(Matte::new(&Colorf::new(1.0, 1.0, 1.0), 0.0)) as Box<Material + Send + Sync>);
        let red_wall = Arc::new(Box::new(Matte::new(&Colorf::new(1.0, 0.2, 0.2), 1.0)) as Box<Material + Send + Sync>);
        let blue_wall = Arc::new(Box::new(Matte::new(&Colorf::new(0.2, 0.2, 1.0), 1.0)) as Box<Material + Send + Sync>);
        let instances = vec![
            // The reflective sphere
            Instance::new(sphere.clone(),
            Arc::new(Box::new(SpecularMetal::new(&Colorf::new(0.155, 0.116, 0.138),
                                                 &Colorf::new(4.828, 3.122, 2.146)))
                     as Box<Material + Send + Sync>), Transform::translate(&Vector::new(-3.0, -4.0, 6.0))),
            // The blue matte sphere
            Instance::new(sphere.clone(),
            Arc::new(Box::new(Matte::new(&Colorf::new(0.0, 0.0, 1.0), 0.8))
                     as Box<Material + Send + Sync>), Transform::translate(&Vector::new(3.0, -4.0, 3.0))),
            // The back wall
            Instance::new(plane.clone(), white_wall.clone(), Transform::translate(&Vector::new(0.0, 0.0, 10.0))
                          * Transform::scale(&Vector::broadcast(20.0))),
            // The left wall
            Instance::new(plane.clone(), red_wall.clone(), Transform::translate(&Vector::new(-6.0, 0.0, 0.0))
                          * Transform::scale(&Vector::broadcast(20.0)) * Transform::rotate_y(90.0)),
            // The right wall
            Instance::new(plane.clone(), blue_wall.clone(), Transform::translate(&Vector::new(6.0, 0.0, 0.0))
                          * Transform::scale(&Vector::broadcast(20.0)) * Transform::rotate_y(-90.0)),
            // The top wall
            Instance::new(plane.clone(), white_wall.clone(), Transform::translate(&Vector::new(0.0, 6.0, 0.0))
                          * Transform::scale(&Vector::broadcast(20.0)) * Transform::rotate_x(90.0)),
            // The bottom wall
            Instance::new(plane.clone(), white_wall.clone(), Transform::translate(&Vector::new(0.0, -6.0, 0.0))
                          * Transform::scale(&Vector::broadcast(20.0)) * Transform::rotate_x(-90.0))
        ];
        Scene {
            camera: Arc::new(Camera::new(Transform::look_at(&Point::new(0.0, 0.0, -16.0),
                &Point::new(0.0, 0.0, 0.0), &Vector::new(0.0, 1.0, 0.0)), 40.0, (w, h))),
            instances: Arc::new(instances),
            integrator: Arc::new(Box::new(Whitted::new(8)) as Box<Integrator + Send + Sync>),
            light: Arc::new(Box::new(light::Point::new(&Point::new(0.0, 5.0, 3.0), &Colorf::broadcast(40.0)))
                            as Box<Light + Send + Sync>),
            sphere: sphere.clone(),
        }
    }
    /// Test the ray for intersections against the objects in the scene.
    /// Returns Some(Intersection) if an intersection was found and None if not.
    pub fn intersect(&self, ray: &mut Ray) -> Option<Intersection> {
        // We can always return the next hit found since the ray's max_t value is updated
        // after an intersection is found. Thus if we find another hit we know that one
        // occured before any previous ones. TODO: Replace with a BVH
        self.instances.iter().fold(None, |p, ref i|
                                    match i.intersect(ray) {
                                        Some(h) => Some(h),
                                        None => p,
                                    })
    }
}

<|endoftext|>


<commit_before>use std::ops::{Deref, DerefMut};

#[derive(Debug, PartialEq)]
pub struct Stack(Vec<i64>);

impl Stack {
    pub fn new() -> Self {
        Stack(Vec::new())
    }

    pub fn peek(&self) -> Option<&i64> {
        self.last()
    }

    pub fn map_top<F>(&mut self, function: F)
        where F: Fn(i64) -> i64
    {
        if let Some(reference) = self.last_mut() {
            *reference = function(*reference);
        }
    }

    pub fn fold_top<F>(&mut self, function: F)
        where F: Fn(i64, i64) -> i64
    {
        match self.pop2() {
            (Some(first), Some(second)) => {
                self.push(function(first, second));
            }

            (Some(first), None) => self.push(first),
            _ => {}
        }
    }

    pub fn pop2(&mut self) -> (Option<i64>, Option<i64>) {
        (self.pop(), self.pop())
    }

    pub fn roll(&mut self, depth: usize, times: i64) {
        if depth >= self.len() {
            return;
        }

        let index = self.len() - depth;

        if times >= 0 {
            for _ in 0..times {
                let value = self.pop().unwrap();
                // We can unwrap here, because if the stack is empty, the depth
                // is always larger than the length, so there would be an
                // early return.

                self.insert(index, value);
            }
        } else {
            for _ in 0..times.abs() {
                let value = self.swap_remove(index);
                self.push(value);
            }
        }
    }
}

impl Deref for Stack {
    type Target = Vec<i64>;

    fn deref(&self) -> &Vec<i64> {
        &self.0
    }
}

impl DerefMut for Stack {
    fn deref_mut(&mut self) -> &mut Vec<i64> {
        &mut self.0
    }
}

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

    #[test]
    fn test_stack_new() {
        assert_eq!(Stack::new(), Stack(Vec::new()));
    }

    #[test]
    fn test_stack_peek() {
        let stack = Stack(vec![1, 2]);
        assert_eq!(stack.peek(), Some(&2));

        let stack = Stack(vec![1]);
        assert_eq!(stack.peek(), Some(&1));

        let stack = Stack(vec![]);
        assert_eq!(stack.peek(), None);
    }

    #[test]
    fn test_stack_map_top() {
        let mut stack = Stack(vec![1, 2]);
        stack.map_top(|_| 42);
        assert_eq!(stack, Stack(vec![1, 42]));

        let mut stack = Stack(vec![1]);
        stack.map_top(|_| 42);
        assert_eq!(stack, Stack(vec![42]));

        let mut stack = Stack(vec![]);
        stack.map_top(|_| 42);
        assert_eq!(stack, Stack(vec![]));
    }

    #[test]
    fn test_stack_fold_top() {
        let mut stack = Stack(vec![1, 4, 6]);
        stack.fold_top(|a, b| a - b);
        assert_eq!(stack, Stack(vec![1, 2]));

        let mut stack = Stack(vec![1, 3]);
        stack.fold_top(|a, b| a - b);
        assert_eq!(stack, Stack(vec![2]));

        let mut stack = Stack(vec![1]);
        stack.fold_top(|a, b| a - b);
        assert_eq!(stack, Stack(vec![1]));

        let mut stack = Stack(vec![]);
        stack.fold_top(|a, b| a - b);
        assert_eq!(stack, Stack(vec![]));
    }

    #[test]
    fn test_stack_pop2() {
        let mut stack = Stack(vec![1, 2, 3]);
        assert_eq!(stack.pop2(), (Some(3), Some(2)));
        assert_eq!(stack, Stack(vec![1]));

        let mut stack = Stack(vec![1, 2]);
        assert_eq!(stack.pop2(), (Some(2), Some(1)));
        assert_eq!(stack, Stack(vec![]));

        let mut stack = Stack(vec![1]);
        assert_eq!(stack.pop2(), (Some(1), None));
        assert_eq!(stack, Stack(vec![]));

        let mut stack = Stack(vec![]);
        assert_eq!(stack.pop2(), (None, None));
        assert_eq!(stack, Stack(vec![]));
    }
}
<commit_msg>Add tests for `Stack::roll`<commit_after>use std::ops::{Deref, DerefMut};

#[derive(Debug, PartialEq)]
pub struct Stack(Vec<i64>);

impl Stack {
    pub fn new() -> Self {
        Stack(Vec::new())
    }

    pub fn peek(&self) -> Option<&i64> {
        self.last()
    }

    pub fn map_top<F>(&mut self, function: F)
        where F: Fn(i64) -> i64
    {
        if let Some(reference) = self.last_mut() {
            *reference = function(*reference);
        }
    }

    pub fn fold_top<F>(&mut self, function: F)
        where F: Fn(i64, i64) -> i64
    {
        match self.pop2() {
            (Some(first), Some(second)) => {
                self.push(function(first, second));
            }

            (Some(first), None) => self.push(first),
            _ => {}
        }
    }

    pub fn pop2(&mut self) -> (Option<i64>, Option<i64>) {
        (self.pop(), self.pop())
    }

    pub fn roll(&mut self, depth: usize, times: i64) {
        if depth >= self.len() {
            return;
        }

        let index = (self.len() - 1) - depth;

        if times >= 0 {
            for _ in 0..times {
                let value = self.pop().unwrap();
                // We can unwrap here, because if the stack is empty, the depth
                // is always larger than the length, so there would be an
                // early return.

                self.insert(index, value);
            }
        } else {
            for _ in 0..times.abs() {
                let value = self.remove(index);
                self.push(value);
            }
        }
    }
}

impl Deref for Stack {
    type Target = Vec<i64>;

    fn deref(&self) -> &Vec<i64> {
        &self.0
    }
}

impl DerefMut for Stack {
    fn deref_mut(&mut self) -> &mut Vec<i64> {
        &mut self.0
    }
}

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

    #[test]
    fn test_stack_new() {
        assert_eq!(Stack::new(), Stack(Vec::new()));
    }

    #[test]
    fn test_stack_peek() {
        let stack = Stack(vec![1, 2]);
        assert_eq!(stack.peek(), Some(&2));

        let stack = Stack(vec![1]);
        assert_eq!(stack.peek(), Some(&1));

        let stack = Stack(vec![]);
        assert_eq!(stack.peek(), None);
    }

    #[test]
    fn test_stack_map_top() {
        let mut stack = Stack(vec![1, 2]);
        stack.map_top(|_| 42);
        assert_eq!(stack, Stack(vec![1, 42]));

        let mut stack = Stack(vec![1]);
        stack.map_top(|_| 42);
        assert_eq!(stack, Stack(vec![42]));

        let mut stack = Stack(vec![]);
        stack.map_top(|_| 42);
        assert_eq!(stack, Stack(vec![]));
    }

    #[test]
    fn test_stack_fold_top() {
        let mut stack = Stack(vec![1, 4, 6]);
        stack.fold_top(|a, b| a - b);
        assert_eq!(stack, Stack(vec![1, 2]));

        let mut stack = Stack(vec![1, 3]);
        stack.fold_top(|a, b| a - b);
        assert_eq!(stack, Stack(vec![2]));

        let mut stack = Stack(vec![1]);
        stack.fold_top(|a, b| a - b);
        assert_eq!(stack, Stack(vec![1]));

        let mut stack = Stack(vec![]);
        stack.fold_top(|a, b| a - b);
        assert_eq!(stack, Stack(vec![]));
    }

    #[test]
    fn test_stack_pop2() {
        let mut stack = Stack(vec![1, 2, 3]);
        assert_eq!(stack.pop2(), (Some(3), Some(2)));
        assert_eq!(stack, Stack(vec![1]));

        let mut stack = Stack(vec![1, 2]);
        assert_eq!(stack.pop2(), (Some(2), Some(1)));
        assert_eq!(stack, Stack(vec![]));

        let mut stack = Stack(vec![1]);
        assert_eq!(stack.pop2(), (Some(1), None));
        assert_eq!(stack, Stack(vec![]));

        let mut stack = Stack(vec![]);
        assert_eq!(stack.pop2(), (None, None));
        assert_eq!(stack, Stack(vec![]));
    }
     #[test]
    fn test_stack_roll() {
        let mut stack = Stack(vec![1, 2, 3, 4]);
        stack.roll(2, 1);
        assert_eq!(stack, Stack(vec![1, 4, 2, 3]));

        let mut stack = Stack(vec![1, 2, 3, 4]);
        stack.roll(2, 2);
        assert_eq!(stack, Stack(vec![1, 3, 4, 2]));

        let mut stack = Stack(vec![1, 2, 3, 4]);
        stack.roll(2, -1);
        assert_eq!(stack, Stack(vec![1, 3, 4, 2]));

        let mut stack = Stack(vec![1, 2, 3, 4]);
        stack.roll(2, -2);
        assert_eq!(stack, Stack(vec![1, 4, 2, 3]));

        let mut stack = Stack(vec![]);
        stack.roll(1, 1);
        assert_eq!(stack, Stack(vec![]));
    }
}
<|endoftext|>


<commit_before>use super::{start_threads};

use std;
use std::io::{Read, Write};
use std::sync::{Arc, Mutex};

use time::{SteadyTime, Duration};

use mio;

const THREADS_N : [usize; 4] = [1, 2, 5, 21];

#[test]
fn empty_handler() {
    for &threads in THREADS_N.iter() {
        let finished_ok = Arc::new(Mutex::new(false));

        let finished_copy = finished_ok.clone();
        start_threads(threads, move |_| {
            let mut lock = finished_copy.lock().unwrap();
            *lock = true;

            Ok(())
        });

        assert!(*finished_ok.lock().unwrap());
    }
}

#[test]
fn empty_subcoroutines() {
    for &threads in THREADS_N.iter() {
        let counter = Arc::new(Mutex::new(0i32));

        let counter_copy = counter.clone();

        start_threads(threads, move |mioco| {

            for _ in 0..512 {
                let counter_subcopy = counter_copy.clone();
                mioco.spawn(move |_| {
                    let mut lock = counter_subcopy.lock().unwrap();
                    *lock += 1;

                    Ok(())
                });
            }

            let mut lock = counter_copy.lock().unwrap();
            *lock += 1;

            Ok(())
        });

        assert_eq!(*counter.lock().unwrap(), 512 + 1);
    }
}

#[test]
fn contain_panics() {
    for &threads in THREADS_N.iter() {
        let finished_ok = Arc::new(Mutex::new(false));

        start_threads(threads, move |_| {
            panic!()
        });

        assert!(!*finished_ok.lock().unwrap());
    }
}

#[test]
fn contain_panics_in_subcoroutines() {
    for &threads in THREADS_N.iter() {
        let finished_ok = Arc::new(Mutex::new(false));

        let finished_copy = finished_ok.clone();
        start_threads(threads, move |mioco| {

            for _ in 0..512 {
                mioco.spawn(|_| {
                    panic!()
                });
            }

            let mut lock = finished_copy.lock().unwrap();
            *lock = true;

            Ok(())
        });

        assert!(*finished_ok.lock().unwrap());
    }
}

#[test]
fn long_chain() {
    for &threads in THREADS_N.iter() {
        let finished_ok = Arc::new(Mutex::new(false));

        let finished_copy = finished_ok.clone();
        start_threads(threads, move |mioco| {

            let (first_reader, first_writer) = try!(mio::unix::pipe());

            let mut prev_reader = first_reader;

            // TODO: increase after https://github.com/dpc/mioco/issues/8 is fixed
            for _ in 0..128 {
                let (reader, writer) = try!(mio::unix::pipe());

                mioco.spawn(move |mioco| {
                    let mut reader = mioco.wrap(prev_reader);
                    let mut writer = mioco.wrap(writer);

                    let _ = std::io::copy(&mut reader, &mut writer);

                    Ok(())
                });

                prev_reader = reader;
            }

            let mut first_writer = mioco.wrap(first_writer);
            let mut last_reader = mioco.wrap(prev_reader);

            for i in 0..9 {
                let test_str = format!("TeSt{}", i);
                let _ = first_writer.write_all(test_str.as_str().as_bytes());
                let mut buf = [0u8; 16];

                let _ = last_reader.read(&mut buf);

                if &buf[0..5] != test_str.as_str().as_bytes() {
                    panic!();
                }
            }

            let mut lock = finished_copy.lock().unwrap();
            *lock = true;

            Ok(())
        });

        assert!(*finished_ok.lock().unwrap());
    }
}

#[test]
fn lots_of_event_sources() {
    for &threads in THREADS_N.iter() {
        let finished_ok = Arc::new(Mutex::new(false));

        let finished_copy = finished_ok.clone();
        start_threads(threads, move |mioco| {

            let (first_reader, first_writer) = try!(mio::unix::pipe());

            let mut prev_reader = first_reader;

            // TODO: increase after https://github.com/dpc/mioco/issues/8 is fixed
            for _ in 0..4 {
                let (reader, writer) = try!(mio::unix::pipe());

                mioco.spawn(move |mioco| {
                    // This fake readers are not really used, they are just registered for the sake of
                    // testing if event sources registered with high id number are handled correctly
                    let mut readers = Vec::new();
                    let mut writers = Vec::new();
                    for _ in 0..100 {
                        let (r, w) = try!(mio::unix::pipe());
                        readers.push(mioco.wrap(r));
                        writers.push(mioco.wrap(w));
                    }

                    let mut reader = mioco.wrap(prev_reader);
                    let mut writer = mioco.wrap(writer);

                    let _ = std::io::copy(&mut reader, &mut writer);

                    Ok(())
                });

                prev_reader = reader;
            }

            let mut first_writer = mioco.wrap(first_writer);
            let mut last_reader = mioco.wrap(prev_reader);

            for i in 0..9 {
                let test_str = format!("TeSt{}", i);
                let _ = first_writer.write_all(test_str.as_str().as_bytes());
                let mut buf = [0u8; 16];

                let _ = last_reader.read(&mut buf);

                if &buf[0..5] != test_str.as_str().as_bytes() {
                    panic!();
                }
            }

            let mut lock = finished_copy.lock().unwrap();
            *lock = true;

            Ok(())
        });

        assert!(*finished_ok.lock().unwrap());
    }
}

#[test]
fn timer_times_out() {
    for &threads in THREADS_N.iter() {
        let finished_ok_1 = Arc::new(Mutex::new(false));
        let finished_ok_2 = Arc::new(Mutex::new(false));

        let finished_ok_1_copy = finished_ok_1.clone();
        let finished_ok_2_copy = finished_ok_2.clone();
        start_threads(threads, move |mioco| {

            let (reader, writer) = try!(mio::unix::pipe());

            mioco.spawn(move |mioco| {
                let reader = mioco.wrap(reader);
                let timer_id = mioco.timer().id();
                mioco.timer().set_timeout(500);
                let ev = mioco.select_read_from(&[reader.id(), timer_id]);
                assert_eq!(ev.id(), timer_id);

                let mut lock = finished_ok_1_copy.lock().unwrap();
                *lock = true;
                Ok(())
            });

            mioco.spawn(move |mioco| {
                let mut writer = mioco.wrap(writer);
                mioco.sleep(1000);
                let _ = writer.write_all("test".as_bytes());

                let mut lock = finished_ok_2_copy.lock().unwrap();
                *lock = true;
                Ok(())
            });

            Ok(())
        });

        assert!(*finished_ok_1.lock().unwrap());
        assert!(*finished_ok_2.lock().unwrap());
    }
}

#[test]
fn timer_default_timeout() {
    for &threads in THREADS_N.iter() {
        let finished_ok = Arc::new(Mutex::new(false));

        let finished_ok_copy = finished_ok.clone();
        start_threads(threads, move |mioco| {

            mioco.spawn(move |mioco| {
                let timer_id = mioco.timer().id();
                let ev = mioco.select_read_from(&[timer_id]);
                assert_eq!(ev.id(), timer_id);

                let mut lock = finished_ok_copy.lock().unwrap();
                *lock = true;
                Ok(())
            });

            Ok(())
        });

        assert!(*finished_ok.lock().unwrap());
    }
}

#[test]
fn sleep_takes_time() {
    for &threads in THREADS_N.iter() {
        let starting_time = SteadyTime::now();

        start_threads(threads, move |mioco| {
            mioco.sleep(500); Ok(())
        });

        assert!((SteadyTime::now() - starting_time) >= Duration::milliseconds(500));
    }
}

#[test]
fn timer_select_takes_time() {
    for &threads in THREADS_N.iter() {
        let starting_time = SteadyTime::now();

        start_threads(threads, move |mioco| {
            let timer_id = mioco.timer().id();
            mioco.timer().set_timeout(500);
            let ev = mioco.select_read_from(&[timer_id]);
            assert_eq!(mioco.timer().id(), ev.id());
            Ok(())
        });

        assert!((SteadyTime::now() - starting_time) >= Duration::milliseconds(500));
    }
}

#[test]
fn basic_timer_stress_test() {
    for &threads in THREADS_N.iter() {
        start_threads(threads, move |mioco| {
            for _ in 0..10 {
                for t in 0..100 {
                    mioco.spawn(move |mioco| {
                        mioco.sleep(t);
                        Ok(())
                    });

                }

                mioco.sleep(1);
            }
            Ok(())
        });
    }
}

#[test]
fn exit_notifier_simple() {
    for &threads in THREADS_N.iter() {
        let finished_ok = Arc::new(Mutex::new(false));

        let finished_copy = finished_ok.clone();
        start_threads(threads, move |mioco| {

            let notify = mioco.spawn(move |_| {
                Ok(())
            }).exit_notificator();

            let notify = mioco.wrap(notify);

            assert!(!notify.read().is_panic());

            let mut lock = finished_copy.lock().unwrap();
            *lock = true;
            Ok(())
        });

        assert!(*finished_ok.lock().unwrap());
    }
}

#[test]
fn exit_notifier_simple_panic() {
    for &threads in THREADS_N.iter() {
        let finished_ok = Arc::new(Mutex::new(false));

        let finished_copy = finished_ok.clone();
        start_threads(threads, move |mioco| {

            let notify = mioco.spawn(move |_| {
                panic!()
            }).exit_notificator();

            let notify = mioco.wrap(notify);

            assert!(notify.read().is_panic());

            let mut lock = finished_copy.lock().unwrap();
            *lock = true;
            Ok(())
        });

        assert!(*finished_ok.lock().unwrap());
    }
}

#[test]
fn exit_notifier_wrap_after_finish() {
    for &threads in THREADS_N.iter() {
        let finished_ok = Arc::new(Mutex::new(false));

        let finished_copy = finished_ok.clone();
        start_threads(threads, move |mioco| {

            let handle1 = mioco.spawn(move |_| {
                panic!()
            });

            mioco.sleep(1000);
            let notify1 = handle1.exit_notificator();

            let handle2 = mioco.spawn(move |mioco| {
                let notify1 = mioco.wrap(notify1);
                assert!(notify1.read().is_panic());
                Ok(())
            });

            let notify2 = handle2.exit_notificator();
            let notify2 = mioco.wrap(notify2);
            assert!(!notify2.read().is_panic());

            let notify1 = handle1.exit_notificator();
            let notify1 = mioco.wrap(notify1);
            assert!(notify1.read().is_panic());

            let mut lock = finished_copy.lock().unwrap();
            *lock = true;
            Ok(())
        });

        assert!(*finished_ok.lock().unwrap());
    }
}
<commit_msg>tests: Add `destructs_io_on_panic`.<commit_after>use super::{start_threads};

use std;
use std::io::{Read, Write};
use std::sync::{Arc, Mutex};

use time::{SteadyTime, Duration};

use mio;

const THREADS_N : [usize; 4] = [1, 2, 5, 21];

#[test]
fn empty_handler() {
    for &threads in THREADS_N.iter() {
        let finished_ok = Arc::new(Mutex::new(false));

        let finished_copy = finished_ok.clone();
        start_threads(threads, move |_| {
            let mut lock = finished_copy.lock().unwrap();
            *lock = true;

            Ok(())
        });

        assert!(*finished_ok.lock().unwrap());
    }
}

#[test]
fn empty_subcoroutines() {
    for &threads in THREADS_N.iter() {
        let counter = Arc::new(Mutex::new(0i32));

        let counter_copy = counter.clone();

        start_threads(threads, move |mioco| {

            for _ in 0..512 {
                let counter_subcopy = counter_copy.clone();
                mioco.spawn(move |_| {
                    let mut lock = counter_subcopy.lock().unwrap();
                    *lock += 1;

                    Ok(())
                });
            }

            let mut lock = counter_copy.lock().unwrap();
            *lock += 1;

            Ok(())
        });

        assert_eq!(*counter.lock().unwrap(), 512 + 1);
    }
}

#[test]
fn contain_panics() {
    for &threads in THREADS_N.iter() {
        let finished_ok = Arc::new(Mutex::new(false));

        start_threads(threads, move |_| {
            panic!()
        });

        assert!(!*finished_ok.lock().unwrap());
    }
}

#[test]
fn contain_panics_in_subcoroutines() {
    for &threads in THREADS_N.iter() {
        let finished_ok = Arc::new(Mutex::new(false));

        let finished_copy = finished_ok.clone();
        start_threads(threads, move |mioco| {

            for _ in 0..512 {
                mioco.spawn(|_| {
                    panic!()
                });
            }

            let mut lock = finished_copy.lock().unwrap();
            *lock = true;

            Ok(())
        });

        assert!(*finished_ok.lock().unwrap());
    }
}

#[test]
fn long_chain() {
    for &threads in THREADS_N.iter() {
        let finished_ok = Arc::new(Mutex::new(false));

        let finished_copy = finished_ok.clone();
        start_threads(threads, move |mioco| {

            let (first_reader, first_writer) = try!(mio::unix::pipe());

            let mut prev_reader = first_reader;

            // TODO: increase after https://github.com/dpc/mioco/issues/8 is fixed
            for _ in 0..128 {
                let (reader, writer) = try!(mio::unix::pipe());

                mioco.spawn(move |mioco| {
                    let mut reader = mioco.wrap(prev_reader);
                    let mut writer = mioco.wrap(writer);

                    let _ = std::io::copy(&mut reader, &mut writer);

                    Ok(())
                });

                prev_reader = reader;
            }

            let mut first_writer = mioco.wrap(first_writer);
            let mut last_reader = mioco.wrap(prev_reader);

            for i in 0..9 {
                let test_str = format!("TeSt{}", i);
                let _ = first_writer.write_all(test_str.as_str().as_bytes());
                let mut buf = [0u8; 16];

                let _ = last_reader.read(&mut buf);

                if &buf[0..5] != test_str.as_str().as_bytes() {
                    panic!();
                }
            }

            let mut lock = finished_copy.lock().unwrap();
            *lock = true;

            Ok(())
        });

        assert!(*finished_ok.lock().unwrap());
    }
}

#[test]
fn lots_of_event_sources() {
    for &threads in THREADS_N.iter() {
        let finished_ok = Arc::new(Mutex::new(false));

        let finished_copy = finished_ok.clone();
        start_threads(threads, move |mioco| {

            let (first_reader, first_writer) = try!(mio::unix::pipe());

            let mut prev_reader = first_reader;

            // TODO: increase after https://github.com/dpc/mioco/issues/8 is fixed
            for _ in 0..4 {
                let (reader, writer) = try!(mio::unix::pipe());

                mioco.spawn(move |mioco| {
                    // This fake readers are not really used, they are just registered for the sake of
                    // testing if event sources registered with high id number are handled correctly
                    let mut readers = Vec::new();
                    let mut writers = Vec::new();
                    for _ in 0..100 {
                        let (r, w) = try!(mio::unix::pipe());
                        readers.push(mioco.wrap(r));
                        writers.push(mioco.wrap(w));
                    }

                    let mut reader = mioco.wrap(prev_reader);
                    let mut writer = mioco.wrap(writer);

                    let _ = std::io::copy(&mut reader, &mut writer);

                    Ok(())
                });

                prev_reader = reader;
            }

            let mut first_writer = mioco.wrap(first_writer);
            let mut last_reader = mioco.wrap(prev_reader);

            for i in 0..9 {
                let test_str = format!("TeSt{}", i);
                let _ = first_writer.write_all(test_str.as_str().as_bytes());
                let mut buf = [0u8; 16];

                let _ = last_reader.read(&mut buf);

                if &buf[0..5] != test_str.as_str().as_bytes() {
                    panic!();
                }
            }

            let mut lock = finished_copy.lock().unwrap();
            *lock = true;

            Ok(())
        });

        assert!(*finished_ok.lock().unwrap());
    }
}

/// Test if drop is performed on IOs when coroutine panics.
#[test]
fn destructs_io_on_panic() {
    for &threads in THREADS_N.iter() {
        let finished_ok = Arc::new(Mutex::new(false));

        let finished_ok_copy = finished_ok.clone();
        start_threads(threads, move |mioco| {

            let (reader, writer) = try!(mio::unix::pipe());

            mioco.spawn(move |mioco| {
                let mut reader = mioco.wrap(reader);
                let mut buf = [0u8; 16];
                let ret = reader.read(&mut buf);
                assert!(ret.is_ok());

                let mut lock = finished_ok_copy.lock().unwrap();
                *lock = true;
                Ok(())
            });

            mioco.spawn(move |mioco| {
                let _writer = mioco.wrap(writer);
                panic!();
            });

            Ok(())
        });

        assert!(*finished_ok.lock().unwrap());
    }
}

#[test]
fn timer_times_out() {
    for &threads in THREADS_N.iter() {
        let finished_ok_1 = Arc::new(Mutex::new(false));
        let finished_ok_2 = Arc::new(Mutex::new(false));

        let finished_ok_1_copy = finished_ok_1.clone();
        let finished_ok_2_copy = finished_ok_2.clone();
        start_threads(threads, move |mioco| {

            let (reader, writer) = try!(mio::unix::pipe());

            mioco.spawn(move |mioco| {
                let reader = mioco.wrap(reader);
                let timer_id = mioco.timer().id();
                mioco.timer().set_timeout(500);
                let ev = mioco.select_read_from(&[reader.id(), timer_id]);
                assert_eq!(ev.id(), timer_id);

                let mut lock = finished_ok_1_copy.lock().unwrap();
                *lock = true;
                Ok(())
            });

            mioco.spawn(move |mioco| {
                let mut writer = mioco.wrap(writer);
                mioco.sleep(1000);
                let _ = writer.write_all("test".as_bytes());

                let mut lock = finished_ok_2_copy.lock().unwrap();
                *lock = true;
                Ok(())
            });

            Ok(())
        });

        assert!(*finished_ok_1.lock().unwrap());
        assert!(*finished_ok_2.lock().unwrap());
    }
}

#[test]
fn timer_default_timeout() {
    for &threads in THREADS_N.iter() {
        let finished_ok = Arc::new(Mutex::new(false));

        let finished_ok_copy = finished_ok.clone();
        start_threads(threads, move |mioco| {

            mioco.spawn(move |mioco| {
                let timer_id = mioco.timer().id();
                let ev = mioco.select_read_from(&[timer_id]);
                assert_eq!(ev.id(), timer_id);

                let mut lock = finished_ok_copy.lock().unwrap();
                *lock = true;
                Ok(())
            });

            Ok(())
        });

        assert!(*finished_ok.lock().unwrap());
    }
}

#[test]
fn sleep_takes_time() {
    for &threads in THREADS_N.iter() {
        let starting_time = SteadyTime::now();

        start_threads(threads, move |mioco| {
            mioco.sleep(500); Ok(())
        });

        assert!((SteadyTime::now() - starting_time) >= Duration::milliseconds(500));
    }
}

#[test]
fn timer_select_takes_time() {
    for &threads in THREADS_N.iter() {
        let starting_time = SteadyTime::now();

        start_threads(threads, move |mioco| {
            let timer_id = mioco.timer().id();
            mioco.timer().set_timeout(500);
            let ev = mioco.select_read_from(&[timer_id]);
            assert_eq!(mioco.timer().id(), ev.id());
            Ok(())
        });

        assert!((SteadyTime::now() - starting_time) >= Duration::milliseconds(500));
    }
}

#[test]
fn basic_timer_stress_test() {
    for &threads in THREADS_N.iter() {
        start_threads(threads, move |mioco| {
            for _ in 0..10 {
                for t in 0..100 {
                    mioco.spawn(move |mioco| {
                        mioco.sleep(t);
                        Ok(())
                    });

                }

                mioco.sleep(1);
            }
            Ok(())
        });
    }
}

#[test]
fn exit_notifier_simple() {
    for &threads in THREADS_N.iter() {
        let finished_ok = Arc::new(Mutex::new(false));

        let finished_copy = finished_ok.clone();
        start_threads(threads, move |mioco| {

            let notify = mioco.spawn(move |_| {
                Ok(())
            }).exit_notificator();

            let notify = mioco.wrap(notify);

            assert!(!notify.read().is_panic());

            let mut lock = finished_copy.lock().unwrap();
            *lock = true;
            Ok(())
        });

        assert!(*finished_ok.lock().unwrap());
    }
}

#[test]
fn exit_notifier_simple_panic() {
    for &threads in THREADS_N.iter() {
        let finished_ok = Arc::new(Mutex::new(false));

        let finished_copy = finished_ok.clone();
        start_threads(threads, move |mioco| {

            let notify = mioco.spawn(move |_| {
                panic!()
            }).exit_notificator();

            let notify = mioco.wrap(notify);

            assert!(notify.read().is_panic());

            let mut lock = finished_copy.lock().unwrap();
            *lock = true;
            Ok(())
        });

        assert!(*finished_ok.lock().unwrap());
    }
}

#[test]
fn exit_notifier_wrap_after_finish() {
    for &threads in THREADS_N.iter() {
        let finished_ok = Arc::new(Mutex::new(false));

        let finished_copy = finished_ok.clone();
        start_threads(threads, move |mioco| {

            let handle1 = mioco.spawn(move |_| {
                panic!()
            });

            mioco.sleep(1000);
            let notify1 = handle1.exit_notificator();

            let handle2 = mioco.spawn(move |mioco| {
                let notify1 = mioco.wrap(notify1);
                assert!(notify1.read().is_panic());
                Ok(())
            });

            let notify2 = handle2.exit_notificator();
            let notify2 = mioco.wrap(notify2);
            assert!(!notify2.read().is_panic());

            let notify1 = handle1.exit_notificator();
            let notify1 = mioco.wrap(notify1);
            assert!(notify1.read().is_panic());

            let mut lock = finished_copy.lock().unwrap();
            *lock = true;
            Ok(())
        });

        assert!(*finished_ok.lock().unwrap());
    }
}
<|endoftext|>