<commit_before>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

//! Rosetta Account API
//!
//! See: [Account API Spec](https://www.rosetta-api.org/docs/AccountApi.html)
//!

use crate::{
    block::{block_index_to_version, version_to_block_index},
    common::{check_network, handle_request, with_context},
    error::{ApiError, ApiResult},
    types::{
        coin_identifier, coin_store_identifier, AccountBalanceRequest, AccountBalanceResponse,
        Amount, BlockIdentifier, Currency, PartialBlockIdentifier,
    },
    RosettaContext,
};
use aptos_crypto::HashValue;
use aptos_logger::{debug, trace};
use aptos_rest_client::{aptos::Balance, aptos_api_types::U64};
use aptos_sdk::move_types::language_storage::TypeTag;
use aptos_types::account_address::AccountAddress;
use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS};
use serde::{Deserialize, Serialize};
use std::{
    collections::{HashMap, HashSet},
    str::FromStr,
    sync::RwLock,
};
use warp::Filter;

/// Account routes e.g. balance
pub fn routes(
    server_context: RosettaContext,
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
    warp::post().and(
        warp::path!("account" / "balance")
            .and(warp::body::json())
            .and(with_context(server_context))
            .and_then(handle_request(account_balance)),
    )
}

/// Account balance command
///
/// [API Spec](https://www.rosetta-api.org/docs/AccountApi.html#accountbalance)
async fn account_balance(
    request: AccountBalanceRequest,
    server_context: RosettaContext,
) -> ApiResult<AccountBalanceResponse> {
    debug!("/account/balance");
    trace!(
        request = ?request,
        server_context = ?server_context,
        "account_balance for [{}]",
        request.account_identifier.address
    );

    let network_identifier = request.network_identifier;

    check_network(network_identifier, &server_context)?;
    let block_size = server_context.block_size;
    let rest_client = server_context.rest_client()?;

    // Retrieve the block index to read
    let block_index =
        get_block_index_from_request(rest_client, request.block_identifier.clone(), block_size)
            .await?;

    // Version to grab is the last entry in the block (balance is at end of block)
    let block_version = block_index_to_version(block_size, block_index);
    let balance_version =
        block_index_to_version(block_size, block_index.saturating_add(1)).saturating_sub(1);

    let balances = get_balances(
        rest_client,
        request.account_identifier.account_address()?,
        balance_version,
    )
    .await?;

    let mut amounts = vec![];

    // Lookup coins, and fill in currency codes
    for (coin, balance) in balances {
        if let Some(currency) = server_context
            .coin_cache
            .get_currency(rest_client, coin, Some(balance_version))
            .await?
        {
            amounts.push(Amount {
                value: balance.coin.value.0.to_string(),
                currency,
            });
        }
    }

    // Filter based on requested currencies
    if let Some(currencies) = request.currencies {
        let mut currencies: HashSet<Currency> = currencies.into_iter().collect();
        // Remove extra currencies not requested
        amounts = amounts
            .into_iter()
            .filter(|amount| currencies.contains(&amount.currency))
            .collect();

        // Zero out currencies that weren't in the account yet
        for amount in amounts.iter() {
            currencies.remove(&amount.currency);
        }
        for currency in currencies {
            amounts.push(Amount {
                value: 0.to_string(),
                currency,
            });
        }
    }

    // Get the block identifier
    let response = rest_client
        .get_transaction_by_version(block_version)
        .await?;
    let block_identifier = BlockIdentifier::from_transaction(block_size, response.inner())?;

    Ok(AccountBalanceResponse {
        block_identifier,
        balances: amounts,
    })
}

/// Determines which block to pull for the request
async fn get_block_index_from_request(
    rest_client: &aptos_rest_client::Client,
    partial_block_identifier: Option<PartialBlockIdentifier>,
    block_size: u64,
) -> ApiResult<u64> {
    Ok(match partial_block_identifier {
        Some(PartialBlockIdentifier {
            index: Some(_),
            hash: Some(_),
        }) => {
            return Err(ApiError::BlockParameterConflict);
        }
        // Lookup by block index
        Some(PartialBlockIdentifier {
            index: Some(block_index),
            hash: None,
        }) => block_index,
        // Lookup by block hash
        Some(PartialBlockIdentifier {
            index: None,
            hash: Some(hash),
        }) => {
            if hash == BlockIdentifier::genesis_txn().hash {
                0
            } else {
                // Lookup by hash doesn't work since we're faking blocks, need to verify that it's a
                // block
                let response =
                    rest_client
                        .get_transaction(HashValue::from_str(&hash).map_err(|err| {
                            ApiError::DeserializationFailed(Some(err.to_string()))
                        })?)
                        .await?;
                let version = response.inner().version();

                if let Some(version) = version {
                    let block_index = version_to_block_index(block_size, version);
                    // If it's not the beginning of a block, then it's invalid
                    if version != block_index_to_version(block_size, block_index) {
                        return Err(ApiError::TransactionIsPending);
                    }

                    block_index
                } else {
                    // If the transaction is pending, it's incomplete
                    return Err(ApiError::BlockIncomplete);
                }
            }
        }
        // Lookup latest version
        _ => {
            let response = rest_client.get_ledger_information().await?;
            let state = response.state();
            version_to_block_index(block_size, state.version) - 1
        }
    })
}

/// Retrieve the balances for an account
async fn get_balances(
    rest_client: &aptos_rest_client::Client,
    address: AccountAddress,
    version: u64,
) -> ApiResult<HashMap<TypeTag, Balance>> {
    // TODO: Handle non not found errors
    if let Ok(response) = rest_client
        .get_account_resources_at_version(address, version)
        .await
    {
        // Retrieve balances
        Ok(response
            .inner()
            .iter()
            .filter(|resource| {
                resource.resource_type.address == AccountAddress::ONE
                    && resource.resource_type.module == coin_identifier()
                    && resource.resource_type.name == coin_store_identifier()
            })
            .filter_map(|resource| {
                // Currency must have a type
                if let Some(coin_type) = resource.resource_type.type_params.first() {
                    match serde_json::from_value::<Balance>(resource.data.clone()) {
                        Ok(resource) => Some((coin_type.clone(), resource)),
                        Err(_) => None,
                    }
                } else {
                    // Skip currencies that don't match
                    None
                }
            })
            .collect())
    } else {
        Ok(HashMap::new())
    }
}

/// A cache for currencies, so we don't have to keep looking up the status of it
#[derive(Debug)]
pub struct CoinCache {
    currencies: RwLock<HashMap<TypeTag, Option<Currency>>>,
}

impl CoinCache {
    pub fn new() -> Self {
        Self {
            currencies: RwLock::new(HashMap::new()),
        }
    }

    /// Retrieve a currency and cache it if applicable
    pub async fn get_currency(
        &self,
        rest_client: &aptos_rest_client::Client,
        coin: TypeTag,
        version: Option<u64>,
    ) -> ApiResult<Option<Currency>> {
        {
            let currencies = self.currencies.read().unwrap();
            if let Some(currency) = currencies.get(&coin) {
                return Ok(currency.clone());
            }
        }

        let currency = self
            .get_currency_inner(rest_client, coin.clone(), version)
            .await?;
        self.currencies
            .write()
            .unwrap()
            .insert(coin, currency.clone());
        Ok(currency)
    }

    /// Pulls currency information from onchain
    pub async fn get_currency_inner(
        &self,
        rest_client: &aptos_rest_client::Client,
        coin: TypeTag,
        version: Option<u64>,
    ) -> ApiResult<Option<Currency>> {
        /// Type for deserializing coin info
        #[derive(Debug, Clone, Serialize, Deserialize)]
        struct CoinInfo {
            name: String,
            symbol: String,
            decimals: U64,
        }

        let struct_tag = match coin {
            TypeTag::Struct(ref tag) => tag,
            // This is a poorly formed coin, and we'll just skip over it
            _ => return Ok(None),
        };

        // Nested types are not supported for now
        if !struct_tag.type_params.is_empty() {
            return Ok(None);
        }

        // Retrieve the coin type
        const ENCODE_CHARS: &AsciiSet = &CONTROLS.add(b'<').add(b'>');
        let address = struct_tag.address;
        let resource_tag = format!("0x1::Coin::CoinInfo<{}>", struct_tag);
        let encoded_resource_tag = utf8_percent_encode(&resource_tag, ENCODE_CHARS).to_string();

        let response = if let Some(version) = version {
            rest_client
                .get_account_resource_at_version(address, &encoded_resource_tag, version)
                .await?
        } else {
            rest_client
                .get_account_resource(address, &encoded_resource_tag)
                .await?
        };

        // At this point if we've retrieved it and it's bad, we error out
        if let Some(resource) = response.into_inner() {
            let coin_info = serde_json::from_value::<CoinInfo>(resource.data).map_err(|_| {
                ApiError::DeserializationFailed(Some(format!(
                    "CoinInfo failed to deserialize for {}",
                    coin
                )))
            })?;

            Ok(Some(Currency {
                symbol: coin_info.symbol,
                decimals: coin_info.decimals.0,
            }))
        } else {
            Err(ApiError::DeserializationFailed(Some(format!(
                "Currency {} not found",
                coin
            ))))
        }
    }
}
<commit_msg>[aptos-rosetta] Breakdown account API<commit_after>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

//! Rosetta Account API
//!
//! See: [Account API Spec](https://www.rosetta-api.org/docs/AccountApi.html)
//!

use crate::{
    block::{block_index_to_version, version_to_block_index},
    common::{check_network, handle_request, with_context},
    error::{ApiError, ApiResult},
    types::{
        coin_identifier, coin_store_identifier, AccountBalanceRequest, AccountBalanceResponse,
        Amount, BlockIdentifier, Currency, PartialBlockIdentifier,
    },
    RosettaContext,
};
use aptos_crypto::HashValue;
use aptos_logger::{debug, trace};
use aptos_rest_client::{aptos::Balance, aptos_api_types::U64};
use aptos_sdk::move_types::language_storage::TypeTag;
use aptos_types::account_address::AccountAddress;
use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS};
use serde::{Deserialize, Serialize};
use std::{
    collections::{HashMap, HashSet},
    str::FromStr,
    sync::{Arc, RwLock},
};
use warp::Filter;

/// Account routes e.g. balance
pub fn routes(
    server_context: RosettaContext,
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
    warp::post().and(
        warp::path!("account" / "balance")
            .and(warp::body::json())
            .and(with_context(server_context))
            .and_then(handle_request(account_balance)),
    )
}

/// Account balance command
///
/// [API Spec](https://www.rosetta-api.org/docs/AccountApi.html#accountbalance)
async fn account_balance(
    request: AccountBalanceRequest,
    server_context: RosettaContext,
) -> ApiResult<AccountBalanceResponse> {
    debug!("/account/balance");
    trace!(
        request = ?request,
        server_context = ?server_context,
        "account_balance for [{}]",
        request.account_identifier.address
    );

    let network_identifier = request.network_identifier;

    check_network(network_identifier, &server_context)?;
    let block_size = server_context.block_size;
    let rest_client = server_context.rest_client()?;

    // Retrieve the block index to read
    let block_index =
        get_block_index_from_request(rest_client, request.block_identifier.clone(), block_size)
            .await?;

    // Version to grab is the last entry in the block (balance is at end of block)
    let block_version = block_index_to_version(block_size, block_index);
    let balance_version =
        block_index_to_version(block_size, block_index.saturating_add(1)).saturating_sub(1);

    let balances = get_balances(
        rest_client,
        request.account_identifier.account_address()?,
        balance_version,
    )
    .await?;

    let amounts = convert_balances_to_amounts(
        rest_client,
        server_context.coin_cache.clone(),
        request.currencies,
        balances,
        balance_version,
    )
    .await?;

    // Get the block identifier
    let response = rest_client
        .get_transaction_by_version(block_version)
        .await?;
    let block_identifier = BlockIdentifier::from_transaction(block_size, response.inner())?;

    Ok(AccountBalanceResponse {
        block_identifier,
        balances: amounts,
    })
}

/// Lookup currencies and convert them to Rosetta types
async fn convert_balances_to_amounts(
    rest_client: &aptos_rest_client::Client,
    coin_cache: Arc<CoinCache>,
    maybe_filter_currencies: Option<Vec<Currency>>,
    balances: HashMap<TypeTag, Balance>,
    balance_version: u64,
) -> ApiResult<Vec<Amount>> {
    let mut amounts = Vec::new();

    // Lookup coins, and fill in currency codes
    for (coin, balance) in balances {
        if let Some(currency) = coin_cache
            .get_currency(rest_client, coin, Some(balance_version))
            .await?
        {
            amounts.push(Amount {
                value: balance.coin.value.0.to_string(),
                currency,
            });
        }
    }

    // Filter based on requested currencies
    if let Some(currencies) = maybe_filter_currencies {
        let mut currencies: HashSet<Currency> = currencies.into_iter().collect();
        // Remove extra currencies not requested
        amounts = amounts
            .into_iter()
            .filter(|amount| currencies.contains(&amount.currency))
            .collect();

        // Zero out currencies that weren't in the account yet
        for amount in amounts.iter() {
            currencies.remove(&amount.currency);
        }
        for currency in currencies {
            amounts.push(Amount {
                value: 0.to_string(),
                currency,
            });
        }
    }

    Ok(amounts)
}

/// Determines which block to pull for the request
async fn get_block_index_from_request(
    rest_client: &aptos_rest_client::Client,
    partial_block_identifier: Option<PartialBlockIdentifier>,
    block_size: u64,
) -> ApiResult<u64> {
    Ok(match partial_block_identifier {
        Some(PartialBlockIdentifier {
            index: Some(_),
            hash: Some(_),
        }) => {
            return Err(ApiError::BlockParameterConflict);
        }
        // Lookup by block index
        Some(PartialBlockIdentifier {
            index: Some(block_index),
            hash: None,
        }) => block_index,
        // Lookup by block hash
        Some(PartialBlockIdentifier {
            index: None,
            hash: Some(hash),
        }) => {
            if hash == BlockIdentifier::genesis_txn().hash {
                0
            } else {
                // Lookup by hash doesn't work since we're faking blocks, need to verify that it's a
                // block
                let response =
                    rest_client
                        .get_transaction(HashValue::from_str(&hash).map_err(|err| {
                            ApiError::DeserializationFailed(Some(err.to_string()))
                        })?)
                        .await?;
                let version = response.inner().version();

                if let Some(version) = version {
                    let block_index = version_to_block_index(block_size, version);
                    // If it's not the beginning of a block, then it's invalid
                    if version != block_index_to_version(block_size, block_index) {
                        return Err(ApiError::TransactionIsPending);
                    }

                    block_index
                } else {
                    // If the transaction is pending, it's incomplete
                    return Err(ApiError::BlockIncomplete);
                }
            }
        }
        // Lookup latest version
        _ => {
            let response = rest_client.get_ledger_information().await?;
            let state = response.state();
            version_to_block_index(block_size, state.version) - 1
        }
    })
}

/// Retrieve the balances for an account
async fn get_balances(
    rest_client: &aptos_rest_client::Client,
    address: AccountAddress,
    version: u64,
) -> ApiResult<HashMap<TypeTag, Balance>> {
    // TODO: Handle non not found errors
    if let Ok(response) = rest_client
        .get_account_resources_at_version(address, version)
        .await
    {
        // Retrieve balances
        Ok(response
            .inner()
            .iter()
            .filter(|resource| {
                resource.resource_type.address == AccountAddress::ONE
                    && resource.resource_type.module == coin_identifier()
                    && resource.resource_type.name == coin_store_identifier()
            })
            .filter_map(|resource| {
                // Currency must have a type
                if let Some(coin_type) = resource.resource_type.type_params.first() {
                    match serde_json::from_value::<Balance>(resource.data.clone()) {
                        Ok(resource) => Some((coin_type.clone(), resource)),
                        Err(_) => None,
                    }
                } else {
                    // Skip currencies that don't match
                    None
                }
            })
            .collect())
    } else {
        Ok(HashMap::new())
    }
}

/// A cache for currencies, so we don't have to keep looking up the status of it
#[derive(Debug)]
pub struct CoinCache {
    currencies: RwLock<HashMap<TypeTag, Option<Currency>>>,
}

impl CoinCache {
    pub fn new() -> Self {
        Self {
            currencies: RwLock::new(HashMap::new()),
        }
    }

    /// Retrieve a currency and cache it if applicable
    pub async fn get_currency(
        &self,
        rest_client: &aptos_rest_client::Client,
        coin: TypeTag,
        version: Option<u64>,
    ) -> ApiResult<Option<Currency>> {
        {
            let currencies = self.currencies.read().unwrap();
            if let Some(currency) = currencies.get(&coin) {
                return Ok(currency.clone());
            }
        }

        let currency = self
            .get_currency_inner(rest_client, coin.clone(), version)
            .await?;
        self.currencies
            .write()
            .unwrap()
            .insert(coin, currency.clone());
        Ok(currency)
    }

    /// Pulls currency information from onchain
    pub async fn get_currency_inner(
        &self,
        rest_client: &aptos_rest_client::Client,
        coin: TypeTag,
        version: Option<u64>,
    ) -> ApiResult<Option<Currency>> {
        /// Type for deserializing coin info
        #[derive(Debug, Clone, Serialize, Deserialize)]
        struct CoinInfo {
            name: String,
            symbol: String,
            decimals: U64,
        }

        let struct_tag = match coin {
            TypeTag::Struct(ref tag) => tag,
            // This is a poorly formed coin, and we'll just skip over it
            _ => return Ok(None),
        };

        // Nested types are not supported for now
        if !struct_tag.type_params.is_empty() {
            return Ok(None);
        }

        // Retrieve the coin type
        const ENCODE_CHARS: &AsciiSet = &CONTROLS.add(b'<').add(b'>');
        let address = struct_tag.address;
        let resource_tag = format!("0x1::Coin::CoinInfo<{}>", struct_tag);
        let encoded_resource_tag = utf8_percent_encode(&resource_tag, ENCODE_CHARS).to_string();

        let response = if let Some(version) = version {
            rest_client
                .get_account_resource_at_version(address, &encoded_resource_tag, version)
                .await?
        } else {
            rest_client
                .get_account_resource(address, &encoded_resource_tag)
                .await?
        };

        // At this point if we've retrieved it and it's bad, we error out
        if let Some(resource) = response.into_inner() {
            let coin_info = serde_json::from_value::<CoinInfo>(resource.data).map_err(|_| {
                ApiError::DeserializationFailed(Some(format!(
                    "CoinInfo failed to deserialize for {}",
                    coin
                )))
            })?;

            Ok(Some(Currency {
                symbol: coin_info.symbol,
                decimals: coin_info.decimals.0,
            }))
        } else {
            Err(ApiError::DeserializationFailed(Some(format!(
                "Currency {} not found",
                coin
            ))))
        }
    }
}
<|endoftext|>


<commit_before>//! Endpoints for spaces.
//!
//! See the [Matrix specification][spec] for more details about spaces.
//!
//! [spec]: https://spec.matrix.org/v1.2/client-server-api/#spaces

use js_int::UInt;
use ruma_common::{
    directory::PublicRoomJoinRule, events::space::child::HierarchySpaceChildStateEvent,
    room::RoomType, serde::Raw, MxcUri, RoomAliasId, RoomId, RoomName,
};
use serde::{Deserialize, Serialize};

pub mod get_hierarchy;

/// A chunk of a space hierarchy response, describing one room.
///
/// To create an instance of this type, first create a `SpaceHierarchyRoomsChunkInit` and convert it
/// via `SpaceHierarchyRoomsChunk::from` / `.into()`.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct SpaceHierarchyRoomsChunk {
    /// The canonical alias of the room, if any.
    #[serde(skip_serializing_if = "Option::is_none")]
    #[cfg_attr(
        feature = "compat",
        serde(default, deserialize_with = "ruma_common::serde::empty_string_as_none")
    )]
    pub canonical_alias: Option<Box<RoomAliasId>>,

    /// The name of the room, if any.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<Box<RoomName>>,

    /// The number of members joined to the room.
    pub num_joined_members: UInt,

    /// The ID of the room.
    pub room_id: Box<RoomId>,

    /// The topic of the room, if any.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub topic: Option<String>,

    /// Whether the room may be viewed by guest users without joining.
    pub world_readable: bool,

    /// Whether guest users may join the room and participate in it.
    ///
    /// If they can, they will be subject to ordinary power level rules like any other user.
    pub guest_can_join: bool,

    /// The URL for the room's avatar, if one is set.
    ///
    /// If you activate the `compat` feature, this field being an empty string in JSON will result
    /// in `None` here during deserialization.
    #[serde(skip_serializing_if = "Option::is_none")]
    #[cfg_attr(
        feature = "compat",
        serde(default, deserialize_with = "ruma_common::serde::empty_string_as_none")
    )]
    pub avatar_url: Option<Box<MxcUri>>,

    /// The join rule of the room.
    #[serde(default, skip_serializing_if = "ruma_common::serde::is_default")]
    pub join_rule: PublicRoomJoinRule,

    /// The type of room from `m.room.create`, if any.
    pub room_type: Option<RoomType>,

    /// The stripped `m.space.child` events of the space-room.
    ///
    /// If the room is not a space-room, this should be empty.
    pub children_state: Vec<Raw<HierarchySpaceChildStateEvent>>,
}

/// Initial set of mandatory fields of `SpaceHierarchyRoomsChunk`.
///
/// This struct will not be updated even if additional fields are added to
/// `SpaceHierarchyRoomsChunk` in a new (non-breaking) release of the Matrix specification.
#[derive(Debug)]
#[allow(clippy::exhaustive_structs)]
pub struct SpaceHierarchyRoomsChunkInit {
    /// The number of members joined to the room.
    pub num_joined_members: UInt,

    /// The ID of the room.
    pub room_id: Box<RoomId>,

    /// Whether the room may be viewed by guest users without joining.
    pub world_readable: bool,

    /// Whether guest users may join the room and participate in it.
    ///
    /// If they can, they will be subject to ordinary power level rules like any other user.
    pub guest_can_join: bool,

    /// The join rule of the room.
    pub join_rule: PublicRoomJoinRule,

    /// The stripped `m.space.child` events of the space-room.
    ///
    /// If the room is not a space-room, this should be empty.
    pub children_state: Vec<Raw<HierarchySpaceChildStateEvent>>,
}

impl From<SpaceHierarchyRoomsChunkInit> for SpaceHierarchyRoomsChunk {
    fn from(init: SpaceHierarchyRoomsChunkInit) -> Self {
        let SpaceHierarchyRoomsChunkInit {
            num_joined_members,
            room_id,
            world_readable,
            guest_can_join,
            join_rule,
            children_state,
        } = init;

        Self {
            canonical_alias: None,
            name: None,
            num_joined_members,
            room_id,
            topic: None,
            world_readable,
            guest_can_join,
            avatar_url: None,
            join_rule,
            room_type: None,
            children_state,
        }
    }
}
<commit_msg>client-api: Create SpaceRoomJoinRule and use it<commit_after>//! Endpoints for spaces.
//!
//! See the [Matrix specification][spec] for more details about spaces.
//!
//! [spec]: https://spec.matrix.org/v1.2/client-server-api/#spaces

use js_int::UInt;
use ruma_common::{
    events::space::child::HierarchySpaceChildStateEvent,
    room::RoomType,
    serde::{Raw, StringEnum},
    MxcUri, RoomAliasId, RoomId, RoomName,
};
use serde::{Deserialize, Serialize};

use crate::PrivOwnedStr;

pub mod get_hierarchy;

/// A chunk of a space hierarchy response, describing one room.
///
/// To create an instance of this type, first create a `SpaceHierarchyRoomsChunkInit` and convert it
/// via `SpaceHierarchyRoomsChunk::from` / `.into()`.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct SpaceHierarchyRoomsChunk {
    /// The canonical alias of the room, if any.
    #[serde(skip_serializing_if = "Option::is_none")]
    #[cfg_attr(
        feature = "compat",
        serde(default, deserialize_with = "ruma_common::serde::empty_string_as_none")
    )]
    pub canonical_alias: Option<Box<RoomAliasId>>,

    /// The name of the room, if any.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<Box<RoomName>>,

    /// The number of members joined to the room.
    pub num_joined_members: UInt,

    /// The ID of the room.
    pub room_id: Box<RoomId>,

    /// The topic of the room, if any.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub topic: Option<String>,

    /// Whether the room may be viewed by guest users without joining.
    pub world_readable: bool,

    /// Whether guest users may join the room and participate in it.
    ///
    /// If they can, they will be subject to ordinary power level rules like any other user.
    pub guest_can_join: bool,

    /// The URL for the room's avatar, if one is set.
    ///
    /// If you activate the `compat` feature, this field being an empty string in JSON will result
    /// in `None` here during deserialization.
    #[serde(skip_serializing_if = "Option::is_none")]
    #[cfg_attr(
        feature = "compat",
        serde(default, deserialize_with = "ruma_common::serde::empty_string_as_none")
    )]
    pub avatar_url: Option<Box<MxcUri>>,

    /// The join rule of the room.
    #[serde(default, skip_serializing_if = "ruma_common::serde::is_default")]
    pub join_rule: SpaceRoomJoinRule,

    /// The type of room from `m.room.create`, if any.
    pub room_type: Option<RoomType>,

    /// The stripped `m.space.child` events of the space-room.
    ///
    /// If the room is not a space-room, this should be empty.
    pub children_state: Vec<Raw<HierarchySpaceChildStateEvent>>,
}

/// Initial set of mandatory fields of `SpaceHierarchyRoomsChunk`.
///
/// This struct will not be updated even if additional fields are added to
/// `SpaceHierarchyRoomsChunk` in a new (non-breaking) release of the Matrix specification.
#[derive(Debug)]
#[allow(clippy::exhaustive_structs)]
pub struct SpaceHierarchyRoomsChunkInit {
    /// The number of members joined to the room.
    pub num_joined_members: UInt,

    /// The ID of the room.
    pub room_id: Box<RoomId>,

    /// Whether the room may be viewed by guest users without joining.
    pub world_readable: bool,

    /// Whether guest users may join the room and participate in it.
    ///
    /// If they can, they will be subject to ordinary power level rules like any other user.
    pub guest_can_join: bool,

    /// The join rule of the room.
    pub join_rule: SpaceRoomJoinRule,

    /// The stripped `m.space.child` events of the space-room.
    ///
    /// If the room is not a space-room, this should be empty.
    pub children_state: Vec<Raw<HierarchySpaceChildStateEvent>>,
}

impl From<SpaceHierarchyRoomsChunkInit> for SpaceHierarchyRoomsChunk {
    fn from(init: SpaceHierarchyRoomsChunkInit) -> Self {
        let SpaceHierarchyRoomsChunkInit {
            num_joined_members,
            room_id,
            world_readable,
            guest_can_join,
            join_rule,
            children_state,
        } = init;

        Self {
            canonical_alias: None,
            name: None,
            num_joined_members,
            room_id,
            topic: None,
            world_readable,
            guest_can_join,
            avatar_url: None,
            join_rule,
            room_type: None,
            children_state,
        }
    }
}

/// The rule used for users wishing to join a room.
///
/// In contrast to the regular [`JoinRule`](ruma_common::events::room::join_rules::JoinRule), this
/// enum does not hold the conditions for joining restricted rooms. Instead, the server is assumed
/// to only return rooms the user is allowed to join in a space hierarchy listing response.
///
/// This type can hold an arbitrary string. To check for join rules that are not available as a
/// documented variant here, use its string representation, obtained through `.as_str()`.
#[derive(Clone, Debug, PartialEq, Eq, StringEnum)]
#[ruma_enum(rename_all = "snake_case")]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub enum SpaceRoomJoinRule {
    /// A user who wishes to join the room must first receive an invite to the room from someone
    /// already inside of the room.
    Invite,

    /// Users can join the room if they are invited, or they can request an invite to the room.
    ///
    /// They can be allowed (invited) or denied (kicked/banned) access.
    Knock,

    /// Reserved but not yet implemented by the Matrix specification.
    Private,

    /// Users can join the room if they are invited, or if they meet any of the conditions
    /// described in a set of [`AllowRule`](ruma_common::events::room::join_rules::AllowRule)s.
    ///
    /// These rules are not made available as part of a space hierarchy listing response and can
    /// only be seen by users inside the room.
    Restricted,

    /// Anyone can join the room without any prior action.
    Public,

    #[doc(hidden)]
    _Custom(PrivOwnedStr),
}

impl Default for SpaceRoomJoinRule {
    fn default() -> Self {
        SpaceRoomJoinRule::Public
    }
}
<|endoftext|>


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

// Marauder is grid-less turn-based strategy game.

extern mod glfw;
extern mod gl;
extern mod cgmath;

use gl::types::{
  GLenum,
  GLuint,
  GLint,
  GLchar,
  GLfloat,
  GLsizeiptr,
  GLboolean
};
use cgmath::matrix::{
  Matrix,
  Mat4,
  Mat3,
  ToMat4
};
use cgmath::vector::{
  Vec3,
  Vec2
};
use cgmath::projection;
use cgmath::angle;

#[link(name = "glfw")]
#[link(name = "m")]
extern {}

/// usage: let s = mvp_matrix; println(type_of(&s));
fn type_of<T>(_: &T) -> &'static str {
  unsafe {
    (*std::unstable::intrinsics::get_tydesc::<T>()).name
  }
}

pub mod misc {
  use cgmath::matrix::Mat4;

  pub fn print_mat4(name: &str, mat: Mat4<f32>) {
    println!("{}:", name);
    for i in range(0u, 4) {
      for j in range(0u, 4) {
        print!("{} ", *mat.cr(i, j));
      }
      println("");
    }
    println("");
  }
}

static mut MOUSE_POS: Vec2<f32> = Vec2{x: 0.0f32, y: 0.0};
static mut CAMERA_POS: Vec3<f32> = Vec3{x: 0.0f32, y: 0.0, z: 0.0};

static WIN_SIZE: Vec2<uint> = Vec2{x: 640, y: 480};
static VERTICES_COUNT: i32 = 3 * 2;

static VERTEX_DATA: [GLfloat, ..VERTICES_COUNT * 3] = [
   0.0,  1.0, 0.0,
   2.0, -1.0, 0.0,
  -2.0, -1.0, 0.0,

  0.0,  1.0,  0.0,
  0.0, -1.0,  2.0,
  0.0, -1.0, -2.0
];

static VERTEX_SHADER_SRC: &'static str = "
  #version 130
  in vec3 position;
  uniform mat4 model_view_proj_matrix;
  void main() {
    vec4 v = vec4(position, 1);
    gl_Position = model_view_proj_matrix * v;
  }
";

static FRAGMENT_SHADER_SRC: &'static str = "
  #version 130
  out vec4 out_color;
  void main() {
    out_color = vec4(1.0, 1.0, 1.0, 1.0);
  }
";

fn compile_shader(src: &str, ty: GLenum) -> GLuint {
  let shader = gl::CreateShader(ty);
  unsafe {
    gl::ShaderSource(shader, 1, &src.to_c_str().unwrap(), std::ptr::null());
    gl::CompileShader(shader);

    let mut status = gl::FALSE as GLint;
    gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);

    if status != (gl::TRUE as GLint) {
      let mut len = 0;
      gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
      // subtract 1 to skip the trailing null character
      let mut buf = std::vec::from_elem(len as uint - 1, 0u8);
      gl::GetShaderInfoLog(shader, len, std::ptr::mut_null(),
        std::vec::raw::to_mut_ptr(buf) as *mut GLchar
      );
      fail!("compile_shader(): " + std::str::raw::from_utf8(buf));
    }
  }
  shader
}

fn link_program(vertex_shader: GLuint, fragment_shader: GLuint) -> GLuint {
  let program = gl::CreateProgram();
  gl::AttachShader(program, vertex_shader);
  gl::AttachShader(program, fragment_shader);
  gl::LinkProgram(program);
  unsafe {
    let mut status = gl::FALSE as GLint;
    gl::GetProgramiv(program, gl::LINK_STATUS, &mut status);

    if status != (gl::TRUE as GLint) {
      let mut len: GLint = 0;
      gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);
      // subtract 1 to skip the trailing null character
      let mut buf = std::vec::from_elem(len as uint - 1, 0u8);
      gl::GetProgramInfoLog(program, len, std::ptr::mut_null(),
        std::vec::raw::to_mut_ptr(buf) as *mut GLchar
      );
      fail!("link_program(): " + std::str::raw::from_utf8(buf));
    }
  }
  program
}

fn tr(m: Mat4<f32>, v: Vec3<f32>) -> Mat4<f32> {
  let mut t = Mat4::<f32>::identity();
  *t.mut_cr(3, 0) = v.x;
  *t.mut_cr(3, 1) = v.y;
  *t.mut_cr(3, 2) = v.z;
  m.mul_m(&t)
}

fn rot_x(m: Mat4<f32>, angle: f32) -> Mat4<f32> {
  let r = Mat3::from_angle_x(angle::rad(angle)).to_mat4();
  m.mul_m(&r)
}

fn rot_y(m: Mat4<f32>, angle: f32) -> Mat4<f32> {
  let r = Mat3::from_angle_y(angle::rad(angle)).to_mat4();
  m.mul_m(&r)
}

struct Win {
  vertex_shader: GLuint,
  fragment_shader: GLuint,
  program: GLuint,
  vertex_array_obj: GLuint,
  vertex_buffer_obj: GLuint,
  matrix_id: GLint,
  projection_matrix: Mat4<f32>,
  window: glfw::Window
}

fn get_projection_matrix() -> Mat4<f32> {
  let fov = angle::deg(45.0f32);
  let ratio = 4.0 / 3.0;
  let display_range_min = 0.1;
  let display_range_max = 100.0;
  projection::perspective(
    fov, ratio, display_range_min, display_range_max
  )
}

impl Win {
  fn new() -> Win {
    let mut win = Win{
      vertex_shader: 0,
      fragment_shader: 0,
      program: 0,
      vertex_array_obj: 0,
      vertex_buffer_obj: 0,
      matrix_id: 0,
      projection_matrix: get_projection_matrix(),
      window: glfw::Window::create(
        WIN_SIZE.x,
        WIN_SIZE.y,
        "OpenGL",
        glfw::Windowed
      ).unwrap()
    };
    win.init_opengl();
    win
  }

  fn init_opengl(&mut self) {
    // TODO: Move to opengl_init method
    // glfw::window_hint::context_version(3, 2);

    self.window.make_context_current();
    self.window.set_cursor_pos_callback(~CursorPosContext);
    self.window.set_key_callback(~KeyContext);

    // Load the OpenGL function pointers
    gl::load_with(glfw::get_proc_address);

    self.vertex_shader = compile_shader(
      VERTEX_SHADER_SRC, gl::VERTEX_SHADER);
    self.fragment_shader = compile_shader(
      FRAGMENT_SHADER_SRC, gl::FRAGMENT_SHADER);
    self.program = link_program(self.vertex_shader, self.fragment_shader);

    unsafe {
      // Create Vertex Array Object
      gl::GenVertexArrays(1, &mut self.vertex_array_obj);
      gl::BindVertexArray(self.vertex_array_obj);

      // Create a Vertex Buffer Object
      gl::GenBuffers(1, &mut self.vertex_buffer_obj);
      gl::BindBuffer(gl::ARRAY_BUFFER, self.vertex_buffer_obj);

      // Copy vertex data to VBO
      let float_size = std::mem::size_of::<GLfloat>();
      let vertices_ptr = (VERTEX_DATA.len() * float_size) as GLsizeiptr;
      gl::BufferData(
        gl::ARRAY_BUFFER,
        vertices_ptr,
        std::cast::transmute(&VERTEX_DATA[0]),
        gl::STATIC_DRAW
      );

      gl::UseProgram(self.program);
      gl::BindFragDataLocation(
        self.program, 0, "out_color".to_c_str().unwrap());

      // Specify the layout of the vertex data
      let pos_attr = gl::GetAttribLocation(
        self.program, "position".to_c_str().unwrap()) as GLuint;
      gl::EnableVertexAttribArray(pos_attr);

      let size = 3;
      let normalized = gl::FALSE as GLboolean;
      let stride = 0;
      gl::VertexAttribPointer(
        pos_attr,
        size,
        gl::FLOAT,
        normalized,
        stride,
        std::ptr::null()
      );

      self.matrix_id = gl::GetUniformLocation(
        self.program, "model_view_proj_matrix".to_c_str().unwrap()
      );
    }
  }

  fn cleanup(&self) {
    gl::DeleteProgram(self.program);
    gl::DeleteShader(self.fragment_shader);
    gl::DeleteShader(self.vertex_shader);
    unsafe {
      gl::DeleteBuffers(1, &self.vertex_buffer_obj);
      gl::DeleteVertexArrays(1, &self.vertex_array_obj);
    }
  }

  fn update_matrices(&self) {
    let mut mvp_matrix = self.projection_matrix;
    unsafe {
      mvp_matrix = tr(mvp_matrix, Vec3{x: 0.0f32, y: 0.0, z: -10.0f32});
      mvp_matrix = rot_x(mvp_matrix, MOUSE_POS.y / 100.0);
      mvp_matrix = rot_y(mvp_matrix, MOUSE_POS.x / 100.0);
      mvp_matrix = tr(mvp_matrix, CAMERA_POS);

      // Send our transformation to the currently bound shader,
      // in the "model_view_proj_matrix" uniform for each model
      // you render, since the model_view_proj_matrix will be
      // different (at least the M part).
      gl::UniformMatrix4fv(self.matrix_id, 1, gl::FALSE, mvp_matrix.cr(0, 0));
    }
  }

  fn draw(&self) {
    self.update_matrices();
    gl::ClearColor(0.3, 0.3, 0.3, 1.0);
    gl::Clear(gl::COLOR_BUFFER_BIT);
    gl::DrawArrays(gl::TRIANGLES, 0, VERTICES_COUNT);
    self.window.swap_buffers();
  }

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

  fn process_events(&self) {
    glfw::poll_events();
  }
}

struct CursorPosContext;
impl glfw::CursorPosCallback for CursorPosContext {
  fn call(&self, w: &glfw::Window, xpos: f64, ypos: f64) {
    if w.get_mouse_button(glfw::MouseButtonRight) == glfw::Press {
      unsafe {
        MOUSE_POS.x = xpos as f32;
        MOUSE_POS.y = ypos as f32;
      }
    }
  }
}

struct ErrorContext;
impl glfw::ErrorCallback for ErrorContext {
  fn call(&self, _: glfw::Error, description: ~str) {
    println!("GLFW Error: {:s}", description);
  }
}

struct KeyContext;
impl glfw::KeyCallback for KeyContext {
  fn call(
    &self,
    window: &glfw::Window,
    key:    glfw::Key,
    _:      std::libc::c_int,
    action: glfw::Action,
    _:      glfw::Modifiers
  ) {
    let distance = 1.0;
    if action != glfw::Press {
      return;
    }
    match key {
      glfw::KeyEscape | glfw::KeyQ
                     => window.set_should_close(true),
      glfw::KeySpace => println!("space"),
      glfw::KeyUp    => unsafe { CAMERA_POS.y -= distance },
      glfw::KeyDown  => unsafe { CAMERA_POS.y += distance },
      glfw::KeyRight => unsafe { CAMERA_POS.x -= distance },
      glfw::KeyLeft  => unsafe { CAMERA_POS.x += distance },
      _ => {}
    }
  }
}

fn main() {
  glfw::set_error_callback(~ErrorContext);
  do glfw::start {
    let win =  Win::new();
    while win.is_running() {
      win.process_events();
      win.draw();
    }
    win.cleanup();
  }
}

#[start]
fn start(argc: int, argv: **u8) -> int {
  std::rt::start_on_main_thread(argc, argv, main)
}

// vim: set tabstop=2 shiftwidth=2 softtabstop=2 expandtab:
<commit_msg>Moved type_of(..) to misc mod<commit_after>// See LICENSE file for copyright and license details.

// Marauder is grid-less turn-based strategy game.

extern mod glfw;
extern mod gl;
extern mod cgmath;

use gl::types::{
  GLenum,
  GLuint,
  GLint,
  GLchar,
  GLfloat,
  GLsizeiptr,
  GLboolean
};
use cgmath::matrix::{
  Matrix,
  Mat4,
  Mat3,
  ToMat4
};
use cgmath::vector::{
  Vec3,
  Vec2
};
use cgmath::projection;
use cgmath::angle;

#[link(name = "glfw")]
#[link(name = "m")]
extern {}

pub mod misc {
  use cgmath::matrix::Mat4;
  use std;

  pub fn print_mat4(name: &str, mat: Mat4<f32>) {
    println!("{}:", name);
    for i in range(0u, 4) {
      for j in range(0u, 4) {
        print!("{} ", *mat.cr(i, j));
      }
      println("");
    }
    println("");
  }

  /// usage: let s = mvp_matrix; println(type_of(&s));
  pub fn type_of<T>(_: &T) -> &'static str {
    unsafe {
      (*std::unstable::intrinsics::get_tydesc::<T>()).name
    }
  }
}

static mut MOUSE_POS: Vec2<f32> = Vec2{x: 0.0f32, y: 0.0};
static mut CAMERA_POS: Vec3<f32> = Vec3{x: 0.0f32, y: 0.0, z: 0.0};

static WIN_SIZE: Vec2<uint> = Vec2{x: 640, y: 480};
static VERTICES_COUNT: i32 = 3 * 2;

static VERTEX_DATA: [GLfloat, ..VERTICES_COUNT * 3] = [
   0.0,  1.0, 0.0,
   2.0, -1.0, 0.0,
  -2.0, -1.0, 0.0,

  0.0,  1.0,  0.0,
  0.0, -1.0,  2.0,
  0.0, -1.0, -2.0
];

static VERTEX_SHADER_SRC: &'static str = "
  #version 130
  in vec3 position;
  uniform mat4 model_view_proj_matrix;
  void main() {
    vec4 v = vec4(position, 1);
    gl_Position = model_view_proj_matrix * v;
  }
";

static FRAGMENT_SHADER_SRC: &'static str = "
  #version 130
  out vec4 out_color;
  void main() {
    out_color = vec4(1.0, 1.0, 1.0, 1.0);
  }
";

fn compile_shader(src: &str, ty: GLenum) -> GLuint {
  let shader = gl::CreateShader(ty);
  unsafe {
    gl::ShaderSource(shader, 1, &src.to_c_str().unwrap(), std::ptr::null());
    gl::CompileShader(shader);

    let mut status = gl::FALSE as GLint;
    gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);

    if status != (gl::TRUE as GLint) {
      let mut len = 0;
      gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
      // subtract 1 to skip the trailing null character
      let mut buf = std::vec::from_elem(len as uint - 1, 0u8);
      gl::GetShaderInfoLog(shader, len, std::ptr::mut_null(),
        std::vec::raw::to_mut_ptr(buf) as *mut GLchar
      );
      fail!("compile_shader(): " + std::str::raw::from_utf8(buf));
    }
  }
  shader
}

fn link_program(vertex_shader: GLuint, fragment_shader: GLuint) -> GLuint {
  let program = gl::CreateProgram();
  gl::AttachShader(program, vertex_shader);
  gl::AttachShader(program, fragment_shader);
  gl::LinkProgram(program);
  unsafe {
    let mut status = gl::FALSE as GLint;
    gl::GetProgramiv(program, gl::LINK_STATUS, &mut status);

    if status != (gl::TRUE as GLint) {
      let mut len: GLint = 0;
      gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);
      // subtract 1 to skip the trailing null character
      let mut buf = std::vec::from_elem(len as uint - 1, 0u8);
      gl::GetProgramInfoLog(program, len, std::ptr::mut_null(),
        std::vec::raw::to_mut_ptr(buf) as *mut GLchar
      );
      fail!("link_program(): " + std::str::raw::from_utf8(buf));
    }
  }
  program
}

fn tr(m: Mat4<f32>, v: Vec3<f32>) -> Mat4<f32> {
  let mut t = Mat4::<f32>::identity();
  *t.mut_cr(3, 0) = v.x;
  *t.mut_cr(3, 1) = v.y;
  *t.mut_cr(3, 2) = v.z;
  m.mul_m(&t)
}

fn rot_x(m: Mat4<f32>, angle: f32) -> Mat4<f32> {
  let r = Mat3::from_angle_x(angle::rad(angle)).to_mat4();
  m.mul_m(&r)
}

fn rot_y(m: Mat4<f32>, angle: f32) -> Mat4<f32> {
  let r = Mat3::from_angle_y(angle::rad(angle)).to_mat4();
  m.mul_m(&r)
}

struct Win {
  vertex_shader: GLuint,
  fragment_shader: GLuint,
  program: GLuint,
  vertex_array_obj: GLuint,
  vertex_buffer_obj: GLuint,
  matrix_id: GLint,
  projection_matrix: Mat4<f32>,
  window: glfw::Window
}

fn get_projection_matrix() -> Mat4<f32> {
  let fov = angle::deg(45.0f32);
  let ratio = 4.0 / 3.0;
  let display_range_min = 0.1;
  let display_range_max = 100.0;
  projection::perspective(
    fov, ratio, display_range_min, display_range_max
  )
}

impl Win {
  fn new() -> Win {
    let mut win = Win{
      vertex_shader: 0,
      fragment_shader: 0,
      program: 0,
      vertex_array_obj: 0,
      vertex_buffer_obj: 0,
      matrix_id: 0,
      projection_matrix: get_projection_matrix(),
      window: glfw::Window::create(
        WIN_SIZE.x,
        WIN_SIZE.y,
        "OpenGL",
        glfw::Windowed
      ).unwrap()
    };
    win.init_opengl();
    win
  }

  fn init_opengl(&mut self) {
    // TODO: Move to opengl_init method
    // glfw::window_hint::context_version(3, 2);

    self.window.make_context_current();
    self.window.set_cursor_pos_callback(~CursorPosContext);
    self.window.set_key_callback(~KeyContext);

    // Load the OpenGL function pointers
    gl::load_with(glfw::get_proc_address);

    self.vertex_shader = compile_shader(
      VERTEX_SHADER_SRC, gl::VERTEX_SHADER);
    self.fragment_shader = compile_shader(
      FRAGMENT_SHADER_SRC, gl::FRAGMENT_SHADER);
    self.program = link_program(self.vertex_shader, self.fragment_shader);

    unsafe {
      // Create Vertex Array Object
      gl::GenVertexArrays(1, &mut self.vertex_array_obj);
      gl::BindVertexArray(self.vertex_array_obj);

      // Create a Vertex Buffer Object
      gl::GenBuffers(1, &mut self.vertex_buffer_obj);
      gl::BindBuffer(gl::ARRAY_BUFFER, self.vertex_buffer_obj);

      // Copy vertex data to VBO
      let float_size = std::mem::size_of::<GLfloat>();
      let vertices_ptr = (VERTEX_DATA.len() * float_size) as GLsizeiptr;
      gl::BufferData(
        gl::ARRAY_BUFFER,
        vertices_ptr,
        std::cast::transmute(&VERTEX_DATA[0]),
        gl::STATIC_DRAW
      );

      gl::UseProgram(self.program);
      gl::BindFragDataLocation(
        self.program, 0, "out_color".to_c_str().unwrap());

      // Specify the layout of the vertex data
      let pos_attr = gl::GetAttribLocation(
        self.program, "position".to_c_str().unwrap()) as GLuint;
      gl::EnableVertexAttribArray(pos_attr);

      let size = 3;
      let normalized = gl::FALSE as GLboolean;
      let stride = 0;
      gl::VertexAttribPointer(
        pos_attr,
        size,
        gl::FLOAT,
        normalized,
        stride,
        std::ptr::null()
      );

      self.matrix_id = gl::GetUniformLocation(
        self.program, "model_view_proj_matrix".to_c_str().unwrap()
      );
    }
  }

  fn cleanup(&self) {
    gl::DeleteProgram(self.program);
    gl::DeleteShader(self.fragment_shader);
    gl::DeleteShader(self.vertex_shader);
    unsafe {
      gl::DeleteBuffers(1, &self.vertex_buffer_obj);
      gl::DeleteVertexArrays(1, &self.vertex_array_obj);
    }
  }

  fn update_matrices(&self) {
    let mut mvp_matrix = self.projection_matrix;
    unsafe {
      mvp_matrix = tr(mvp_matrix, Vec3{x: 0.0f32, y: 0.0, z: -10.0f32});
      mvp_matrix = rot_x(mvp_matrix, MOUSE_POS.y / 100.0);
      mvp_matrix = rot_y(mvp_matrix, MOUSE_POS.x / 100.0);
      mvp_matrix = tr(mvp_matrix, CAMERA_POS);

      // Send our transformation to the currently bound shader,
      // in the "model_view_proj_matrix" uniform for each model
      // you render, since the model_view_proj_matrix will be
      // different (at least the M part).
      gl::UniformMatrix4fv(self.matrix_id, 1, gl::FALSE, mvp_matrix.cr(0, 0));
    }
  }

  fn draw(&self) {
    self.update_matrices();
    gl::ClearColor(0.3, 0.3, 0.3, 1.0);
    gl::Clear(gl::COLOR_BUFFER_BIT);
    gl::DrawArrays(gl::TRIANGLES, 0, VERTICES_COUNT);
    self.window.swap_buffers();
  }

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

  fn process_events(&self) {
    glfw::poll_events();
  }
}

struct CursorPosContext;
impl glfw::CursorPosCallback for CursorPosContext {
  fn call(&self, w: &glfw::Window, xpos: f64, ypos: f64) {
    if w.get_mouse_button(glfw::MouseButtonRight) == glfw::Press {
      unsafe {
        MOUSE_POS.x = xpos as f32;
        MOUSE_POS.y = ypos as f32;
      }
    }
  }
}

struct ErrorContext;
impl glfw::ErrorCallback for ErrorContext {
  fn call(&self, _: glfw::Error, description: ~str) {
    println!("GLFW Error: {:s}", description);
  }
}

struct KeyContext;
impl glfw::KeyCallback for KeyContext {
  fn call(
    &self,
    window: &glfw::Window,
    key:    glfw::Key,
    _:      std::libc::c_int,
    action: glfw::Action,
    _:      glfw::Modifiers
  ) {
    let distance = 1.0;
    if action != glfw::Press {
      return;
    }
    match key {
      glfw::KeyEscape | glfw::KeyQ
                     => window.set_should_close(true),
      glfw::KeySpace => println!("space"),
      glfw::KeyUp    => unsafe { CAMERA_POS.y -= distance },
      glfw::KeyDown  => unsafe { CAMERA_POS.y += distance },
      glfw::KeyRight => unsafe { CAMERA_POS.x -= distance },
      glfw::KeyLeft  => unsafe { CAMERA_POS.x += distance },
      _ => {}
    }
  }
}

fn main() {
  glfw::set_error_callback(~ErrorContext);
  do glfw::start {
    let win =  Win::new();
    while win.is_running() {
      win.process_events();
      win.draw();
    }
    win.cleanup();
  }
}

#[start]
fn start(argc: int, argv: **u8) -> int {
  std::rt::start_on_main_thread(argc, argv, main)
}

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

extern crate libc;

use core_foundation_sys::runloop::*;
use libc::c_void;
use platform::iohid::CFRunLoopEntryObserver;
use platform::iokit::{IOHIDDeviceRef, SendableRunLoop};
use platform::monitor::Monitor;
use std::io;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::thread;
use util::{io_err, to_io_err, OnceCallback};

// A transaction will run the given closure in a new thread, thereby using a
// separate per-thread state machine for each HID. It will either complete or
// fail through user action, timeout, or be cancelled when overridden by a new
// transaction.
pub struct Transaction {
    runloop: SendableRunLoop,
    thread: Option<thread::JoinHandle<()>>,
}

impl Transaction {
    pub fn new<F, T>(timeout: u64, callback: OnceCallback<T>, new_device_cb: F) -> io::Result<Self>
    where
        F: Fn(IOHIDDeviceRef, Receiver<Vec<u8>>, &Fn() -> bool) + Sync + Send + 'static,
        T: 'static,
    {
        let (tx, rx) = channel();
        let cbc = callback.clone();
        let timeout = (timeout as f64) / 1000.0;

        let builder = thread::Builder::new();
        let thread = builder.spawn(move || {
            // Add a runloop observer that will be notified when we enter the
            // runloop and tx.send() the current runloop to the owning thread.
            // We need to ensure the runloop was entered before unblocking
            // Transaction::new(), so we can always properly cancel.
            let context = &tx as *const _ as *mut c_void;
            let obs = CFRunLoopEntryObserver::new(Transaction::observe, context);
            obs.add_to_current_runloop();

            // Create a new HID device monitor and start polling.
            let mut monitor = Monitor::new(new_device_cb);
            try_or!(monitor.start(), |e| cbc.call(Err(e)));

            // This will block until completion, abortion, or timeout.
            unsafe { CFRunLoopRunInMode(kCFRunLoopDefaultMode, timeout, 0) };

            // Close the monitor and its devices.
            monitor.stop();

            // Send an error, if the callback wasn't called already.
            cbc.call(Err(io_err("aborted or timed out")));
        })?;

        // Block until we enter the CFRunLoop.
        let runloop = rx.recv().map_err(to_io_err)?;

        Ok(Self {
            runloop,
            thread: Some(thread),
        })
    }

    extern "C" fn observe(_: CFRunLoopObserverRef, _: CFRunLoopActivity, context: *mut c_void) {
        let tx: &Sender<SendableRunLoop> = unsafe { &*(context as *mut _) };

        // Send the current runloop to the receiver to unblock it.
        let _ = tx.send(SendableRunLoop(unsafe { CFRunLoopGetCurrent() }));
    }

    pub fn cancel(&mut self) {
        // (This call doesn't block.)
        unsafe { CFRunLoopStop(*self.runloop) };

        // This must never be None. Ignore return value.
        let _ = self.thread.take().unwrap().join();
    }
}
<commit_msg>Small cleanup macos::Transaction::new()<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/. */

extern crate libc;

use core_foundation_sys::runloop::*;
use libc::c_void;
use platform::iohid::CFRunLoopEntryObserver;
use platform::iokit::{IOHIDDeviceRef, SendableRunLoop};
use platform::monitor::Monitor;
use std::io;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::thread;
use util::{io_err, to_io_err, OnceCallback};

// A transaction will run the given closure in a new thread, thereby using a
// separate per-thread state machine for each HID. It will either complete or
// fail through user action, timeout, or be cancelled when overridden by a new
// transaction.
pub struct Transaction {
    runloop: SendableRunLoop,
    thread: Option<thread::JoinHandle<()>>,
}

impl Transaction {
    pub fn new<F, T>(timeout: u64, callback: OnceCallback<T>, new_device_cb: F) -> io::Result<Self>
    where
        F: Fn(IOHIDDeviceRef, Receiver<Vec<u8>>, &Fn() -> bool) + Sync + Send + 'static,
        T: 'static,
    {
        let (tx, rx) = channel();
        let timeout = (timeout as f64) / 1000.0;

        let builder = thread::Builder::new();
        let thread = builder.spawn(move || {
            // Add a runloop observer that will be notified when we enter the
            // runloop and tx.send() the current runloop to the owning thread.
            // We need to ensure the runloop was entered before unblocking
            // Transaction::new(), so we can always properly cancel.
            let context = &tx as *const _ as *mut c_void;
            let obs = CFRunLoopEntryObserver::new(Transaction::observe, context);
            obs.add_to_current_runloop();

            // Create a new HID device monitor and start polling.
            let mut monitor = Monitor::new(new_device_cb);
            try_or!(monitor.start(), |e| callback.call(Err(e)));

            // This will block until completion, abortion, or timeout.
            unsafe { CFRunLoopRunInMode(kCFRunLoopDefaultMode, timeout, 0) };

            // Close the monitor and its devices.
            monitor.stop();

            // Send an error, if the callback wasn't called already.
            callback.call(Err(io_err("aborted or timed out")));
        })?;

        // Block until we enter the CFRunLoop.
        let runloop = rx.recv().map_err(to_io_err)?;

        Ok(Self {
            runloop,
            thread: Some(thread),
        })
    }

    extern "C" fn observe(_: CFRunLoopObserverRef, _: CFRunLoopActivity, context: *mut c_void) {
        let tx: &Sender<SendableRunLoop> = unsafe { &*(context as *mut _) };

        // Send the current runloop to the receiver to unblock it.
        let _ = tx.send(SendableRunLoop(unsafe { CFRunLoopGetCurrent() }));
    }

    pub fn cancel(&mut self) {
        // (This call doesn't block.)
        unsafe { CFRunLoopStop(*self.runloop) };

        // This must never be None. Ignore return value.
        let _ = self.thread.take().unwrap().join();
    }
}
<|endoftext|>


<commit_before>use rutorch::*;
use std::ops::{Index, IndexMut, Deref, DerefMut};
//use std::convert::From;
use std::cmp::max;

use storage::*;
use rand;
use {Ixs, RcMut};

pub struct Tensor<T> {
    value: RcMut<TensorImpl<T, Output = T>>,
}

pub trait New<D, T> {
    fn new(args: D) -> T;
    fn new_(&self, args: D) -> T;
}

impl<T> New<usize, Tensor<T>> for Tensor<T> {
    fn new(args: usize) -> Self {
        unimplemented!()
    }
    fn new_(&self, args: usize) -> Self {
        unimplemented!()
    }
}

impl<T> Tensor<T> {
    pub fn len(&self) -> usize {
        unimplemented!()
    }
    pub fn cuda(&self) -> Self {
        unimplemented!()
    }
    pub fn cpu(&self) -> Self {
        unimplemented!()
    }
    pub fn reduce_max(&self, axis: usize) -> (Self, Tensor<i64>) {
        unimplemented!()
    }
    pub fn tensor_eq(&self, rhs: &Self) -> Tensor<i64> {
        unimplemented!()
    }
    pub fn sum(&self) -> u32 {
        unimplemented!()
    }
    pub fn s(&self, dim: usize) -> Self {
        unimplemented!()
    }
}

impl<T> Default for Tensor<T> {
    fn default() -> Self {
        unimplemented!()
    }
}

impl<T> Clone for Tensor<T> {
    fn clone(&self) -> Self {
        Tensor { value: self.value.clone() }
    }
}

impl<T: Copy> Index<isize> for Tensor<T> {
    type Output = T;

    fn index(&self, idx: isize) -> &Self::Output {
        unimplemented!()
    }
}
pub trait TensorImpl<T>: Index<Ixs, Output = T> {
    //fn view<'a>(&self, dims: &[i32]) -> Tensor<'a>;
}

pub struct FloatTensor {
    t: *mut THFloatTensor,
    storage: FloatStorage,
    dims: Vec<isize>,
}

impl Default for FloatTensor {
    fn default() -> Self {
        FloatTensor::new()
    }
}

impl FloatTensor {
    pub fn new() -> Self {
        unsafe {
            FloatTensor {
                t: THFloatTensor_new(),
                storage: FloatStorage::new(),
                dims: Vec::new(),
            }
        }
    }
    pub fn with_capacity(dims: &[isize]) -> Self {
        let size = dims.iter().product();
        let storage = FloatStorage::with_capacity(size);
        let strides = vec![1; dims.len()];
        let mut t = THFloatTensor {
            size: dims.clone().as_ptr() as *mut ::std::os::raw::c_long,
            stride: strides.as_ptr() as *mut ::std::os::raw::c_long,
            nDimension: dims.len() as i32,
            storage: storage.t,
            storageOffset: 0,
            refcount: 1,
            flag: TH_TENSOR_REFCOUNTED as i8,
        };
        FloatTensor {
            t: &mut t,
            storage: storage,
            dims: Vec::from(dims),
        }
    }
    pub fn randn(dims: &[isize]) -> Self {
        /* XXX */
        let mut t = FloatTensor::with_capacity(dims);
        for x in t.storage.iter_mut() {
            *x = rand::random::<f32>()
        }
        t
    }
}

impl<'a> Index<&'a [isize]> for FloatTensor {
    type Output = f32;

    fn index(&self, idx: &'a [isize]) -> &Self::Output {
        let mut index: isize = 0;
        let lastidx = max(0, idx.len() as isize - 1) as usize;
        if idx.len() != self.dims.len() {
            panic!("bad dimlen")
        }
        for i in 0..lastidx {
            if idx[i] >= self.dims[i] {
                panic!("bad dimlen")
            }
            index += idx[i] * self.dims[i]
        }
        if idx[lastidx] >= self.dims[lastidx] {
            panic!("bad dimlen")
        }
        index += idx[lastidx];
        &self.storage[index]
    }
}

impl<'a> IndexMut<&'a [isize]> for FloatTensor {
    fn index_mut(&mut self, idx: &'a [isize]) -> &mut Self::Output {
        let mut index: isize = 0;
        let lastidx = max(0, idx.len() as isize - 1) as usize;
        if idx.len() != self.dims.len() {
            panic!("bad dimlen")
        }
        for i in 0..lastidx {
            if idx[i] >= self.dims[i] {
                panic!("bad dimlen")
            }
            index += idx[i] * self.dims[i]
        }
        if idx[lastidx] >= self.dims[lastidx] {
            panic!("bad dimlen")
        }
        index += idx[lastidx];
        &mut self.storage[index]
    }
}
<commit_msg>add 32-bit id to tensor.rs to allow for identity comparison, define tensor list types<commit_after>use rutorch::*;
use std::ops::{Index, IndexMut, Deref, DerefMut};
//use std::convert::From;
use std::cmp::max;

use storage::*;
use rand;
use {Ixs, RcMut};

pub type TensorList<T> = Vec<Tensor<T>>;
pub type RefTensorList<'a, T> = Vec<&'a mut Tensor<T>>;
pub type TensorId = i32;

pub struct Tensor<T> {
    pub id: i32,
    value: RcMut<TensorImpl<T, Output = T>>,
}

pub trait New<D, T> {
    fn new(args: D) -> T;
    fn new_(&self, args: D) -> T;
}

impl<T> New<usize, Tensor<T>> for Tensor<T> {
    fn new(args: usize) -> Self {
        unimplemented!()
    }
    fn new_(&self, args: usize) -> Self {
        unimplemented!()
    }
}

impl<T> Tensor<T> {
    pub fn len(&self) -> usize {
        unimplemented!()
    }
    pub fn cuda(&self) -> Self {
        unimplemented!()
    }
    pub fn cpu(&self) -> Self {
        unimplemented!()
    }
    pub fn reduce_max(&self, axis: usize) -> (Self, Tensor<i64>) {
        unimplemented!()
    }
    pub fn tensor_eq(&self, rhs: &Self) -> Tensor<i64> {
        unimplemented!()
    }
    pub fn sum(&self) -> u32 {
        unimplemented!()
    }
    pub fn s(&self, dim: usize) -> Self {
        unimplemented!()
    }
}

impl<T> Default for Tensor<T> {
    fn default() -> Self {
        unimplemented!()
    }
}

impl<T> Clone for Tensor<T> {
    fn clone(&self) -> Self {
        Tensor {
            id: self.id,
            value: self.value.clone(),
        }
    }
}

impl<T: Copy> Index<isize> for Tensor<T> {
    type Output = T;

    fn index(&self, idx: isize) -> &Self::Output {
        unimplemented!()
    }
}
pub trait TensorImpl<T>: Index<Ixs, Output = T> {
    //fn view<'a>(&self, dims: &[i32]) -> Tensor<'a>;
}

pub struct FloatTensor {
    t: *mut THFloatTensor,
    storage: FloatStorage,
    dims: Vec<isize>,
}

impl Default for FloatTensor {
    fn default() -> Self {
        FloatTensor::new()
    }
}

impl FloatTensor {
    pub fn new() -> Self {
        unsafe {
            FloatTensor {
                t: THFloatTensor_new(),
                storage: FloatStorage::new(),
                dims: Vec::new(),
            }
        }
    }
    pub fn with_capacity(dims: &[isize]) -> Self {
        let size = dims.iter().product();
        let storage = FloatStorage::with_capacity(size);
        let strides = vec![1; dims.len()];
        let mut t = THFloatTensor {
            size: dims.clone().as_ptr() as *mut ::std::os::raw::c_long,
            stride: strides.as_ptr() as *mut ::std::os::raw::c_long,
            nDimension: dims.len() as i32,
            storage: storage.t,
            storageOffset: 0,
            refcount: 1,
            flag: TH_TENSOR_REFCOUNTED as i8,
        };
        FloatTensor {
            t: &mut t,
            storage: storage,
            dims: Vec::from(dims),
        }
    }
    pub fn randn(dims: &[isize]) -> Self {
        /* XXX */
        let mut t = FloatTensor::with_capacity(dims);
        for x in t.storage.iter_mut() {
            *x = rand::random::<f32>()
        }
        t
    }
}

impl<'a> Index<&'a [isize]> for FloatTensor {
    type Output = f32;

    fn index(&self, idx: &'a [isize]) -> &Self::Output {
        let mut index: isize = 0;
        let lastidx = max(0, idx.len() as isize - 1) as usize;
        if idx.len() != self.dims.len() {
            panic!("bad dimlen")
        }
        for i in 0..lastidx {
            if idx[i] >= self.dims[i] {
                panic!("bad dimlen")
            }
            index += idx[i] * self.dims[i]
        }
        if idx[lastidx] >= self.dims[lastidx] {
            panic!("bad dimlen")
        }
        index += idx[lastidx];
        &self.storage[index]
    }
}

impl<'a> IndexMut<&'a [isize]> for FloatTensor {
    fn index_mut(&mut self, idx: &'a [isize]) -> &mut Self::Output {
        let mut index: isize = 0;
        let lastidx = max(0, idx.len() as isize - 1) as usize;
        if idx.len() != self.dims.len() {
            panic!("bad dimlen")
        }
        for i in 0..lastidx {
            if idx[i] >= self.dims[i] {
                panic!("bad dimlen")
            }
            index += idx[i] * self.dims[i]
        }
        if idx[lastidx] >= self.dims[lastidx] {
            panic!("bad dimlen")
        }
        index += idx[lastidx];
        &mut self.storage[index]
    }
}
<|endoftext|>


<commit_before>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// Simple Prometheus support. Still a work in progress.
// The goal here is to develop a prometheus client that
// is fully designed for prometheus with the minimal
// dependencies and overhead.
//
// We currently provide no abstraction around collecting metrics.
// This Reporter eventually needs to handle the multiplexing and
// organization of metrics.

extern crate iron;
extern crate router;
extern crate persistent;
extern crate protobuf; // depend on rust-protobuf runtime
extern crate lru_cache;
extern crate time;

pub mod promo_proto;
use router::Router;
use iron::typemap::Key;
use iron::prelude::*;
use iron::status;
use lru_cache::LruCache;
use protobuf::Message;
use std::sync::{Arc, RwLock};
use std::thread;
use std::hash::{Hash, Hasher};
use std::collections::hash_map::DefaultHasher;

// http handler storage
#[derive(Copy, Clone)]
struct HandlerStorage;

// refer to https://prometheus.io/docs/instrumenting/exposition_formats/
const CONTENT_TYPE: &'static str = "application/vnd.google.protobuf; \
                                    proto=io.prometheus.client.MetricFamily;
                                    encoding=delimited";

impl Key for HandlerStorage {
    type Value = Arc<RwLock<LruCache<u64, promo_proto::MetricFamily>>>;
}

fn families_to_u8(metric_families: Vec<(&u64, &promo_proto::MetricFamily)>) -> Vec<u8> {
    let mut buf = Vec::new();
    for (_, family) in metric_families {
        family.write_length_delimited_to_writer(&mut buf).unwrap();
    }
    buf
}

fn handler(req: &mut Request) -> IronResult<Response> {
    match req.get::<persistent::Read<HandlerStorage>>() {
        Ok(ts_and_metrics) => {
            // TODO catch unwrap
            let serialized: Vec<u8> =
                families_to_u8((*ts_and_metrics).read().unwrap().iter().collect());
            // TODO lifecycle out the metrics we sent up
            Ok(Response::with((CONTENT_TYPE, status::Ok, serialized)))
        }
        Err(_) => Ok(Response::with((status::InternalServerError, "ERROR"))),
    }
}

// TODO perhaps we autodiscover the host and port
pub struct PrometheusReporter {
    cache: Arc<RwLock<LruCache<u64, promo_proto::MetricFamily>>>
}

impl PrometheusReporter {
    pub fn new(host_and_port: &'static str) -> Self {
        let reporter = PrometheusReporter {
            // TODO make it configurable
            cache: Arc::new(RwLock::new(LruCache::new(86400)))
        };
        let mut router = Router::new();
        router.get("/metrics", handler);
        let mut chain = Chain::new(router);
        chain.link_before(persistent::Read::<HandlerStorage>::one(reporter.cache.clone()));
        // TODO get rid of the unwrap

        thread::spawn(move ||{
            match Iron::new(chain).http(host_and_port) {
                Ok(_) => {}
                Err(x) => panic!("Unable to start prometheus reporter {}",x),
            }
        });
        reporter
    }
    // TODO require start before add
    pub fn add(&mut self, metric_families: Vec<promo_proto::MetricFamily>) -> Result<i64, String> {

        match self.cache.write() {
            Ok(mut cache) => {
                let mut counter = 0;
                for metric_family in metric_families {

                    let mut hasher = DefaultHasher::new();
                    metric_family.get_name().hash(&mut hasher);
                    cache.insert(hasher.finish(), metric_family);
                    counter = counter + 1;
                }
                Ok(counter)
            }
            Err(y) => Err(format!("Unable to add {}", y)),
        }
    }

    pub fn remove(&mut self, metrics_to_remove: Vec<String>) -> Result<i64, String> {
        let mut hasher = DefaultHasher::new();
        match self.cache.write() {
            Ok(mut cache) => {
                let mut counter = 0;
                for metric_name_to_remove in metrics_to_remove {

                    let mut hasher = DefaultHasher::new();
                    metric_name_to_remove.hash(&mut hasher);
                    if let Some(_) = cache.remove(&hasher.finish()) {
                        counter = counter + 1;
                    }
                }
                Ok(counter)
            }
            Err(y) => Err(format!("Unable to remove {}", y)),
        }
    }
}

#[cfg(test)]
mod test {

    extern crate hyper;
    extern crate lru_cache;
    use std::thread;
    use std::time::Duration;
    use protobuf::repeated::RepeatedField;
    use std::io::Read;
    use super::*;

    fn a_metric_family_name() -> String {
       "MetricFamily".to_string()
    }

    fn a_metric_family() -> promo_proto::MetricFamily {
        let mut family = promo_proto::MetricFamily::new();
        family.set_name(a_metric_family_name());
        family.set_help("Help".to_string());
        family.set_field_type(promo_proto::MetricType::GAUGE);

        let mut metric = promo_proto::Metric::new();
        metric.set_timestamp_ms(a_ts());
        metric.set_gauge(a_gauge());
        metric.set_label(a_label_pair());
        family.set_metric(RepeatedField::from_vec(vec![metric]));

        family
    }

    fn a_label_pair() -> RepeatedField<promo_proto::LabelPair> {
        let mut label_pair = promo_proto::LabelPair::new();
        // The name and value alas are the names of the
        // protobuf fields in the pair in prometheus protobuf spec
        // Thes undescriptive names are part of the protocol alas.
        label_pair.set_name("name".to_string());
        label_pair.set_value("value".to_string());
        RepeatedField::from_vec(vec![label_pair])
    }

    fn a_gauge() -> promo_proto::Gauge {
        let mut gauge = promo_proto::Gauge::new();
        gauge.set_value(0.1);
        gauge
    }

    fn a_ts() -> i64 {
        0
    }

    #[test]
    fn add_some_stats_and_slurp_them_with_http() {
        let mut reporter = PrometheusReporter::new("0.0.0.0:8080");
        thread::sleep(Duration::from_millis(1024));
        reporter.add(vec![a_metric_family()]);
        let client = hyper::client::Client::new();
        let mut res = client.get("http://127.0.0.1:8080/metrics").send().unwrap();
        assert_eq!(res.status, hyper::Ok);
        let mut buffer = Vec::new();
        let size_of_buffer = res.read_to_end(&mut buffer).unwrap();
        println!("{:?} size:{} ", buffer, size_of_buffer);
        assert_eq!(size_of_buffer, 53);
    }

    #[test]
    fn add_and_remove_metric() {
        let mut reporter = PrometheusReporter::new("0.0.0.0:8081");
        thread::sleep(Duration::from_millis(1024));
        reporter.add(vec![a_metric_family()]);
        if let Ok(number_removed) = reporter.remove(vec![a_metric_family_name()]) {
           assert_eq!(number_removed, 1);
        }
        if let Ok(number_removed) = reporter.remove(vec![a_metric_family_name()]) {
           assert_eq!(number_removed, 0);
        }
    }

}
<commit_msg>Remove unused code<commit_after>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// Simple Prometheus support. Still a work in progress.
// The goal here is to develop a prometheus client that
// is fully designed for prometheus with the minimal
// dependencies and overhead.
//
// We currently provide no abstraction around collecting metrics.
// This Reporter eventually needs to handle the multiplexing and
// organization of metrics.

extern crate iron;
extern crate router;
extern crate persistent;
extern crate protobuf; // depend on rust-protobuf runtime
extern crate lru_cache;
extern crate time;

pub mod promo_proto;
use router::Router;
use iron::typemap::Key;
use iron::prelude::*;
use iron::status;
use lru_cache::LruCache;
use protobuf::Message;
use std::sync::{Arc, RwLock};
use std::thread;
use std::hash::{Hash, Hasher};
use std::collections::hash_map::DefaultHasher;

// http handler storage
#[derive(Copy, Clone)]
struct HandlerStorage;

// refer to https://prometheus.io/docs/instrumenting/exposition_formats/
const CONTENT_TYPE: &'static str = "application/vnd.google.protobuf; \
                                    proto=io.prometheus.client.MetricFamily;
                                    encoding=delimited";

impl Key for HandlerStorage {
    type Value = Arc<RwLock<LruCache<u64, promo_proto::MetricFamily>>>;
}

fn families_to_u8(metric_families: Vec<(&u64, &promo_proto::MetricFamily)>) -> Vec<u8> {
    let mut buf = Vec::new();
    for (_, family) in metric_families {
        family.write_length_delimited_to_writer(&mut buf).unwrap();
    }
    buf
}

fn handler(req: &mut Request) -> IronResult<Response> {
    match req.get::<persistent::Read<HandlerStorage>>() {
        Ok(ts_and_metrics) => {
            // TODO catch unwrap
            let serialized: Vec<u8> =
                families_to_u8((*ts_and_metrics).read().unwrap().iter().collect());
            // TODO lifecycle out the metrics we sent up
            Ok(Response::with((CONTENT_TYPE, status::Ok, serialized)))
        }
        Err(_) => Ok(Response::with((status::InternalServerError, "ERROR"))),
    }
}

// TODO perhaps we autodiscover the host and port
pub struct PrometheusReporter {
    cache: Arc<RwLock<LruCache<u64, promo_proto::MetricFamily>>>
}

impl PrometheusReporter {
    pub fn new(host_and_port: &'static str) -> Self {
        let reporter = PrometheusReporter {
            // TODO make it configurable
            cache: Arc::new(RwLock::new(LruCache::new(86400)))
        };
        let mut router = Router::new();
        router.get("/metrics", handler);
        let mut chain = Chain::new(router);
        chain.link_before(persistent::Read::<HandlerStorage>::one(reporter.cache.clone()));
        // TODO get rid of the unwrap

        thread::spawn(move ||{
            match Iron::new(chain).http(host_and_port) {
                Ok(_) => {}
                Err(x) => panic!("Unable to start prometheus reporter {}",x),
            }
        });
        reporter
    }
    // TODO require start before add
    pub fn add(&mut self, metric_families: Vec<promo_proto::MetricFamily>) -> Result<i64, String> {

        match self.cache.write() {
            Ok(mut cache) => {
                let mut counter = 0;
                for metric_family in metric_families {

                    let mut hasher = DefaultHasher::new();
                    metric_family.get_name().hash(&mut hasher);
                    cache.insert(hasher.finish(), metric_family);
                    counter = counter + 1;
                }
                Ok(counter)
            }
            Err(y) => Err(format!("Unable to add {}", y)),
        }
    }

    pub fn remove(&mut self, metrics_to_remove: Vec<String>) -> Result<i64, String> {
        match self.cache.write() {
            Ok(mut cache) => {
                let mut counter = 0;
                for metric_name_to_remove in metrics_to_remove {

                    let mut hasher = DefaultHasher::new();
                    metric_name_to_remove.hash(&mut hasher);
                    if let Some(_) = cache.remove(&hasher.finish()) {
                        counter = counter + 1;
                    }
                }
                Ok(counter)
            }
            Err(y) => Err(format!("Unable to remove {}", y)),
        }
    }
}

#[cfg(test)]
mod test {

    extern crate hyper;
    extern crate lru_cache;
    use std::thread;
    use std::time::Duration;
    use protobuf::repeated::RepeatedField;
    use std::io::Read;
    use super::*;

    fn a_metric_family_name() -> String {
       "MetricFamily".to_string()
    }

    fn a_metric_family() -> promo_proto::MetricFamily {
        let mut family = promo_proto::MetricFamily::new();
        family.set_name(a_metric_family_name());
        family.set_help("Help".to_string());
        family.set_field_type(promo_proto::MetricType::GAUGE);

        let mut metric = promo_proto::Metric::new();
        metric.set_timestamp_ms(a_ts());
        metric.set_gauge(a_gauge());
        metric.set_label(a_label_pair());
        family.set_metric(RepeatedField::from_vec(vec![metric]));

        family
    }

    fn a_label_pair() -> RepeatedField<promo_proto::LabelPair> {
        let mut label_pair = promo_proto::LabelPair::new();
        // The name and value alas are the names of the
        // protobuf fields in the pair in prometheus protobuf spec
        // Thes undescriptive names are part of the protocol alas.
        label_pair.set_name("name".to_string());
        label_pair.set_value("value".to_string());
        RepeatedField::from_vec(vec![label_pair])
    }

    fn a_gauge() -> promo_proto::Gauge {
        let mut gauge = promo_proto::Gauge::new();
        gauge.set_value(0.1);
        gauge
    }

    fn a_ts() -> i64 {
        0
    }

    #[test]
    fn add_some_stats_and_slurp_them_with_http() {
        let mut reporter = PrometheusReporter::new("0.0.0.0:8080");
        thread::sleep(Duration::from_millis(1024));
        reporter.add(vec![a_metric_family()]);
        let client = hyper::client::Client::new();
        let mut res = client.get("http://127.0.0.1:8080/metrics").send().unwrap();
        assert_eq!(res.status, hyper::Ok);
        let mut buffer = Vec::new();
        let size_of_buffer = res.read_to_end(&mut buffer).unwrap();
        println!("{:?} size:{} ", buffer, size_of_buffer);
        assert_eq!(size_of_buffer, 53);
    }

    #[test]
    fn add_and_remove_metric() {
        let mut reporter = PrometheusReporter::new("0.0.0.0:8081");
        thread::sleep(Duration::from_millis(1024));
        reporter.add(vec![a_metric_family()]);
        if let Ok(number_removed) = reporter.remove(vec![a_metric_family_name()]) {
           assert_eq!(number_removed, 1);
        }
        if let Ok(number_removed) = reporter.remove(vec![a_metric_family_name()]) {
           assert_eq!(number_removed, 0);
        }
    }

}
<|endoftext|>


<commit_before>// Demosaic methods adapted from dcraw 9.27
use decoders::Image;
use imageops::fcol;
use std::cmp;

pub fn ppg(img: &Image, inb: &[f32]) -> Vec<f32> {
  let mut out: Vec<f32> = vec![0.0; (img.width*img.height*3) as usize];

  // First we set the colors we already have
  let mut ipos = 0;
  let mut opos = 0;
  for row in 0..img.width {
    for col in 0..img.height {
      let color = fcol(img, row, col);
      out[opos+color] = inb[ipos];
      ipos += 1;
      opos += 3;
    }
  }

  // Now we go around the image setting the unset colors to the average of the
  // surrounding pixels
  for row in 0..img.height {
    for col in 0..img.width {
      let mut sums: [f32; 4] = [0.0;4];
      let mut counts: [f32; 4] = [0.0;4];

      for y in (cmp::max(0,(row as isize)-1) as usize) .. cmp::min(img.height, row+1) {
        for x in (cmp::max(0,(col as isize)-1) as usize) .. cmp::min(img.width, col+1) {
          let color = fcol(img, x, y);
          //println!("Fetching color {} at pos {}x{}", color, x, y);
          sums[color] += inb[y*img.width+x];
          counts[color] += 1.0;
        }
      }

      let color = fcol(img, row, col);
      for c in 0..3 {
        if c != color {
          out[(row*img.width+col)*3+c] = sums[c] / counts[c];
        }
      }
    }
  }

  out
}
<commit_msg>demosaic: fix several wrong bits<commit_after>// Demosaic methods adapted from dcraw 9.27
use decoders::Image;
use imageops::fcol;
use std::cmp;

pub fn ppg(img: &Image, inb: &[f32]) -> Vec<f32> {
  let mut out: Vec<f32> = vec![0.0; (img.width*img.height*3) as usize];

  // First we set the colors we already have
  let mut ipos = 0;
  let mut opos = 0;
  for row in 0..img.height {
    for col in 0..img.width {
      let color = fcol(img, row, col);
      out[opos+color] = inb[ipos];
      ipos += 1;
      opos += 3;
    }
  }

  // Now we go around the image setting the unset colors to the average of the
  // surrounding pixels
  for row in 0..img.height {
    for col in 0..img.width {
      let mut sums: [f32; 4] = [0.0;4];
      let mut counts: [f32; 4] = [0.0;4];
      let color = fcol(img, row, col);

      for y in (cmp::max(0,(row as isize)-1) as usize) .. cmp::min(img.height, row+2) {
        for x in (cmp::max(0,(col as isize)-1) as usize) .. cmp::min(img.width, col+2) {
          let c = fcol(img, y, x);
          if c != color {
            sums[c] += inb[y*img.width+x];
            counts[c] += 1.0;
          }
        }
      }

      for c in 0..3 {
        if c != color {
          out[(row*img.width+col)*3+c] = sums[c] / counts[c];
        }
      }
    }
  }

  out
}
<|endoftext|>