<commit_before>extern crate ssz;

use self::ssz::{
    SszStream,
};
use std::sync::Arc;
use super::{
    validate_ssz_block,
    BlockStatus,
    AttesterMap,
    ProposerMap,
};
use super::db::stores::{
    BlockStore,
    PoWChainStore,
    ValidatorStore,
};
use super::db::{
    MemoryDB,
};
use super::utils::hash::canonical_hash;
use super::utils::types::{
    Hash256,
    Bitfield,
};
use super::SszBlock;
use super::super::Block;
use super::super::attestation_record::AttestationRecord;
use super::super::super::bls::{
    Keypair,
    Signature,
    AggregateSignature,
};

pub struct TestStore {
    pub db: Arc<MemoryDB>,
    pub block: Arc<BlockStore<MemoryDB>>,
    pub pow_chain: Arc<PoWChainStore<MemoryDB>>,
    pub validator: Arc<ValidatorStore<MemoryDB>>,
}

impl TestStore {
    pub fn new() -> Self {
        let db = Arc::new(MemoryDB::open());
        let block = Arc::new(BlockStore::new(db.clone()));
        let pow_chain = Arc::new(PoWChainStore::new(db.clone()));
        let validator = Arc::new(ValidatorStore::new(db.clone()));
        Self {
            db,
            block,
            pow_chain,
            validator,
        }
    }
}

pub fn generate_attestations_for_slot(attestation_slot: u64,
                                  block_slot: u64,
                                  shard_count: u16,
                                  validators_per_shard: usize,
                                  cycle_length: u8,
                                  parent_hashes: &[Hash256],
                                  shard_block_hash: &Hash256,
                                  justified_block_hash: &Hash256,
                                  justified_slot: u64,
                                  stores: &TestStore)
    -> (AttesterMap, Vec<AttestationRecord>, Vec<Keypair>)
{
    let mut i = 0;
    let mut attester_map = AttesterMap::new();
    let mut attestations = vec![];
    let mut keypairs = vec![];
    for shard in 0..shard_count {
        let mut attesters = vec![];
        let mut attester_bitfield = Bitfield::new();
        let mut aggregate_sig = AggregateSignature::new();

        let parent_hashes_slice = {
            let distance: usize = (block_slot - attestation_slot) as usize;
            let last: usize = parent_hashes.len() - distance;
            let first: usize = last - usize::from(cycle_length);
            &parent_hashes[first..last]
        };

        let attestation_message = {
            let mut stream = SszStream::new();
            stream.append(&attestation_slot);
            stream.append_vec(&parent_hashes_slice.to_vec());
            stream.append(&shard);
            stream.append(shard_block_hash);
            stream.append(&justified_slot);
            let bytes = stream.drain();
            canonical_hash(&bytes)
        };

        for attestation_index in 0..validators_per_shard {
           /*
            * Add the attester to the attestation indices for this shard.
            */
           attesters.push(i);
           /*
            * Set the voters bit on the bitfield to true.
            */
           attester_bitfield.set_bit(attestation_index, true);
           /*
            * Generate a random keypair for this validatior and clone it into the
            * list of keypairs.
            */
           let keypair = Keypair::random();
           keypairs.push(keypair.clone());
           /*
            * Store the validators public key in the database.
            */
           stores.validator.put_public_key_by_index(i, &keypair.pk).unwrap();
           /*
            * Generate a new signature and aggregate it on the rolling signature.
            */
           let sig = Signature::new(&attestation_message, &keypair.sk);
           aggregate_sig.add(&sig);
           /*
            * Increment the validator counter to monotonically assign validators.
            */
           i += 1;
        }

        attester_map.insert((attestation_slot, shard), attesters);
        let attestation = AttestationRecord {
            slot: attestation_slot,
            shard_id: shard,
            oblique_parent_hashes: vec![],
            shard_block_hash: *shard_block_hash,
            attester_bitfield,
            justified_slot,
            justified_block_hash: *justified_block_hash,
            aggregate_sig,
        };
        attestations.push(attestation);
    }
    (attester_map, attestations, keypairs)
}

#[test]
fn test_block_validation() {
    let stores = TestStore::new();

    let cycle_length: u8 = 2;
    let shard_count: u16 = 2;
    let present_slot = u64::from(cycle_length) * 10000;
    let justified_slot = present_slot - u64::from(cycle_length);
    let justified_block_hash = Hash256::from("justified_hash".as_bytes());
    let shard_block_hash = Hash256::from("shard_hash".as_bytes());
    let parent_hashes: Vec<Hash256> = (0..(cycle_length * 2))
        .map(|i| Hash256::from(i as u64))
        .collect();
    let pow_chain_ref = Hash256::from("pow_chain".as_bytes());
    let active_state_root = Hash256::from("active_state".as_bytes());
    let crystallized_state_root = Hash256::from("cry_state".as_bytes());

    stores.pow_chain.put_block_hash(pow_chain_ref.as_ref()).unwrap();
    stores.block.put_block(justified_block_hash.as_ref(), &vec![42]).unwrap();

    let validators_per_shard = 10;

    let block_slot = present_slot;
    let validator_index: usize = 0;
    let proposer_map = {
        let mut proposer_map = ProposerMap::new();
        proposer_map.insert(present_slot, validator_index);
        proposer_map
    };
    let attestation_slot = block_slot - 1;
    let (attester_map, attestations, _keypairs) =
        generate_attestations_for_slot(
            attestation_slot,
            block_slot,
            shard_count,
            validators_per_shard,
            cycle_length,
            &parent_hashes,
            &shard_block_hash,
            &justified_block_hash,
            justified_slot,
            &stores);

    let block = Block {
        parent_hash: Hash256::from("parent".as_bytes()),
        slot_number: block_slot,
        randao_reveal: Hash256::from("randao".as_bytes()),
        attestations,
        pow_chain_ref,
        active_state_root,
        crystallized_state_root,
    };

    let mut stream = SszStream::new();
    stream.append(&block);
    let serialized_block = stream.drain();
    let ssz_block = SszBlock::from_slice(&serialized_block[..]).unwrap();

    let status = validate_ssz_block(
        &ssz_block,
        present_slot,
        cycle_length,
        justified_slot,
        &Arc::new(parent_hashes),
        &Arc::new(proposer_map),
        &Arc::new(attester_map),
        &stores.block.clone(),
        &stores.validator.clone(),
        &stores.pow_chain.clone()).unwrap();

    assert_eq!(status, BlockStatus::NewBlock);
}
<commit_msg>Refact block val. tests to be more modular<commit_after>extern crate ssz;

use self::ssz::{
    SszStream,
};
use std::sync::Arc;
use super::{
    validate_ssz_block,
    SszBlockValidationError,
    BlockStatus,
    AttesterMap,
    ProposerMap,
};
use super::db::stores::{
    BlockStore,
    PoWChainStore,
    ValidatorStore,
};
use super::db::{
    MemoryDB,
};
use super::utils::hash::canonical_hash;
use super::utils::types::{
    Hash256,
    Bitfield,
};
use super::SszBlock;
use super::super::Block;
use super::super::attestation_record::AttestationRecord;
use super::super::super::bls::{
    Keypair,
    Signature,
    AggregateSignature,
};

pub struct TestStore {
    pub db: Arc<MemoryDB>,
    pub block: Arc<BlockStore<MemoryDB>>,
    pub pow_chain: Arc<PoWChainStore<MemoryDB>>,
    pub validator: Arc<ValidatorStore<MemoryDB>>,
}

impl TestStore {
    pub fn new() -> Self {
        let db = Arc::new(MemoryDB::open());
        let block = Arc::new(BlockStore::new(db.clone()));
        let pow_chain = Arc::new(PoWChainStore::new(db.clone()));
        let validator = Arc::new(ValidatorStore::new(db.clone()));
        Self {
            db,
            block,
            pow_chain,
            validator,
        }
    }
}

#[derive(Debug)]
pub struct TestParams {
    pub total_validators: usize,
    pub cycle_length: u8,
    pub shard_count: u16,
    pub shards_per_slot: u16,
    pub validators_per_shard: usize,
    pub block_slot: u64,
    pub attestations_justified_slot: u64,
}
type ParentHashes = Vec<Hash256>;

/// Setup for a block validation function, without actually executing the
/// block validation function.
pub fn setup_block_validation_scenario(params: &TestParams)
    -> (Block, ParentHashes, AttesterMap, ProposerMap, TestStore)
{
    let stores = TestStore::new();

    let cycle_length = params.cycle_length;
    let shards_per_slot = params.shards_per_slot;
    let validators_per_shard = params.validators_per_shard;
    let block_slot = params.block_slot;
    let attestations_justified_slot = params.attestations_justified_slot;

    let parent_hashes: Vec<Hash256> = (0..(cycle_length * 2))
        .map(|i| Hash256::from(i as u64))
        .collect();
    let parent_hash = Hash256::from("parent_hash".as_bytes());
    let randao_reveal = Hash256::from("randao_reveal".as_bytes());
    let justified_block_hash = Hash256::from("justified_hash".as_bytes());
    let pow_chain_ref = Hash256::from("pow_chain".as_bytes());
    let active_state_root = Hash256::from("active_state".as_bytes());
    let crystallized_state_root = Hash256::from("cry_state".as_bytes());
    let shard_block_hash = Hash256::from("shard_block_hash".as_bytes());

    stores.pow_chain.put_block_hash(pow_chain_ref.as_ref()).unwrap();
    stores.block.put_block(justified_block_hash.as_ref(), &vec![42]).unwrap();

    let validator_index: usize = 0;
    let proposer_map = {
        let mut proposer_map = ProposerMap::new();
        proposer_map.insert(block_slot, validator_index);
        proposer_map
    };
    /*
    let attestation_slot = block_slot - 1;
    let (attester_map, attestations, _keypairs) =
        generate_attestations_for_slot(
            attestation_slot,
            block_slot,
            shards_per_slot,
            validators_per_shard,
            cycle_length,
            &parent_hashes,
            &Hash256::from("shard_hash".as_bytes()),
            &justified_block_hash,
            attestations_justified_slot,
            &stores);
    */
    let (attester_map, attestations, _keypairs) = {
        let mut i = 0;
        let attestation_slot = block_slot - 1;
        let mut attester_map = AttesterMap::new();
        let mut attestations = vec![];
        let mut keypairs = vec![];
        for shard in 0..shards_per_slot {
            let mut attesters = vec![];
            let mut attester_bitfield = Bitfield::new();
            let mut aggregate_sig = AggregateSignature::new();

            let parent_hashes_slice = {
                let distance: usize = (block_slot - attestation_slot) as usize;
                let last: usize = parent_hashes.len() - distance;
                let first: usize = last - usize::from(cycle_length);
                &parent_hashes[first..last]
            };

            let attestation_message = {
                let mut stream = SszStream::new();
                stream.append(&attestation_slot);
                stream.append_vec(&parent_hashes_slice.to_vec());
                stream.append(&shard);
                stream.append(&shard_block_hash);
                stream.append(&attestations_justified_slot);
                let bytes = stream.drain();
                canonical_hash(&bytes)
            };

            for attestation_index in 0..validators_per_shard {
               /*
                * Add the attester to the attestation indices for this shard.
                */
               attesters.push(i);
               /*
                * Set the voters bit on the bitfield to true.
                */
               attester_bitfield.set_bit(attestation_index, true);
               /*
                * Generate a random keypair for this validatior and clone it into the
                * list of keypairs.
                */
               let keypair = Keypair::random();
               keypairs.push(keypair.clone());
               /*
                * Store the validators public key in the database.
                */
               stores.validator.put_public_key_by_index(i, &keypair.pk).unwrap();
               /*
                * Generate a new signature and aggregate it on the rolling signature.
                */
               let sig = Signature::new(&attestation_message, &keypair.sk);
               aggregate_sig.add(&sig);
               /*
                * Increment the validator counter to monotonically assign validators.
                */
               i += 1;
            }

            attester_map.insert((attestation_slot, shard), attesters);
            let attestation = AttestationRecord {
                slot: attestation_slot,
                shard_id: shard,
                oblique_parent_hashes: vec![],
                shard_block_hash,
                attester_bitfield,
                justified_slot: attestations_justified_slot,
                justified_block_hash,
                aggregate_sig,
            };
            attestations.push(attestation);
        }
        (attester_map, attestations, keypairs)
    };

    let block = Block {
        parent_hash,
        slot_number: block_slot,
        randao_reveal,
        attestations,
        pow_chain_ref,
        active_state_root,
        crystallized_state_root,
    };

    (block,
     parent_hashes,
     attester_map,
     proposer_map,
     stores)
}

/// Helper function to take some Block and SSZ serialize it.
pub fn serialize_block(b: &Block) -> Vec<u8> {
    let mut stream = SszStream::new();
    stream.append(b);
    stream.drain()
}

/// Setup and run a block validation scenario, given some parameters.
///
/// Returns the Result returned from the block validation function.
pub fn run_block_validation_scenario<F>(
    validation_slot: u64,
    validation_last_justified_slot: u64,
    params: &TestParams,
    mutator_func: F)
    -> Result<BlockStatus, SszBlockValidationError>
    where F: FnOnce(Block, AttesterMap, ProposerMap, TestStore)
                -> (Block, AttesterMap, ProposerMap, TestStore)
{
    let (block,
     parent_hashes,
     attester_map,
     proposer_map,
     stores) = setup_block_validation_scenario(&params);

    let (block,
         attester_map,
         proposer_map,
         stores) = mutator_func(block, attester_map, proposer_map, stores);

    let ssz_bytes = serialize_block(&block);
    let ssz_block = SszBlock::from_slice(&ssz_bytes[..])
        .unwrap();

    validate_ssz_block(
        &ssz_block,
        validation_slot,
        params.cycle_length,
        validation_last_justified_slot,
        &Arc::new(parent_hashes),
        &Arc::new(proposer_map),
        &Arc::new(attester_map),
        &stores.block.clone(),
        &stores.validator.clone(),
        &stores.pow_chain.clone())
}

fn get_simple_params() -> TestParams {
    let validators_per_shard: usize = 5;
    let cycle_length: u8 = 2;
    let shard_count: u16 = 4;
    let shards_per_slot: u16 = shard_count / u16::from(cycle_length);
    let total_validators: usize = validators_per_shard * shard_count as usize;
    let block_slot = u64::from(cycle_length) * 10000;
    let attestations_justified_slot = block_slot - u64::from(cycle_length);

    TestParams {
        total_validators,
        cycle_length,
        shard_count,
        shards_per_slot,
        validators_per_shard,
        block_slot,
        attestations_justified_slot,
    }
}

#[test]
fn test_block_validation_simple_scenario_valid() {
    let params = get_simple_params();

    let validation_slot = params.block_slot;
    let validation_last_justified_slot = params.attestations_justified_slot;

    let no_mutate = |block, attester_map, proposer_map, stores| {
        (block, attester_map, proposer_map, stores)
    };

    let status = run_block_validation_scenario(
        validation_slot,
        validation_last_justified_slot,
        &params,
        no_mutate);

    assert_eq!(status.unwrap(), BlockStatus::NewBlock);
}

#[test]
fn test_block_validation_simple_scenario_invalid_2nd_attestation() {
    let params = get_simple_params();

    let validation_slot = params.block_slot;
    let validation_last_justified_slot = params.attestations_justified_slot;

    let mutator = |mut block: Block, attester_map, proposer_map, stores| {
        /*
         * Set the second attestaion record to have an invalid signature.
         */
        block.attestations[1].aggregate_sig = AggregateSignature::new();
        (block, attester_map, proposer_map, stores)
    };

    let status = run_block_validation_scenario(
        validation_slot,
        validation_last_justified_slot,
        &params,
        mutator);

    assert_eq!(status, Err(SszBlockValidationError::AttestationSignatureFailed));
}
<|endoftext|>


<commit_before>// Unused mut warning is false in this context
#[macro_use]
extern crate slog;
extern crate slog_term;
extern crate slog_stream;

#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate serde;
extern crate futures;
extern crate hyper;
extern crate tokio_core;
extern crate hyper_tls;
extern crate time;
extern crate clap; // TODO Add application help
extern crate curl;
extern crate webbrowser;

mod utils;
mod models;
mod client;
mod app;
mod endpoint;
mod cli;

use app::*;
use models::*;
use client::*;
use cli::*;

use tokio_core::reactor::{Core, Handle};
use std::io::{self, BufRead};
use std::thread::spawn;
use std::thread;
use std::process;
use futures::{Stream, Sink, Future};
use futures::sync::mpsc;
use futures::sync::mpsc::{Receiver, Sender};
use futures::stream::BoxStream;
use curl::easy::Easy;

fn main() {

    let mut app_domain = AppDomain::new();
    let mut app_cache: AppCache = AppCache::new();
    let mut app_state_machine: AppStateMachine = AppStateMachine::new();
    let mut main_core = Core::new().expect("Failed to create core");

    info!(&app_domain.logger, "Application started");
    app_cache.retrieved_top_stories = get_top_story_ids(&mut app_domain, &mut app_state_machine)
        .ok();
    print_ten_stories(&mut app_domain, &mut app_cache, &mut app_state_machine);
    let (mut sender, receiver) = mpsc::channel(1);

    spawn(move || {
        let stdin = io::stdin();
        for line in stdin.lock().lines() {
            let sender = sender.clone();
            sender.send(line).wait().unwrap();
        }
    });
    let listener =
        receiver.for_each(|msg| {
            gui_listener(msg, &mut app_domain, &mut app_cache, &mut app_state_machine)
        });

    main_core.run(listener);
}

fn gui_listener(msg_result: Result<String, io::Error>,
                app_domain: &mut AppDomain,
                app_cache: &mut AppCache,
                app_state_machine: &mut AppStateMachine)
                -> Result<(), ()> {
    // TODO Logging
    match msg_result {
        Ok(msg) => {
            if msg.len() >= 4 && &msg[0..4] == "next" {
                app_state_machine.listing_page_index += 1;
                print_ten_stories(app_domain, app_cache, app_state_machine);
                info!(&app_domain.logger,
                      format!("Retrieved next ten stories with index {}",
                              &app_state_machine.listing_page_index))
            }

            else if msg.len() >= 3 && &msg[0..3] == "top" {
                print_ten_stories(app_domain, app_cache, app_state_machine);
            }

            else if msg.len() >= 4 && &msg[0..4] == "exit" {
                info!(&app_domain.logger, "Exited application normally");
                process::exit(0);
            }

            else if msg.len() >= 8 && &msg[0..8] == "comments" {
                let numb = (msg[9..].parse::<i32>().unwrap() - 1) as usize; // FIXME not handling errors either
                load_comments_for_item(numb, app_domain, app_cache, app_state_machine);
                let parent = app_cache.last_parent_items.back();
                match parent {
                    Some(ref parent) => {
                        match app_cache.last_retrieved_comments {
                            Some(ref comments) => cli::print_comments(parent, &comments),
                            None => cli::could_not_get_any_commments_for_item(parent),
                        }
                    }
                    None => ()
                }
            }

            else if msg.len() >= 4 && &msg[0..4] == "load" {
                let numb = (msg[5..].parse::<i32>().unwrap() - 1) as usize; // FIXME not handling errors either
                load_page_to_local(numb, app_domain, app_cache, app_state_machine);
            }

            else if msg.len() >= 4 && &msg[0..4] == "back" {
                if app_state_machine.listing_page_index >= 0 {
                    app_state_machine.listing_page_index -= 1;
                }
                print_ten_stories(app_domain, app_cache, app_state_machine);
                info!(&app_domain.logger,
                      format!("Retrieved previous ten stories with index {}",
                              &app_state_machine.listing_page_index));
            }

            else if msg.len() >= 4 && &msg[0..4] == "open" {
                let numb = (msg[5..].parse::<i32>().unwrap() - 1) as usize; // FIXME not handling errors either
                open_page_with_default_browser(numb, app_domain, app_cache, app_state_machine);
            }
        }
        Err(_) => println!("{}", "Error"),
    }
    Ok(())
}

fn load_comments_for_item(numb: usize,
                          app_domain: &mut AppDomain,
                          app_cache: &mut AppCache,
                          app_state_machine: &mut AppStateMachine) {
    let parent = get_item(numb, app_domain, app_cache, app_state_machine).unwrap(); // FIXME No error handling
    let comments = client::get_comments_for_item(&parent, app_domain, app_state_machine);

    match comments {
        Some(commentVec) => {
            app_cache.last_parent_items.push_back(parent); // move parent to this location
            app_cache.last_retrieved_comments = Some(commentVec); // return comments and return
        }
        None => ()
    }
}

fn get_item(numb: usize,
            app_domain: &mut AppDomain,
            app_cache: &mut AppCache,
            app_state_machine: &mut AppStateMachine)
            -> Option<HnItem> {
    let s = format!("{}",
                    app_cache.retrieved_top_stories.as_ref().unwrap().values[numb]); // FIXME unsafe way to do this
    let item = client::get_item_by_id(&s, app_domain, app_state_machine).ok();
    item
}

fn open_page_with_default_browser(numb: usize,
                                  app_domain: &mut AppDomain,
                                  app_cache: &mut AppCache,
                                  app_state_machine: &mut AppStateMachine) {
    let s = format!("{}",
                    app_cache.retrieved_top_stories.as_ref().unwrap().values[numb]); // FIXME unsafe way to do this
    let item = client::get_item_by_id(&s, app_domain, app_state_machine).unwrap();
    if item.url.is_some() && webbrowser::open(&item.url.as_ref().unwrap()).is_ok() {
        println!("{} {}",
                 "Opened browser to url ",
                 item.url.as_ref().unwrap());
        info!(&app_domain.logger,
              format!("Opened page with default browser from url {}",
                      item.url.as_ref().unwrap()));
    }
}

fn load_page_to_local(numb: usize,
                      app_domain: &mut AppDomain,
                      app_cache: &mut AppCache,
                      app_state_machine: &mut AppStateMachine) {
    let s = format!("{}",
                    app_cache.retrieved_top_stories.as_ref().unwrap().values[numb]); // FIXME unsafe way to do this
    let item = client::get_item_by_id(&s, app_domain, app_state_machine).unwrap();
    let filen = client::download_page_from_item(&item, app_domain, app_state_machine);
    match filen {
        Ok(n) => {
            cli::print_filename_of_loaded_page(&n, item.title.as_ref().unwrap());
            info!(&app_domain.logger,
                  format!("Loaded page {} to file {}", item.url.as_ref().unwrap(), &n));
        }
        Err(e) => {
            cli::could_not_load_page(item.title.as_ref().unwrap());
            warn!(&app_domain.logger, format!("Could not load page to file"));
        }
    }

}

fn print_ten_stories(app_domain: &mut AppDomain,
                     app_cache: &mut AppCache,
                     app_state_machine: &mut AppStateMachine) {

    // This probably should not need all the parameters
    let top_stories = app_cache.retrieved_top_stories.as_ref().unwrap();
    info!(&app_domain.logger,
          format!("Received {} top stories", top_stories.values.len()));
    let skipped: usize = (app_state_machine.listing_page_index * 10) as usize;
    let mut index = 0 + skipped as i32;

    for item_id in top_stories.values.iter().skip(skipped).take(10) {
        index += 1;
        let s = format!("{}", item_id);
        let item: HnItem = client::get_item_by_id(&s, app_domain, app_state_machine).unwrap();
        cli::print_headline_with_author(&item, &index);
    }
}
<commit_msg>spezialized the comment retrieval for story vs comment<commit_after>// Unused mut warning is false in this context
#[macro_use]
extern crate slog;
extern crate slog_term;
extern crate slog_stream;

#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate serde;
extern crate futures;
extern crate hyper;
extern crate tokio_core;
extern crate hyper_tls;
extern crate time;
extern crate clap; // TODO Add application help
extern crate curl;
extern crate webbrowser;

mod utils;
mod models;
mod client;
mod app;
mod endpoint;
mod cli;

use app::*;
use models::*;
use client::*;
use cli::*;

use tokio_core::reactor::{Core, Handle};
use std::io::{self, BufRead};
use std::thread::spawn;
use std::thread;
use std::process;
use futures::{Stream, Sink, Future};
use futures::sync::mpsc;
use futures::sync::mpsc::{Receiver, Sender};
use futures::stream::BoxStream;
use curl::easy::Easy;

fn main() {

    let mut app_domain = AppDomain::new();
    let mut app_cache: AppCache = AppCache::new();
    let mut app_state_machine: AppStateMachine = AppStateMachine::new();
    let mut main_core = Core::new().expect("Failed to create core");

    info!(&app_domain.logger, "Application started");
    app_cache.retrieved_top_stories = get_top_story_ids(&mut app_domain, &mut app_state_machine)
        .ok();
    print_ten_stories(&mut app_domain, &mut app_cache, &mut app_state_machine);
    let (mut sender, receiver) = mpsc::channel(1);

    spawn(move || {
        let stdin = io::stdin();
        for line in stdin.lock().lines() {
            let sender = sender.clone();
            sender.send(line).wait().unwrap();
        }
    });
    let listener =
        receiver.for_each(|msg| {
            gui_listener(msg, &mut app_domain, &mut app_cache, &mut app_state_machine)
        });

    main_core.run(listener);
}

fn gui_listener(msg_result: Result<String, io::Error>,
                app_domain: &mut AppDomain,
                app_cache: &mut AppCache,
                app_state_machine: &mut AppStateMachine)
                -> Result<(), ()> {
    // TODO Logging
    match msg_result {
        Ok(msg) => {
            if msg.len() >= 4 && &msg[0..4] == "next" {
                app_state_machine.listing_page_index += 1;
                print_ten_stories(app_domain, app_cache, app_state_machine);
                info!(&app_domain.logger,
                      format!("Retrieved next ten stories with index {}",
                              &app_state_machine.listing_page_index))
            } else if msg.len() >= 3 && &msg[0..3] == "top" {
                print_ten_stories(app_domain, app_cache, app_state_machine);
            } else if msg.len() >= 4 && &msg[0..4] == "exit" {
                info!(&app_domain.logger, "Exited application normally");
                process::exit(0);
            } else if msg.len() >= 8 && &msg[0..8] == "comments" {
                let numb = (msg[9..].parse::<i32>().unwrap() - 1) as usize; // FIXME not handling errors either
                load_comments_for_story(numb, app_domain, app_cache, app_state_machine);
                let parent = app_cache.last_parent_items.back();
                match parent {
                    Some(ref parent) => {
                        match app_cache.last_retrieved_comments {
                            Some(ref comments) => cli::print_comments(parent, &comments),
                            None => cli::could_not_get_any_commments_for_item(parent),
                        }
                    }
                    None => (),
                }
            } else if msg.len() >= 4 && &msg[0..4] == "load" {
                let numb = (msg[5..].parse::<i32>().unwrap() - 1) as usize; // FIXME not handling errors either
                load_page_to_local(numb, app_domain, app_cache, app_state_machine);
            } else if msg.len() >= 4 && &msg[0..4] == "back" {
                if app_state_machine.listing_page_index >= 0 {
                    app_state_machine.listing_page_index -= 1;
                }
                print_ten_stories(app_domain, app_cache, app_state_machine);
                info!(&app_domain.logger,
                      format!("Retrieved previous ten stories with index {}",
                              &app_state_machine.listing_page_index));
            } else if msg.len() >= 4 && &msg[0..4] == "open" {
                let numb = (msg[5..].parse::<i32>().unwrap() - 1) as usize; // FIXME not handling errors either
                open_page_with_default_browser(numb, app_domain, app_cache, app_state_machine);
            }
        }
        Err(_) => println!("{}", "Error"),
    }
    Ok(())
}

fn load_comments_for_story(numb: usize,
                           app_domain: &mut AppDomain,
                           app_cache: &mut AppCache,
                           app_state_machine: &mut AppStateMachine) {
    let parent = get_story(numb, app_domain, app_cache, app_state_machine).unwrap(); // FIXME No error handling
    load_comments_for_item(parent, app_domain, app_cache, app_state_machine)
}

fn load_comments_for_commment(numb: usize,
                              app_domain: &mut AppDomain,
                              app_cache: &mut AppCache,
                              app_state_machine: &mut AppStateMachine) {
    // todo
}

fn load_comments_for_item(parent: HnItem,
                          app_domain: &mut AppDomain,
                          app_cache: &mut AppCache,
                          app_state_machine: &mut AppStateMachine) {
    let comments = client::get_comments_for_item(&parent, app_domain, app_state_machine);
    match comments {
        Some(commentVec) => {
            app_cache.last_parent_items.push_back(parent); // move parent to this location
            app_cache.last_retrieved_comments = Some(commentVec); // return comments and return
        }
        None => (),
    }
}

fn get_story(numb: usize,
             app_domain: &mut AppDomain,
             app_cache: &mut AppCache,
             app_state_machine: &mut AppStateMachine)
             -> Option<HnItem> {
    let s = format!("{}",
                    app_cache.retrieved_top_stories.as_ref().unwrap().values[numb]); // FIXME unsafe way to do this
    let item = client::get_item_by_id(&s, app_domain, app_state_machine).ok();
    item
}

fn open_page_with_default_browser(numb: usize,
                                  app_domain: &mut AppDomain,
                                  app_cache: &mut AppCache,
                                  app_state_machine: &mut AppStateMachine) {
    let s = format!("{}",
                    app_cache.retrieved_top_stories.as_ref().unwrap().values[numb]); // FIXME unsafe way to do this
    let item = client::get_item_by_id(&s, app_domain, app_state_machine).unwrap();
    if item.url.is_some() && webbrowser::open(&item.url.as_ref().unwrap()).is_ok() {
        println!("{} {}",
                 "Opened browser to url ",
                 item.url.as_ref().unwrap());
        info!(&app_domain.logger,
              format!("Opened page with default browser from url {}",
                      item.url.as_ref().unwrap()));
    }
}

fn load_page_to_local(numb: usize,
                      app_domain: &mut AppDomain,
                      app_cache: &mut AppCache,
                      app_state_machine: &mut AppStateMachine) {
    let s = format!("{}",
                    app_cache.retrieved_top_stories.as_ref().unwrap().values[numb]); // FIXME unsafe way to do this
    let item = client::get_item_by_id(&s, app_domain, app_state_machine).unwrap();
    let filen = client::download_page_from_item(&item, app_domain, app_state_machine);
    match filen {
        Ok(n) => {
            cli::print_filename_of_loaded_page(&n, item.title.as_ref().unwrap());
            info!(&app_domain.logger,
                  format!("Loaded page {} to file {}", item.url.as_ref().unwrap(), &n));
        }
        Err(e) => {
            cli::could_not_load_page(item.title.as_ref().unwrap());
            warn!(&app_domain.logger, format!("Could not load page to file"));
        }
    }

}

fn print_ten_stories(app_domain: &mut AppDomain,
                     app_cache: &mut AppCache,
                     app_state_machine: &mut AppStateMachine) {

    // This probably should not need all the parameters
    let top_stories = app_cache.retrieved_top_stories.as_ref().unwrap();
    info!(&app_domain.logger,
          format!("Received {} top stories", top_stories.values.len()));
    let skipped: usize = (app_state_machine.listing_page_index * 10) as usize;
    let mut index = 0 + skipped as i32;

    for item_id in top_stories.values.iter().skip(skipped).take(10) {
        index += 1;
        let s = format!("{}", item_id);
        let item: HnItem = client::get_item_by_id(&s, app_domain, app_state_machine).unwrap();
        cli::print_headline_with_author(&item, &index);
    }
}
<|endoftext|>


<commit_before>//! Enabling and disabling interrupts

/// Returns whether interrupts are enabled.
pub fn are_enabled() -> bool {
    use crate::registers::rflags::{self, RFlags};

    rflags::read().contains(RFlags::INTERRUPT_FLAG)
}

/// Enable interrupts.
///
/// This is a wrapper around the `sti` instruction.
pub fn enable() {
    unsafe {
        asm!("sti" :::: "volatile");
    }
}

/// Disable interrupts.
///
/// This is a wrapper around the `cli` instruction.
pub fn disable() {
    unsafe {
        asm!("cli" :::: "volatile");
    }
}

/// Run a closue with disabled interrupts.
///
/// Run the given closure, disabling interrupts before running it (if they aren't already disabled).
/// Afterwards, interrupts are enabling again if they were enabled before.
///
/// If you have other `enable` and `disable` calls _within_ the closure, things may not work as expected.
///
/// # Examples
///
/// ```ignore
/// // interrupts are enabled
/// without_interrupts(|| {
///     // interrupts are disabled
///     without_interrupts(|| {
///         // interrupts are disabled
///     });
///     // interrupts are still disabled
/// });
/// // interrupts are enabled again
/// ```
pub fn without_interrupts<F, R>(f: F) -> R
where
    F: FnOnce() -> R,
{
    // true if the interrupt flag is set (i.e. interrupts are enabled)
    let saved_intpt_flag = are_enabled();

    // if interrupts are enabled, disable them for now
    if saved_intpt_flag {
        disable();
    }

    // do `f` while interrupts are disabled
    let ret = f();

    // re-enable interrupts if they were previously enabled
    if saved_intpt_flag {
        enable();
    }

    // return the result of `f` to the caller
    ret
}
<commit_msg>Correct typo ("closue" instead of "closure") (#62)<commit_after>//! Enabling and disabling interrupts

/// Returns whether interrupts are enabled.
pub fn are_enabled() -> bool {
    use crate::registers::rflags::{self, RFlags};

    rflags::read().contains(RFlags::INTERRUPT_FLAG)
}

/// Enable interrupts.
///
/// This is a wrapper around the `sti` instruction.
pub fn enable() {
    unsafe {
        asm!("sti" :::: "volatile");
    }
}

/// Disable interrupts.
///
/// This is a wrapper around the `cli` instruction.
pub fn disable() {
    unsafe {
        asm!("cli" :::: "volatile");
    }
}

/// Run a closure with disabled interrupts.
///
/// Run the given closure, disabling interrupts before running it (if they aren't already disabled).
/// Afterwards, interrupts are enabling again if they were enabled before.
///
/// If you have other `enable` and `disable` calls _within_ the closure, things may not work as expected.
///
/// # Examples
///
/// ```ignore
/// // interrupts are enabled
/// without_interrupts(|| {
///     // interrupts are disabled
///     without_interrupts(|| {
///         // interrupts are disabled
///     });
///     // interrupts are still disabled
/// });
/// // interrupts are enabled again
/// ```
pub fn without_interrupts<F, R>(f: F) -> R
where
    F: FnOnce() -> R,
{
    // true if the interrupt flag is set (i.e. interrupts are enabled)
    let saved_intpt_flag = are_enabled();

    // if interrupts are enabled, disable them for now
    if saved_intpt_flag {
        disable();
    }

    // do `f` while interrupts are disabled
    let ret = f();

    // re-enable interrupts if they were previously enabled
    if saved_intpt_flag {
        enable();
    }

    // return the result of `f` to the caller
    ret
}
<|endoftext|>


<commit_before>// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Support for inlining external documentation into the current AST.

use syntax::ast;
use syntax::ast_util;
use syntax::attr::AttrMetaMethods;

use rustc::metadata::csearch;
use rustc::metadata::decoder;
use rustc::middle::ty;

use core;
use doctree;
use clean;

use super::Clean;

/// Attempt to inline the definition of a local node id into this AST.
///
/// This function will fetch the definition of the id specified, and if it is
/// from another crate it will attempt to inline the documentation from the
/// other crate into this crate.
///
/// This is primarily used for `pub use` statements which are, in general,
/// implementation details. Inlining the documentation should help provide a
/// better experience when reading the documentation in this use case.
///
/// The returned value is `None` if the `id` could not be inlined, and `Some`
/// of a vector of items if it was successfully expanded.
pub fn try_inline(id: ast::NodeId) -> Option<Vec<clean::Item>> {
    let cx = ::ctxtkey.get().unwrap();
    let tcx = match cx.maybe_typed {
        core::Typed(ref tycx) => tycx,
        core::NotTyped(_) => return None,
    };
    let def = match tcx.def_map.borrow().find(&id) {
        Some(def) => *def,
        None => return None,
    };
    let did = ast_util::def_id_of_def(def);
    if ast_util::is_local(did) { return None }
    try_inline_def(&**cx, tcx, def)
}

fn try_inline_def(cx: &core::DocContext,
                  tcx: &ty::ctxt,
                  def: ast::Def) -> Option<Vec<clean::Item>> {
    let mut ret = Vec::new();
    let did = ast_util::def_id_of_def(def);
    let inner = match def {
        ast::DefTrait(did) => {
            record_extern_fqn(cx, did, clean::TypeTrait);
            clean::TraitItem(build_external_trait(tcx, did))
        }
        ast::DefFn(did, style) => {
            // If this function is a tuple struct constructor, we just skip it
            if csearch::get_tuple_struct_definition_if_ctor(&tcx.sess.cstore,
                                                            did).is_some() {
                return None
            }
            record_extern_fqn(cx, did, clean::TypeFunction);
            clean::FunctionItem(build_external_function(tcx, did, style))
        }
        ast::DefStruct(did) => {
            record_extern_fqn(cx, did, clean::TypeStruct);
            ret.extend(build_impls(cx, tcx, did).move_iter());
            clean::StructItem(build_struct(tcx, did))
        }
        ast::DefTy(did) => {
            record_extern_fqn(cx, did, clean::TypeEnum);
            ret.extend(build_impls(cx, tcx, did).move_iter());
            build_type(tcx, did)
        }
        // Assume that the enum type is reexported next to the variant, and
        // variants don't show up in documentation specially.
        ast::DefVariant(..) => return Some(Vec::new()),
        ast::DefMod(did) => {
            record_extern_fqn(cx, did, clean::TypeModule);
            clean::ModuleItem(build_module(cx, tcx, did))
        }
        _ => return None,
    };
    let fqn = csearch::get_item_path(tcx, did);
    cx.inlined.borrow_mut().get_mut_ref().insert(did);
    ret.push(clean::Item {
        source: clean::Span::empty(),
        name: Some(fqn.last().unwrap().to_str().to_string()),
        attrs: load_attrs(tcx, did),
        inner: inner,
        visibility: Some(ast::Public),
        def_id: did,
    });
    Some(ret)
}

pub fn load_attrs(tcx: &ty::ctxt, did: ast::DefId) -> Vec<clean::Attribute> {
    let mut attrs = Vec::new();
    csearch::get_item_attrs(&tcx.sess.cstore, did, |v| {
        attrs.extend(v.move_iter().map(|mut a| {
            // FIXME this isn't quite always true, it's just true about 99% of
            //       the time when dealing with documentation. For example,
            //       this would treat doc comments of the form `#[doc = "foo"]`
            //       incorrectly.
            if a.name().get() == "doc" && a.value_str().is_some() {
                a.node.is_sugared_doc = true;
            }
            a.clean()
        }));
    });
    attrs
}

/// Record an external fully qualified name in the external_paths cache.
///
/// These names are used later on by HTML rendering to generate things like
/// source links back to the original item.
pub fn record_extern_fqn(cx: &core::DocContext,
                         did: ast::DefId,
                         kind: clean::TypeKind) {
    match cx.maybe_typed {
        core::Typed(ref tcx) => {
            let fqn = csearch::get_item_path(tcx, did);
            let fqn = fqn.move_iter().map(|i| i.to_str().to_string()).collect();
            cx.external_paths.borrow_mut().get_mut_ref().insert(did, (fqn, kind));
        }
        core::NotTyped(..) => {}
    }
}

pub fn build_external_trait(tcx: &ty::ctxt, did: ast::DefId) -> clean::Trait {
    let def = ty::lookup_trait_def(tcx, did);
    let methods = ty::trait_methods(tcx, did);
    clean::Trait {
        generics: def.generics.clean(),
        methods: methods.iter().map(|i| i.clean()).collect(),
        parents: Vec::new(), // FIXME: this is likely wrong
    }
}

fn build_external_function(tcx: &ty::ctxt,
                           did: ast::DefId,
                           style: ast::FnStyle) -> clean::Function {
    let t = ty::lookup_item_type(tcx, did);
    clean::Function {
        decl: match ty::get(t.ty).sty {
            ty::ty_bare_fn(ref f) => (did, &f.sig).clean(),
            _ => fail!("bad function"),
        },
        generics: t.generics.clean(),
        fn_style: style,
    }
}

fn build_struct(tcx: &ty::ctxt, did: ast::DefId) -> clean::Struct {
    use syntax::parse::token::special_idents::unnamed_field;

    let t = ty::lookup_item_type(tcx, did);
    let fields = ty::lookup_struct_fields(tcx, did);

    clean::Struct {
        struct_type: match fields.as_slice() {
            [] => doctree::Unit,
            [ref f] if f.name == unnamed_field.name => doctree::Newtype,
            [ref f, ..] if f.name == unnamed_field.name => doctree::Tuple,
            _ => doctree::Plain,
        },
        generics: t.generics.clean(),
        fields: fields.iter().map(|f| f.clean()).collect(),
        fields_stripped: false,
    }
}

fn build_type(tcx: &ty::ctxt, did: ast::DefId) -> clean::ItemEnum {
    let t = ty::lookup_item_type(tcx, did);
    match ty::get(t.ty).sty {
        ty::ty_enum(edid, _) => {
            return clean::EnumItem(clean::Enum {
                generics: t.generics.clean(),
                variants_stripped: false,
                variants: ty::enum_variants(tcx, edid).clean(),
            })
        }
        _ => {}
    }

    clean::TypedefItem(clean::Typedef {
        type_: t.ty.clean(),
        generics: t.generics.clean(),
    })
}

fn build_impls(cx: &core::DocContext,
               tcx: &ty::ctxt,
               did: ast::DefId) -> Vec<clean::Item> {
    ty::populate_implementations_for_type_if_necessary(tcx, did);
    let mut impls = Vec::new();

    match tcx.inherent_impls.borrow().find(&did) {
        None => {}
        Some(i) => {
            impls.extend(i.borrow().iter().map(|&did| { build_impl(tcx, did) }));
        }
    }

    // If this is the first time we've inlined something from this crate, then
    // we inline *all* impls from the crate into this crate. Note that there's
    // currently no way for us to filter this based on type, and we likely need
    // many impls for a variety of reasons.
    //
    // Primarily, the impls will be used to populate the documentation for this
    // type being inlined, but impls can also be used when generating
    // documentation for primitives (no way to find those specifically).
    if cx.populated_crate_impls.borrow_mut().insert(did.krate) {
        csearch::each_top_level_item_of_crate(&tcx.sess.cstore,
                                              did.krate,
                                              |def, _, _| {
            populate_impls(tcx, def, &mut impls)
        });

        fn populate_impls(tcx: &ty::ctxt,
                          def: decoder::DefLike,
                          impls: &mut Vec<clean::Item>) {
            match def {
                decoder::DlImpl(did) => impls.push(build_impl(tcx, did)),
                decoder::DlDef(ast::DefMod(did)) => {
                    csearch::each_child_of_item(&tcx.sess.cstore,
                                                did,
                                                |def, _, _| {
                        populate_impls(tcx, def, impls)
                    })
                }
                _ => {}
            }
        }
    }

    impls
}

fn build_impl(tcx: &ty::ctxt, did: ast::DefId) -> clean::Item {
    let associated_trait = csearch::get_impl_trait(tcx, did);
    let attrs = load_attrs(tcx, did);
    let ty = ty::lookup_item_type(tcx, did);
    let methods = csearch::get_impl_methods(&tcx.sess.cstore,
                                            did).iter().filter_map(|did| {
        let method = ty::method(tcx, *did);
        if method.vis != ast::Public && associated_trait.is_none() {
            return None
        }
        let mut item = match ty::method(tcx, *did).clean() {
            clean::Provided(item) => item,
            clean::Required(item) => item,
        };
        item.inner = match item.inner.clone() {
            clean::TyMethodItem(clean::TyMethod {
                fn_style, decl, self_, generics
            }) => {
                clean::MethodItem(clean::Method {
                    fn_style: fn_style,
                    decl: decl,
                    self_: self_,
                    generics: generics,
                })
            }
            _ => fail!("not a tymethod"),
        };
        Some(item)
    }).collect();
    clean::Item {
        inner: clean::ImplItem(clean::Impl {
            derived: clean::detect_derived(attrs.as_slice()),
            trait_: associated_trait.clean().map(|bound| {
                match bound {
                    clean::TraitBound(ty) => ty,
                    clean::RegionBound => unreachable!(),
                }
            }),
            for_: ty.ty.clean(),
            generics: ty.generics.clean(),
            methods: methods,
        }),
        source: clean::Span::empty(),
        name: None,
        attrs: attrs,
        visibility: Some(ast::Inherited),
        def_id: did,
    }
}

fn build_module(cx: &core::DocContext, tcx: &ty::ctxt,
                did: ast::DefId) -> clean::Module {
    let mut items = Vec::new();

    // FIXME: this doesn't handle reexports inside the module itself.
    //        Should they be handled?
    csearch::each_child_of_item(&tcx.sess.cstore, did, |def, _, vis| {
        if vis != ast::Public { return }
        match def {
            decoder::DlDef(def) => {
                match try_inline_def(cx, tcx, def) {
                    Some(i) => items.extend(i.move_iter()),
                    None => {}
                }
            }
            // All impls were inlined above
            decoder::DlImpl(..) => {}
            decoder::DlField => fail!("unimplemented field"),
        }
    });

    clean::Module {
        items: items,
        is_crate: false,
    }
}
<commit_msg>rustdoc: Ensure external impls are inlined once<commit_after>// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Support for inlining external documentation into the current AST.

use syntax::ast;
use syntax::ast_util;
use syntax::attr::AttrMetaMethods;

use rustc::metadata::csearch;
use rustc::metadata::decoder;
use rustc::middle::ty;

use core;
use doctree;
use clean;

use super::Clean;

/// Attempt to inline the definition of a local node id into this AST.
///
/// This function will fetch the definition of the id specified, and if it is
/// from another crate it will attempt to inline the documentation from the
/// other crate into this crate.
///
/// This is primarily used for `pub use` statements which are, in general,
/// implementation details. Inlining the documentation should help provide a
/// better experience when reading the documentation in this use case.
///
/// The returned value is `None` if the `id` could not be inlined, and `Some`
/// of a vector of items if it was successfully expanded.
pub fn try_inline(id: ast::NodeId) -> Option<Vec<clean::Item>> {
    let cx = ::ctxtkey.get().unwrap();
    let tcx = match cx.maybe_typed {
        core::Typed(ref tycx) => tycx,
        core::NotTyped(_) => return None,
    };
    let def = match tcx.def_map.borrow().find(&id) {
        Some(def) => *def,
        None => return None,
    };
    let did = ast_util::def_id_of_def(def);
    if ast_util::is_local(did) { return None }
    try_inline_def(&**cx, tcx, def)
}

fn try_inline_def(cx: &core::DocContext,
                  tcx: &ty::ctxt,
                  def: ast::Def) -> Option<Vec<clean::Item>> {
    let mut ret = Vec::new();
    let did = ast_util::def_id_of_def(def);
    let inner = match def {
        ast::DefTrait(did) => {
            record_extern_fqn(cx, did, clean::TypeTrait);
            clean::TraitItem(build_external_trait(tcx, did))
        }
        ast::DefFn(did, style) => {
            // If this function is a tuple struct constructor, we just skip it
            if csearch::get_tuple_struct_definition_if_ctor(&tcx.sess.cstore,
                                                            did).is_some() {
                return None
            }
            record_extern_fqn(cx, did, clean::TypeFunction);
            clean::FunctionItem(build_external_function(tcx, did, style))
        }
        ast::DefStruct(did) => {
            record_extern_fqn(cx, did, clean::TypeStruct);
            ret.extend(build_impls(cx, tcx, did).move_iter());
            clean::StructItem(build_struct(tcx, did))
        }
        ast::DefTy(did) => {
            record_extern_fqn(cx, did, clean::TypeEnum);
            ret.extend(build_impls(cx, tcx, did).move_iter());
            build_type(tcx, did)
        }
        // Assume that the enum type is reexported next to the variant, and
        // variants don't show up in documentation specially.
        ast::DefVariant(..) => return Some(Vec::new()),
        ast::DefMod(did) => {
            record_extern_fqn(cx, did, clean::TypeModule);
            clean::ModuleItem(build_module(cx, tcx, did))
        }
        _ => return None,
    };
    let fqn = csearch::get_item_path(tcx, did);
    cx.inlined.borrow_mut().get_mut_ref().insert(did);
    ret.push(clean::Item {
        source: clean::Span::empty(),
        name: Some(fqn.last().unwrap().to_str().to_string()),
        attrs: load_attrs(tcx, did),
        inner: inner,
        visibility: Some(ast::Public),
        def_id: did,
    });
    Some(ret)
}

pub fn load_attrs(tcx: &ty::ctxt, did: ast::DefId) -> Vec<clean::Attribute> {
    let mut attrs = Vec::new();
    csearch::get_item_attrs(&tcx.sess.cstore, did, |v| {
        attrs.extend(v.move_iter().map(|mut a| {
            // FIXME this isn't quite always true, it's just true about 99% of
            //       the time when dealing with documentation. For example,
            //       this would treat doc comments of the form `#[doc = "foo"]`
            //       incorrectly.
            if a.name().get() == "doc" && a.value_str().is_some() {
                a.node.is_sugared_doc = true;
            }
            a.clean()
        }));
    });
    attrs
}

/// Record an external fully qualified name in the external_paths cache.
///
/// These names are used later on by HTML rendering to generate things like
/// source links back to the original item.
pub fn record_extern_fqn(cx: &core::DocContext,
                         did: ast::DefId,
                         kind: clean::TypeKind) {
    match cx.maybe_typed {
        core::Typed(ref tcx) => {
            let fqn = csearch::get_item_path(tcx, did);
            let fqn = fqn.move_iter().map(|i| i.to_str().to_string()).collect();
            cx.external_paths.borrow_mut().get_mut_ref().insert(did, (fqn, kind));
        }
        core::NotTyped(..) => {}
    }
}

pub fn build_external_trait(tcx: &ty::ctxt, did: ast::DefId) -> clean::Trait {
    let def = ty::lookup_trait_def(tcx, did);
    let methods = ty::trait_methods(tcx, did);
    clean::Trait {
        generics: def.generics.clean(),
        methods: methods.iter().map(|i| i.clean()).collect(),
        parents: Vec::new(), // FIXME: this is likely wrong
    }
}

fn build_external_function(tcx: &ty::ctxt,
                           did: ast::DefId,
                           style: ast::FnStyle) -> clean::Function {
    let t = ty::lookup_item_type(tcx, did);
    clean::Function {
        decl: match ty::get(t.ty).sty {
            ty::ty_bare_fn(ref f) => (did, &f.sig).clean(),
            _ => fail!("bad function"),
        },
        generics: t.generics.clean(),
        fn_style: style,
    }
}

fn build_struct(tcx: &ty::ctxt, did: ast::DefId) -> clean::Struct {
    use syntax::parse::token::special_idents::unnamed_field;

    let t = ty::lookup_item_type(tcx, did);
    let fields = ty::lookup_struct_fields(tcx, did);

    clean::Struct {
        struct_type: match fields.as_slice() {
            [] => doctree::Unit,
            [ref f] if f.name == unnamed_field.name => doctree::Newtype,
            [ref f, ..] if f.name == unnamed_field.name => doctree::Tuple,
            _ => doctree::Plain,
        },
        generics: t.generics.clean(),
        fields: fields.iter().map(|f| f.clean()).collect(),
        fields_stripped: false,
    }
}

fn build_type(tcx: &ty::ctxt, did: ast::DefId) -> clean::ItemEnum {
    let t = ty::lookup_item_type(tcx, did);
    match ty::get(t.ty).sty {
        ty::ty_enum(edid, _) => {
            return clean::EnumItem(clean::Enum {
                generics: t.generics.clean(),
                variants_stripped: false,
                variants: ty::enum_variants(tcx, edid).clean(),
            })
        }
        _ => {}
    }

    clean::TypedefItem(clean::Typedef {
        type_: t.ty.clean(),
        generics: t.generics.clean(),
    })
}

fn build_impls(cx: &core::DocContext,
               tcx: &ty::ctxt,
               did: ast::DefId) -> Vec<clean::Item> {
    ty::populate_implementations_for_type_if_necessary(tcx, did);
    let mut impls = Vec::new();

    match tcx.inherent_impls.borrow().find(&did) {
        None => {}
        Some(i) => {
            impls.extend(i.borrow().iter().map(|&did| { build_impl(cx, tcx, did) }));
        }
    }

    // If this is the first time we've inlined something from this crate, then
    // we inline *all* impls from the crate into this crate. Note that there's
    // currently no way for us to filter this based on type, and we likely need
    // many impls for a variety of reasons.
    //
    // Primarily, the impls will be used to populate the documentation for this
    // type being inlined, but impls can also be used when generating
    // documentation for primitives (no way to find those specifically).
    if cx.populated_crate_impls.borrow_mut().insert(did.krate) {
        csearch::each_top_level_item_of_crate(&tcx.sess.cstore,
                                              did.krate,
                                              |def, _, _| {
            populate_impls(cx, tcx, def, &mut impls)
        });

        fn populate_impls(cx: &core::DocContext,
                          tcx: &ty::ctxt,
                          def: decoder::DefLike,
                          impls: &mut Vec<Option<clean::Item>>) {
            match def {
                decoder::DlImpl(did) => impls.push(build_impl(cx, tcx, did)),
                decoder::DlDef(ast::DefMod(did)) => {
                    csearch::each_child_of_item(&tcx.sess.cstore,
                                                did,
                                                |def, _, _| {
                        populate_impls(cx, tcx, def, impls)
                    })
                }
                _ => {}
            }
        }
    }

    impls.move_iter().filter_map(|a| a).collect()
}

fn build_impl(cx: &core::DocContext,
              tcx: &ty::ctxt,
              did: ast::DefId) -> Option<clean::Item> {
    if !cx.inlined.borrow_mut().get_mut_ref().insert(did) {
        return None
    }

    let associated_trait = csearch::get_impl_trait(tcx, did);
    let attrs = load_attrs(tcx, did);
    let ty = ty::lookup_item_type(tcx, did);
    let methods = csearch::get_impl_methods(&tcx.sess.cstore,
                                            did).iter().filter_map(|did| {
        let method = ty::method(tcx, *did);
        if method.vis != ast::Public && associated_trait.is_none() {
            return None
        }
        let mut item = match ty::method(tcx, *did).clean() {
            clean::Provided(item) => item,
            clean::Required(item) => item,
        };
        item.inner = match item.inner.clone() {
            clean::TyMethodItem(clean::TyMethod {
                fn_style, decl, self_, generics
            }) => {
                clean::MethodItem(clean::Method {
                    fn_style: fn_style,
                    decl: decl,
                    self_: self_,
                    generics: generics,
                })
            }
            _ => fail!("not a tymethod"),
        };
        Some(item)
    }).collect();
    Some(clean::Item {
        inner: clean::ImplItem(clean::Impl {
            derived: clean::detect_derived(attrs.as_slice()),
            trait_: associated_trait.clean().map(|bound| {
                match bound {
                    clean::TraitBound(ty) => ty,
                    clean::RegionBound => unreachable!(),
                }
            }),
            for_: ty.ty.clean(),
            generics: ty.generics.clean(),
            methods: methods,
        }),
        source: clean::Span::empty(),
        name: None,
        attrs: attrs,
        visibility: Some(ast::Inherited),
        def_id: did,
    })
}

fn build_module(cx: &core::DocContext, tcx: &ty::ctxt,
                did: ast::DefId) -> clean::Module {
    let mut items = Vec::new();

    // FIXME: this doesn't handle reexports inside the module itself.
    //        Should they be handled?
    csearch::each_child_of_item(&tcx.sess.cstore, did, |def, _, vis| {
        if vis != ast::Public { return }
        match def {
            decoder::DlDef(def) => {
                match try_inline_def(cx, tcx, def) {
                    Some(i) => items.extend(i.move_iter()),
                    None => {}
                }
            }
            // All impls were inlined above
            decoder::DlImpl(..) => {}
            decoder::DlField => fail!("unimplemented field"),
        }
    });

    clean::Module {
        items: items,
        is_crate: false,
    }
}
<|endoftext|>


<commit_before>use std::sync::mpsc;
use std::sync::Arc;
use std::thread;
use std::io;
use std::net::SocketAddr;
use std::net::ToSocketAddrs;

use bytes::Bytes;

use futures;
use futures::Future;
use futures::Stream;
use futures::stream;

use tokio_core::reactor;

use native_tls::TlsConnector;

use futures_misc::*;

use solicit::header::*;
use solicit::HttpResult;
use solicit::HttpError;
use solicit::HttpScheme;

use solicit_async::*;

use client_conn::*;
use client_conf::*;
use http_common::*;
use message::*;

pub use client_tls::ClientTlsOption;

// Data sent from event loop to Http2Client
struct LoopToClient {
    // used only once to send shutdown signal
    shutdown: ShutdownSignal,
    _loop_handle: reactor::Remote,
    http_conn: Arc<HttpClientConnectionAsync>,
}

pub struct HttpClient {
    loop_to_client: LoopToClient,
    thread_join_handle: Option<thread::JoinHandle<()>>,
    http_scheme: HttpScheme,
}

impl HttpClient {

    pub fn new(host: &str, port: u16, tls: bool, conf: HttpClientConf) -> HttpResult<HttpClient> {
        // TODO: sync
        // TODO: try connect to all addrs
        let socket_addr = (host, port).to_socket_addrs()?.next().unwrap();

        let tls_enabled = match tls {
            true => {
                let connector = Arc::new(TlsConnector::builder().unwrap().build().unwrap());
                ClientTlsOption::Tls(host.to_owned(), connector)
            },
            false => ClientTlsOption::Plain,
        };

        HttpClient::new_expl(&socket_addr, tls_enabled, conf)
    }

    pub fn new_expl(addr: &SocketAddr, tls: ClientTlsOption, conf: HttpClientConf) -> HttpResult<HttpClient> {
        // We need some data back from event loop.
        // This channel is used to exchange that data
        let (get_from_loop_tx, get_from_loop_rx) = mpsc::channel();

        let addr = addr.clone();
        let http_scheme = tls.http_scheme();

        // Start event loop.
        let join_handle = thread::Builder::new()
            .name(conf.thread_name.clone().unwrap_or_else(|| "http2-client-loop".to_owned()).to_string())
            .spawn(move || {
                run_client_event_loop(addr, tls, conf, get_from_loop_tx);
            })
            .expect("spawn");

        // Get back call channel and shutdown channel.
        let loop_to_client = get_from_loop_rx.recv()
            .map_err(|_| HttpError::IoError(io::Error::new(io::ErrorKind::Other, "get response from loop")))?;

        Ok(HttpClient {
            loop_to_client: loop_to_client,
            thread_join_handle: Some(join_handle),
            http_scheme: http_scheme,
        })
    }

    pub fn start_request(
        &self,
        headers: Headers,
        body: HttpFutureStreamSend<Bytes>)
            -> HttpPartFutureStreamSend
    {
        debug!("start request {:?}", headers);
        self.loop_to_client.http_conn.start_request(headers, body)
    }

    pub fn start_request_simple(
        &self,
        headers: Headers,
        body: Bytes)
            -> HttpPartFutureStreamSend
    {
        self.start_request(
            headers,
            Box::new(stream::once(Ok(body))))
    }

    pub fn start_get(
        &self,
        path: &str,
        authority: &str)
            -> HttpPartFutureStreamSend
    {
        let headers = Headers(vec![
            Header::new(":method", "GET"),
            Header::new(":path", path.to_owned()),
            Header::new(":authority", authority.to_owned()),
            Header::new(":scheme", self.http_scheme.as_bytes()),
        ]);
        self.start_request_simple(headers, Bytes::new())
    }

    pub fn start_post(
        &self,
        path: &str,
        authority: &str,
        body: Bytes)
            -> HttpPartFutureStreamSend
    {
        let headers = Headers(vec![
            Header::new(":method", "POST"),
            Header::new(":path", path.to_owned()),
            Header::new(":authority", authority.to_owned()),
            Header::new(":scheme", self.http_scheme.as_bytes()),
        ]);
        self.start_request_simple(headers, body)
    }

    pub fn start_get_simple_response(
        &self,
        path: &str,
        authority: &str)
            -> HttpFutureSend<SimpleHttpMessage>
    {
        Box::new(self.start_get(path, authority).collect()
            .map(SimpleHttpMessage::from_parts))
    }

    pub fn start_post_simple_response(
        &self,
        path: &str,
        authority: &str,
        body: Bytes)
            -> HttpFutureSend<SimpleHttpMessage>
    {
        Box::new(self.start_post(path, authority, body).collect()
            .map(SimpleHttpMessage::from_parts))
    }

    pub fn dump_state(&self) -> HttpFutureSend<ConnectionStateSnapshot> {
        self.loop_to_client.http_conn.dump_state()
    }
}

// Event loop entry point
fn run_client_event_loop(
    socket_addr: SocketAddr,
    tls: ClientTlsOption,
    conf: HttpClientConf,
    send_to_back: mpsc::Sender<LoopToClient>)
{
    // Create an event loop.
    let mut lp = reactor::Core::new().unwrap();

    // Create a channel to receive shutdown signal.
    let (shutdown_signal, shutdown_future) = shutdown_signal();

    let (http_conn, http_conn_future) =
        HttpClientConnectionAsync::new(lp.handle(), &socket_addr, tls, conf);

    // Send channels back to Http2Client
    send_to_back
        .send(LoopToClient {
            shutdown: shutdown_signal,
            _loop_handle: lp.remote(),
            http_conn: Arc::new(http_conn),
        })
        .expect("send back");

    let shutdown_future = shutdown_future
        .then(move |_| {
            // Must complete with error,
            // so `join` with this future cancels another future.
            futures::failed::<(), _>(HttpError::Shutdown)
        });

    // Wait for either completion of connection (i. e. error)
    // or shutdown signal.
    let done = http_conn_future.join(shutdown_future);

    match lp.run(done) {
        Ok(_) => {}
        Err(e) => {
            error!("Core::run failed: {:?}", e);
        }
    }
}

// We shutdown the client in the destructor.
impl Drop for HttpClient {
    fn drop(&mut self) {
        self.loop_to_client.shutdown.shutdown();

        // do not ignore errors because we own event loop thread
        self.thread_join_handle.take().expect("handle.take")
            .join().expect("join thread");
    }
}
<commit_msg>Do not log successful shutdown<commit_after>use std::sync::mpsc;
use std::sync::Arc;
use std::thread;
use std::io;
use std::net::SocketAddr;
use std::net::ToSocketAddrs;

use bytes::Bytes;

use futures;
use futures::Future;
use futures::Stream;
use futures::stream;

use tokio_core::reactor;

use native_tls::TlsConnector;

use futures_misc::*;

use solicit::header::*;
use solicit::HttpResult;
use solicit::HttpError;
use solicit::HttpScheme;

use solicit_async::*;

use client_conn::*;
use client_conf::*;
use http_common::*;
use message::*;

pub use client_tls::ClientTlsOption;

// Data sent from event loop to Http2Client
struct LoopToClient {
    // used only once to send shutdown signal
    shutdown: ShutdownSignal,
    _loop_handle: reactor::Remote,
    http_conn: Arc<HttpClientConnectionAsync>,
}

pub struct HttpClient {
    loop_to_client: LoopToClient,
    thread_join_handle: Option<thread::JoinHandle<()>>,
    http_scheme: HttpScheme,
}

impl HttpClient {

    pub fn new(host: &str, port: u16, tls: bool, conf: HttpClientConf) -> HttpResult<HttpClient> {
        // TODO: sync
        // TODO: try connect to all addrs
        let socket_addr = (host, port).to_socket_addrs()?.next().unwrap();

        let tls_enabled = match tls {
            true => {
                let connector = Arc::new(TlsConnector::builder().unwrap().build().unwrap());
                ClientTlsOption::Tls(host.to_owned(), connector)
            },
            false => ClientTlsOption::Plain,
        };

        HttpClient::new_expl(&socket_addr, tls_enabled, conf)
    }

    pub fn new_expl(addr: &SocketAddr, tls: ClientTlsOption, conf: HttpClientConf) -> HttpResult<HttpClient> {
        // We need some data back from event loop.
        // This channel is used to exchange that data
        let (get_from_loop_tx, get_from_loop_rx) = mpsc::channel();

        let addr = addr.clone();
        let http_scheme = tls.http_scheme();

        // Start event loop.
        let join_handle = thread::Builder::new()
            .name(conf.thread_name.clone().unwrap_or_else(|| "http2-client-loop".to_owned()).to_string())
            .spawn(move || {
                run_client_event_loop(addr, tls, conf, get_from_loop_tx);
            })
            .expect("spawn");

        // Get back call channel and shutdown channel.
        let loop_to_client = get_from_loop_rx.recv()
            .map_err(|_| HttpError::IoError(io::Error::new(io::ErrorKind::Other, "get response from loop")))?;

        Ok(HttpClient {
            loop_to_client: loop_to_client,
            thread_join_handle: Some(join_handle),
            http_scheme: http_scheme,
        })
    }

    pub fn start_request(
        &self,
        headers: Headers,
        body: HttpFutureStreamSend<Bytes>)
            -> HttpPartFutureStreamSend
    {
        debug!("start request {:?}", headers);
        self.loop_to_client.http_conn.start_request(headers, body)
    }

    pub fn start_request_simple(
        &self,
        headers: Headers,
        body: Bytes)
            -> HttpPartFutureStreamSend
    {
        self.start_request(
            headers,
            Box::new(stream::once(Ok(body))))
    }

    pub fn start_get(
        &self,
        path: &str,
        authority: &str)
            -> HttpPartFutureStreamSend
    {
        let headers = Headers(vec![
            Header::new(":method", "GET"),
            Header::new(":path", path.to_owned()),
            Header::new(":authority", authority.to_owned()),
            Header::new(":scheme", self.http_scheme.as_bytes()),
        ]);
        self.start_request_simple(headers, Bytes::new())
    }

    pub fn start_post(
        &self,
        path: &str,
        authority: &str,
        body: Bytes)
            -> HttpPartFutureStreamSend
    {
        let headers = Headers(vec![
            Header::new(":method", "POST"),
            Header::new(":path", path.to_owned()),
            Header::new(":authority", authority.to_owned()),
            Header::new(":scheme", self.http_scheme.as_bytes()),
        ]);
        self.start_request_simple(headers, body)
    }

    pub fn start_get_simple_response(
        &self,
        path: &str,
        authority: &str)
            -> HttpFutureSend<SimpleHttpMessage>
    {
        Box::new(self.start_get(path, authority).collect()
            .map(SimpleHttpMessage::from_parts))
    }

    pub fn start_post_simple_response(
        &self,
        path: &str,
        authority: &str,
        body: Bytes)
            -> HttpFutureSend<SimpleHttpMessage>
    {
        Box::new(self.start_post(path, authority, body).collect()
            .map(SimpleHttpMessage::from_parts))
    }

    pub fn dump_state(&self) -> HttpFutureSend<ConnectionStateSnapshot> {
        self.loop_to_client.http_conn.dump_state()
    }
}

// Event loop entry point
fn run_client_event_loop(
    socket_addr: SocketAddr,
    tls: ClientTlsOption,
    conf: HttpClientConf,
    send_to_back: mpsc::Sender<LoopToClient>)
{
    // Create an event loop.
    let mut lp = reactor::Core::new().unwrap();

    // Create a channel to receive shutdown signal.
    let (shutdown_signal, shutdown_future) = shutdown_signal();

    let (http_conn, http_conn_future) =
        HttpClientConnectionAsync::new(lp.handle(), &socket_addr, tls, conf);

    // Send channels back to Http2Client
    send_to_back
        .send(LoopToClient {
            shutdown: shutdown_signal,
            _loop_handle: lp.remote(),
            http_conn: Arc::new(http_conn),
        })
        .expect("send back");

    let shutdown_future = shutdown_future
        .then(move |_| {
            // Must complete with error,
            // so `join` with this future cancels another future.
            futures::failed::<(), _>(HttpError::Shutdown)
        });

    // Wait for either completion of connection (i. e. error)
    // or shutdown signal.
    let done = http_conn_future.join(shutdown_future);

    match lp.run(done) {
        Ok(_) => {}
        Err(HttpError::Shutdown) => {}
        Err(e) => {
            error!("Core::run failed: {:?}", e);
        }
    }
}

// We shutdown the client in the destructor.
impl Drop for HttpClient {
    fn drop(&mut self) {
        self.loop_to_client.shutdown.shutdown();

        // do not ignore errors because we own event loop thread
        self.thread_join_handle.take().expect("handle.take")
            .join().expect("join thread");
    }
}
<|endoftext|>