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

use std::str;
use std::num::ParseIntError;

use params::{Map as ParamsMap, Params, Value};
use router::Router;
use iron::prelude::*;
use bodyparser;
use exonum::api::{Api, ApiError};
use exonum::crypto::{CryptoHash, PublicKey, SecretKey, Hash};
use exonum::blockchain::{Blockchain, StoredConfiguration, Schema};
use exonum::storage::StorageValue;
use exonum::node::TransactionSend;
use exonum::encoding::serialize::FromHex;
use exonum::encoding::serialize::json::reexport as serde_json;
use exonum::helpers::Height;

use super::{StorageValueConfigProposeData, TxConfigPropose, TxConfigVote, ConfigurationSchema};

pub type ApiResponseVotesInfo = Option<Vec<Option<TxConfigVote>>>;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ApiResponseConfigHashInfo {
    pub hash: Hash,
    pub config: StoredConfiguration,
    pub propose: Option<Hash>,
    pub votes: ApiResponseVotesInfo,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ApiResponseProposeHashInfo {
    pub hash: Hash,
    pub propose_data: StorageValueConfigProposeData,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ApiResponseConfigInfo {
    pub committed_config: Option<StoredConfiguration>,
    pub propose: Option<StorageValueConfigProposeData>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ApiResponseProposePost {
    pub tx_hash: Hash,
    pub cfg_hash: Hash,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ApiResponseVotePost {
    pub tx_hash: Hash,
}

#[derive(Clone)]
pub struct PrivateConfigApi<T: TransactionSend + Clone> {
    pub channel: T,
    pub config: (PublicKey, SecretKey),
}

#[derive(Clone)]
pub struct PublicConfigApi {
    pub blockchain: Blockchain,
}

impl PublicConfigApi {
    fn get_config_with_proofs(&self, config: StoredConfiguration) -> ApiResponseConfigHashInfo {
        let propose = ConfigurationSchema::new(&self.blockchain.snapshot()).
            get_propose(&config.hash())
            .map(|p| p.hash());
        let votes = self.get_votes_for_propose_impl(&config.hash());
        ApiResponseConfigHashInfo {
            hash: config.hash(),
            config,
            propose,
            votes,
        }
    }

    fn get_actual_config(&self) -> Result<ApiResponseConfigHashInfo, ApiError> {
        let snapshot = self.blockchain.snapshot();
        let configuration_schema = Schema::new(&snapshot);
        let actual_cfg = configuration_schema.actual_configuration();
        Ok(self.get_config_with_proofs(actual_cfg))
    }

    fn get_following_config(&self) -> Result<Option<ApiResponseConfigHashInfo>, ApiError> {
        let following_cfg = match Schema::new(&self.blockchain.snapshot()).following_configuration() {
            Some(val) => val,
            None => {
                return Ok(None);
            }
        };
        Ok(Some(self.get_config_with_proofs(following_cfg)))
    }

    fn get_config_by_hash(&self, hash: &Hash) -> Result<ApiResponseConfigInfo, ApiError> {
        let snapshot = self.blockchain.snapshot();
        let general_schema = Schema::new(&snapshot);
        let committed_config = general_schema.configs().get(hash);

        let configuration_schema = ConfigurationSchema::new(&snapshot);
        let propose = configuration_schema.propose_data_by_config_hash().get(hash);
        let res = ApiResponseConfigInfo {
            committed_config,
            propose,
        };
        Ok(res)
    }

    fn get_votes_for_propose_impl(&self, config_hash: &Hash) -> ApiResponseVotesInfo {
        let snapshot = self.blockchain.snapshot();
        let configuration_schema = ConfigurationSchema::new(&snapshot);
        configuration_schema.propose_data_by_config_hash().get(config_hash)
            .map(|_| configuration_schema.get_votes(config_hash))
    }

    fn get_votes_for_propose(&self, config_hash: &Hash) -> Result<ApiResponseVotesInfo, ApiError> {
        Ok(self.get_votes_for_propose_impl(config_hash))
    }

    fn filter_cfg_predicate(
        cfg: &StoredConfiguration,
        previous_cfg_hash_filter: Option<Hash>,
        actual_from_filter: Option<Height>,
    ) -> bool {
        if let Some(prev_ref) = previous_cfg_hash_filter {
            if cfg.previous_cfg_hash != prev_ref {
                return false;
            }
        }
        if let Some(from_height) = actual_from_filter {
            if cfg.actual_from < from_height {
                return false;
            }
        }
        true
    }

    fn get_all_proposes(
        &self,
        previous_cfg_hash_filter: Option<Hash>,
        actual_from_filter: Option<Height>,
    ) -> Result<Vec<ApiResponseProposeHashInfo>, ApiError> {
        let snapshot = self.blockchain.snapshot();
        let configuration_schema = ConfigurationSchema::new(&snapshot);
        let index = configuration_schema.config_hash_by_ordinal();
        let proposes = {
            index
                .into_iter()
                .map(|cfg_hash| {
                    let propose_data = configuration_schema
                        .propose_data_by_config_hash()
                        .get(&cfg_hash)
                        .expect(&format!(
                            "Not found propose for following cfg_hash: {:?}",
                            cfg_hash
                        ));

                    (cfg_hash, propose_data)
                })
                .filter(|&(_, ref propose_data)| {
                    let cfg = <StoredConfiguration as StorageValue>::from_bytes(
                        propose_data.tx_propose().cfg().as_bytes().into(),
                    );
                    PublicConfigApi::filter_cfg_predicate(
                        &cfg,
                        previous_cfg_hash_filter,
                        actual_from_filter,
                    )
                })
                .map(|(cfg_hash, propose_data)| {
                    ApiResponseProposeHashInfo {
                        hash: cfg_hash,
                        propose_data,
                    }
                })
                .collect::<Vec<_>>()
        };
        Ok(proposes)
    }

    fn get_all_committed(
        &self,
        previous_cfg_hash_filter: Option<Hash>,
        actual_from_filter: Option<Height>,
    ) -> Result<Vec<ApiResponseConfigHashInfo>, ApiError> {
        let snapshot = self.blockchain.snapshot();
        let general_schema = Schema::new(&snapshot);
        let index = general_schema.configs_actual_from();
        let committed_configs = {
            index
                .into_iter()
                .map(|reference| {
                    let config_hash = reference.cfg_hash();
                    general_schema.configs().get(config_hash).expect(&format!(
                        "Config with hash {:?} is absent in configs table",
                        config_hash
                    ))
                })
                .filter(|config| {
                    PublicConfigApi::filter_cfg_predicate(
                        config,
                        previous_cfg_hash_filter,
                        actual_from_filter,
                    )
                })
                .map(|config| {
                    self.get_config_with_proofs(config)
                })
                .collect::<Vec<_>>()
        };
        Ok(committed_configs)
    }

    fn retrieve_params(map: &ParamsMap) -> Result<(Option<Hash>, Option<Height>), ApiError> {
        let actual_from: Option<Height>;
        let previous_cfg_hash: Option<Hash>;
        previous_cfg_hash = match map.find(&["previous_cfg_hash"]) {
            Some(&Value::String(ref hash_string)) => {
                Some(Hash::from_hex(hash_string).map_err(ApiError::FromHex)?)
            }
            _ => None,
        };
        actual_from = match map.find(&["actual_from"]) {
            Some(&Value::String(ref from_str)) => {
                Some(from_str.parse().map(Height).map_err(|e: ParseIntError| {
                    ApiError::IncorrectRequest(Box::new(e))
                })?)
            }
            _ => None,
        };
        Ok((previous_cfg_hash, actual_from))
    }
}

impl<T> PrivateConfigApi<T>
where
    T: TransactionSend + Clone,
{
    fn put_config_propose(
        &self,
        cfg: StoredConfiguration,
    ) -> Result<ApiResponseProposePost, ApiError> {
        let cfg_hash = cfg.hash();
        let config_propose = TxConfigPropose::new(
            &self.config.0,
            str::from_utf8(cfg.into_bytes().as_slice()).unwrap(),
            &self.config.1,
        );
        let tx_hash = config_propose.hash();
        let ch = self.channel.clone();
        ch.send(Box::new(config_propose))?;
        let res = ApiResponseProposePost {
            tx_hash,
            cfg_hash,
        };
        Ok(res)
    }

    fn put_config_vote(&self, cfg_hash: &Hash) -> Result<ApiResponseVotePost, ApiError> {
        let config_vote = TxConfigVote::new(&self.config.0, cfg_hash, &self.config.1);
        let tx_hash = config_vote.hash();
        let ch = self.channel.clone();
        ch.send(Box::new(config_vote))?;
        let res = ApiResponseVotePost { tx_hash };
        Ok(res)
    }
}

impl Api for PublicConfigApi {
    fn wire(&self, router: &mut Router) {

        let self_ = self.clone();
        let config_actual = move |_: &mut Request| -> IronResult<Response> {
            let info = self_.get_actual_config()?;
            self_.ok_response(&serde_json::to_value(info).unwrap())
        };

        let self_ = self.clone();
        let config_following = move |_: &mut Request| -> IronResult<Response> {
            let info = self_.get_following_config()?;
            self_.ok_response(&serde_json::to_value(info).unwrap())
        };

        let self_ = self.clone();
        let config_by_hash = move |req: &mut Request| -> IronResult<Response> {
            let params = req.extensions.get::<Router>().unwrap();
            match params.find("hash") {
                Some(hash_str) => {
                    let hash = Hash::from_hex(hash_str).map_err(ApiError::from)?;
                    let info = self_.get_config_by_hash(&hash)?;
                    self_.ok_response(&serde_json::to_value(info).unwrap())
                }
                None => {
                    Err(ApiError::IncorrectRequest(
                        "Required route \
                                           parameter of configuration \
                                           'hash' is missing"
                            .into(),
                    ))?
                }
            }
        };

        let self_ = self.clone();
        let get_votes_for_propose = move |req: &mut Request| -> IronResult<Response> {
            let params = req.extensions.get::<Router>().unwrap();
            match params.find("hash") {
                Some(hash_str) => {
                    let propose_cfg_hash = Hash::from_hex(hash_str).map_err(ApiError::from)?;
                    let info = self_.get_votes_for_propose(&propose_cfg_hash)?;
                    self_.ok_response(&serde_json::to_value(info).unwrap())
                }
                None => {
                    Err(ApiError::IncorrectRequest(
                        "Required route \
                                           parameter of configuration \
                                           'hash' is missing"
                            .into(),
                    ))?
                }
            }
        };

        let self_ = self.clone();
        let get_all_proposes = move |req: &mut Request| -> IronResult<Response> {
            let map = req.get_ref::<Params>().unwrap();
            let (previous_cfg_hash, actual_from) = PublicConfigApi::retrieve_params(map)?;
            let info = self_.get_all_proposes(previous_cfg_hash, actual_from)?;
            self_.ok_response(&serde_json::to_value(info).unwrap())
        };

        let self_ = self.clone();
        let get_all_committed = move |req: &mut Request| -> IronResult<Response> {
            let map = req.get_ref::<Params>().unwrap();
            let (previous_cfg_hash, actual_from) = PublicConfigApi::retrieve_params(map)?;
            let info = self_.get_all_committed(previous_cfg_hash, actual_from)?;
            self_.ok_response(&serde_json::to_value(info).unwrap())
        };
        router.get("/v1/configs/actual", config_actual, "config_actual");
        router.get(
            "/v1/configs/following",
            config_following,
            "config_following",
        );
        router.get("/v1/configs/:hash", config_by_hash, "config_by_hash");
        router.get(
            "/v1/configs/:hash/votes",
            get_votes_for_propose,
            "get_votes_for_propose",
        );
        router.get("/v1/configs/proposed", get_all_proposes, "get_all_proposes");
        router.get(
            "/v1/configs/committed",
            get_all_committed,
            "get_all_committed",
        );

    }
}

impl<T> Api for PrivateConfigApi<T>
where
    T: 'static + TransactionSend + Clone,
{
    fn wire(&self, router: &mut Router) {
        let self_ = self.clone();
        let put_config_propose = move |req: &mut Request| -> IronResult<Response> {
            match req.get::<bodyparser::Struct<StoredConfiguration>>() {
                Ok(Some(cfg)) => {
                    let info = self_.put_config_propose(cfg)?;
                    self_.ok_response(&serde_json::to_value(info).unwrap())
                }
                Ok(None) => Err(ApiError::IncorrectRequest("Empty request body".into()))?,
                Err(e) => Err(ApiError::IncorrectRequest(Box::new(e)))?,
            }
        };

        let self_ = self.clone();
        let put_config_vote = move |req: &mut Request| -> IronResult<Response> {
            let params = req.extensions.get::<Router>().unwrap();
            match params.find("hash") {
                Some(hash_str) => {
                    let propose_cfg_hash = Hash::from_hex(hash_str).map_err(ApiError::from)?;
                    let info = self_.put_config_vote(&propose_cfg_hash)?;
                    self_.ok_response(&serde_json::to_value(info).unwrap())
                }
                None => {
                    Err(ApiError::IncorrectRequest(
                        "Required route \
                                           parameter of configuration \
                                           'hash' is missing"
                            .into(),
                    ))?
                }
            }
        };
        router.post(
            "/v1/configs/postpropose",
            put_config_propose,
            "put_config_propose",
        );
        router.post(
            "/v1/configs/:hash/postvote",
            put_config_vote,
            "put_config_vote",
        );
    }
}
<commit_msg>Remove useless results<commit_after>// Copyright 2017 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::str;
use std::num::ParseIntError;

use params::{Map as ParamsMap, Params, Value};
use router::Router;
use iron::prelude::*;
use bodyparser;
use exonum::api::{Api, ApiError};
use exonum::crypto::{CryptoHash, PublicKey, SecretKey, Hash};
use exonum::blockchain::{Blockchain, StoredConfiguration, Schema};
use exonum::storage::StorageValue;
use exonum::node::TransactionSend;
use exonum::encoding::serialize::FromHex;
use exonum::encoding::serialize::json::reexport as serde_json;
use exonum::helpers::Height;

use super::{StorageValueConfigProposeData, TxConfigPropose, TxConfigVote, ConfigurationSchema};

pub type ApiResponseVotesInfo = Option<Vec<Option<TxConfigVote>>>;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ApiResponseConfigHashInfo {
    pub hash: Hash,
    pub config: StoredConfiguration,
    pub propose: Option<Hash>,
    pub votes: ApiResponseVotesInfo,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ApiResponseProposeHashInfo {
    pub hash: Hash,
    pub propose_data: StorageValueConfigProposeData,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ApiResponseConfigInfo {
    pub committed_config: Option<StoredConfiguration>,
    pub propose: Option<StorageValueConfigProposeData>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ApiResponseProposePost {
    pub tx_hash: Hash,
    pub cfg_hash: Hash,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ApiResponseVotePost {
    pub tx_hash: Hash,
}

#[derive(Clone)]
pub struct PrivateConfigApi<T: TransactionSend + Clone> {
    pub channel: T,
    pub config: (PublicKey, SecretKey),
}

#[derive(Clone)]
pub struct PublicConfigApi {
    pub blockchain: Blockchain,
}

impl PublicConfigApi {
    fn get_config_with_proofs(&self, config: StoredConfiguration) -> ApiResponseConfigHashInfo {
        let propose = ConfigurationSchema::new(&self.blockchain.snapshot())
            .get_propose(&config.hash())
            .map(|p| p.hash());
        let votes = self.get_votes_for_propose(&config.hash());
        ApiResponseConfigHashInfo {
            hash: config.hash(),
            config,
            propose,
            votes,
        }
    }

    fn get_actual_config(&self) -> ApiResponseConfigHashInfo {
        let snapshot = self.blockchain.snapshot();
        let configuration_schema = Schema::new(&snapshot);
        let actual_cfg = configuration_schema.actual_configuration();
        self.get_config_with_proofs(actual_cfg)
    }

    fn get_following_config(&self) -> Option<ApiResponseConfigHashInfo> {
        Schema::new(&self.blockchain.snapshot())
            .following_configuration()
            .map(|following_cfg| self.get_config_with_proofs(following_cfg))
    }

    fn get_config_by_hash(&self, hash: &Hash) -> ApiResponseConfigInfo {
        let snapshot = self.blockchain.snapshot();
        let general_schema = Schema::new(&snapshot);
        let committed_config = general_schema.configs().get(hash);

        let configuration_schema = ConfigurationSchema::new(&snapshot);
        let propose = configuration_schema.propose_data_by_config_hash().get(hash);
        ApiResponseConfigInfo {
            committed_config,
            propose,
        }
    }

    fn get_votes_for_propose(&self, config_hash: &Hash) -> ApiResponseVotesInfo {
        let snapshot = self.blockchain.snapshot();
        let configuration_schema = ConfigurationSchema::new(&snapshot);
        configuration_schema
            .propose_data_by_config_hash()
            .get(config_hash)
            .map(|_| configuration_schema.get_votes(config_hash))
    }

    fn filter_cfg_predicate(
        cfg: &StoredConfiguration,
        previous_cfg_hash_filter: Option<Hash>,
        actual_from_filter: Option<Height>,
    ) -> bool {
        if let Some(prev_ref) = previous_cfg_hash_filter {
            if cfg.previous_cfg_hash != prev_ref {
                return false;
            }
        }
        if let Some(from_height) = actual_from_filter {
            if cfg.actual_from < from_height {
                return false;
            }
        }
        true
    }

    fn get_all_proposes(
        &self,
        previous_cfg_hash_filter: Option<Hash>,
        actual_from_filter: Option<Height>,
    ) -> Vec<ApiResponseProposeHashInfo> {
        let snapshot = self.blockchain.snapshot();
        let configuration_schema = ConfigurationSchema::new(&snapshot);
        let index = configuration_schema.config_hash_by_ordinal();
        let proposes = {
            index
                .into_iter()
                .map(|cfg_hash| {
                    let propose_data = configuration_schema
                        .propose_data_by_config_hash()
                        .get(&cfg_hash)
                        .expect(&format!(
                            "Not found propose for following cfg_hash: {:?}",
                            cfg_hash
                        ));

                    (cfg_hash, propose_data)
                })
                .filter(|&(_, ref propose_data)| {
                    let cfg = <StoredConfiguration as StorageValue>::from_bytes(
                        propose_data.tx_propose().cfg().as_bytes().into(),
                    );
                    PublicConfigApi::filter_cfg_predicate(
                        &cfg,
                        previous_cfg_hash_filter,
                        actual_from_filter,
                    )
                })
                .map(|(cfg_hash, propose_data)| {
                    ApiResponseProposeHashInfo {
                        hash: cfg_hash,
                        propose_data,
                    }
                })
                .collect::<Vec<_>>()
        };
        proposes
    }

    fn get_all_committed(
        &self,
        previous_cfg_hash_filter: Option<Hash>,
        actual_from_filter: Option<Height>,
    ) -> Vec<ApiResponseConfigHashInfo> {
        let snapshot = self.blockchain.snapshot();
        let general_schema = Schema::new(&snapshot);
        let index = general_schema.configs_actual_from();
        let committed_configs = {
            index
                .into_iter()
                .map(|reference| {
                    let config_hash = reference.cfg_hash();
                    general_schema.configs().get(config_hash).expect(&format!(
                        "Config with hash {:?} is absent in configs table",
                        config_hash
                    ))
                })
                .filter(|config| {
                    PublicConfigApi::filter_cfg_predicate(
                        config,
                        previous_cfg_hash_filter,
                        actual_from_filter,
                    )
                })
                .map(|config| self.get_config_with_proofs(config))
                .collect::<Vec<_>>()
        };
        committed_configs
    }

    fn retrieve_params(map: &ParamsMap) -> Result<(Option<Hash>, Option<Height>), ApiError> {
        let actual_from: Option<Height>;
        let previous_cfg_hash: Option<Hash>;
        previous_cfg_hash = match map.find(&["previous_cfg_hash"]) {
            Some(&Value::String(ref hash_string)) => {
                Some(Hash::from_hex(hash_string).map_err(ApiError::FromHex)?)
            }
            _ => None,
        };
        actual_from = match map.find(&["actual_from"]) {
            Some(&Value::String(ref from_str)) => {
                Some(from_str.parse().map(Height).map_err(|e: ParseIntError| {
                    ApiError::IncorrectRequest(Box::new(e))
                })?)
            }
            _ => None,
        };
        Ok((previous_cfg_hash, actual_from))
    }
}

impl<T> PrivateConfigApi<T>
where
    T: TransactionSend + Clone,
{
    fn put_config_propose(
        &self,
        cfg: StoredConfiguration,
    ) -> Result<ApiResponseProposePost, ApiError> {
        let cfg_hash = cfg.hash();
        let config_propose = TxConfigPropose::new(
            &self.config.0,
            str::from_utf8(cfg.into_bytes().as_slice()).unwrap(),
            &self.config.1,
        );
        let tx_hash = config_propose.hash();
        let ch = self.channel.clone();
        ch.send(Box::new(config_propose))?;
        let res = ApiResponseProposePost { tx_hash, cfg_hash };
        Ok(res)
    }

    fn put_config_vote(&self, cfg_hash: &Hash) -> Result<ApiResponseVotePost, ApiError> {
        let config_vote = TxConfigVote::new(&self.config.0, cfg_hash, &self.config.1);
        let tx_hash = config_vote.hash();
        let ch = self.channel.clone();
        ch.send(Box::new(config_vote))?;
        let res = ApiResponseVotePost { tx_hash };
        Ok(res)
    }
}

impl Api for PublicConfigApi {
    fn wire(&self, router: &mut Router) {

        let self_ = self.clone();
        let config_actual = move |_: &mut Request| -> IronResult<Response> {
            let info = self_.get_actual_config();
            self_.ok_response(&serde_json::to_value(info).unwrap())
        };

        let self_ = self.clone();
        let config_following = move |_: &mut Request| -> IronResult<Response> {
            let info = self_.get_following_config();
            self_.ok_response(&serde_json::to_value(info).unwrap())
        };

        let self_ = self.clone();
        let config_by_hash = move |req: &mut Request| -> IronResult<Response> {
            let params = req.extensions.get::<Router>().unwrap();
            match params.find("hash") {
                Some(hash_str) => {
                    let hash = Hash::from_hex(hash_str).map_err(ApiError::from)?;
                    let info = self_.get_config_by_hash(&hash);
                    self_.ok_response(&serde_json::to_value(info).unwrap())
                }
                None => {
                    Err(ApiError::IncorrectRequest(
                        "Required route \
                                           parameter of configuration \
                                           'hash' is missing"
                            .into(),
                    ))?
                }
            }
        };

        let self_ = self.clone();
        let get_votes_for_propose = move |req: &mut Request| -> IronResult<Response> {
            let params = req.extensions.get::<Router>().unwrap();
            match params.find("hash") {
                Some(hash_str) => {
                    let propose_cfg_hash = Hash::from_hex(hash_str).map_err(ApiError::from)?;
                    let info = self_.get_votes_for_propose(&propose_cfg_hash);
                    self_.ok_response(&serde_json::to_value(info).unwrap())
                }
                None => {
                    Err(ApiError::IncorrectRequest(
                        "Required route \
                                           parameter of configuration \
                                           'hash' is missing"
                            .into(),
                    ))?
                }
            }
        };

        let self_ = self.clone();
        let get_all_proposes = move |req: &mut Request| -> IronResult<Response> {
            let map = req.get_ref::<Params>().unwrap();
            let (previous_cfg_hash, actual_from) = PublicConfigApi::retrieve_params(map)?;
            let info = self_.get_all_proposes(previous_cfg_hash, actual_from);
            self_.ok_response(&serde_json::to_value(info).unwrap())
        };

        let self_ = self.clone();
        let get_all_committed = move |req: &mut Request| -> IronResult<Response> {
            let map = req.get_ref::<Params>().unwrap();
            let (previous_cfg_hash, actual_from) = PublicConfigApi::retrieve_params(map)?;
            let info = self_.get_all_committed(previous_cfg_hash, actual_from);
            self_.ok_response(&serde_json::to_value(info).unwrap())
        };
        router.get("/v1/configs/actual", config_actual, "config_actual");
        router.get(
            "/v1/configs/following",
            config_following,
            "config_following",
        );
        router.get("/v1/configs/:hash", config_by_hash, "config_by_hash");
        router.get(
            "/v1/configs/:hash/votes",
            get_votes_for_propose,
            "get_votes_for_propose",
        );
        router.get("/v1/configs/proposed", get_all_proposes, "get_all_proposes");
        router.get(
            "/v1/configs/committed",
            get_all_committed,
            "get_all_committed",
        );

    }
}

impl<T> Api for PrivateConfigApi<T>
where
    T: 'static + TransactionSend + Clone,
{
    fn wire(&self, router: &mut Router) {
        let self_ = self.clone();
        let put_config_propose = move |req: &mut Request| -> IronResult<Response> {
            match req.get::<bodyparser::Struct<StoredConfiguration>>() {
                Ok(Some(cfg)) => {
                    let info = self_.put_config_propose(cfg)?;
                    self_.ok_response(&serde_json::to_value(info).unwrap())
                }
                Ok(None) => Err(ApiError::IncorrectRequest("Empty request body".into()))?,
                Err(e) => Err(ApiError::IncorrectRequest(Box::new(e)))?,
            }
        };

        let self_ = self.clone();
        let put_config_vote = move |req: &mut Request| -> IronResult<Response> {
            let params = req.extensions.get::<Router>().unwrap();
            match params.find("hash") {
                Some(hash_str) => {
                    let propose_cfg_hash = Hash::from_hex(hash_str).map_err(ApiError::from)?;
                    let info = self_.put_config_vote(&propose_cfg_hash)?;
                    self_.ok_response(&serde_json::to_value(info).unwrap())
                }
                None => {
                    Err(ApiError::IncorrectRequest(
                        "Required route \
                                           parameter of configuration \
                                           'hash' is missing"
                            .into(),
                    ))?
                }
            }
        };
        router.post(
            "/v1/configs/postpropose",
            put_config_propose,
            "put_config_propose",
        );
        router.post(
            "/v1/configs/:hash/postvote",
            put_config_vote,
            "put_config_vote",
        );
    }
}
<|endoftext|>


<commit_before>use math3d::matrix::Matrix4x4;
use num::Num;

#[derive(Eq, PartialEq, Clone, Hash, Debug, Copy)]
pub struct Frustum<N: Num> {
	pub projection_matrix:Matrix4x4<N>
}

impl<N: Num> Frustum<N> {
	pub fn new() -> Frustum<N> {
		Frustum {
			projection_matrix:Matrix4x4::<N>::identity()
		}
	}
	pub fn set_perspective(&mut self, fov:N, aspect_ratio:N, near:N, far:N){

	}
	pub fn set_orthographic(&mut self){

	}
}

/*

void Frustum::setOrthographic(GLfloat xmin, GLfloat xmax, GLfloat ymin, GLfloat ymax, GLfloat zmin, GLfloat zmax){
    makeOrthographicMatrix(projectionMatrix, xmin, xmax, ymin, ymax, zmin, zmax);
    projectionMatrix[15] = 1.0f;

    // values for untransformed frustum corners
    nearUL[0] = xmin; nearUL[1] = ymax; nearUL[2] = zmin; nearUL[3] = 1.0f;
    nearLL[0] = xmin; nearLL[1] = ymin; nearLL[2] = zmin; nearLL[3] = 1.0f;
    nearUR[0] = xmax; nearUR[1] = ymax; nearUR[2] = zmin; nearUR[3] = 1.0f;
    nearLR[0] = xmax; nearLR[1] = ymin; nearLR[2] = zmin; nearLR[3] = 1.0f;
    farUL[0] = xmin; farUL[1] = ymax; farUL[2] = zmax; farUL[3] = 1.0f;
    farLL[0] = xmin; farLL[1] = ymin; farLL[2] = zmax; farLL[3] = 1.0f;
    farUR[0] = xmax; farUR[1] = ymax; farUR[2] = zmax; farUR[3] = 1.0f;
    farLR[0] = xmax; farLR[1] = ymin; farLR[2] = zmax; farLR[3] = 1.0f;
}

void Frustum::setPerspective(GLfloat fov, GLfloat aspect, GLfloat near, GLfloat far){
    float nxmin, nxmax, nymin, nymax; // dimensions of near clipping plane;
    float fxmin, fxmax, fymin, fymax; // dimensions of far clipping plane;

    // near clipping plane
    nymax = near * float(tan(fov * PI / 360.0));
    nymin = -nymax;
    nxmin = nymin * aspect;
    nxmax = -nxmin;

    // construct projection matrix
    loadIdentity44(projectionMatrix);
    projectionMatrix[0] = (2.0f * near)/(nxmax - nxmin);
    projectionMatrix[5] = (2.0f * near)/(nymax - nymin);
    projectionMatrix[8] = (nxmax + nxmin) / (nxmax - nxmin);
    projectionMatrix[9] = (nymax + nymin) / (nymax - nymin);
    projectionMatrix[10] = -((far + near) / (far - near));
    projectionMatrix[11] = -1.0f;
    projectionMatrix[14] = -((2.0f * far * near)/(far - near));
    projectionMatrix[15] = 0.0f;

    // do the math for the far clipping plane
    fymax = far * float(tan(fov * PI / 360.0));
    fymin = -fymax;
    fxmin = fymin * aspect;
    fxmax = -fxmin;

    // values for transformed frustum corners
    nearUL[0] = nxmin; nearUL[1] = nymax; nearUL[2] = -near; nearUL[3] = 1.0f;
    nearLL[0] = nxmin; nearLL[1] = nymin; nearLL[2] = -near; nearLL[3] = 1.0f;
    nearUR[0] = nxmax; nearUR[1] = nymax; nearUR[2] = -near; nearUR[3] = 1.0f;
    nearLR[0] = nxmax; nearLR[1] = nymin; nearLR[2] = -near; nearLR[3] = 1.0f;
    farUL[0] = fxmin; farUL[1] = fymax; farUL[2] = -far; farUL[3] = 1.0f;
    farLL[0] = fxmin; farLL[1] = fymin; farLL[2] = -far; farLL[3] = 1.0f;
    farUR[0] = fxmax; farUR[1] = fymax; farUR[2] = -far; farUR[3] = 1.0f;
    farLR[0] = fxmax; farLR[1] = fymin; farLR[2] = -far; farLR[3] = 1.0f;
}
*/<commit_msg>Generate perspective projection matrix<commit_after>use math3d::matrix::Matrix4x4;
use num::{Float,NumCast,cast,Zero,One};
use std::f32;

#[derive(Eq, PartialEq, Clone, Hash, Debug, Copy)]
pub struct Frustum<N: Float> {
	pub projection_matrix:Matrix4x4<N>
}

impl<N: Float+NumCast+Zero+One> Frustum<N> {
	pub fn new() -> Frustum<N> {
		Frustum {
			projection_matrix:Matrix4x4::<N>::identity()
		}
	}
	pub fn set_perspective(&mut self, fov:N, aspect_ratio:N, near:N, far:N){
        // calculate the near clipping plane
        // TODO: this number casting might not be ideal, maybe using traits on the numbers is more ideal
		let nymax = near * (fov * cast(f32::consts::PI).unwrap() / cast(360).unwrap()).tan();
        let nymin = -nymax;
        let nxmin = nymin * aspect_ratio;
        let nxmax = -nxmin;

        // update the projection matrix (creating new one right now)
        let zero = N::zero();
        let one = N::one();
        let two = one + one;
        self.projection_matrix = Matrix4x4::<N>::identity();
        self.projection_matrix.m11 = (two * near) / (nxmax - nxmin);
        self.projection_matrix.m22 = (two * near) / (nymax - nymin);
        self.projection_matrix.m31 = (nxmax + nxmin) / (nxmax - nxmin);
        self.projection_matrix.m32 = (nymax + nymin) / (nymax - nymin);
        self.projection_matrix.m33 = -((far + near) / (far - near));
        self.projection_matrix.m34 = -one;
        self.projection_matrix.m43 = -((two * far * near) / (far - near));
        self.projection_matrix.m44 = zero;
	}
	pub fn set_orthographic(&mut self){

	}
}

/*

void Frustum::setPerspective(GLfloat fov, GLfloat aspect, GLfloat near, GLfloat far){
    float nxmin, nxmax, nymin, nymax; // dimensions of near clipping plane;
    float fxmin, fxmax, fymin, fymax; // dimensions of far clipping plane;

    // near clipping plane
    nymax = near * float(tan(fov * PI / 360.0));
    nymin = -nymax;
    nxmin = nymin * aspect;
    nxmax = -nxmin;

    // construct projection matrix
    loadIdentity44(projectionMatrix);
    projectionMatrix[0] = (2.0f * near)/(nxmax - nxmin);
    projectionMatrix[5] = (2.0f * near)/(nymax - nymin);
    projectionMatrix[8] = (nxmax + nxmin) / (nxmax - nxmin);
    projectionMatrix[9] = (nymax + nymin) / (nymax - nymin);
    projectionMatrix[10] = -((far + near) / (far - near));
    projectionMatrix[11] = -1.0f;
    projectionMatrix[14] = -((2.0f * far * near)/(far - near));
    projectionMatrix[15] = 0.0f;

    // do the math for the far clipping plane
    fymax = far * float(tan(fov * PI / 360.0));
    fymin = -fymax;
    fxmin = fymin * aspect;
    fxmax = -fxmin;

    // values for transformed frustum corners
    nearUL[0] = nxmin; nearUL[1] = nymax; nearUL[2] = -near; nearUL[3] = 1.0f;
    nearLL[0] = nxmin; nearLL[1] = nymin; nearLL[2] = -near; nearLL[3] = 1.0f;
    nearUR[0] = nxmax; nearUR[1] = nymax; nearUR[2] = -near; nearUR[3] = 1.0f;
    nearLR[0] = nxmax; nearLR[1] = nymin; nearLR[2] = -near; nearLR[3] = 1.0f;
    farUL[0] = fxmin; farUL[1] = fymax; farUL[2] = -far; farUL[3] = 1.0f;
    farLL[0] = fxmin; farLL[1] = fymin; farLL[2] = -far; farLL[3] = 1.0f;
    farUR[0] = fxmax; farUR[1] = fymax; farUR[2] = -far; farUR[3] = 1.0f;
    farLR[0] = fxmax; farLR[1] = fymin; farLR[2] = -far; farLR[3] = 1.0f;
}

void Frustum::setOrthographic(GLfloat xmin, GLfloat xmax, GLfloat ymin, GLfloat ymax, GLfloat zmin, GLfloat zmax){
    makeOrthographicMatrix(projectionMatrix, xmin, xmax, ymin, ymax, zmin, zmax);
    projectionMatrix[15] = 1.0f;

    // values for untransformed frustum corners
    nearUL[0] = xmin; nearUL[1] = ymax; nearUL[2] = zmin; nearUL[3] = 1.0f;
    nearLL[0] = xmin; nearLL[1] = ymin; nearLL[2] = zmin; nearLL[3] = 1.0f;
    nearUR[0] = xmax; nearUR[1] = ymax; nearUR[2] = zmin; nearUR[3] = 1.0f;
    nearLR[0] = xmax; nearLR[1] = ymin; nearLR[2] = zmin; nearLR[3] = 1.0f;
    farUL[0] = xmin; farUL[1] = ymax; farUL[2] = zmax; farUL[3] = 1.0f;
    farLL[0] = xmin; farLL[1] = ymin; farLL[2] = zmax; farLL[3] = 1.0f;
    farUR[0] = xmax; farUR[1] = ymax; farUR[2] = zmax; farUR[3] = 1.0f;
    farLR[0] = xmax; farLR[1] = ymin; farLR[2] = zmax; farLR[3] = 1.0f;
}

*/<|endoftext|>


<commit_before>use std::{env, marker::PhantomData, path, path::PathBuf};

use super::Module;
use crate::{terminal::Color, Segment, R};

#[cfg(not(feature = "libgit"))]
mod process;
#[cfg(not(feature = "libgit"))]
use process as internal;

#[cfg(feature = "libgit")]
mod libgit;
#[cfg(feature = "libgit")]
use libgit as internal;

pub struct Git<S> {
	scheme: PhantomData<S>,
}

#[derive(Clone)]
pub struct GitStats {
	pub untracked: u32,
	pub conflicted: u32,
	pub non_staged: u32,
	pub ahead: u32,
	pub behind: u32,
	pub staged: u32,
	pub branch_name: String,
}

pub trait GitScheme {
	const GIT_AHEAD_BG: Color;
	const GIT_AHEAD_FG: Color;
	const GIT_BEHIND_BG: Color;
	const GIT_BEHIND_FG: Color;
	const GIT_STAGED_BG: Color;
	const GIT_STAGED_FG: Color;
	const GIT_NOTSTAGED_BG: Color;
	const GIT_NOTSTAGED_FG: Color;
	const GIT_UNTRACKED_BG: Color;
	const GIT_UNTRACKED_FG: Color;
	const GIT_CONFLICTED_BG: Color;
	const GIT_CONFLICTED_FG: Color;
	const GIT_REPO_CLEAN_BG: Color;
	const GIT_REPO_CLEAN_FG: Color;
	const GIT_REPO_DIRTY_BG: Color;
	const GIT_REPO_DIRTY_FG: Color;
}

impl<S: GitScheme> Git<S> {
	pub fn new() -> Git<S> {
		Git { scheme: PhantomData }
	}

	pub fn get_git_data(&mut self, path: PathBuf) -> R<GitStats> {
		internal::run_git(&path)
	}
}

impl GitStats {
	pub fn is_dirty(&self) -> bool {
		(self.untracked + self.conflicted + self.staged + self.non_staged) > 0
	}
}

fn find_git_dir() -> Option<path::PathBuf> {
	let mut git_dir = env::current_dir().unwrap();
	loop {
		git_dir.push(".git/");

		if git_dir.exists() {
			git_dir.pop();
			return Some(git_dir);
		}
		git_dir.pop();

		if !git_dir.pop() {
			return None;
		}
	}
}

impl<S: GitScheme> Module for Git<S> {
	fn append_segments(&mut self, segments: &mut Vec<Segment>) -> R<()> {
		let git_dir = match find_git_dir() {
			Some(dir) => dir,
			_ => return Ok(()),
		};

		let stats = self.get_git_data(git_dir)?;

		let (branch_fg, branch_bg) = if stats.is_dirty() {
			(S::GIT_REPO_DIRTY_FG, S::GIT_REPO_DIRTY_BG)
		} else {
			(S::GIT_REPO_CLEAN_FG, S::GIT_REPO_CLEAN_BG)
		};

		segments.push(Segment::simple(format!(" {} ", stats.branch_name), branch_fg, branch_bg));

		let mut add_elem = |count, symbol, fg, bg| {
			let quantity = |val: u32| -> String {
				if val > 1 {
					format!("{}", val)
				} else {
					String::new()
				}
			};

			if count > 0 {
				let text = format!(" {}{} ", quantity(count), symbol);
				segments.push(Segment::simple(&text, fg, bg));
			}
		};
		add_elem(stats.ahead, '\u{2B06}', S::GIT_AHEAD_FG, S::GIT_AHEAD_BG);
		add_elem(stats.behind, '\u{2B07}', S::GIT_BEHIND_FG, S::GIT_BEHIND_BG);
		add_elem(stats.staged, '\u{2714}', S::GIT_STAGED_FG, S::GIT_STAGED_BG);
		add_elem(stats.non_staged, '\u{270E}', S::GIT_NOTSTAGED_FG, S::GIT_NOTSTAGED_BG);
		add_elem(stats.untracked, '\u{2753}', S::GIT_UNTRACKED_FG, S::GIT_UNTRACKED_BG);
		add_elem(stats.conflicted, '\u{273C}', S::GIT_CONFLICTED_FG, S::GIT_CONFLICTED_BG);

		Ok(())
	}
}
<commit_msg>less alloc in git<commit_after>use std::{env, marker::PhantomData, path, path::PathBuf};

use super::Module;
use crate::{terminal::Color, Segment, R};

#[cfg(not(feature = "libgit"))]
mod process;
#[cfg(not(feature = "libgit"))]
use process as internal;

#[cfg(feature = "libgit")]
mod libgit;
#[cfg(feature = "libgit")]
use libgit as internal;

pub struct Git<S> {
	scheme: PhantomData<S>,
}

#[derive(Clone)]
pub struct GitStats {
	pub untracked: u32,
	pub conflicted: u32,
	pub non_staged: u32,
	pub ahead: u32,
	pub behind: u32,
	pub staged: u32,
	pub branch_name: String,
}

pub trait GitScheme {
	const GIT_AHEAD_BG: Color;
	const GIT_AHEAD_FG: Color;
	const GIT_BEHIND_BG: Color;
	const GIT_BEHIND_FG: Color;
	const GIT_STAGED_BG: Color;
	const GIT_STAGED_FG: Color;
	const GIT_NOTSTAGED_BG: Color;
	const GIT_NOTSTAGED_FG: Color;
	const GIT_UNTRACKED_BG: Color;
	const GIT_UNTRACKED_FG: Color;
	const GIT_CONFLICTED_BG: Color;
	const GIT_CONFLICTED_FG: Color;
	const GIT_REPO_CLEAN_BG: Color;
	const GIT_REPO_CLEAN_FG: Color;
	const GIT_REPO_DIRTY_BG: Color;
	const GIT_REPO_DIRTY_FG: Color;
}

impl<S: GitScheme> Git<S> {
	pub fn new() -> Git<S> {
		Git { scheme: PhantomData }
	}

	pub fn get_git_data(&mut self, path: PathBuf) -> R<GitStats> {
		internal::run_git(&path)
	}
}

impl GitStats {
	pub fn is_dirty(&self) -> bool {
		(self.untracked + self.conflicted + self.staged + self.non_staged) > 0
	}
}

fn find_git_dir() -> Option<path::PathBuf> {
	let mut git_dir = env::current_dir().unwrap();
	loop {
		git_dir.push(".git/");

		if git_dir.exists() {
			git_dir.pop();
			return Some(git_dir);
		}
		git_dir.pop();

		if !git_dir.pop() {
			return None;
		}
	}
}

impl<S: GitScheme> Module for Git<S> {
	fn append_segments(&mut self, segments: &mut Vec<Segment>) -> R<()> {
		let git_dir = match find_git_dir() {
			Some(dir) => dir,
			_ => return Ok(()),
		};

		let stats = self.get_git_data(git_dir)?;

		let (branch_fg, branch_bg) = if stats.is_dirty() {
			(S::GIT_REPO_DIRTY_FG, S::GIT_REPO_DIRTY_BG)
		} else {
			(S::GIT_REPO_CLEAN_FG, S::GIT_REPO_CLEAN_BG)
		};

		segments.push(Segment::simple(format!(" {} ", stats.branch_name), branch_fg, branch_bg));

		let mut add_elem = |count, symbol, fg, bg| {
			if count > 0 {
				if quantity > 1 {
					segments.push(Segment::simple(format!(" {}{} ", quantity, symbol), fg, bg));
				} else {
					segments.push(Segment::simple(format!(" {} ", symbolf), fg, bg));
				}
			}
		};
		add_elem(stats.ahead, '\u{2B06}', S::GIT_AHEAD_FG, S::GIT_AHEAD_BG);
		add_elem(stats.behind, '\u{2B07}', S::GIT_BEHIND_FG, S::GIT_BEHIND_BG);
		add_elem(stats.staged, '\u{2714}', S::GIT_STAGED_FG, S::GIT_STAGED_BG);
		add_elem(stats.non_staged, '\u{270E}', S::GIT_NOTSTAGED_FG, S::GIT_NOTSTAGED_BG);
		add_elem(stats.untracked, '\u{2753}', S::GIT_UNTRACKED_FG, S::GIT_UNTRACKED_BG);
		add_elem(stats.conflicted, '\u{273C}', S::GIT_CONFLICTED_FG, S::GIT_CONFLICTED_BG);

		Ok(())
	}
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use fnv::FnvHasher;
use std::collections::HashMap;
use std::hash::BuildHasherDefault;
use tiling::AuxiliaryListsMap;
use webrender_traits::{AuxiliaryLists, BuiltDisplayList, PipelineId, Epoch, ColorF};
use webrender_traits::{DisplayItem, SpecificDisplayItem, StackingContext};
use webrender_traits::LayerSize;

trait DisplayListHelpers {
    fn starting_stacking_context<'a>(&'a self) -> Option<&'a StackingContext>;
}

impl DisplayListHelpers for Vec<DisplayItem> {
    fn starting_stacking_context<'a>(&'a self) -> Option<&'a StackingContext> {
        self.first().and_then(|item| match item.item {
            SpecificDisplayItem::PushStackingContext(ref item) => Some(&item.stacking_context),
            _ => None,
        })
    }
}

/// A representation of the layout within the display port for a given document or iframe.
#[derive(Debug)]
pub struct ScenePipeline {
    pub pipeline_id: PipelineId,
    pub epoch: Epoch,
    pub viewport_size: LayerSize,
    pub background_color: Option<ColorF>,
}

/// A complete representation of the layout bundling visible pipelines together.
pub struct Scene {
    pub root_pipeline_id: Option<PipelineId>,
    pub pipeline_map: HashMap<PipelineId, ScenePipeline, BuildHasherDefault<FnvHasher>>,
    pub pipeline_sizes: HashMap<PipelineId, LayerSize>,
    pub pipeline_auxiliary_lists: AuxiliaryListsMap,
    pub display_lists: HashMap<PipelineId, Vec<DisplayItem>, BuildHasherDefault<FnvHasher>>,
}

impl Scene {
    pub fn new() -> Scene {
        Scene {
            root_pipeline_id: None,
            pipeline_sizes: HashMap::new(),
            pipeline_map: HashMap::with_hasher(Default::default()),
            pipeline_auxiliary_lists: HashMap::with_hasher(Default::default()),
            display_lists: HashMap::with_hasher(Default::default()),
        }
    }

    pub fn set_root_pipeline_id(&mut self, pipeline_id: PipelineId) {
        self.root_pipeline_id = Some(pipeline_id);
    }

    pub fn set_root_display_list(&mut self,
                                 pipeline_id: PipelineId,
                                 epoch: Epoch,
                                 built_display_list: BuiltDisplayList,
                                 background_color: Option<ColorF>,
                                 viewport_size: LayerSize,
                                 auxiliary_lists: AuxiliaryLists) {
        self.pipeline_auxiliary_lists.insert(pipeline_id, auxiliary_lists);
        self.display_lists.insert(pipeline_id, built_display_list.all_display_items().to_vec());

        let new_pipeline = ScenePipeline {
            pipeline_id: pipeline_id,
            epoch: epoch,
            viewport_size: viewport_size,
            background_color: background_color,
        };

        self.pipeline_map.insert(pipeline_id, new_pipeline);
    }
}
<commit_msg>Auto merge of #674 - servo:jrmuizel-unused-trait, r=glennw<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use fnv::FnvHasher;
use std::collections::HashMap;
use std::hash::BuildHasherDefault;
use tiling::AuxiliaryListsMap;
use webrender_traits::{AuxiliaryLists, BuiltDisplayList, PipelineId, Epoch, ColorF};
use webrender_traits::{DisplayItem, SpecificDisplayItem, StackingContext};
use webrender_traits::LayerSize;

/// A representation of the layout within the display port for a given document or iframe.
#[derive(Debug)]
pub struct ScenePipeline {
    pub pipeline_id: PipelineId,
    pub epoch: Epoch,
    pub viewport_size: LayerSize,
    pub background_color: Option<ColorF>,
}

/// A complete representation of the layout bundling visible pipelines together.
pub struct Scene {
    pub root_pipeline_id: Option<PipelineId>,
    pub pipeline_map: HashMap<PipelineId, ScenePipeline, BuildHasherDefault<FnvHasher>>,
    pub pipeline_sizes: HashMap<PipelineId, LayerSize>,
    pub pipeline_auxiliary_lists: AuxiliaryListsMap,
    pub display_lists: HashMap<PipelineId, Vec<DisplayItem>, BuildHasherDefault<FnvHasher>>,
}

impl Scene {
    pub fn new() -> Scene {
        Scene {
            root_pipeline_id: None,
            pipeline_sizes: HashMap::new(),
            pipeline_map: HashMap::with_hasher(Default::default()),
            pipeline_auxiliary_lists: HashMap::with_hasher(Default::default()),
            display_lists: HashMap::with_hasher(Default::default()),
        }
    }

    pub fn set_root_pipeline_id(&mut self, pipeline_id: PipelineId) {
        self.root_pipeline_id = Some(pipeline_id);
    }

    pub fn set_root_display_list(&mut self,
                                 pipeline_id: PipelineId,
                                 epoch: Epoch,
                                 built_display_list: BuiltDisplayList,
                                 background_color: Option<ColorF>,
                                 viewport_size: LayerSize,
                                 auxiliary_lists: AuxiliaryLists) {
        self.pipeline_auxiliary_lists.insert(pipeline_id, auxiliary_lists);
        self.display_lists.insert(pipeline_id, built_display_list.all_display_items().to_vec());

        let new_pipeline = ScenePipeline {
            pipeline_id: pipeline_id,
            epoch: epoch,
            viewport_size: viewport_size,
            background_color: background_color,
        };

        self.pipeline_map.insert(pipeline_id, new_pipeline);
    }
}
<|endoftext|>


<commit_before>//! Implementation of [HPP model](https://en.wikipedia.org/wiki/HPP_model).
//! Assumes Von Nuemann's neighborhood.

#![cfg(test)]
use traits::Cell;
use traits::Coord;
use traits::Grid;
use traits::Engine;
use traits::Consumer;
use engine::Sequential;
use grid::nhood::VonNeumannNhood;
use grid::square::SquareGrid;

#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
enum Stage {
    Collision,
    Transport,
}

impl Default for Stage {
    fn default() -> Self {
        Stage::Collision
    }
}

#[derive(Clone, Copy, Debug, PartialEq)]
enum Direction {
    Up,
    Left,
    Right,
    Down,
}

impl Direction {
    fn opposite(&self) -> Self {

        match *self {
            Direction::Up => Direction::Down,
            Direction::Left => Direction::Right,
            Direction::Right => Direction::Left,
            Direction::Down => Direction::Up,
        }
    }

    fn perpendicular(&self) -> Self {

        match *self {
            Direction::Up => Direction::Left,
            Direction::Left => Direction::Down,
            Direction::Right => Direction::Up,
            Direction::Down => Direction::Right,
        }
    }
}

impl From<usize> for Direction {
    fn from(int: usize) -> Self {
        match int {
            0 => Direction::Up,
            1 => Direction::Left,
            2 => Direction::Right,
            3 => Direction::Down,
            i => panic!("No associated variant to number {}.", i),
        }
    }
}

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
struct HPP {
    particles: [bool; 4],
    stage: Stage,
    coord: (i32, i32),
}

impl Cell for HPP {
    type Coord = (i32, i32);

    fn step<'a, I>(&'a self, neighbors: I) -> Self
        where I: Iterator<Item = Option<&'a Self>>
    {

        match self.stage {
            Stage::Collision => self.collision(neighbors),
            Stage::Transport => self.transport(neighbors),
        }
    }

    fn with_coord<C: Coord>(coord: C) -> Self {
        HPP {
            stage: Stage::Collision,
            coord: (coord.x(), coord.y()),
            ..Default::default()
        }
    }

    fn coord(&self) -> &Self::Coord {
        &self.coord
    }

    fn set_coord<C: Coord>(&mut self, coord: &C) {
        self.coord = (coord.x(), coord.y());
    }
}

impl HPP {
    fn collision<'a, I>(&self, neighbors: I) -> Self
        where I: Iterator<Item = Option<&'a Self>>
    {

        let mut new = HPP { stage: Stage::Transport, ..Default::default() };

        for (neighbor, direction) in neighbors.zip(self.directions().iter()) {

            match neighbor {

                Some(neighbor) => {
                    let opposite = direction.opposite();
                    let head_on = self.particle(&direction) && neighbor.particle(&opposite) &&
                                  !self.particle(&direction.perpendicular()) &&
                                  !neighbor.particle(&opposite.perpendicular());

                    if head_on {
                        new.set_particle(&direction.perpendicular(), self.particle(&direction));
                    } else {
                        let particle = new.particle(&direction) || self.particle(&direction);
                        new.set_particle(&direction, particle);
                    }
                }
                // Rebound
                None => {
                    if self.particle(&direction) {
                        let opposite = direction.opposite();
                        new.set_particle(&opposite, true);
                    }
                }
            }
        }

        new
    }

    fn transport<'a, I>(&self, neighbors: I) -> Self
        where I: Iterator<Item = Option<&'a Self>>
    {

        let mut new = HPP { stage: Stage::Collision, ..Default::default() };

        for (neighbor, direction) in neighbors.zip(self.directions().iter()) {

            match neighbor {
                Some(neighbor) => {
                    let opposite = direction.opposite();

                    if neighbor.particle(&opposite) {
                        new.set_particle(&opposite, neighbor.particle(&opposite));
                    }
                }
                None => {
                    if self.particle(&direction) {
                        new.set_particle(&direction, true);
                    }
                }
            }
        }

        new
    }

    pub fn particle(&self, direction: &Direction) -> bool {
        let index = *direction as usize;
        self.particles[index]
    }

    fn set_particle(&mut self, direction: &Direction, exists: bool) {
        let index = *direction as usize;
        self.particles[index] = exists;
    }

    #[inline]
    pub fn directions(&self) -> [Direction; 4] {
        [Direction::Up, Direction::Left, Direction::Right, Direction::Down]
    }
}

#[derive(Debug)]
struct HPPRulesTestConsumer;

use test_helpers::to_cell;

impl HPPRulesTestConsumer {
    pub fn new() -> Self {
        HPPRulesTestConsumer {}
    }

    fn particles_count<C: Cell>(&self, cells: &Vec<C>, direction: &Direction) -> i32 {

        cells.iter()
            .map(|c| to_cell(c))
            .filter(|c: &HPP| c.particle(direction) == true)
            .count() as i32
    }

    fn find_cell<C: Cell>(&self, cells: &Vec<C>, x: i32, y: i32) -> HPP {

        assert!(cells.iter().any(|c| c.coord().x() == x && c.coord().y() == y));

        let found = cells.iter()
            .find(|c| c.coord().x() == x && c.coord().y() == y)
            .unwrap();

        to_cell(found)
    }

    fn test_collision<G: Grid>(&self, grid: &G) {

        let left_particle_count = self.particles_count(&grid.cells(), &Direction::Left);
        assert_eq!(left_particle_count, 0);

        let right_particle_count = self.particles_count(&grid.cells(), &Direction::Right);
        assert_eq!(right_particle_count, 2);

        let up_particles_count = self.particles_count(&grid.cells(), &Direction::Up);
        assert_eq!(up_particles_count, 1);

        let down_particles_count = self.particles_count(&grid.cells(), &Direction::Down);
        assert_eq!(down_particles_count, 1);

        let rebound_to_right = self.find_cell(&grid.cells(), 0, 1);
        assert_eq!(rebound_to_right.particle(&Direction::Right), true);

        let head_on_up = self.find_cell(&grid.cells(), 1, 0);
        assert_eq!(head_on_up.particle(&Direction::Up), true);

        let head_on_down = self.find_cell(&grid.cells(), 2, 0);
        assert_eq!(head_on_down.particle(&Direction::Down), true);
    }

    fn test_transport<G: Grid>(&self, grid: &G) {

        let simple_move_to_right = self.find_cell(&grid.cells(), 1, 2);
        assert_eq!(simple_move_to_right.particle(&Direction::Right), true);

        let move_to_right_after_rebound = self.find_cell(&grid.cells(), 1, 2);
        assert_eq!(move_to_right_after_rebound.particle(&Direction::Right),
                   true);

        let move_to_down_after_head_on = self.find_cell(&grid.cells(), 2, 1);
        assert_eq!(move_to_down_after_head_on.particle(&Direction::Down), true);

        let fixed_to_up_after_head_on = self.find_cell(&grid.cells(), 1, 0);
        assert_eq!(fixed_to_up_after_head_on.particle(&Direction::Up), true);
    }

    fn pretty_print<G: Grid>(&self, grid: &G) {
        println!("");

        for y in 0..3 {
            print!("|");

            for x in 0..3 {
                let cell = self.find_cell(grid.cells(), x, y);
                let directions = cell.directions();
                let maybe_particle = directions.iter().find(|d| cell.particle(d));

                let to_print = match maybe_particle {
                    Some(&Direction::Up) => " ^ |",
                    Some(&Direction::Left) => " < |",
                    Some(&Direction::Right) => " > |",
                    Some(&Direction::Down) => " v |",
                    None => " * |",
                };
                print!("{}", to_print);
            }

            println!("");
        }
    }
}

impl Consumer for HPPRulesTestConsumer {
    type Cell = HPP;

    fn consume<G: Grid>(&mut self, grid: &mut G) {
        assert_eq!(grid.cells().len(), 9);

        self.pretty_print(grid);

        // We are testing previous state.
        let first: HPP = to_cell(&grid.cells()[0]);

        match first.stage {
            Stage::Collision => self.test_transport(grid),
            Stage::Transport => self.test_collision(grid),
        }
    }
}

#[test]
fn test_rules() {
    // initial      collision    transport
    // | * > < |    | * ^ v |    | * ^ * |
    // | < * * | => | > * * | => | * > v |
    // | > * * |    | > * * |    | * > * |
    // 3x3 grid with 4 particles. There should be
    // one rebound, head-on collision and simple move.

    let left_particle = [false, true, false, false];
    let right_particle = [false, false, true, false];

    let cells = vec![
        HPP { stage: Stage::Collision, particles: right_particle, coord: (1, 0) },
        HPP { stage: Stage::Collision, particles: left_particle, coord: (2, 0) },
        HPP { stage: Stage::Collision, particles: left_particle, coord: (0, 1) },
        HPP { stage: Stage::Collision, particles: right_particle, coord: (0, 2) },
    ];

    let nhood = VonNeumannNhood::new();
    let mut grid: SquareGrid<HPP, _> = SquareGrid::new(3, 3, nhood);
    grid.set_cells(cells);

    let right_particles_count = grid.cells()
        .iter()
        .map(|c| to_cell(c))
        .filter(|c: &HPP| c.particle(&Direction::Right) == true)
        .count();
    assert_eq!(right_particles_count, 2);

    let consumer = HPPRulesTestConsumer::new();
    let mut engine = Sequential::new(grid, consumer);
    engine.run_times(2);
}

struct HPPSpreadTestConsumer {
    cur_y: i32,
    cur_direction: Direction,
}

impl HPPSpreadTestConsumer {
    pub fn new() -> Self {
        HPPSpreadTestConsumer {
            cur_y: 0,
            cur_direction: Direction::Down,
        }
    }

    fn particles_count<C: Cell>(&self, cells: &Vec<C>, direction: &Direction) -> i32 {

        cells.iter()
            .map(|c| to_cell(c))
            .filter(|c: &HPP| c.particle(direction) == true)
            .count() as i32
    }

    fn find_cell<C: Cell>(&self, cells: &Vec<C>, x: i32, y: i32) -> HPP {

        assert!(cells.iter().any(|c| c.coord().x() == x && c.coord().y() == y));

        let found = cells.iter()
            .find(|c| c.coord().x() == x && c.coord().y() == y)
            .unwrap();

        to_cell(found)
    }

    fn pretty_print<G: Grid>(&self, grid: &G) {
        println!("");

        for y in 0..5 {
            print!("|");

            for x in 0..5 {
                let cell = self.find_cell(grid.cells(), x, y);
                let directions = cell.directions();
                let maybe_particle = directions.iter().find(|d| cell.particle(d));

                let to_print = match maybe_particle {
                    Some(&Direction::Up) => " ^ |",
                    Some(&Direction::Left) => " < |",
                    Some(&Direction::Right) => " > |",
                    Some(&Direction::Down) => " v |",
                    None => " * |",
                };
                print!("{}", to_print);
            }

            println!("");
        }
    }

    fn test_collision<G: Grid>(&mut self, grid: &G) {

        if self.cur_y == 0 {
            self.cur_direction = Direction::Down;
        }

        let particles_count = self.particles_count(&grid.cells(), &self.cur_direction);
        assert_eq!(particles_count, 5);

        if self.cur_direction == Direction::Down {
            self.cur_y += 1;
        } else {
            self.cur_y -= 1;
        }
    }

    fn test_transport<G: Grid>(&mut self, grid: &G) {

        let particles_count = self.particles_count(&grid.cells(), &self.cur_direction);
        assert_eq!(particles_count, 5);

        assert!(grid.cells()
            .iter()
            .map(|c| to_cell(c))
            .filter(|c: &HPP| c.coord().y() == self.cur_y)
            .all(|c| c.particle(&self.cur_direction) == true));

        if self.cur_y == 4 {
            self.cur_direction = Direction::Up;
        }
    }
}

impl Consumer for HPPSpreadTestConsumer {
    type Cell = HPP;

    fn consume<G: Grid>(&mut self, grid: &mut G) {
        assert_eq!(grid.cells().len(), 25);

        self.pretty_print(grid);

        // We are testing previous state.
        let first: HPP = to_cell(&grid.cells()[0]);

        match first.stage {
            Stage::Collision => self.test_transport(grid),
            Stage::Transport => self.test_collision(grid),
        }
    }
}

#[test]
fn test_spread() {
    // initial          later
    // | v v v v v |    | * * * * * |
    // | * * * * * |    | v v v v v |
    // | * * * * * | => | * * * * * | => (so on)
    // | * * * * * |    | * * * * * |
    // | * * * * * |    | * * * * * |
    // 5x5 grid with 5 particles. Particles should move
    // to lower border then rebound, then move to upper and so on.

    let down_particle = [false, false, false, true];

    let mut cells = Vec::new();
    for x in 0..5 {
        cells.push(HPP {
            stage: Stage::Collision,
            particles: down_particle,
            coord: (x, 0),
        });
    }

    let nhood = VonNeumannNhood::new();
    let mut grid: SquareGrid<HPP, _> = SquareGrid::new(5, 5, nhood);
    grid.set_cells(cells);

    let consumer = HPPSpreadTestConsumer::new();
    let mut engine = Sequential::new(grid, consumer);
    // 2 phases * 10 full cycles = 20 times.
    engine.run_times(20);
}
<commit_msg>Fixed HPP logic.<commit_after>//! Implementation of [HPP model](https://en.wikipedia.org/wiki/HPP_model).
//! Assumes Von Nuemann's neighborhood.

#![cfg(test)]
use traits::Cell;
use traits::Coord;
use traits::Grid;
use traits::Engine;
use traits::Consumer;
use engine::Sequential;
use grid::nhood::VonNeumannNhood;
use grid::square::SquareGrid;

#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
enum Stage {
    Collision,
    Transport,
}

impl Default for Stage {
    fn default() -> Self {
        Stage::Collision
    }
}

#[derive(Clone, Copy, Debug, PartialEq)]
enum Direction {
    Up,
    Left,
    Right,
    Down,
}

impl Direction {
    fn opposite(&self) -> Self {

        match *self {
            Direction::Up => Direction::Down,
            Direction::Left => Direction::Right,
            Direction::Right => Direction::Left,
            Direction::Down => Direction::Up,
        }
    }

    fn perpendicular(&self) -> Self {

        match *self {
            Direction::Up => Direction::Left,
            Direction::Left => Direction::Down,
            Direction::Right => Direction::Up,
            Direction::Down => Direction::Right,
        }
    }
}

impl From<usize> for Direction {
    fn from(int: usize) -> Self {
        match int {
            0 => Direction::Up,
            1 => Direction::Left,
            2 => Direction::Right,
            3 => Direction::Down,
            i => panic!("No associated variant to number {}.", i),
        }
    }
}

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
struct HPP {
    particles: [bool; 4],
    stage: Stage,
    coord: (i32, i32),
}

impl Cell for HPP {
    type Coord = (i32, i32);

    fn step<'a, I>(&'a self, neighbors: I) -> Self
        where I: Iterator<Item = Option<&'a Self>>
    {

        match self.stage {
            Stage::Collision => self.collision(neighbors),
            Stage::Transport => self.transport(neighbors),
        }
    }

    fn with_coord<C: Coord>(coord: C) -> Self {
        HPP {
            stage: Stage::Collision,
            coord: (coord.x(), coord.y()),
            ..Default::default()
        }
    }

    fn coord(&self) -> &Self::Coord {
        &self.coord
    }

    fn set_coord<C: Coord>(&mut self, coord: &C) {
        self.coord = (coord.x(), coord.y());
    }
}

impl HPP {
    fn collision<'a, I>(&self, neighbors: I) -> Self
        where I: Iterator<Item = Option<&'a Self>>
    {

        let mut new = HPP { stage: Stage::Transport, ..Default::default() };

        for (neighbor, direction) in neighbors.zip(self.directions().iter()) {

            match neighbor {

                Some(_) => {
                    let opposite = direction.opposite();

                    let head_on = self.particle(&direction) && self.particle(&opposite) &&
                                  !self.particle(&direction.perpendicular()) &&
                                  !self.particle(&opposite.perpendicular());
                    if head_on {
                        new.set_particle(&direction.perpendicular(), true);
                        new.set_particle(&opposite.perpendicular(), true);
                    }
                }
                // Rebound
                None => {
                    let opposite = direction.opposite();
                    if self.particle(&opposite) {
                        new.set_particle(&direction, true);
                    }
                }
            }
        }

        new
    }

    fn transport<'a, I>(&self, neighbors: I) -> Self
        where I: Iterator<Item = Option<&'a Self>>
    {

        let mut new = HPP { stage: Stage::Collision, ..Default::default() };

        for (neighbor, direction) in neighbors.zip(self.directions().iter()) {

            match neighbor {
                Some(neighbor) => {
                    if neighbor.particle(&direction) {
                        new.set_particle(&direction, neighbor.particle(&direction));
                    }
                }
                None => {
                    new.set_particle(&direction, self.particle(&direction));
                }
            }
        }

        new
    }

    pub fn particle(&self, direction: &Direction) -> bool {
        let index = *direction as usize;
        self.particles[index]
    }

    fn set_particle(&mut self, direction: &Direction, exists: bool) {
        let index = *direction as usize;
        self.particles[index] = exists;
    }

    #[inline]
    pub fn directions(&self) -> [Direction; 4] {
        [Direction::Up, Direction::Left, Direction::Right, Direction::Down]
    }
}

#[derive(Debug)]
struct HPPRulesTestConsumer;

use test_helpers::to_cell;

impl HPPRulesTestConsumer {
    pub fn new() -> Self {
        HPPRulesTestConsumer {}
    }

    fn particles_count<C: Cell>(&self, cells: &Vec<C>, direction: &Direction) -> i32 {

        cells.iter()
            .map(|c| to_cell(c))
            .filter(|c: &HPP| c.particle(direction) == true)
            .count() as i32
    }

    fn find_cell<C: Cell>(&self, cells: &Vec<C>, x: i32, y: i32) -> HPP {

        assert!(cells.iter().any(|c| c.coord().x() == x && c.coord().y() == y));

        let found = cells.iter()
            .find(|c| c.coord().x() == x && c.coord().y() == y)
            .unwrap();

        to_cell(found)
    }

    fn test_collision<G: Grid>(&self, grid: &G) {

        let left_particle_count = self.particles_count(&grid.cells(), &Direction::Left);
        assert_eq!(left_particle_count, 0);

        let right_particle_count = self.particles_count(&grid.cells(), &Direction::Right);
        assert_eq!(right_particle_count, 2);

        let up_particles_count = self.particles_count(&grid.cells(), &Direction::Up);
        assert_eq!(up_particles_count, 1);

        let down_particles_count = self.particles_count(&grid.cells(), &Direction::Down);
        assert_eq!(down_particles_count, 1);

        let rebound_to_right = self.find_cell(&grid.cells(), 0, 1);
        assert_eq!(rebound_to_right.particle(&Direction::Right), true);

        let head_on_up = self.find_cell(&grid.cells(), 1, 0);
        assert_eq!(head_on_up.particle(&Direction::Up), true);

        let head_on_down = self.find_cell(&grid.cells(), 2, 0);
        assert_eq!(head_on_down.particle(&Direction::Down), true);
    }

    fn test_transport<G: Grid>(&self, grid: &G) {

        let simple_move_to_right = self.find_cell(&grid.cells(), 1, 2);
        assert_eq!(simple_move_to_right.particle(&Direction::Right), true);

        let move_to_right_after_rebound = self.find_cell(&grid.cells(), 1, 2);
        assert_eq!(move_to_right_after_rebound.particle(&Direction::Right),
                   true);

        let move_to_down_after_head_on = self.find_cell(&grid.cells(), 2, 1);
        assert_eq!(move_to_down_after_head_on.particle(&Direction::Down), true);

        let fixed_to_up_after_head_on = self.find_cell(&grid.cells(), 1, 0);
        assert_eq!(fixed_to_up_after_head_on.particle(&Direction::Up), true);
    }

    fn pretty_print<G: Grid>(&self, grid: &G) {
        println!("");

        for y in 0..3 {
            print!("|");

            for x in 0..3 {
                let cell = self.find_cell(grid.cells(), x, y);
                let directions = cell.directions();
                let maybe_particle = directions.iter().find(|d| cell.particle(d));

                let to_print = match maybe_particle {
                    Some(&Direction::Up) => " ^ |",
                    Some(&Direction::Left) => " < |",
                    Some(&Direction::Right) => " > |",
                    Some(&Direction::Down) => " v |",
                    None => " * |",
                };
                print!("{}", to_print);
            }

            println!("");
        }
    }
}

impl Consumer for HPPRulesTestConsumer {
    type Cell = HPP;

    fn consume<G: Grid>(&mut self, grid: &mut G) {
        assert_eq!(grid.cells().len(), 9);

        self.pretty_print(grid);

        // We are testing previous state.
        let first: HPP = to_cell(&grid.cells()[0]);

        match first.stage {
            Stage::Collision => self.test_transport(grid),
            Stage::Transport => self.test_collision(grid),
        }
    }
}

#[test]
fn test_rules() {
    // initial      collision    transport
    // | * > < |    | * ^ v |    | * ^ * |
    // | < * * | => | > * * | => | * > v |
    // | > * * |    | > * * |    | * > * |
    // 3x3 grid with 4 particles. There should be
    // one rebound, head-on collision and simple move.

    let left_particle = [false, true, false, false];
    let right_particle = [false, false, true, false];

    let cells = vec![
        HPP { stage: Stage::Collision, particles: right_particle, coord: (1, 0) },
        HPP { stage: Stage::Collision, particles: left_particle, coord: (2, 0) },
        HPP { stage: Stage::Collision, particles: left_particle, coord: (0, 1) },
        HPP { stage: Stage::Collision, particles: right_particle, coord: (0, 2) },
    ];

    let nhood = VonNeumannNhood::new();
    let mut grid: SquareGrid<HPP, _> = SquareGrid::new(3, 3, nhood);
    grid.set_cells(cells);

    let right_particles_count = grid.cells()
        .iter()
        .map(|c| to_cell(c))
        .filter(|c: &HPP| c.particle(&Direction::Right) == true)
        .count();
    assert_eq!(right_particles_count, 2);

    let consumer = HPPRulesTestConsumer::new();
    let mut engine = Sequential::new(grid, consumer);
    engine.run_times(2);
}

struct HPPSpreadTestConsumer {
    cur_y: i32,
    cur_direction: Direction,
}

impl HPPSpreadTestConsumer {
    pub fn new() -> Self {
        HPPSpreadTestConsumer {
            cur_y: 0,
            cur_direction: Direction::Down,
        }
    }

    fn particles_count<C: Cell>(&self, cells: &Vec<C>, direction: &Direction) -> i32 {

        cells.iter()
            .map(|c| to_cell(c))
            .filter(|c: &HPP| c.particle(direction) == true)
            .count() as i32
    }

    fn find_cell<C: Cell>(&self, cells: &Vec<C>, x: i32, y: i32) -> HPP {

        assert!(cells.iter().any(|c| c.coord().x() == x && c.coord().y() == y));

        let found = cells.iter()
            .find(|c| c.coord().x() == x && c.coord().y() == y)
            .unwrap();

        to_cell(found)
    }

    fn pretty_print<G: Grid>(&self, grid: &G) {
        println!("");

        for y in 0..5 {
            print!("|");

            for x in 0..5 {
                let cell = self.find_cell(grid.cells(), x, y);
                let directions = cell.directions();
                let maybe_particle = directions.iter().find(|d| cell.particle(d));

                let to_print = match maybe_particle {
                    Some(&Direction::Up) => " ^ |",
                    Some(&Direction::Left) => " < |",
                    Some(&Direction::Right) => " > |",
                    Some(&Direction::Down) => " v |",
                    None => " * |",
                };
                print!("{}", to_print);
            }

            println!("");
        }
    }

    fn test_collision<G: Grid>(&mut self, grid: &G) {

        if self.cur_y == 0 {
            self.cur_direction = Direction::Down;
        }

        let particles_count = self.particles_count(&grid.cells(), &self.cur_direction);
        assert_eq!(particles_count, 5);

        if self.cur_direction == Direction::Down {
            self.cur_y += 1;
        } else {
            self.cur_y -= 1;
        }
    }

    fn test_transport<G: Grid>(&mut self, grid: &G) {

        let particles_count = self.particles_count(&grid.cells(), &self.cur_direction);
        assert_eq!(particles_count, 5);

        assert!(grid.cells()
            .iter()
            .map(|c| to_cell(c))
            .filter(|c: &HPP| c.coord().y() == self.cur_y)
            .all(|c| c.particle(&self.cur_direction) == true));

        if self.cur_y == 4 {
            self.cur_direction = Direction::Up;
        }
    }
}

impl Consumer for HPPSpreadTestConsumer {
    type Cell = HPP;

    fn consume<G: Grid>(&mut self, grid: &mut G) {
        assert_eq!(grid.cells().len(), 25);

        self.pretty_print(grid);

        // We are testing previous state.
        let first: HPP = to_cell(&grid.cells()[0]);

        match first.stage {
            Stage::Collision => self.test_transport(grid),
            Stage::Transport => self.test_collision(grid),
        }
    }
}

#[test]
fn test_spread() {
    // initial          later
    // | v v v v v |    | * * * * * |
    // | * * * * * |    | v v v v v |
    // | * * * * * | => | * * * * * | => (so on)
    // | * * * * * |    | * * * * * |
    // | * * * * * |    | * * * * * |
    // 5x5 grid with 5 particles. Particles should move
    // to lower border then rebound, then move to upper and so on.

    let down_particle = [false, false, false, true];

    let mut cells = Vec::new();
    for x in 0..5 {
        cells.push(HPP {
            stage: Stage::Collision,
            particles: down_particle,
            coord: (x, 0),
        });
    }

    let nhood = VonNeumannNhood::new();
    let mut grid: SquareGrid<HPP, _> = SquareGrid::new(5, 5, nhood);
    grid.set_cells(cells);

    let consumer = HPPSpreadTestConsumer::new();
    let mut engine = Sequential::new(grid, consumer);
    // 2 phases * 10 full cycles = 20 times.
    engine.run_times(20);
}
<|endoftext|>


<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2013 Jeremy Letang (letang.jeremy@gmail.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#![allow(non_upper_case_globals, missing_docs)]

//! The MAC_CORE specific API.

use ffi;
use pa::{
    DeviceIndex,
    HostApiTypeId,
    Sample,
    Stream
};

pub static MacCoreChangeDeviceParameters : u32 = 0x01;
pub static MacCoreFailIfConversionRequired : u32 = 0x02;
pub static MacCoreConversionQualityMin : u32 = 0x0100;
pub static MacCoreConversionQualityMedium : u32 = 0x0200;
pub static MacCoreConversionQualityLow : u32 = 0x0300;
pub static MacCoreConversionQualityHigh : u32 = 0x0400;
pub static MacCoreConversionQualityMax : u32 = 0x0000;
pub static MacCorePlayNice : u32 = 0x00;
pub static MacCorePro : u32 = 0x01;
pub static MacCoreMinimizeCPUButPlayNice : u32 = 0x0100;
pub static MacCoreMinimizeCPU : u32 = 0x0101;

/// Not implemented
#[allow(raw_pointer_derive)]
#[derive(Clone, Copy)]
pub struct MacCoreStreamInfo {
    size : u32,
    host_api_type : HostApiTypeId,
    version : u32,
    flags : u32,
    channel_map : *const i32,
    channel_map_size : u32
}

pub trait MacCore {
    fn get_stream_input_device(&self) -> DeviceIndex;
    fn get_stream_output_device(&self) -> DeviceIndex;
}

// // fn get_buffer_size_range(device : PaDeviceIndex) -> Result<(u32, u32), PaError> {
//     let mut min_buffer_size_frames : u32 = 0;
//     let mut max_buffer_size_frames : u32 = 0;
//     let err = unsafe { ffi::PaMacCore_GetBufferSizeRange(device, &min_buffer_size_frames, &max_buffer_size_frames) };
//     match err {
//         PaNoError   => Ok((min_buffer_size_frames, max_buffer_size_frames)),
//         _           => Err(err)
//     }
// }

impl<I: Sample, O: Sample> MacCore for Stream<I, O> {
        fn get_stream_input_device(&self) -> DeviceIndex {
        unsafe {
            ffi::PaMacCore_GetStreamInputDevice(self.get_c_pa_stream())
        }
    }
        fn get_stream_output_device(&self) -> DeviceIndex {
        unsafe {
            ffi::PaMacCore_GetStreamOutputDevice(self.get_c_pa_stream())
        }
    }
}
<commit_msg>Update mac_core extension module to latest Stream changes<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2013 Jeremy Letang (letang.jeremy@gmail.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#![allow(non_upper_case_globals, missing_docs)]

//! The MAC_CORE specific API.

use ffi;
use pa::{
    DeviceIndex,
    HostApiTypeId,
    Stream
};

pub static MacCoreChangeDeviceParameters : u32 = 0x01;
pub static MacCoreFailIfConversionRequired : u32 = 0x02;
pub static MacCoreConversionQualityMin : u32 = 0x0100;
pub static MacCoreConversionQualityMedium : u32 = 0x0200;
pub static MacCoreConversionQualityLow : u32 = 0x0300;
pub static MacCoreConversionQualityHigh : u32 = 0x0400;
pub static MacCoreConversionQualityMax : u32 = 0x0000;
pub static MacCorePlayNice : u32 = 0x00;
pub static MacCorePro : u32 = 0x01;
pub static MacCoreMinimizeCPUButPlayNice : u32 = 0x0100;
pub static MacCoreMinimizeCPU : u32 = 0x0101;

/// Not implemented
#[allow(raw_pointer_derive)]
#[derive(Clone, Copy)]
pub struct MacCoreStreamInfo {
    size : u32,
    host_api_type : HostApiTypeId,
    version : u32,
    flags : u32,
    channel_map : *const i32,
    channel_map_size : u32
}

pub trait MacCore {
    fn get_stream_input_device(&self) -> DeviceIndex;
    fn get_stream_output_device(&self) -> DeviceIndex;
}

// // fn get_buffer_size_range(device : PaDeviceIndex) -> Result<(u32, u32), PaError> {
//     let mut min_buffer_size_frames : u32 = 0;
//     let mut max_buffer_size_frames : u32 = 0;
//     let err = unsafe { ffi::PaMacCore_GetBufferSizeRange(device, &min_buffer_size_frames, &max_buffer_size_frames) };
//     match err {
//         PaNoError   => Ok((min_buffer_size_frames, max_buffer_size_frames)),
//         _           => Err(err)
//     }
// }

impl<'a, M, F> MacCore for Stream<'a, M, F> {
    fn get_stream_input_device(&self) -> DeviceIndex {
        unsafe {
            ffi::PaMacCore_GetStreamInputDevice(self.unsafe_pa_stream()).into()
        }
    }
    fn get_stream_output_device(&self) -> DeviceIndex {
        unsafe {
            ffi::PaMacCore_GetStreamOutputDevice(self.unsafe_pa_stream()).into()
        }
    }
}
<|endoftext|>


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

use app::App;
use app::Msg::CreateWindow;
use webview::Msg::PageOpen;
use url::{Url, Position};

impl App {
    /// Open the given URL in the web view.
    pub fn open(&self, url: &str) {
        let url = self.transform_url(url);
        self.webview.emit(PageOpen(url));
    }

    /// Open the given URL in a new window.
    pub fn open_in_new_window(&mut self, url: &str) {
        let url = self.transform_url(url);
        self.model.relm.stream().emit(CreateWindow(url));
    }

    /// Open in a new window the url from the system clipboard.
    pub fn win_paste_url(&mut self) {
        if let Some(url) = self.get_url_from_clipboard() {
            self.open_in_new_window(&url);
        }
    }

    /// Go up one directory in url
    pub fn go_parent_directory(&self) {
        if let Some(ref url) = self.webview.widget().get_uri() {
            if let Ok(base_url) = Url::parse(url) {

                // #TODO: go up nth directories
                if let Ok(parent) = base_url.join("../") {
                    self.open(parent.as_str());
                }
            }
        }
    }

    /// Go to the root directory or url hostname
    pub fn go_root_directory(&self) {
        if let Some(ref url) = self.webview.widget().get_uri() {
            if let Ok(base_url) = Url::parse(url) {
                let root = &base_url[..Position::BeforePath];

                // #TODO: Test on file:/// and edge cases
                if !root.is_empty() {
                    self.open(&base_url[..Position::BeforePath]);
                }
            }
        }
    }
}
<commit_msg>small fix<commit_after>/*
 * Copyright (c) 2017 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

use app::App;
use app::Msg::CreateWindow;
use webview::Msg::PageOpen;
use url::{Url, Position};

impl App {
    /// Open the given URL in the web view.
    pub fn open(&self, url: &str) {
        let url = self.transform_url(url);
        self.webview.emit(PageOpen(url));
    }

    /// Open the given URL in a new window.
    pub fn open_in_new_window(&mut self, url: &str) {
        let url = self.transform_url(url);
        self.model.relm.stream().emit(CreateWindow(url));
    }

    /// Open in a new window the url from the system clipboard.
    pub fn win_paste_url(&mut self) {
        if let Some(url) = self.get_url_from_clipboard() {
            self.open_in_new_window(&url);
        }
    }

    /// Go up one directory in url
    pub fn go_parent_directory(&self) {
        if let Some(ref url) = self.webview.widget().get_uri() {
            if let Ok(base_url) = Url::parse(url) {

                // #TODO: go up nth directories
                if let Ok(parent) = base_url.join("../") {
                    self.open(parent.as_str());
                }
            }
        }
    }

    /// Go to the root directory or url hostname
    pub fn go_root_directory(&self) {
        if let Some(ref url) = self.webview.widget().get_uri() {
            if let Ok(base_url) = Url::parse(url) {
                let root = &base_url[..Position::BeforePath];

                // #TODO: Test on file:/// and edge cases
                if !root.is_empty() {
                    self.open(root);
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use {osc_types as ot,
    errors as oe};

use std::{io, string, mem, error};
use std::io::BufRead;

use byteorder;
use byteorder::{BigEndian, ReadBytesExt};

/// Common MTP size for ethernet
pub const MTP: usize = 1536;

pub fn decode(msg: &[u8], size: usize) -> ot::OscResult<ot::OscPacket> {
    match msg[0] as char {
        '/' => {
            decode_message(msg, size)
        }
        '#' => {
            decode_bundle(msg)
        }
        _ => Err(oe::OscError::BadOscPacket("Unknown message format.".to_string())),
    }
}

fn decode_message(msg: &[u8], size: usize) -> ot::OscResult<ot::OscPacket> {
    let mut cursor: io::Cursor<&[u8]> = io::Cursor::new(msg);
    let mut pos: u64 = 0;

    match read_osc_string(&mut cursor) {
        Ok(s) => {
            let addr: String = s;
            println!("{}",try!(read_osc_string(&mut cursor)));
        }
        Err(e) => {
            return Err(e)
        }
        Err(e) => return Err(e)
    }

    Ok(ot::OscPacket::Message(ot::OscMessage))
}

fn read_osc_string(cursor: &mut io::Cursor<&[u8]>) -> ot::OscResult<String> {
    let mut str_buf: Vec<u8> = Vec::new();
    match cursor.read_until(0, &mut str_buf) {
        Ok(_) => {
            pad_cursor(cursor);
            String::from_utf8(str_buf).map_err(|e| oe::OscError::StringError(e))
        },
        Err(e) => Err(oe::OscError::ReadError(e)),
    }
}

fn decode_bundle(msg: &[u8]) -> ot::OscResult<ot::OscPacket> {
    Err(oe::OscError::BadOscBundle)
}

fn pad_cursor(cursor: &mut io::Cursor<&[u8]>) {
    let pos = cursor.position();
    match pos % 4 {
        0 => (),
        d => cursor.set_position(pos + (4 - d)),
    }
}<commit_msg>Trim zero bytes from string<commit_after>use {osc_types as ot,
    errors as oe};

use std::{io, string, mem, error};
use std::io::BufRead;

use byteorder;
use byteorder::{BigEndian, ReadBytesExt};

/// Common MTP size for ethernet
pub const MTP: usize = 1536;

pub fn decode(msg: &[u8], size: usize) -> ot::OscResult<ot::OscPacket> {
    match msg[0] as char {
        '/' => {
            decode_message(msg, size)
        }
        '#' => {
            decode_bundle(msg)
        }
        _ => Err(oe::OscError::BadOscPacket("Unknown message format.".to_string())),
    }
}

fn decode_message(msg: &[u8], size: usize) -> ot::OscResult<ot::OscPacket> {
    let mut cursor: io::Cursor<&[u8]> = io::Cursor::new(msg);
    let mut pos: u64 = 0;

    match read_osc_string(&mut cursor) {
        Ok(s) => {
            let addr: String = s;
            println!("{}",try!(read_osc_string(&mut cursor)));
        }
        Err(e) => {
            return Err(e)
        }
        Err(e) => return Err(e)
    }

    Ok(ot::OscPacket::Message(ot::OscMessage))
}

fn read_osc_string(cursor: &mut io::Cursor<&[u8]>) -> ot::OscResult<String> {
    let mut str_buf: Vec<u8> = Vec::new();
    match cursor.read_until(0, &mut str_buf) {
        Ok(_) => {
            pad_cursor(cursor);
            String::from_utf8(str_buf)
                .map_err(|e| oe::OscError::StringError(e))
                .map(|s| s.trim_matches(0u8 as char).to_string())
        },
        Err(e) => Err(oe::OscError::ReadError(e)),
    }
}

fn decode_bundle(msg: &[u8]) -> ot::OscResult<ot::OscPacket> {
    Err(oe::OscError::BadOscBundle)
}

fn pad_cursor(cursor: &mut io::Cursor<&[u8]>) {
    let pos = cursor.position();
    match pos % 4 {
        0 => (),
        d => cursor.set_position(pos + (4 - d)),
    }
}<|endoftext|>