<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{errors::JsonRpcError, views::AccountView, JsonRpcResponse};
use anyhow::{ensure, format_err, Error, Result};
use libra_types::{account_address::AccountAddress, transaction::SignedTransaction};
use reqwest::{Client, ClientBuilder, Url};
use serde_json::{json, Value};
use std::{collections::HashSet, convert::TryFrom, fmt, time::Duration};

#[derive(Clone, Default)]
pub struct JsonRpcBatch {
    pub requests: Vec<(String, Vec<Value>)>,
}

impl JsonRpcBatch {
    pub fn new() -> Self {
        Self { requests: vec![] }
    }

    pub fn json_request(&self) -> Value {
        let requests = self
            .requests
            .iter()
            .enumerate()
            .map(|(id, (method, params))| {
                serde_json::json!({"jsonrpc": "2.0", "method": method, "params": params, "id": id})
            })
            .collect();
        serde_json::Value::Array(requests)
    }

    pub fn add_request(&mut self, method_name: String, parameters: Vec<Value>) {
        self.requests.push((method_name, parameters));
    }

    pub fn add_submit_request(&mut self, transaction: SignedTransaction) -> Result<()> {
        let txn_payload = hex::encode(lcs::to_bytes(&transaction)?);
        self.add_request("submit".to_string(), vec![Value::String(txn_payload)]);
        Ok(())
    }

    pub fn add_get_account_request(&mut self, address: AccountAddress) {
        self.add_request(
            "get_account".to_string(),
            vec![Value::String(address.to_string())],
        );
    }

    pub fn add_get_metadata_request(&mut self, version: Option<u64>) {
        let params = match version {
            Some(version) => vec![json!(version)],
            None => vec![],
        };
        self.add_request("get_metadata".to_string(), params)
    }

    pub fn add_get_currencies_info(&mut self) {
        self.add_request("get_currencies".to_string(), vec![]);
    }

    pub fn add_get_transactions_request(
        &mut self,
        start_version: u64,
        limit: u64,
        include_events: bool,
    ) {
        self.add_request(
            "get_transactions".to_string(),
            vec![json!(start_version), json!(limit), json!(include_events)],
        );
    }

    pub fn add_get_account_transaction_request(
        &mut self,
        account: AccountAddress,
        sequence: u64,
        include_events: bool,
    ) {
        self.add_request(
            "get_account_transaction".to_string(),
            vec![
                json!(account.to_string()),
                json!(sequence),
                json!(include_events),
            ],
        );
    }

    pub fn add_get_account_transactions_request(
        &mut self,
        account: AccountAddress,
        start: u64,
        limit: u64,
        include_events: bool,
    ) {
        self.add_request(
            "get_account_transactions".to_string(),
            vec![
                json!(account.to_string()),
                json!(start),
                json!(limit),
                json!(include_events),
            ],
        );
    }

    pub fn add_get_events_request(&mut self, event_key: String, start: u64, limit: u64) {
        self.add_request(
            "get_events".to_string(),
            vec![json!(event_key), json!(start), json!(limit)],
        );
    }

    pub fn add_get_state_proof_request(&mut self, known_version: u64) {
        self.add_request("get_state_proof".to_string(), vec![json!(known_version)]);
    }

    pub fn add_get_account_state_with_proof_request(
        &mut self,
        account: AccountAddress,
        version: Option<u64>,
        ledger_version: Option<u64>,
    ) {
        self.add_request(
            "get_account_state_with_proof".to_string(),
            vec![
                json!(account.to_string()),
                json!(version),
                json!(ledger_version),
            ],
        );
    }

    pub fn add_get_network_status_request(&mut self) {
        self.add_request("get_network_status".to_string(), vec![]);
    }
}

#[derive(Debug)]
pub enum JsonRpcAsyncClientError {
    ClientError(reqwest::Error),
    InvalidArgument(String),
    InvalidServerResponse(String),
    JsonRpcError(JsonRpcError),
}

impl JsonRpcAsyncClientError {
    pub fn is_retriable(&self) -> bool {
        match self {
            JsonRpcAsyncClientError::ClientError(e) => {
                // TODO: Add following condition when is_request is added to reqwest stable release
                // // Error sending request
                //  if e.is_request() {
                //      return true;
                //  }
                if e.is_timeout() {
                    return true;
                }

                if let Some(status) = e.status() {
                    // Returned status code indicates a server error
                    return status.is_server_error();
                }
                false
            }
            _ => false,
        }
    }
}

impl std::convert::From<JsonRpcAsyncClientError> for anyhow::Error {
    fn from(e: JsonRpcAsyncClientError) -> Self {
        anyhow::Error::msg(e.to_string())
    }
}

impl std::fmt::Display for JsonRpcAsyncClientError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            JsonRpcAsyncClientError::InvalidArgument(e) => write!(f, "InvalidArgument: {}", e),
            JsonRpcAsyncClientError::ClientError(e) => write!(f, "ClientError: {}", e.to_string()),
            JsonRpcAsyncClientError::InvalidServerResponse(e) => {
                write!(f, "InvalidServerResponse {}", e)
            }
            JsonRpcAsyncClientError::JsonRpcError(e) => write!(f, "JsonRpcError {}", e),
        }
    }
}

#[derive(Clone)]
pub struct JsonRpcAsyncClient {
    address: String,
    client: Client,
}

impl JsonRpcAsyncClient {
    /// Pass in full url for endpoint, supports HTTPS
    pub fn new(url: Url) -> Self {
        Self {
            address: url.to_string(),
            client: ClientBuilder::new()
                .timeout(Duration::from_secs(30))
                .build()
                .expect("Unable to build Client."),
        }
    }

    pub fn new_with_client(client: Client, url: Url) -> Self {
        Self {
            address: url.to_string(),
            client,
        }
    }

    pub async fn get_accounts(
        &self,
        accounts: &[AccountAddress],
    ) -> Result<Vec<Option<AccountView>>, JsonRpcAsyncClientError> {
        let mut batch = JsonRpcBatch::new();
        for account in accounts {
            batch.add_get_account_request(*account);
        }
        let exec_results = self.execute(batch).await?;
        let mut results = vec![];
        for exec_result in exec_results {
            let exec_result = exec_result
                .map_err(|e| JsonRpcAsyncClientError::InvalidServerResponse(e.to_string()))?;
            if let JsonRpcResponse::AccountResponse(r) = exec_result {
                results.push(r);
            } else {
                panic!("Unexpected response for get_accounts {:?}", exec_result)
            }
        }

        if results.len() != accounts.len() {
            return Err(JsonRpcAsyncClientError::InvalidServerResponse(format!(
                "Received unexpected number of JSON RPC responses ({}) for {} requests",
                results.len(),
                accounts.len()
            )));
        }

        Ok(results)
    }

    pub async fn submit_transaction(
        &self,
        txn: SignedTransaction,
    ) -> Result<(), JsonRpcAsyncClientError> {
        let mut batch = JsonRpcBatch::new();
        batch
            .add_submit_request(txn)
            .map_err(|e| JsonRpcAsyncClientError::InvalidArgument(e.to_string()))?;
        let mut exec_result = self.execute(batch).await?;
        assert!(exec_result.len() == 1);
        exec_result
            .remove(0)
            .map(|_| ())
            .map_err(|e| JsonRpcAsyncClientError::InvalidServerResponse(e.to_string()))
    }

    pub async fn execute(
        &self,
        batch: JsonRpcBatch,
    ) -> Result<Vec<Result<JsonRpcResponse>>, JsonRpcAsyncClientError> {
        let requests = batch.json_request();
        let resp = self
            .client
            .post(&self.address)
            .json(&requests)
            .send()
            .await
            .map_err(JsonRpcAsyncClientError::ClientError)?;

        if resp.status() != 200 {
            return Err(JsonRpcAsyncClientError::InvalidServerResponse(format!(
                "Http error code {}",
                resp.status()
            )));
        }

        let json: Value = resp
            .json()
            .await
            .map_err(|e| JsonRpcAsyncClientError::InvalidServerResponse(e.to_string()))?;

        if let Value::Array(responses) = json {
            process_batch_response(batch, responses)
                .map_err(|e| JsonRpcAsyncClientError::InvalidServerResponse(e.to_string()))
        } else if let Value::Object(response) = json {
            let error_value = response.get("error").ok_or_else(|| {
                JsonRpcAsyncClientError::InvalidServerResponse(
                    "batch response should be an array".to_string(),
                )
            })?;
            let rpc_err: JsonRpcError = serde_json::from_value(error_value.clone())
                .map_err(|e| JsonRpcAsyncClientError::InvalidServerResponse(e.to_string()))?;
            Err(JsonRpcAsyncClientError::JsonRpcError(rpc_err))
        } else {
            Err(JsonRpcAsyncClientError::InvalidServerResponse(
                json.to_string(),
            ))
        }
    }
}

impl fmt::Debug for JsonRpcAsyncClient {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.address)
    }
}

////////////////////////////////////////////////////
/// Helper methods for basic payload processing ///
///////////////////////////////////////////////////

pub fn process_batch_response(
    batch: JsonRpcBatch,
    responses: Vec<Value>,
) -> Result<Vec<Result<JsonRpcResponse>>> {
    let mut result = batch
        .requests
        .iter()
        .map(|_| Err(format_err!("request is missing")))
        .collect::<Vec<_>>();

    let mut seen_ids = HashSet::new();
    for response in responses {
        // check JSON RPC protocol
        let json_rpc_protocol = response.get("jsonrpc");
        ensure!(
            json_rpc_protocol == Some(&json!("2.0")),
            "JSON RPC response with incorrect protocol: {:?}",
            json_rpc_protocol
        );

        if let Ok(req_id) = fetch_id(&response) {
            ensure!(
                !seen_ids.contains(&req_id),
                "received JSON RPC response with duplicate response ID"
            );
            seen_ids.insert(req_id);
            if req_id < result.len() {
                let resp = if let Some(err_data) = response.get("error") {
                    let err_json: JsonRpcError = serde_json::from_value(err_data.clone())?;
                    Err(Error::new(err_json))
                } else if let Some(data) = response.get("result") {
                    let method = batch.requests[req_id].0.clone();
                    Ok(JsonRpcResponse::try_from((method, data.clone()))?)
                } else {
                    continue;
                };
                result[req_id] = resp;
            }
        }
    }

    Ok(result)
}

pub fn get_response_from_batch(
    index: usize,
    batch: &[Result<JsonRpcResponse>],
) -> Result<&Result<JsonRpcResponse>> {
    batch.get(index).ok_or_else(|| {
        format_err!(
            "[JSON RPC client] response missing in batch at index {}",
            index
        )
    })
}

fn fetch_id(response: &Value) -> Result<usize> {
    match response.get("id") {
        Some(id) => Ok(serde_json::from_value::<usize>(id.clone())?),
        None => Err(format_err!("request id is missing")),
    }
}

#[cfg(test)]
pub mod tests {
    use super::*;
    use reqwest::{blocking::ClientBuilder, Url};

    #[test]
    fn test_error_is_retriable() {
        let server_err = JsonRpcAsyncClientError::InvalidServerResponse(
            "test invalid server response".to_string(),
        );
        // Reqwest error's builder is private to the crate, so send out a
        // fake request that should fail to get an error
        let test_client = ClientBuilder::new()
            .timeout(Duration::from_millis(1))
            .build()
            .unwrap();
        let req_err = test_client
            .get(Url::parse("http://192.108.0.1").unwrap())
            .send()
            .unwrap_err();
        let client_err = JsonRpcAsyncClientError::ClientError(req_err);
        let arg_err = JsonRpcAsyncClientError::InvalidArgument("test invalid argument".to_string());
        // Make sure display is implemented correctly for error enum
        println!("{}", client_err);
        assert_eq!(server_err.is_retriable(), false);
        assert_eq!(arg_err.is_retriable(), false);
        assert_eq!(client_err.is_retriable(), true);
    }
}
<commit_msg>Update JsonRpcAsyncClientError logic to retry on all client errors<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{errors::JsonRpcError, views::AccountView, JsonRpcResponse};
use anyhow::{ensure, format_err, Error, Result};
use libra_types::{account_address::AccountAddress, transaction::SignedTransaction};
use reqwest::{Client, ClientBuilder, Url};
use serde_json::{json, Value};
use std::{collections::HashSet, convert::TryFrom, fmt, time::Duration};

#[derive(Clone, Default)]
pub struct JsonRpcBatch {
    pub requests: Vec<(String, Vec<Value>)>,
}

impl JsonRpcBatch {
    pub fn new() -> Self {
        Self { requests: vec![] }
    }

    pub fn json_request(&self) -> Value {
        let requests = self
            .requests
            .iter()
            .enumerate()
            .map(|(id, (method, params))| {
                serde_json::json!({"jsonrpc": "2.0", "method": method, "params": params, "id": id})
            })
            .collect();
        serde_json::Value::Array(requests)
    }

    pub fn add_request(&mut self, method_name: String, parameters: Vec<Value>) {
        self.requests.push((method_name, parameters));
    }

    pub fn add_submit_request(&mut self, transaction: SignedTransaction) -> Result<()> {
        let txn_payload = hex::encode(lcs::to_bytes(&transaction)?);
        self.add_request("submit".to_string(), vec![Value::String(txn_payload)]);
        Ok(())
    }

    pub fn add_get_account_request(&mut self, address: AccountAddress) {
        self.add_request(
            "get_account".to_string(),
            vec![Value::String(address.to_string())],
        );
    }

    pub fn add_get_metadata_request(&mut self, version: Option<u64>) {
        let params = match version {
            Some(version) => vec![json!(version)],
            None => vec![],
        };
        self.add_request("get_metadata".to_string(), params)
    }

    pub fn add_get_currencies_info(&mut self) {
        self.add_request("get_currencies".to_string(), vec![]);
    }

    pub fn add_get_transactions_request(
        &mut self,
        start_version: u64,
        limit: u64,
        include_events: bool,
    ) {
        self.add_request(
            "get_transactions".to_string(),
            vec![json!(start_version), json!(limit), json!(include_events)],
        );
    }

    pub fn add_get_account_transaction_request(
        &mut self,
        account: AccountAddress,
        sequence: u64,
        include_events: bool,
    ) {
        self.add_request(
            "get_account_transaction".to_string(),
            vec![
                json!(account.to_string()),
                json!(sequence),
                json!(include_events),
            ],
        );
    }

    pub fn add_get_account_transactions_request(
        &mut self,
        account: AccountAddress,
        start: u64,
        limit: u64,
        include_events: bool,
    ) {
        self.add_request(
            "get_account_transactions".to_string(),
            vec![
                json!(account.to_string()),
                json!(start),
                json!(limit),
                json!(include_events),
            ],
        );
    }

    pub fn add_get_events_request(&mut self, event_key: String, start: u64, limit: u64) {
        self.add_request(
            "get_events".to_string(),
            vec![json!(event_key), json!(start), json!(limit)],
        );
    }

    pub fn add_get_state_proof_request(&mut self, known_version: u64) {
        self.add_request("get_state_proof".to_string(), vec![json!(known_version)]);
    }

    pub fn add_get_account_state_with_proof_request(
        &mut self,
        account: AccountAddress,
        version: Option<u64>,
        ledger_version: Option<u64>,
    ) {
        self.add_request(
            "get_account_state_with_proof".to_string(),
            vec![
                json!(account.to_string()),
                json!(version),
                json!(ledger_version),
            ],
        );
    }

    pub fn add_get_network_status_request(&mut self) {
        self.add_request("get_network_status".to_string(), vec![]);
    }
}

#[derive(Debug)]
pub enum JsonRpcAsyncClientError {
    ClientError(reqwest::Error),
    InvalidArgument(String),
    InvalidServerResponse(String),
    JsonRpcError(JsonRpcError),
}

impl JsonRpcAsyncClientError {
    pub fn is_retriable(&self) -> bool {
        match self {
            JsonRpcAsyncClientError::ClientError(e) => {
                if e.is_timeout() || e.is_request() {
                    return true;
                }
                if let Some(status) = e.status() {
                    // Returned status code indicates a server error
                    return status.is_server_error();
                }
                false
            }
            _ => false,
        }
    }
}

impl std::convert::From<JsonRpcAsyncClientError> for anyhow::Error {
    fn from(e: JsonRpcAsyncClientError) -> Self {
        anyhow::Error::msg(e.to_string())
    }
}

impl std::fmt::Display for JsonRpcAsyncClientError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            JsonRpcAsyncClientError::InvalidArgument(e) => write!(f, "InvalidArgument: {}", e),
            JsonRpcAsyncClientError::ClientError(e) => write!(f, "ClientError: {}", e.to_string()),
            JsonRpcAsyncClientError::InvalidServerResponse(e) => {
                write!(f, "InvalidServerResponse {}", e)
            }
            JsonRpcAsyncClientError::JsonRpcError(e) => write!(f, "JsonRpcError {}", e),
        }
    }
}

#[derive(Clone)]
pub struct JsonRpcAsyncClient {
    address: String,
    client: Client,
}

impl JsonRpcAsyncClient {
    /// Pass in full url for endpoint, supports HTTPS
    pub fn new(url: Url) -> Self {
        Self {
            address: url.to_string(),
            client: ClientBuilder::new()
                .timeout(Duration::from_secs(30))
                .build()
                .expect("Unable to build Client."),
        }
    }

    pub fn new_with_client(client: Client, url: Url) -> Self {
        Self {
            address: url.to_string(),
            client,
        }
    }

    pub async fn get_accounts(
        &self,
        accounts: &[AccountAddress],
    ) -> Result<Vec<Option<AccountView>>, JsonRpcAsyncClientError> {
        let mut batch = JsonRpcBatch::new();
        for account in accounts {
            batch.add_get_account_request(*account);
        }
        let exec_results = self.execute(batch).await?;
        let mut results = vec![];
        for exec_result in exec_results {
            let exec_result = exec_result
                .map_err(|e| JsonRpcAsyncClientError::InvalidServerResponse(e.to_string()))?;
            if let JsonRpcResponse::AccountResponse(r) = exec_result {
                results.push(r);
            } else {
                panic!("Unexpected response for get_accounts {:?}", exec_result)
            }
        }

        if results.len() != accounts.len() {
            return Err(JsonRpcAsyncClientError::InvalidServerResponse(format!(
                "Received unexpected number of JSON RPC responses ({}) for {} requests",
                results.len(),
                accounts.len()
            )));
        }

        Ok(results)
    }

    pub async fn submit_transaction(
        &self,
        txn: SignedTransaction,
    ) -> Result<(), JsonRpcAsyncClientError> {
        let mut batch = JsonRpcBatch::new();
        batch
            .add_submit_request(txn)
            .map_err(|e| JsonRpcAsyncClientError::InvalidArgument(e.to_string()))?;
        let mut exec_result = self.execute(batch).await?;
        assert!(exec_result.len() == 1);
        exec_result
            .remove(0)
            .map(|_| ())
            .map_err(|e| JsonRpcAsyncClientError::InvalidServerResponse(e.to_string()))
    }

    pub async fn execute(
        &self,
        batch: JsonRpcBatch,
    ) -> Result<Vec<Result<JsonRpcResponse>>, JsonRpcAsyncClientError> {
        let requests = batch.json_request();
        let resp = self
            .client
            .post(&self.address)
            .json(&requests)
            .send()
            .await
            .map_err(JsonRpcAsyncClientError::ClientError)?;

        if resp.status() != 200 {
            return Err(JsonRpcAsyncClientError::InvalidServerResponse(format!(
                "Http error code {}",
                resp.status()
            )));
        }

        let json: Value = resp
            .json()
            .await
            .map_err(|e| JsonRpcAsyncClientError::InvalidServerResponse(e.to_string()))?;

        if let Value::Array(responses) = json {
            process_batch_response(batch, responses)
                .map_err(|e| JsonRpcAsyncClientError::InvalidServerResponse(e.to_string()))
        } else if let Value::Object(response) = json {
            let error_value = response.get("error").ok_or_else(|| {
                JsonRpcAsyncClientError::InvalidServerResponse(
                    "batch response should be an array".to_string(),
                )
            })?;
            let rpc_err: JsonRpcError = serde_json::from_value(error_value.clone())
                .map_err(|e| JsonRpcAsyncClientError::InvalidServerResponse(e.to_string()))?;
            Err(JsonRpcAsyncClientError::JsonRpcError(rpc_err))
        } else {
            Err(JsonRpcAsyncClientError::InvalidServerResponse(
                json.to_string(),
            ))
        }
    }
}

impl fmt::Debug for JsonRpcAsyncClient {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.address)
    }
}

////////////////////////////////////////////////////
/// Helper methods for basic payload processing ///
///////////////////////////////////////////////////

pub fn process_batch_response(
    batch: JsonRpcBatch,
    responses: Vec<Value>,
) -> Result<Vec<Result<JsonRpcResponse>>> {
    let mut result = batch
        .requests
        .iter()
        .map(|_| Err(format_err!("request is missing")))
        .collect::<Vec<_>>();

    let mut seen_ids = HashSet::new();
    for response in responses {
        // check JSON RPC protocol
        let json_rpc_protocol = response.get("jsonrpc");
        ensure!(
            json_rpc_protocol == Some(&json!("2.0")),
            "JSON RPC response with incorrect protocol: {:?}",
            json_rpc_protocol
        );

        if let Ok(req_id) = fetch_id(&response) {
            ensure!(
                !seen_ids.contains(&req_id),
                "received JSON RPC response with duplicate response ID"
            );
            seen_ids.insert(req_id);
            if req_id < result.len() {
                let resp = if let Some(err_data) = response.get("error") {
                    let err_json: JsonRpcError = serde_json::from_value(err_data.clone())?;
                    Err(Error::new(err_json))
                } else if let Some(data) = response.get("result") {
                    let method = batch.requests[req_id].0.clone();
                    Ok(JsonRpcResponse::try_from((method, data.clone()))?)
                } else {
                    continue;
                };
                result[req_id] = resp;
            }
        }
    }

    Ok(result)
}

pub fn get_response_from_batch(
    index: usize,
    batch: &[Result<JsonRpcResponse>],
) -> Result<&Result<JsonRpcResponse>> {
    batch.get(index).ok_or_else(|| {
        format_err!(
            "[JSON RPC client] response missing in batch at index {}",
            index
        )
    })
}

fn fetch_id(response: &Value) -> Result<usize> {
    match response.get("id") {
        Some(id) => Ok(serde_json::from_value::<usize>(id.clone())?),
        None => Err(format_err!("request id is missing")),
    }
}

#[cfg(test)]
pub mod tests {
    use super::*;
    use reqwest::{blocking::ClientBuilder, Url};

    #[test]
    fn test_error_is_retriable() {
        let server_err = JsonRpcAsyncClientError::InvalidServerResponse(
            "test invalid server response".to_string(),
        );
        // Reqwest error's builder is private to the crate, so send out a
        // fake request that should fail to get an error
        let test_client = ClientBuilder::new()
            .timeout(Duration::from_millis(1))
            .build()
            .unwrap();
        let req_err = test_client
            .get(Url::parse("http://192.108.0.1").unwrap())
            .send()
            .unwrap_err();
        let client_err = JsonRpcAsyncClientError::ClientError(req_err);
        let arg_err = JsonRpcAsyncClientError::InvalidArgument("test invalid argument".to_string());
        // Make sure display is implemented correctly for error enum
        println!("{}", client_err);
        assert_eq!(server_err.is_retriable(), false);
        assert_eq!(arg_err.is_retriable(), false);
        assert_eq!(client_err.is_retriable(), true);
    }
}
<|endoftext|>


<commit_before>//! This modules implements a function to resolve a path `foo::bar::baz` to a
//! def, which is used within the name resolution.
//!
//! When name resolution is finished, the result of resolving a path is either
//! `Some(def)` or `None`. However, when we are in process of resolving imports
//! or macros, there's a third possibility:
//!
//!   I can't resolve this path right now, but I might be resolve this path
//!   later, when more macros are expanded.
//!
//! `ReachedFixedPoint` signals about this.

use std::iter::successors;

use base_db::Edition;
use hir_expand::name::Name;
use test_utils::mark;

use crate::{
    db::DefDatabase,
    item_scope::BUILTIN_SCOPE,
    nameres::{BuiltinShadowMode, DefMap},
    path::{ModPath, PathKind},
    per_ns::PerNs,
    visibility::{RawVisibility, Visibility},
    AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId, ModuleId,
};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum ResolveMode {
    Import,
    Other,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum ReachedFixedPoint {
    Yes,
    No,
}

#[derive(Debug, Clone)]
pub(super) struct ResolvePathResult {
    pub(super) resolved_def: PerNs,
    pub(super) segment_index: Option<usize>,
    pub(super) reached_fixedpoint: ReachedFixedPoint,
    pub(super) krate: Option<CrateId>,
}

impl ResolvePathResult {
    fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult {
        ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None, None)
    }

    fn with(
        resolved_def: PerNs,
        reached_fixedpoint: ReachedFixedPoint,
        segment_index: Option<usize>,
        krate: Option<CrateId>,
    ) -> ResolvePathResult {
        ResolvePathResult { resolved_def, reached_fixedpoint, segment_index, krate }
    }
}

impl DefMap {
    pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs {
        self.extern_prelude
            .get(name)
            .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public))
    }

    pub(crate) fn resolve_visibility(
        &self,
        db: &dyn DefDatabase,
        original_module: LocalModuleId,
        visibility: &RawVisibility,
    ) -> Option<Visibility> {
        match visibility {
            RawVisibility::Module(path) => {
                let (result, remaining) =
                    self.resolve_path(db, original_module, &path, BuiltinShadowMode::Module);
                if remaining.is_some() {
                    return None;
                }
                let types = result.take_types()?;
                match types {
                    ModuleDefId::ModuleId(m) => Some(Visibility::Module(m)),
                    _ => {
                        // error: visibility needs to refer to module
                        None
                    }
                }
            }
            RawVisibility::Public => Some(Visibility::Public),
        }
    }

    // Returns Yes if we are sure that additions to `ItemMap` wouldn't change
    // the result.
    pub(super) fn resolve_path_fp_with_macro(
        &self,
        db: &dyn DefDatabase,
        mode: ResolveMode,
        original_module: LocalModuleId,
        path: &ModPath,
        shadow: BuiltinShadowMode,
    ) -> ResolvePathResult {
        let mut segments = path.segments.iter().enumerate();
        let mut curr_per_ns: PerNs = match path.kind {
            PathKind::DollarCrate(krate) => {
                if krate == self.krate {
                    mark::hit!(macro_dollar_crate_self);
                    PerNs::types(
                        ModuleId { krate: self.krate, local_id: self.root }.into(),
                        Visibility::Public,
                    )
                } else {
                    let def_map = db.crate_def_map(krate);
                    let module = ModuleId { krate, local_id: def_map.root };
                    mark::hit!(macro_dollar_crate_other);
                    PerNs::types(module.into(), Visibility::Public)
                }
            }
            PathKind::Crate => PerNs::types(
                ModuleId { krate: self.krate, local_id: self.root }.into(),
                Visibility::Public,
            ),
            // plain import or absolute path in 2015: crate-relative with
            // fallback to extern prelude (with the simplification in
            // rust-lang/rust#57745)
            // FIXME there must be a nicer way to write this condition
            PathKind::Plain | PathKind::Abs
                if self.edition == Edition::Edition2015
                    && (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
            {
                let (_, segment) = match segments.next() {
                    Some((idx, segment)) => (idx, segment),
                    None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
                };
                log::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
                self.resolve_name_in_crate_root_or_extern_prelude(&segment)
            }
            PathKind::Plain => {
                let (_, segment) = match segments.next() {
                    Some((idx, segment)) => (idx, segment),
                    None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
                };
                // The first segment may be a builtin type. If the path has more
                // than one segment, we first try resolving it as a module
                // anyway.
                // FIXME: If the next segment doesn't resolve in the module and
                // BuiltinShadowMode wasn't Module, then we need to try
                // resolving it as a builtin.
                let prefer_module =
                    if path.segments.len() == 1 { shadow } else { BuiltinShadowMode::Module };

                log::debug!("resolving {:?} in module", segment);
                self.resolve_name_in_module(db, original_module, &segment, prefer_module)
            }
            PathKind::Super(lvl) => {
                let m = successors(Some(original_module), |m| self.modules[*m].parent)
                    .nth(lvl as usize);
                if let Some(local_id) = m {
                    PerNs::types(
                        ModuleId { krate: self.krate, local_id }.into(),
                        Visibility::Public,
                    )
                } else {
                    log::debug!("super path in root module");
                    return ResolvePathResult::empty(ReachedFixedPoint::Yes);
                }
            }
            PathKind::Abs => {
                // 2018-style absolute path -- only extern prelude
                let segment = match segments.next() {
                    Some((_, segment)) => segment,
                    None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
                };
                if let Some(def) = self.extern_prelude.get(&segment) {
                    log::debug!("absolute path {:?} resolved to crate {:?}", path, def);
                    PerNs::types(*def, Visibility::Public)
                } else {
                    return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
                }
            }
        };

        for (i, segment) in segments {
            let (curr, vis) = match curr_per_ns.take_types_vis() {
                Some(r) => r,
                None => {
                    // we still have path segments left, but the path so far
                    // didn't resolve in the types namespace => no resolution
                    // (don't break here because `curr_per_ns` might contain
                    // something in the value namespace, and it would be wrong
                    // to return that)
                    return ResolvePathResult::empty(ReachedFixedPoint::No);
                }
            };
            // resolve segment in curr

            curr_per_ns = match curr {
                ModuleDefId::ModuleId(module) => {
                    if module.krate != self.krate {
                        let path = ModPath {
                            segments: path.segments[i..].to_vec(),
                            kind: PathKind::Super(0),
                        };
                        log::debug!("resolving {:?} in other crate", path);
                        let defp_map = db.crate_def_map(module.krate);
                        let (def, s) = defp_map.resolve_path(db, module.local_id, &path, shadow);
                        return ResolvePathResult::with(
                            def,
                            ReachedFixedPoint::Yes,
                            s.map(|s| s + i),
                            Some(module.krate),
                        );
                    }

                    // Since it is a qualified path here, it should not contains legacy macros
                    self[module.local_id].scope.get(&segment)
                }
                ModuleDefId::AdtId(AdtId::EnumId(e)) => {
                    // enum variant
                    mark::hit!(can_import_enum_variant);
                    let enum_data = db.enum_data(e);
                    match enum_data.variant(&segment) {
                        Some(local_id) => {
                            let variant = EnumVariantId { parent: e, local_id };
                            match &*enum_data.variants[local_id].variant_data {
                                crate::adt::VariantData::Record(_) => {
                                    PerNs::types(variant.into(), Visibility::Public)
                                }
                                crate::adt::VariantData::Tuple(_)
                                | crate::adt::VariantData::Unit => {
                                    PerNs::both(variant.into(), variant.into(), Visibility::Public)
                                }
                            }
                        }
                        None => {
                            return ResolvePathResult::with(
                                PerNs::types(e.into(), vis),
                                ReachedFixedPoint::Yes,
                                Some(i),
                                Some(self.krate),
                            );
                        }
                    }
                }
                s => {
                    // could be an inherent method call in UFCS form
                    // (`Struct::method`), or some other kind of associated item
                    log::debug!(
                        "path segment {:?} resolved to non-module {:?}, but is not last",
                        segment,
                        curr,
                    );

                    return ResolvePathResult::with(
                        PerNs::types(s, vis),
                        ReachedFixedPoint::Yes,
                        Some(i),
                        Some(self.krate),
                    );
                }
            };
        }

        ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None, Some(self.krate))
    }

    fn resolve_name_in_module(
        &self,
        db: &dyn DefDatabase,
        module: LocalModuleId,
        name: &Name,
        shadow: BuiltinShadowMode,
    ) -> PerNs {
        // Resolve in:
        //  - legacy scope of macro
        //  - current module / scope
        //  - extern prelude
        //  - std prelude
        let from_legacy_macro = self[module]
            .scope
            .get_legacy_macro(name)
            .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public));
        let from_scope = self[module].scope.get(name);
        let from_builtin = BUILTIN_SCOPE.get(name).copied().unwrap_or_else(PerNs::none);
        let from_scope_or_builtin = match shadow {
            BuiltinShadowMode::Module => from_scope.or(from_builtin),
            BuiltinShadowMode::Other => {
                if let Some(ModuleDefId::ModuleId(_)) = from_scope.take_types() {
                    from_builtin.or(from_scope)
                } else {
                    from_scope.or(from_builtin)
                }
            }
        };
        let from_extern_prelude = self
            .extern_prelude
            .get(name)
            .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public));
        let from_prelude = self.resolve_in_prelude(db, name);

        from_legacy_macro.or(from_scope_or_builtin).or(from_extern_prelude).or(from_prelude)
    }

    fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs {
        let from_crate_root = self[self.root].scope.get(name);
        let from_extern_prelude = self.resolve_name_in_extern_prelude(name);

        from_crate_root.or(from_extern_prelude)
    }

    fn resolve_in_prelude(&self, db: &dyn DefDatabase, name: &Name) -> PerNs {
        if let Some(prelude) = self.prelude {
            let keep;
            let def_map = if prelude.krate == self.krate {
                self
            } else {
                // Extend lifetime
                keep = db.crate_def_map(prelude.krate);
                &keep
            };
            def_map[prelude.local_id].scope.get(name)
        } else {
            PerNs::none()
        }
    }
}
<commit_msg>Fall back to parent DefMaps when resolving paths<commit_after>//! This modules implements a function to resolve a path `foo::bar::baz` to a
//! def, which is used within the name resolution.
//!
//! When name resolution is finished, the result of resolving a path is either
//! `Some(def)` or `None`. However, when we are in process of resolving imports
//! or macros, there's a third possibility:
//!
//!   I can't resolve this path right now, but I might be resolve this path
//!   later, when more macros are expanded.
//!
//! `ReachedFixedPoint` signals about this.

use std::iter::successors;

use base_db::Edition;
use hir_expand::name::Name;
use test_utils::mark;

use crate::{
    db::DefDatabase,
    item_scope::BUILTIN_SCOPE,
    nameres::{BuiltinShadowMode, DefMap},
    path::{ModPath, PathKind},
    per_ns::PerNs,
    visibility::{RawVisibility, Visibility},
    AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId, ModuleId,
};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum ResolveMode {
    Import,
    Other,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum ReachedFixedPoint {
    Yes,
    No,
}

#[derive(Debug, Clone)]
pub(super) struct ResolvePathResult {
    pub(super) resolved_def: PerNs,
    pub(super) segment_index: Option<usize>,
    pub(super) reached_fixedpoint: ReachedFixedPoint,
    pub(super) krate: Option<CrateId>,
}

impl ResolvePathResult {
    fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult {
        ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None, None)
    }

    fn with(
        resolved_def: PerNs,
        reached_fixedpoint: ReachedFixedPoint,
        segment_index: Option<usize>,
        krate: Option<CrateId>,
    ) -> ResolvePathResult {
        ResolvePathResult { resolved_def, reached_fixedpoint, segment_index, krate }
    }
}

impl DefMap {
    pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs {
        self.extern_prelude
            .get(name)
            .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public))
    }

    pub(crate) fn resolve_visibility(
        &self,
        db: &dyn DefDatabase,
        original_module: LocalModuleId,
        visibility: &RawVisibility,
    ) -> Option<Visibility> {
        match visibility {
            RawVisibility::Module(path) => {
                let (result, remaining) =
                    self.resolve_path(db, original_module, &path, BuiltinShadowMode::Module);
                if remaining.is_some() {
                    return None;
                }
                let types = result.take_types()?;
                match types {
                    ModuleDefId::ModuleId(m) => Some(Visibility::Module(m)),
                    _ => {
                        // error: visibility needs to refer to module
                        None
                    }
                }
            }
            RawVisibility::Public => Some(Visibility::Public),
        }
    }

    // Returns Yes if we are sure that additions to `ItemMap` wouldn't change
    // the result.
    pub(super) fn resolve_path_fp_with_macro(
        &self,
        db: &dyn DefDatabase,
        mode: ResolveMode,
        original_module: LocalModuleId,
        path: &ModPath,
        shadow: BuiltinShadowMode,
    ) -> ResolvePathResult {
        let mut result = ResolvePathResult::empty(ReachedFixedPoint::No);
        result.segment_index = Some(usize::max_value());

        let mut current_map = self;
        loop {
            let new = current_map.resolve_path_fp_with_macro_single(
                db,
                mode,
                original_module,
                path,
                shadow,
            );

            // Merge `new` into `result`.
            result.resolved_def = result.resolved_def.or(new.resolved_def);
            if result.reached_fixedpoint == ReachedFixedPoint::No {
                result.reached_fixedpoint = new.reached_fixedpoint;
            }
            // FIXME: this doesn't seem right; what if the different namespace resolutions come from different crates?
            result.krate = result.krate.or(new.krate);
            result.segment_index = result.segment_index.min(new.segment_index);

            match &current_map.parent {
                Some(map) => current_map = map,
                None => return result,
            }
        }
    }

    pub(super) fn resolve_path_fp_with_macro_single(
        &self,
        db: &dyn DefDatabase,
        mode: ResolveMode,
        original_module: LocalModuleId,
        path: &ModPath,
        shadow: BuiltinShadowMode,
    ) -> ResolvePathResult {
        let mut segments = path.segments.iter().enumerate();
        let mut curr_per_ns: PerNs = match path.kind {
            PathKind::DollarCrate(krate) => {
                if krate == self.krate {
                    mark::hit!(macro_dollar_crate_self);
                    PerNs::types(
                        ModuleId { krate: self.krate, local_id: self.root }.into(),
                        Visibility::Public,
                    )
                } else {
                    let def_map = db.crate_def_map(krate);
                    let module = ModuleId { krate, local_id: def_map.root };
                    mark::hit!(macro_dollar_crate_other);
                    PerNs::types(module.into(), Visibility::Public)
                }
            }
            PathKind::Crate => PerNs::types(
                ModuleId { krate: self.krate, local_id: self.root }.into(),
                Visibility::Public,
            ),
            // plain import or absolute path in 2015: crate-relative with
            // fallback to extern prelude (with the simplification in
            // rust-lang/rust#57745)
            // FIXME there must be a nicer way to write this condition
            PathKind::Plain | PathKind::Abs
                if self.edition == Edition::Edition2015
                    && (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
            {
                let (_, segment) = match segments.next() {
                    Some((idx, segment)) => (idx, segment),
                    None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
                };
                log::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
                self.resolve_name_in_crate_root_or_extern_prelude(&segment)
            }
            PathKind::Plain => {
                let (_, segment) = match segments.next() {
                    Some((idx, segment)) => (idx, segment),
                    None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
                };
                // The first segment may be a builtin type. If the path has more
                // than one segment, we first try resolving it as a module
                // anyway.
                // FIXME: If the next segment doesn't resolve in the module and
                // BuiltinShadowMode wasn't Module, then we need to try
                // resolving it as a builtin.
                let prefer_module =
                    if path.segments.len() == 1 { shadow } else { BuiltinShadowMode::Module };

                log::debug!("resolving {:?} in module", segment);
                self.resolve_name_in_module(db, original_module, &segment, prefer_module)
            }
            PathKind::Super(lvl) => {
                let m = successors(Some(original_module), |m| self.modules[*m].parent)
                    .nth(lvl as usize);
                if let Some(local_id) = m {
                    PerNs::types(
                        ModuleId { krate: self.krate, local_id }.into(),
                        Visibility::Public,
                    )
                } else {
                    log::debug!("super path in root module");
                    return ResolvePathResult::empty(ReachedFixedPoint::Yes);
                }
            }
            PathKind::Abs => {
                // 2018-style absolute path -- only extern prelude
                let segment = match segments.next() {
                    Some((_, segment)) => segment,
                    None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
                };
                if let Some(def) = self.extern_prelude.get(&segment) {
                    log::debug!("absolute path {:?} resolved to crate {:?}", path, def);
                    PerNs::types(*def, Visibility::Public)
                } else {
                    return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
                }
            }
        };

        for (i, segment) in segments {
            let (curr, vis) = match curr_per_ns.take_types_vis() {
                Some(r) => r,
                None => {
                    // we still have path segments left, but the path so far
                    // didn't resolve in the types namespace => no resolution
                    // (don't break here because `curr_per_ns` might contain
                    // something in the value namespace, and it would be wrong
                    // to return that)
                    return ResolvePathResult::empty(ReachedFixedPoint::No);
                }
            };
            // resolve segment in curr

            curr_per_ns = match curr {
                ModuleDefId::ModuleId(module) => {
                    if module.krate != self.krate {
                        let path = ModPath {
                            segments: path.segments[i..].to_vec(),
                            kind: PathKind::Super(0),
                        };
                        log::debug!("resolving {:?} in other crate", path);
                        let defp_map = db.crate_def_map(module.krate);
                        let (def, s) = defp_map.resolve_path(db, module.local_id, &path, shadow);
                        return ResolvePathResult::with(
                            def,
                            ReachedFixedPoint::Yes,
                            s.map(|s| s + i),
                            Some(module.krate),
                        );
                    }

                    // Since it is a qualified path here, it should not contains legacy macros
                    self[module.local_id].scope.get(&segment)
                }
                ModuleDefId::AdtId(AdtId::EnumId(e)) => {
                    // enum variant
                    mark::hit!(can_import_enum_variant);
                    let enum_data = db.enum_data(e);
                    match enum_data.variant(&segment) {
                        Some(local_id) => {
                            let variant = EnumVariantId { parent: e, local_id };
                            match &*enum_data.variants[local_id].variant_data {
                                crate::adt::VariantData::Record(_) => {
                                    PerNs::types(variant.into(), Visibility::Public)
                                }
                                crate::adt::VariantData::Tuple(_)
                                | crate::adt::VariantData::Unit => {
                                    PerNs::both(variant.into(), variant.into(), Visibility::Public)
                                }
                            }
                        }
                        None => {
                            return ResolvePathResult::with(
                                PerNs::types(e.into(), vis),
                                ReachedFixedPoint::Yes,
                                Some(i),
                                Some(self.krate),
                            );
                        }
                    }
                }
                s => {
                    // could be an inherent method call in UFCS form
                    // (`Struct::method`), or some other kind of associated item
                    log::debug!(
                        "path segment {:?} resolved to non-module {:?}, but is not last",
                        segment,
                        curr,
                    );

                    return ResolvePathResult::with(
                        PerNs::types(s, vis),
                        ReachedFixedPoint::Yes,
                        Some(i),
                        Some(self.krate),
                    );
                }
            };
        }

        ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None, Some(self.krate))
    }

    fn resolve_name_in_module(
        &self,
        db: &dyn DefDatabase,
        module: LocalModuleId,
        name: &Name,
        shadow: BuiltinShadowMode,
    ) -> PerNs {
        // Resolve in:
        //  - legacy scope of macro
        //  - current module / scope
        //  - extern prelude
        //  - std prelude
        let from_legacy_macro = self[module]
            .scope
            .get_legacy_macro(name)
            .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public));
        let from_scope = self[module].scope.get(name);
        let from_builtin = BUILTIN_SCOPE.get(name).copied().unwrap_or_else(PerNs::none);
        let from_scope_or_builtin = match shadow {
            BuiltinShadowMode::Module => from_scope.or(from_builtin),
            BuiltinShadowMode::Other => {
                if let Some(ModuleDefId::ModuleId(_)) = from_scope.take_types() {
                    from_builtin.or(from_scope)
                } else {
                    from_scope.or(from_builtin)
                }
            }
        };
        let from_extern_prelude = self
            .extern_prelude
            .get(name)
            .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public));
        let from_prelude = self.resolve_in_prelude(db, name);

        from_legacy_macro.or(from_scope_or_builtin).or(from_extern_prelude).or(from_prelude)
    }

    fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs {
        let from_crate_root = self[self.root].scope.get(name);
        let from_extern_prelude = self.resolve_name_in_extern_prelude(name);

        from_crate_root.or(from_extern_prelude)
    }

    fn resolve_in_prelude(&self, db: &dyn DefDatabase, name: &Name) -> PerNs {
        if let Some(prelude) = self.prelude {
            let keep;
            let def_map = if prelude.krate == self.krate {
                self
            } else {
                // Extend lifetime
                keep = db.crate_def_map(prelude.krate);
                &keep
            };
            def_map[prelude.local_id].scope.get(name)
        } else {
            PerNs::none()
        }
    }
}
<|endoftext|>


<commit_before>// See LICENSE file for copyright and license details.

#[phase(plugin)]
extern crate gl_generator;

extern crate glutin;
extern crate cgmath;
extern crate serialize;

use self::glutin::{Event, VirtualKeyCode}; // TODO: why 'self'?
use core_types::{Size2, MInt};
use visualizer_types::{Color3, Color4, ColorId};
use mgl::Mgl;
use mgl;
use std::mem;

// TODO: remove 'gl'
use gl;
use gl::types::{GLfloat, GLuint};

static VS_SRC: &'static str = "\
    #version 100\n\
    attribute vec2 position;\n\
    void main() {\n\
        gl_Position = vec4(position, 0.0, 1.0);\n\
    }\n\
";

static FS_SRC: &'static str = "\
    #version 100\n\
    precision mediump float;
    uniform vec4 col;\n\
    void main() {\n\
        gl_FragColor = col;\n\
    }\n\
";

fn print_gl_info(mgl: &Mgl) {
    println!("GL_VERSION: {}", mgl.get_info(gl::VERSION));
    println!("GL_SHADING_LANGUAGE_VERSION: {}", mgl.get_info(gl::SHADING_LANGUAGE_VERSION));
    println!("GL_VENDOR: {}", mgl.get_info(gl::VENDOR));
    println!("GL_RENDERER: {}", mgl.get_info(gl::RENDERER));
    // println!("GL_EXTENSIONS: {}", mgl.get_info(gl::EXTENSIONS));
}

// TODO: Create 'Shader' class
fn compile_shaders(mgl: &Mgl) -> GLuint {
    let vs = mgl::compile_shader(&mgl.gl, VS_SRC, gl::VERTEX_SHADER);
    let fs = mgl::compile_shader(&mgl.gl, FS_SRC, gl::FRAGMENT_SHADER);
    mgl::link_program(&mgl.gl, vs, fs)
}

pub struct Visualizer {
    mgl: Mgl,
    window: glutin::Window,
    should_close: bool,
    color_counter: i32, // TODO: remove
    test_color: Color4,
    program: GLuint,
    color_unifrom_location: ColorId,
}

impl Visualizer {
    pub fn new() -> Visualizer {
        let window_builder = glutin::WindowBuilder::new().with_gl_version((2, 0));
        let window = window_builder.build().ok().expect("Can`t create window");
        unsafe {
            window.make_current();
        };
        let mut mgl = Mgl::new(|s| window.get_proc_address(s));
        print_gl_info(&mgl);
        let program = compile_shaders(&mgl);
        let color_unifrom_location = ColorId {
            id: mgl.get_uniform(program, "col") as GLuint
        };
        mgl.set_clear_color(Color3{r: 0.0, g: 0.0, b: 0.4});
        Visualizer {
            mgl: mgl,
            window: window,
            should_close: false,
            color_counter: 0,
            test_color: Color4{r: 0.0, g: 0.0, b: 0.0, a: 0.0},
            program: program,
            color_unifrom_location: color_unifrom_location,
        }
    }

    pub fn is_running(&self) -> bool {
        !self.should_close
    }

    fn handle_events(&mut self) {
        let events = self.window.poll_events().collect::<Vec<_>>();
        if !events.is_empty() {
            println!("{}", events);
        }
        for event in events.iter() {
            match *event {
                Event::KeyboardInput(_, _, Some(VirtualKeyCode::Escape))
                    | Event::Closed =>
                {
                    self.should_close = true;
                },
                _ => {},
            }
        }
    }

    fn update_test_color(&mut self) {
        self.test_color = match self.color_counter {
            0 => Color4{r: 1.0, g: 0.0, b: 0.0, a: 1.0},
            30 => Color4{r: 0.0, g: 1.0, b: 0.0, a: 1.0},
            60 => Color4{r: 0.0, g: 0.0, b: 1.0, a: 1.0},
            _ => self.test_color,
        };
        self.color_counter += 1;
        if self.color_counter > 90 {
            self.color_counter = -1;
        }
    }

    fn draw(&mut self) {
        self.mgl.clear_screen();
        let vertices: [GLfloat, ..3 * 3] = [
            0.0,  0.5, 0.0,
            0.5, -0.5, 0.0,
            -0.5, -0.5, 0.0,
        ];
        let (w, h) = self.window.get_inner_size()
            .expect("Can`t get window size");
        self.mgl.set_viewport(Size2{w: w as MInt, h: h as MInt});
        unsafe {
            self.mgl.gl.UseProgram(self.program);
            self.mgl.set_uniform_color(self.color_unifrom_location, &self.test_color);
            self.mgl.gl.VertexAttribPointer(
                0, 3, gl::FLOAT, gl::FALSE, 0, mem::transmute(&vertices));
            self.mgl.gl.EnableVertexAttribArray(0);
            self.mgl.gl.DrawArrays(gl::TRIANGLES, 0, 3);
        }
        self.window.swap_buffers();
    }

    pub fn tick(&mut self) {
        self.handle_events();
        // self.logic();
        self.update_test_color();
        self.draw();
        // self.update_time();
    }
}

// vim: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab:
<commit_msg>Removed 'extern crate gl_generator'<commit_after>// See LICENSE file for copyright and license details.

extern crate glutin;
extern crate cgmath;
extern crate serialize;

use self::glutin::{Event, VirtualKeyCode}; // TODO: why 'self'?
use core_types::{Size2, MInt};
use visualizer_types::{Color3, Color4, ColorId};
use mgl::Mgl;
use mgl;
use std::mem;

// TODO: remove 'gl'
use gl;
use gl::types::{GLfloat, GLuint};

static VS_SRC: &'static str = "\
    #version 100\n\
    attribute vec2 position;\n\
    void main() {\n\
        gl_Position = vec4(position, 0.0, 1.0);\n\
    }\n\
";

static FS_SRC: &'static str = "\
    #version 100\n\
    precision mediump float;
    uniform vec4 col;\n\
    void main() {\n\
        gl_FragColor = col;\n\
    }\n\
";

fn print_gl_info(mgl: &Mgl) {
    println!("GL_VERSION: {}", mgl.get_info(gl::VERSION));
    println!("GL_SHADING_LANGUAGE_VERSION: {}", mgl.get_info(gl::SHADING_LANGUAGE_VERSION));
    println!("GL_VENDOR: {}", mgl.get_info(gl::VENDOR));
    println!("GL_RENDERER: {}", mgl.get_info(gl::RENDERER));
    // println!("GL_EXTENSIONS: {}", mgl.get_info(gl::EXTENSIONS));
}

// TODO: Create 'Shader' class
fn compile_shaders(mgl: &Mgl) -> GLuint {
    let vs = mgl::compile_shader(&mgl.gl, VS_SRC, gl::VERTEX_SHADER);
    let fs = mgl::compile_shader(&mgl.gl, FS_SRC, gl::FRAGMENT_SHADER);
    mgl::link_program(&mgl.gl, vs, fs)
}

pub struct Visualizer {
    mgl: Mgl,
    window: glutin::Window,
    should_close: bool,
    color_counter: i32, // TODO: remove
    test_color: Color4,
    program: GLuint,
    color_unifrom_location: ColorId,
}

impl Visualizer {
    pub fn new() -> Visualizer {
        let window_builder = glutin::WindowBuilder::new().with_gl_version((2, 0));
        let window = window_builder.build().ok().expect("Can`t create window");
        unsafe {
            window.make_current();
        };
        let mut mgl = Mgl::new(|s| window.get_proc_address(s));
        print_gl_info(&mgl);
        let program = compile_shaders(&mgl);
        let color_unifrom_location = ColorId {
            id: mgl.get_uniform(program, "col") as GLuint
        };
        mgl.set_clear_color(Color3{r: 0.0, g: 0.0, b: 0.4});
        Visualizer {
            mgl: mgl,
            window: window,
            should_close: false,
            color_counter: 0,
            test_color: Color4{r: 0.0, g: 0.0, b: 0.0, a: 0.0},
            program: program,
            color_unifrom_location: color_unifrom_location,
        }
    }

    pub fn is_running(&self) -> bool {
        !self.should_close
    }

    fn handle_events(&mut self) {
        let events = self.window.poll_events().collect::<Vec<_>>();
        if !events.is_empty() {
            println!("{}", events);
        }
        for event in events.iter() {
            match *event {
                Event::KeyboardInput(_, _, Some(VirtualKeyCode::Escape))
                    | Event::Closed =>
                {
                    self.should_close = true;
                },
                _ => {},
            }
        }
    }

    fn update_test_color(&mut self) {
        self.test_color = match self.color_counter {
            0 => Color4{r: 1.0, g: 0.0, b: 0.0, a: 1.0},
            30 => Color4{r: 0.0, g: 1.0, b: 0.0, a: 1.0},
            60 => Color4{r: 0.0, g: 0.0, b: 1.0, a: 1.0},
            _ => self.test_color,
        };
        self.color_counter += 1;
        if self.color_counter > 90 {
            self.color_counter = -1;
        }
    }

    fn draw(&mut self) {
        self.mgl.clear_screen();
        let vertices: [GLfloat, ..3 * 3] = [
            0.0,  0.5, 0.0,
            0.5, -0.5, 0.0,
            -0.5, -0.5, 0.0,
        ];
        let (w, h) = self.window.get_inner_size()
            .expect("Can`t get window size");
        self.mgl.set_viewport(Size2{w: w as MInt, h: h as MInt});
        unsafe {
            self.mgl.gl.UseProgram(self.program);
            self.mgl.set_uniform_color(self.color_unifrom_location, &self.test_color);
            self.mgl.gl.VertexAttribPointer(
                0, 3, gl::FLOAT, gl::FALSE, 0, mem::transmute(&vertices));
            self.mgl.gl.EnableVertexAttribArray(0);
            self.mgl.gl.DrawArrays(gl::TRIANGLES, 0, 3);
        }
        self.window.swap_buffers();
    }

    pub fn tick(&mut self) {
        self.handle_events();
        // self.logic();
        self.update_test_color();
        self.draw();
        // self.update_time();
    }
}

// vim: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab:
<|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.

//! Mining plugin manager, using the cuckoo-miner crate to provide
//! a mining worker implementation
//!

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

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

use p2p;
use util::LOGGER;

const PEER_MAX_COUNT: u32 = 25;
const PEER_PREFERRED_COUNT: u32 = 8;
const SEEDS_URL: &'static str = "http://grin-tech.org/seeds.txt";

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

	capabilities: p2p::Capabilities,
}

impl Seeder {
	pub fn new(
		capabilities: p2p::Capabilities,
		p2p: Arc<p2p::Server>,
	) -> Seeder {
		Seeder {
			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(|e| {
			error!(LOGGER, "Seeding or peer monitoring error: {}", e);
			()
		}));
	}

	fn monitor_peers(
		&self,
		tx: mpsc::UnboundedSender<SocketAddr>,
	) -> Box<Future<Item = (), Error = String>> {
		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 |_| {
				debug!(
					LOGGER,
					"monitoring peers ({} / {} / {})",
					p2p_server.most_work_peers().len(),
					p2p_server.connected_peers().len(),
					p2p_server.all_peers().len(),
				);

				// maintenance step first, clean up p2p server peers
				let _ = p2p_server.clean_peers();

				// we don't have enough peers, getting more from db
				if p2p_server.peer_count() < PEER_PREFERRED_COUNT {
					let mut peers = p2p_server.find_peers(
						p2p::State::Healthy,
						p2p::UNKNOWN,
						(2 * PEER_MAX_COUNT) as usize,
					);
					peers.retain(|p| !p2p_server.is_known(&p.addr));
					if peers.len() > 0 {
						debug!(
							LOGGER,
							"Got {} 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.unbounded_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>> {

		// 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 p2p_server = self.p2p.clone();
		let seeder = thread_pool
			.spawn_fn(move || {
				// check if we have some peers in db
				let peers = p2p_server.find_peers(
					p2p::State::Healthy,
					p2p::FULL_HIST,
					(2 * PEER_MAX_COUNT) as usize,
				);
				Ok(peers)
			})
			.and_then(|mut peers| {
				// if so, get their addresses, otherwise use our seeds
				if peers.len() > 3 {
					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!(LOGGER, "Connecting to seed: {}.", addr);
					tx.unbounded_send(*addr).unwrap();
				}
				if peer_addrs.len() == 0 {
					warn!(LOGGER, "No seeds were retrieved.");
				}
				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!(LOGGER, "New peer address to connect to: {}.", peer_addr);
			let inner_h = h.clone();
			if p2p_server.peer_count() < PEER_MAX_COUNT {
				h.spawn(
					connect_and_req(
						capab,
						p2p_server.clone(),
						inner_h,
						peer_addr,
					)
				)
			};
			Box::new(future::ok(()))
		});
		Box::new(listener)
	}
}

/// Extract the list of seeds from a pre-defined text file available through
/// http. Easy method until we have a set of DNS names we can rely on.
pub fn web_seeds(h: reactor::Handle) -> Box<Future<Item = Vec<SocketAddr>, Error = String>> {
	let url = hyper::Uri::from_str(&SEEDS_URL).unwrap();
	let seeds = future::ok(()).and_then(move |_| {
		let client = hyper::Client::new(&h);
		debug!(LOGGER, "Retrieving seed nodes from {}", &SEEDS_URL);

		// 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<_>>();
						debug!(LOGGER, "Retrieved seed addresses: {:?}", addrs);
						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 connect_peer = p2p.connect_peer(addr, h).map_err(|_| ());
	let timer = Timer::default();
	let timeout = timer.timeout(connect_peer, Duration::from_secs(5));
	let p2p_server = p2p.clone();

	let fut = timeout.then(move |p| {
		match p {
			Ok(Some(p)) => {
				let p = p.read().unwrap();
				let peer_result = p.send_peer_request(capab);
				match peer_result {
					Ok(()) => {}
					Err(_) => {}
				}
			}
			Err(_) => {
				let update_result = p2p_server.update_state(addr, p2p::State::Defunct);
				match update_result {
					Ok(()) => {}
					Err(_) => {}
				}
			}
			_ => {}
		}
		Ok(())
	});
	Box::new(fut)
}
<commit_msg>add logging in monitor_peers for healthy/banned/defunct counts (#436)<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.

//! Mining plugin manager, using the cuckoo-miner crate to provide
//! a mining worker implementation
//!

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

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

use p2p;
use util::LOGGER;

const PEER_MAX_COUNT: u32 = 25;
const PEER_PREFERRED_COUNT: u32 = 8;
const SEEDS_URL: &'static str = "http://grin-tech.org/seeds.txt";

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

	capabilities: p2p::Capabilities,
}

impl Seeder {
	pub fn new(
		capabilities: p2p::Capabilities,
		p2p: Arc<p2p::Server>,
	) -> Seeder {
		Seeder {
			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(|e| {
			error!(LOGGER, "Seeding or peer monitoring error: {}", e);
			()
		}));
	}

	fn monitor_peers(
		&self,
		tx: mpsc::UnboundedSender<SocketAddr>,
	) -> Box<Future<Item = (), Error = String>> {
		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 |_| {
				debug!(
					LOGGER,
					"monitoring_peers: {} / {} / {}",
					p2p_server.most_work_peers().len(),
					p2p_server.connected_peers().len(),
					p2p_server.all_peers().len(),
				);

				let all_peers = p2p_server.all_peers();
				let healthy_count = all_peers
					.iter()
					.filter(|x| x.flags == p2p::State::Healthy)
					.count();
				let banned_count = all_peers
					.iter()
					.filter(|x| x.flags == p2p::State::Banned)
					.count();
				let defunct_count = all_peers
					.iter()
					.filter(|x| x.flags == p2p::State::Defunct)
					.count();

				debug!(
					LOGGER,
					"monitoring_peers: all - {}, healthy - {}, banned - {}, defunct - {}",
					all_peers.len(),
					healthy_count,
					banned_count,
					defunct_count,
				);

				// maintenance step first, clean up p2p server peers
				let _ = p2p_server.clean_peers();

				// we don't have enough peers, getting more from db
				if p2p_server.peer_count() < PEER_PREFERRED_COUNT {
					let mut peers = p2p_server.find_peers(
						p2p::State::Healthy,
						p2p::UNKNOWN,
						(2 * PEER_MAX_COUNT) as usize,
					);
					peers.retain(|p| !p2p_server.is_known(&p.addr));
					if peers.len() > 0 {
						debug!(
							LOGGER,
							"Got {} 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.unbounded_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>> {

		// 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 p2p_server = self.p2p.clone();
		let seeder = thread_pool
			.spawn_fn(move || {
				// check if we have some peers in db
				let peers = p2p_server.find_peers(
					p2p::State::Healthy,
					p2p::FULL_HIST,
					(2 * PEER_MAX_COUNT) as usize,
				);
				Ok(peers)
			})
			.and_then(|mut peers| {
				// if so, get their addresses, otherwise use our seeds
				if peers.len() > 3 {
					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!(LOGGER, "Connecting to seed: {}.", addr);
					tx.unbounded_send(*addr).unwrap();
				}
				if peer_addrs.len() == 0 {
					warn!(LOGGER, "No seeds were retrieved.");
				}
				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!(LOGGER, "New peer address to connect to: {}.", peer_addr);
			let inner_h = h.clone();
			if p2p_server.peer_count() < PEER_MAX_COUNT {
				h.spawn(
					connect_and_req(
						capab,
						p2p_server.clone(),
						inner_h,
						peer_addr,
					)
				)
			};
			Box::new(future::ok(()))
		});
		Box::new(listener)
	}
}

/// Extract the list of seeds from a pre-defined text file available through
/// http. Easy method until we have a set of DNS names we can rely on.
pub fn web_seeds(h: reactor::Handle) -> Box<Future<Item = Vec<SocketAddr>, Error = String>> {
	let url = hyper::Uri::from_str(&SEEDS_URL).unwrap();
	let seeds = future::ok(()).and_then(move |_| {
		let client = hyper::Client::new(&h);
		debug!(LOGGER, "Retrieving seed nodes from {}", &SEEDS_URL);

		// 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<_>>();
						debug!(LOGGER, "Retrieved seed addresses: {:?}", addrs);
						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 connect_peer = p2p.connect_peer(addr, h).map_err(|_| ());
	let timer = Timer::default();
	let timeout = timer.timeout(connect_peer, Duration::from_secs(5));
	let p2p_server = p2p.clone();

	let fut = timeout.then(move |p| {
		match p {
			Ok(Some(p)) => {
				let p = p.read().unwrap();
				let peer_result = p.send_peer_request(capab);
				match peer_result {
					Ok(()) => {}
					Err(_) => {}
				}
			}
			Err(_) => {
				let update_result = p2p_server.update_state(addr, p2p::State::Defunct);
				match update_result {
					Ok(()) => {}
					Err(_) => {}
				}
			}
			_ => {}
		}
		Ok(())
	});
	Box::new(fut)
}
<|endoftext|>


<commit_before>// Copyright 2019 Google LLC
//
// 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
//
//     https://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 h1b::hil::flash::{Flash,Hardware};
use kernel::hil::time::Alarm;
use kernel::ReturnCode;
use test::require;

// These are in counts of a 16 MHz clock.
#[cfg(test)]
const ERASE_TIME: u32 = 53653;
#[cfg(test)]
const WRITE_WORD_TIME: u32 = 840;

#[cfg(test)]
#[derive(Clone,Copy,PartialEq)]
enum MockClientState {
	EraseDone(kernel::ReturnCode),
	WriteDone(kernel::ReturnCode),
}

#[cfg(test)]
struct MockClient {
	state: core::cell::Cell<Option<MockClientState>>,
}

#[cfg(test)]
impl MockClient {
	pub fn new() -> Self {
		MockClient { state: core::cell::Cell::new(None) }
	}

	pub fn state(&self) -> Option<MockClientState> { self.state.get() }
}

#[cfg(test)]
impl h1b::hil::flash::Client for MockClient {
	fn erase_done(&self, code: kernel::ReturnCode) {
		self.state.set(Some(MockClientState::EraseDone(code)));
	}

	fn write_done(&self, code: kernel::ReturnCode) {
		self.state.set(Some(MockClientState::WriteDone(code)));
	}
}

#[test]
fn erase() -> bool {
	use kernel::hil::time::Client;
	let alarm = crate::hil::flash::mock_alarm::MockAlarm::new();
	let client = MockClient::new();
	let hw = h1b::hil::flash::fake::FakeHw::new();

	// First attempt.
	let driver = unsafe { h1b::hil::flash::FlashImpl::new(&alarm, &hw) };
	driver.set_client(&client);
	require!(driver.erase(2) == kernel::ReturnCode::SUCCESS);
	require!(alarm.get_alarm() == alarm.now() + ERASE_TIME);
	require!(client.state() == None);
	require!(hw.is_programming() == true);

	// Indicate an error, let the driver retry.
	alarm.set_time(ERASE_TIME);
	hw.inject_result(0b100);
	driver.fired();
	require!(alarm.get_alarm() == 2 * ERASE_TIME);
	require!(hw.is_programming() == true);
	require!(client.state() == None);

	// Let the operation finish successfully.
	alarm.set_time(2 * ERASE_TIME);
	hw.finish_operation();
	driver.fired();
	require!(alarm.get_alarm() == 0);
	require!(hw.is_programming() == false);
	require!(hw.read(1300) == ReturnCode::SuccessWithValue { value: 0xFFFFFFFF });
	require!(client.state() == Some(MockClientState::EraseDone(kernel::ReturnCode::SUCCESS)));
	require!(driver.read(1300) == ReturnCode::SuccessWithValue { value: 0xFFFFFFFF });

	true
}

#[test]
fn erase_max_retries() -> bool {
	use kernel::hil::time::Client;
	let alarm = crate::hil::flash::mock_alarm::MockAlarm::new();
	let client = MockClient::new();
	let hw = h1b::hil::flash::fake::FakeHw::new();
	let driver = unsafe { h1b::hil::flash::FlashImpl::new(&alarm, &hw) };
	driver.set_client(&client);
	require!(driver.erase(2) == kernel::ReturnCode::SUCCESS);
	require!(alarm.get_alarm() == alarm.now() + ERASE_TIME);
	require!(client.state() == None);
	require!(hw.is_programming() == true);

	for i in 1..45 {
		// Indicate an error, let the driver retry.
		alarm.set_time(ERASE_TIME * i);
		hw.inject_result(0b100);
		driver.fired();
		require!(alarm.get_alarm() == alarm.now() + ERASE_TIME);
		require!(hw.is_programming() == true);
		require!(client.state() == None);
	}

	// Last try.
	hw.inject_result(0b100);
	driver.fired();
	require!(alarm.get_alarm() == 0);
	require!(hw.is_programming() == false);
	require!(client.state() == Some(MockClientState::EraseDone(kernel::ReturnCode::FAIL)));
	require!(hw.read(1300) == ReturnCode::SuccessWithValue { value: 0xFFFFFFFF });
	require!(driver.read(1300) == ReturnCode::SuccessWithValue { value: 0xFFFFFFFF });

	true
}

#[test]
fn write_then_erase() -> bool {
	use kernel::hil::time::Client;
	let alarm = crate::hil::flash::mock_alarm::MockAlarm::new();
	let client = MockClient::new();
	let hw = h1b::hil::flash::fake::FakeHw::new();

	// Write
	let driver = unsafe { h1b::hil::flash::FlashImpl::new(&alarm, &hw) };
	driver.set_client(&client);
	require!(driver.write(1300, &[0xFFFFABCD]) == kernel::ReturnCode::SUCCESS);
	require!(alarm.get_alarm() == alarm.now() + WRITE_WORD_TIME);
	require!(client.state() == None);
	require!(hw.is_programming() == true);
	alarm.set_time(WRITE_WORD_TIME);
	hw.inject_result(0);
	driver.fired();
	require!(hw.is_programming() == true);
	require!(client.state() == None);
	hw.finish_operation();
	driver.fired();
	require!(alarm.get_alarm() == 0);
	require!(hw.is_programming() == false);
	require!(hw.read(1300) == ReturnCode::SuccessWithValue { value: 0xFFFFABCD });
	require!(client.state() == Some(MockClientState::WriteDone(kernel::ReturnCode::SUCCESS)));
	require!(driver.read(1300) == ReturnCode::SuccessWithValue { value: 0xFFFFABCD });

	// Erase
	let driver = unsafe { h1b::hil::flash::FlashImpl::new(&alarm, &hw) };
	driver.set_client(&client);
	require!(driver.erase(2) == kernel::ReturnCode::SUCCESS);
	require!(alarm.get_alarm() == alarm.now() + ERASE_TIME);
	require!(hw.is_programming() == true);
	alarm.set_time(WRITE_WORD_TIME + ERASE_TIME);
	hw.finish_operation();
	driver.fired();
	require!(alarm.get_alarm() == 0);
	require!(hw.is_programming() == false);
	require!(hw.read(1300) == ReturnCode::SuccessWithValue { value: 0xFFFFFFFF });
	require!(client.state() == Some(MockClientState::EraseDone(kernel::ReturnCode::SUCCESS)));
	require!(driver.read(1300) == ReturnCode::SuccessWithValue { value: 0xFFFFFFFF });

	true
}

#[test]
fn successful_program() -> bool {
	use kernel::hil::time::Client;
	let alarm = crate::hil::flash::mock_alarm::MockAlarm::new();
	let client = MockClient::new();
	let hw = h1b::hil::flash::fake::FakeHw::new();

	// First attempt.
	let driver = unsafe { h1b::hil::flash::FlashImpl::new(&alarm, &hw) };
	driver.set_client(&client);
	require!(driver.write(1300, &[0xFFFFABCD]) == kernel::ReturnCode::SUCCESS);
	require!(alarm.get_alarm() == alarm.now() + WRITE_WORD_TIME);
	require!(client.state() == None);
	require!(hw.is_programming() == true);

	// Indicate an error, let the driver retry.
	alarm.set_time(WRITE_WORD_TIME);
	hw.inject_result(0b100);
	driver.fired();
	require!(alarm.get_alarm() == 2 * WRITE_WORD_TIME);
	require!(hw.is_programming() == true);
	require!(client.state() == None);

	// Let the operation finish successfully (including the final pulse).
	alarm.set_time(2 * WRITE_WORD_TIME);
	hw.inject_result(0);
	driver.fired();
	require!(alarm.get_alarm() == 3 * WRITE_WORD_TIME);
	require!(hw.is_programming() == true);
	require!(client.state() == None);
	hw.finish_operation();
	driver.fired();
	require!(alarm.get_alarm() == 0);
	require!(hw.is_programming() == false);
	require!(client.state() == Some(MockClientState::WriteDone(kernel::ReturnCode::SUCCESS)));
	require!(driver.read(1300) == ReturnCode::SuccessWithValue { value: 0xFFFFABCD });

	true
}

#[test]
fn timeout() -> bool {
	use kernel::hil::time::Client;
	let alarm = crate::hil::flash::mock_alarm::MockAlarm::new();
	let client = MockClient::new();
	let hw = h1b::hil::flash::fake::FakeHw::new();
	hw.set_transaction(1300, 1);
	hw.set_write_data(&[0xFFFF0FFF]);

	// First attempt.
	let driver = unsafe { h1b::hil::flash::FlashImpl::new(&alarm, &hw) };
	driver.set_client(&client);
	require!(driver.write(1300, &[0xFFFFABCD]) == kernel::ReturnCode::SUCCESS);
	require!(alarm.get_alarm() == alarm.now() + WRITE_WORD_TIME);
	require!(client.state() == None);
	require!(hw.is_programming() == true);

	// Indicate a timeout.
	alarm.set_time(WRITE_WORD_TIME);
	driver.fired();
	require!(alarm.get_alarm() == 0);
	require!(client.state() == Some(MockClientState::WriteDone(kernel::ReturnCode::FAIL)));

	true
}

#[test]
fn write_max_retries() -> bool {
	use kernel::hil::time::Client;
	let alarm = crate::hil::flash::mock_alarm::MockAlarm::new();
	let client = MockClient::new();
	let hw = h1b::hil::flash::fake::FakeHw::new();
	let driver = unsafe { h1b::hil::flash::FlashImpl::new(&alarm, &hw) };
	driver.set_client(&client);
	require!(driver.write(1300, &[0xFFFFABCD]) == kernel::ReturnCode::SUCCESS);
	require!(alarm.get_alarm() == alarm.now() + WRITE_WORD_TIME);
	require!(client.state() == None);
	require!(hw.is_programming() == true);

	for _ in 1..8 {
		// Indicate an error, let the driver retry.
		alarm.set_time(100 * WRITE_WORD_TIME);
		hw.inject_result(0b100);
		driver.fired();
		require!(alarm.get_alarm() == alarm.now() + WRITE_WORD_TIME);
		require!(hw.is_programming() == true);
		require!(client.state() == None);
	}

	// Last try.
	hw.inject_result(0b100);
	driver.fired();
	require!(alarm.get_alarm() == 0);
	require!(hw.is_programming() == false);
	require!(client.state() == Some(MockClientState::WriteDone(kernel::ReturnCode::FAIL)));

	true
}
<commit_msg>Compiling, but failing on RO image load.<commit_after>// Copyright 2019 Google LLC
//
// 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
//
//     https://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 h1b::hil::flash::{Flash,Hardware};
use kernel::hil::time::Alarm;
use kernel::ReturnCode;
use test::require;

// These are in counts of a 16 MHz clock.
#[cfg(test)]
const ERASE_TIME: u32 = 53653;
#[cfg(test)]
const WRITE_WORD_TIME: u32 = 840;

static mut WRITE_BUF: [u32; 1] = [0; 1];

#[cfg(test)]
#[derive(Clone,Copy,PartialEq)]
enum MockClientState {
        EraseDone(kernel::ReturnCode),
        WriteDone(kernel::ReturnCode),
}

#[cfg(test)]
struct MockClient {
    state: core::cell::Cell<Option<MockClientState>>,
}

#[cfg(test)]
impl<'a> MockClient {
        pub fn new() -> Self {
                MockClient { state: core::cell::Cell::new(None) }
        }

        pub fn state(&self) -> Option<MockClientState> { self.state.get() }
}

#[cfg(test)]
impl<'a> h1b::hil::flash::Client<'a> for MockClient {
        fn erase_done(&self, code: kernel::ReturnCode) {
                self.state.set(Some(MockClientState::EraseDone(code)));
        }

        fn write_done(&self, _data: &'a mut [u32], code: kernel::ReturnCode) {
                self.state.set(Some(MockClientState::WriteDone(code)));
        }
}

#[test]
fn erase() -> bool {
        use kernel::hil::time::Client;
        let alarm = crate::hil::flash::mock_alarm::MockAlarm::new();
        let client = MockClient::new();
        let hw = h1b::hil::flash::fake::FakeHw::new();

        // First attempt.
        let driver = unsafe { h1b::hil::flash::FlashImpl::new(&alarm, &hw) };
        driver.set_client(&client);
        require!(driver.erase(2) == kernel::ReturnCode::SUCCESS);
        require!(alarm.get_alarm() == alarm.now() + ERASE_TIME);
        require!(client.state() == None);
        require!(hw.is_programming() == true);

        // Indicate an error, let the driver retry.
        alarm.set_time(ERASE_TIME);
        hw.inject_result(0b100);
        driver.fired();
        require!(alarm.get_alarm() == 2 * ERASE_TIME);
        require!(hw.is_programming() == true);
        require!(client.state() == None);

        // Let the operation finish successfully.
        alarm.set_time(2 * ERASE_TIME);
        hw.finish_operation();
        driver.fired();
        require!(alarm.get_alarm() == 0);
        require!(hw.is_programming() == false);
        require!(hw.read(1300) == ReturnCode::SuccessWithValue { value: 0xFFFFFFFF });
        require!(client.state() == Some(MockClientState::EraseDone(kernel::ReturnCode::SUCCESS)));
        require!(driver.read(1300) == ReturnCode::SuccessWithValue { value: 0xFFFFFFFF });

        true
}

#[test]
fn erase_max_retries() -> bool {
        use kernel::hil::time::Client;
        let alarm = crate::hil::flash::mock_alarm::MockAlarm::new();
        let client = MockClient::new();
        let hw = h1b::hil::flash::fake::FakeHw::new();
        let driver = unsafe { h1b::hil::flash::FlashImpl::new(&alarm, &hw) };
        driver.set_client(&client);
        require!(driver.erase(2) == kernel::ReturnCode::SUCCESS);
        require!(alarm.get_alarm() == alarm.now() + ERASE_TIME);
        require!(client.state() == None);
        require!(hw.is_programming() == true);

        for i in 1..45 {
                // Indicate an error, let the driver retry.
                alarm.set_time(ERASE_TIME * i);
                hw.inject_result(0b100);
                driver.fired();
                require!(alarm.get_alarm() == alarm.now() + ERASE_TIME);
                require!(hw.is_programming() == true);
                require!(client.state() == None);
        }

        // Last try.
        hw.inject_result(0b100);
        driver.fired();
        require!(alarm.get_alarm() == 0);
        require!(hw.is_programming() == false);
        require!(client.state() == Some(MockClientState::EraseDone(kernel::ReturnCode::FAIL)));
        require!(hw.read(1300) == ReturnCode::SuccessWithValue { value: 0xFFFFFFFF });
        require!(driver.read(1300) == ReturnCode::SuccessWithValue { value: 0xFFFFFFFF });

        true
}

#[test]
fn write_then_erase() -> bool {
        use kernel::hil::time::Client;
        let alarm = crate::hil::flash::mock_alarm::MockAlarm::new();
        let client = MockClient::new();
        let hw = h1b::hil::flash::fake::FakeHw::new();

        // Write
        let driver = unsafe { h1b::hil::flash::FlashImpl::new(&alarm, &hw) };
        driver.set_client(&client);

        unsafe {
            WRITE_BUF[0] = 0xFFFFABCD;
            require!(driver.write(1300, &mut WRITE_BUF) == (kernel::ReturnCode::SUCCESS, None));
        }
        require!(alarm.get_alarm() == alarm.now() + WRITE_WORD_TIME);
        require!(client.state() == None);
        require!(hw.is_programming() == true);
        alarm.set_time(WRITE_WORD_TIME);
        hw.inject_result(0);
        driver.fired();
        require!(hw.is_programming() == true);
        require!(client.state() == None);
        hw.finish_operation();
        driver.fired();
        require!(alarm.get_alarm() == 0);
        require!(hw.is_programming() == false);
        require!(hw.read(1300) == ReturnCode::SuccessWithValue { value: 0xFFFFABCD });
        require!(client.state() == Some(MockClientState::WriteDone(kernel::ReturnCode::SUCCESS)));
        require!(driver.read(1300) == ReturnCode::SuccessWithValue { value: 0xFFFFABCD });

        // Erase
        let driver = unsafe { h1b::hil::flash::FlashImpl::new(&alarm, &hw) };
        driver.set_client(&client);
        require!(driver.erase(2) == kernel::ReturnCode::SUCCESS);
        require!(alarm.get_alarm() == alarm.now() + ERASE_TIME);
        require!(hw.is_programming() == true);
        alarm.set_time(WRITE_WORD_TIME + ERASE_TIME);
        hw.finish_operation();
        driver.fired();
        require!(alarm.get_alarm() == 0);
        require!(hw.is_programming() == false);
        require!(hw.read(1300) == ReturnCode::SuccessWithValue { value: 0xFFFFFFFF });
        require!(client.state() == Some(MockClientState::EraseDone(kernel::ReturnCode::SUCCESS)));
        require!(driver.read(1300) == ReturnCode::SuccessWithValue { value: 0xFFFFFFFF });

        true
}

#[test]
fn successful_program() -> bool {
        use kernel::hil::time::Client;
        let alarm = crate::hil::flash::mock_alarm::MockAlarm::new();
        let client = MockClient::new();
        let hw = h1b::hil::flash::fake::FakeHw::new();

        // First attempt.
        let driver = unsafe { h1b::hil::flash::FlashImpl::new(&alarm, &hw) };
        driver.set_client(&client);

        unsafe {
            WRITE_BUF[0] = 0xFFFFABCD;
            require!(driver.write(1300, &mut WRITE_BUF) == (kernel::ReturnCode::SUCCESS, None));
        }
        require!(alarm.get_alarm() == alarm.now() + WRITE_WORD_TIME);
        require!(client.state() == None);
        require!(hw.is_programming() == true);

        // Indicate an error, let the driver retry.
        alarm.set_time(WRITE_WORD_TIME);
        hw.inject_result(0b100);
        driver.fired();
        require!(alarm.get_alarm() == 2 * WRITE_WORD_TIME);
        require!(hw.is_programming() == true);
        require!(client.state() == None);

        // Let the operation finish successfully (including the final pulse).
        alarm.set_time(2 * WRITE_WORD_TIME);
        hw.inject_result(0);
        driver.fired();
        require!(alarm.get_alarm() == 3 * WRITE_WORD_TIME);
        require!(hw.is_programming() == true);
        require!(client.state() == None);
        hw.finish_operation();
        driver.fired();
        require!(alarm.get_alarm() == 0);
        require!(hw.is_programming() == false);
        require!(client.state() == Some(MockClientState::WriteDone(kernel::ReturnCode::SUCCESS)));
        require!(driver.read(1300) == ReturnCode::SuccessWithValue { value: 0xFFFFABCD });

        true
}

#[test]
fn timeout() -> bool {
        use kernel::hil::time::Client;
        let alarm = crate::hil::flash::mock_alarm::MockAlarm::new();
        let client = MockClient::new();
        let hw = h1b::hil::flash::fake::FakeHw::new();
        hw.set_transaction(1300, 1);
        hw.set_write_data(&[0xFFFF0FFF]);

        // First attempt.
        let driver = unsafe { h1b::hil::flash::FlashImpl::new(&alarm, &hw) };
        driver.set_client(&client);

        unsafe {
            WRITE_BUF[0] = 0xFFFFABCD;
            require!(driver.write(1300, &mut WRITE_BUF) == (kernel::ReturnCode::SUCCESS, None));
        }
        require!(alarm.get_alarm() == alarm.now() + WRITE_WORD_TIME);
        require!(client.state() == None);
        require!(hw.is_programming() == true);

        // Indicate a timeout.
        alarm.set_time(WRITE_WORD_TIME);
        driver.fired();
        require!(alarm.get_alarm() == 0);
        require!(client.state() == Some(MockClientState::WriteDone(kernel::ReturnCode::FAIL)));

        true
}

#[test]
fn write_max_retries() -> bool {
        use kernel::hil::time::Client;
        let alarm = crate::hil::flash::mock_alarm::MockAlarm::new();
        let client = MockClient::new();
        let hw = h1b::hil::flash::fake::FakeHw::new();
        let driver = unsafe { h1b::hil::flash::FlashImpl::new(&alarm, &hw) };
        driver.set_client(&client);

        unsafe {
            WRITE_BUF[0] = 0xFFFFABCD;
            require!(driver.write(1300, &mut WRITE_BUF) == (kernel::ReturnCode::SUCCESS, None));
        }
        require!(alarm.get_alarm() == alarm.now() + WRITE_WORD_TIME);
        require!(client.state() == None);
        require!(hw.is_programming() == true);

        for _ in 1..8 {
                // Indicate an error, let the driver retry.
                alarm.set_time(100 * WRITE_WORD_TIME);
                hw.inject_result(0b100);
                driver.fired();
                require!(alarm.get_alarm() == alarm.now() + WRITE_WORD_TIME);
                require!(hw.is_programming() == true);
                require!(client.state() == None);
        }

        // Last try.
        hw.inject_result(0b100);
        driver.fired();
        require!(alarm.get_alarm() == 0);
        require!(hw.is_programming() == false);
        require!(client.state() == Some(MockClientState::WriteDone(kernel::ReturnCode::FAIL)));

        true
}
<|endoftext|>