<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::types::{
    AccountBalanceMetadata, ACCOUNT_MODULE, ACCOUNT_RESOURCE, COIN_MODULE, COIN_STORE_RESOURCE,
};
use crate::{
    common::{
        check_network, get_block_index_from_request, handle_request, native_coin, native_coin_tag,
        with_context,
    },
    error::{ApiError, ApiResult},
    types::{AccountBalanceRequest, AccountBalanceResponse, Amount, Currency, CurrencyMetadata},
    RosettaContext,
};
use aptos_logger::{debug, trace};
use aptos_rest_client::aptos_api_types::AccountData;
use aptos_rest_client::{
    aptos::{AptosCoin, 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},
    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 rest_client = server_context.rest_client()?;

    // Retrieve the block index to read
    let block_height =
        get_block_index_from_request(&server_context, request.block_identifier.clone()).await?;

    // Version to grab is the last entry in the block (balance is at end of block)
    let block_info = server_context
        .block_cache()?
        .get_block_info_by_height(block_height, server_context.chain_id)
        .await?;
    let balance_version = block_info.last_version;

    let (sequence_number, 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?;

    Ok(AccountBalanceResponse {
        block_identifier: block_info.block_id,
        balances: amounts,
        metadata: AccountBalanceMetadata {
            sequence_number: sequence_number.into(),
        },
    })
}

/// 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)
}

/// Retrieve the balances for an account
async fn get_balances(
    rest_client: &aptos_rest_client::Client,
    address: AccountAddress,
    version: u64,
) -> ApiResult<(u64, HashMap<TypeTag, Balance>)> {
    if let Ok(response) = rest_client
        .get_account_resources_at_version(address, version)
        .await
    {
        let response = response.into_inner();

        let maybe_sequence_number = if let Some(account_resource) =
            response.iter().find(|resource| {
                resource.resource_type.address == AccountAddress::ONE
                    && resource.resource_type.module.as_str() == ACCOUNT_MODULE
                    && resource.resource_type.name.as_str() == ACCOUNT_RESOURCE
            }) {
            if let Ok(resource) =
                serde_json::from_value::<AccountData>(account_resource.data.clone())
            {
                Some(resource.sequence_number.0)
            } else {
                None
            }
        } else {
            None
        };

        let sequence_number = if let Some(sequence_number) = maybe_sequence_number {
            sequence_number
        } else {
            return Err(ApiError::InternalError(Some(
                "Failed to retrieve account sequence number".to_string(),
            )));
        };

        let balances = response
            .iter()
            .filter(|resource| {
                resource.resource_type.address == AccountAddress::ONE
                    && resource.resource_type.module.as_str() == COIN_MODULE
                    && resource.resource_type.name.as_str() == COIN_STORE_RESOURCE
            })
            .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();

        // Retrieve balances
        Ok((sequence_number, balances))
    } else {
        let mut currency_map = HashMap::new();
        currency_map.insert(
            native_coin_tag(),
            Balance {
                coin: AptosCoin { value: U64(0) },
            },
        );
        Ok((0, currency_map))
    }
}

/// 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>> {
        // Short circuit for the default coin
        if coin == native_coin_tag() {
            return Ok(Some(native_coin()));
        }

        {
            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: u8,
            _supply: Option<Vec<u8>>,
        }

        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_bcs::<CoinInfo>(
                    address,
                    &encoded_resource_tag,
                    version,
                )
                .await?
        } else {
            rest_client
                .get_account_resource_bcs::<CoinInfo>(address, &encoded_resource_tag)
                .await?
        };

        // At this point if we've retrieved it and it's bad, we error out
        let coin_info = response.into_inner();

        // TODO: The symbol has to come from a trusted list, as the move type is the real indicator
        Ok(Some(Currency {
            symbol: coin_info.symbol,
            decimals: coin_info.decimals,
            metadata: Some(CurrencyMetadata {
                move_type: struct_tag.to_string(),
            }),
        }))
    }
}
<commit_msg>[aptos-rosetta] Cleanup get balances to use matching<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::types::{
    AccountBalanceMetadata, ACCOUNT_MODULE, ACCOUNT_RESOURCE, COIN_MODULE, COIN_STORE_RESOURCE,
};
use crate::{
    common::{
        check_network, get_block_index_from_request, handle_request, native_coin, native_coin_tag,
        with_context,
    },
    error::{ApiError, ApiResult},
    types::{AccountBalanceRequest, AccountBalanceResponse, Amount, Currency, CurrencyMetadata},
    RosettaContext,
};
use aptos_logger::{debug, trace};
use aptos_rest_client::aptos_api_types::AccountData;
use aptos_rest_client::{
    aptos::{AptosCoin, 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},
    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 rest_client = server_context.rest_client()?;

    // Retrieve the block index to read
    let block_height =
        get_block_index_from_request(&server_context, request.block_identifier.clone()).await?;

    // Version to grab is the last entry in the block (balance is at end of block)
    let block_info = server_context
        .block_cache()?
        .get_block_info_by_height(block_height, server_context.chain_id)
        .await?;
    let balance_version = block_info.last_version;

    let (sequence_number, 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?;

    Ok(AccountBalanceResponse {
        block_identifier: block_info.block_id,
        balances: amounts,
        metadata: AccountBalanceMetadata {
            sequence_number: sequence_number.into(),
        },
    })
}

/// 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)
}

/// Retrieve the balances for an account
async fn get_balances(
    rest_client: &aptos_rest_client::Client,
    address: AccountAddress,
    version: u64,
) -> ApiResult<(u64, HashMap<TypeTag, Balance>)> {
    if let Ok(response) = rest_client
        .get_account_resources_at_version(address, version)
        .await
    {
        let resources = response.into_inner();
        let mut maybe_sequence_number = None;
        let mut balances = HashMap::new();

        for resource in resources {
            match (
                resource.resource_type.address,
                resource.resource_type.module.as_str(),
                resource.resource_type.name.as_str(),
            ) {
                (AccountAddress::ONE, ACCOUNT_MODULE, ACCOUNT_RESOURCE) => {
                    if let Ok(resource) =
                        serde_json::from_value::<AccountData>(resource.data.clone())
                    {
                        maybe_sequence_number = Some(resource.sequence_number.0)
                    }
                }
                (AccountAddress::ONE, COIN_MODULE, COIN_STORE_RESOURCE) => {
                    if let Some(coin_type) = resource.resource_type.type_params.first() {
                        if let Ok(resource) =
                            serde_json::from_value::<Balance>(resource.data.clone())
                        {
                            balances.insert(coin_type.clone(), resource);
                        }
                    }
                }
                _ => {}
            }
        }

        let sequence_number = if let Some(sequence_number) = maybe_sequence_number {
            sequence_number
        } else {
            return Err(ApiError::InternalError(Some(
                "Failed to retrieve account sequence number".to_string(),
            )));
        };

        // Retrieve balances
        Ok((sequence_number, balances))
    } else {
        let mut currency_map = HashMap::new();
        currency_map.insert(
            native_coin_tag(),
            Balance {
                coin: AptosCoin { value: U64(0) },
            },
        );
        Ok((0, currency_map))
    }
}

/// 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>> {
        // Short circuit for the default coin
        if coin == native_coin_tag() {
            return Ok(Some(native_coin()));
        }

        {
            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: u8,
            _supply: Option<Vec<u8>>,
        }

        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_bcs::<CoinInfo>(
                    address,
                    &encoded_resource_tag,
                    version,
                )
                .await?
        } else {
            rest_client
                .get_account_resource_bcs::<CoinInfo>(address, &encoded_resource_tag)
                .await?
        };

        // At this point if we've retrieved it and it's bad, we error out
        let coin_info = response.into_inner();

        // TODO: The symbol has to come from a trusted list, as the move type is the real indicator
        Ok(Some(Currency {
            symbol: coin_info.symbol,
            decimals: coin_info.decimals,
            metadata: Some(CurrencyMetadata {
                move_type: struct_tag.to_string(),
            }),
        }))
    }
}
<|endoftext|>


<commit_before>extern crate fitsio;

use std::env;
use std::process;

fn main() {
    let mut nfiles = 0;
    env::args().skip(1).for_each(|arg| {
        let mut f = fitsio::FitsFile::open(arg).expect("opening file");
        f.pretty_print().expect("printing summary");
        nfiles += 1;
    });

    if nfiles == 0 {
        eprintln!("No files supplied");
        process::exit(1);
    }
}
<commit_msg>Update fitssummary example to silently ignore invalid fits files<commit_after>extern crate fitsio;

use std::env;
use std::process;

fn main() {
    let mut nfiles = 0;
    env::args().skip(1).for_each(|arg| {
        if let Ok(_) = fitsio::FitsFile::open(arg).map(|mut f| f.pretty_print()) {
            nfiles += 1;
        }
    });

    if nfiles == 0 {
        eprintln!("No valid fits files supplied");
        process::exit(1);
    }
}
<|endoftext|>


<commit_before>use super::Rope;
use std::iter;

#[test]
fn rebalance_test_1() {
    let mut r = Rope::from("This is a large string \
                        that will need to be rebalanced.".to_string());
    r.rebalance();
    assert!(r.is_balanced());
}

#[test]
fn rebalance_test_2() {
    let mut r =
        Rope::from("Lorem ipsum dolor sit amet, consectetur adipiscing eli\
                    t, sed do eiusmod tempor incididunt ut labore et dolor\
                    e magna aliqua. Ut enim ad minim veniam, quis nostrud \
                    exercitation ullamco laboris nisi ut aliquip ex ea com\
                    modo consequat. Duis aute irure dolor in reprehenderit\
                     in voluptate velit esse cillum dolore eu fugiat nulla\
                     pariatur. Excepteur sint occaecat cupidatat non proid\
                    ent, sunt in culpa qui officia deserunt mollit anim id\
                     est laborum.".to_string());
    r.rebalance();
    assert!(r.is_balanced());
}

#[test]
fn big_rebalance() {
    let s: String = iter::repeat('a').take(10_000).collect();
    let mut r = Rope::from(s);
    r.rebalance();
    assert!(r.is_balanced());
}

#[test]
fn repeated_concat_left_rebalance() {
    let s: String = iter::repeat('a').take(10_000).collect();
    let mut r = Rope::from(s);
    for _ in 1..1000 {
        r = r + iter::repeat('a').take(100).collect::<String>();
    }
    assert!(r.is_balanced());
}

#[test]
fn repeated_concat_right_rebalance() {
    let s: String = iter::repeat('a').take(10_000).collect();
    let mut r = Rope::from(s);
    for _ in 1..1000 {
        let s2 = iter::repeat('a').take(100).collect::<String>();
        r = Rope::from(s2) + r;
    }
    assert!(r.is_balanced());
}

#[test]
fn merge_rebalance_test() {
    let s = "Lorem ipsum dolor sit amet, consectetur adipiscing eli\
             t, sed do eiusmod tempor incididunt ut labore et dolor\
             e magna aliqua. Ut enim ad minim veniam, quis nostrud \
             exercitation ullamco laboris nisi ut aliquip ex ea com\
             modo consequat. Duis aute irure dolor in reprehenderit\
              in voluptate velit esse cillum dolore eu fugiat nulla\
              pariatur. Excepteur sint occaecat cupidatat non proid\
             ent, sunt in culpa qui officia deserunt mollit anim id\
              est laborum.";

     let t = Rope::from(s.to_owned());
     let mut u = t + s;
     u.rebalance();
     assert!(u.is_balanced());
}

#[test]
fn with_insert_rope_balance_test() {
    let s: String = iter::repeat('a').take(10).collect();
    let mut r_1 = Rope::from(s);
    for _ in 0..99 {
        let t: String = iter::repeat('a').take(10).collect();
        r_1 = r_1.with_insert_rope(5, Rope::from(t));
    }
    //  this isn't necessary, with_insert_rope() will automatically rebalance
    //      - eliza, 12/18/2016
    // r_1.rebalance();
    assert!(r_1.is_balanced());
}

#[test]
fn with_insert_rope_test_1() {
    let s: String = iter::repeat('a').take(1_000).collect();
    let r_1 = Rope::from(s);
    let mut r_2 = Rope::new();
    for _ in 0..100 {
        let t: String = iter::repeat('a').take(10).collect();
        r_2 = r_2.with_insert_rope(0, Rope::from(t));
    }
    assert_eq!(r_1, r_2);
}

#[test]
fn with_insert_rope_test_2() {
    let s: String = iter::repeat('a').take(10).collect();
    let mut r_1 = Rope::from(s);
    for _ in 0..99 {
        let t: String = iter::repeat('a').take(10).collect();
        r_1 = r_1.with_insert_rope(5, Rope::from(t));
    }

    let q: String = iter::repeat('a').take(1_000).collect();
    let r_2 = Rope::from(q);
    assert_eq!(r_1, r_2);
}

#[test]
fn mutable_insert_rope_test_1() {
    let mut s_1 = Rope::from(String::from("aaaaa"));
    let mut s_2 = Rope::from(String::from("bbbbb"));
    let s_3 = Rope::from(String::from("ccccc"));
    s_2.insert_rope(0, s_3);
    s_1.insert_rope(0, s_2);
    assert_eq!(&s_1, "cccccbbbbbaaaaa");
}

#[test]
fn mutable_insert_str_test_1() {
    let mut s = Rope::from("aaaaa");
    s.insert_str(0, "bbbbb");
    s.insert_str(10, "ccccc");
    assert_eq!(&s, "bbbbbaaaaaccccc");
}

#[test]
fn mutable_insert_char_test_1() {
    let mut s = Rope::from("aaaaa");
    for _ in 0..5 { s.insert(0, 'b')}
    for _ in 0..5 { s.insert(10, 'c')}
    assert_eq!(&s, "bbbbbaaaaaccccc");
}

#[test]
fn mutable_insert_char_test_2() {
    // this is the same as with_insert_char_test1() except mutable
    let mut s = Rope::from("aaaaa");
    assert_eq!(&s, "aaaaa");
    s.insert(5, 'b');
    assert_eq!(&s, "aaaaab");
    s.insert(4, 'b');
    assert_eq!(&s, "aaaabab");
    s.insert(3, 'b');
    assert_eq!(&s, "aaababab");
    s.insert(2, 'b');
    assert_eq!(&s, "aabababab");
    s.insert(1, 'b');
    assert_eq!(&s, "ababababab");

}

#[test]
fn with_insert_char_test_1() {
    let s = Rope::from("aaaaa");
    let s_1 = s.with_insert(5, 'b');
    let s_2 = s_1.with_insert(4, 'b');
    let s_3 = s_2.with_insert(3, 'b');
    let s_4 = s_3.with_insert(2, 'b');
    let s_5 = s_4.with_insert(1, 'b');
    assert_eq!(&s, "aaaaa");
    assert_eq!(&s_1, "aaaaab");
    assert_eq!(&s_2, "aaaabab");
    assert_eq!(&s_3, "aaababab");
    assert_eq!(&s_4, "aabababab");
    assert_eq!(&s_5, "ababababab");

}

#[test]
fn with_insert_str_test_1() {
    let s = Rope::from("aaaaa");
    let s_1 = s.with_insert_str(5, "ccccc");
    let s_2 = s_1.with_insert_str(5, "bbbbb");
    assert_eq!(&s, "aaaaa");
    assert_eq!(&s_1, "aaaaaccccc");
    assert_eq!(&s_2, "aaaaabbbbbccccc");
}

#[test]
fn rope_char_indices() {
    let rope = Rope::from("aaaaa")
        .with_append(Rope::from("bbbbbb"))
        .with_append(Rope::from("cccccccccccc"))
        .with_append(Rope::from("defgdefgaabababab"));
    let string = String::from("aaaaabbbbbbccccccccccccdefgdefgaabababab");
    let indices = rope.char_indices().zip(string.char_indices());
    for ((ridx, rch), (sidx, sch)) in indices {
        assert_eq!(rch, sch);
        assert_eq!(ridx, sidx);
    }
}
<commit_msg>rope: add test for RopeSlice::char_indices()<commit_after>use super::Rope;
use std::iter;

#[test]
fn rebalance_test_1() {
    let mut r = Rope::from("This is a large string \
                        that will need to be rebalanced.".to_string());
    r.rebalance();
    assert!(r.is_balanced());
}

#[test]
fn rebalance_test_2() {
    let mut r =
        Rope::from("Lorem ipsum dolor sit amet, consectetur adipiscing eli\
                    t, sed do eiusmod tempor incididunt ut labore et dolor\
                    e magna aliqua. Ut enim ad minim veniam, quis nostrud \
                    exercitation ullamco laboris nisi ut aliquip ex ea com\
                    modo consequat. Duis aute irure dolor in reprehenderit\
                     in voluptate velit esse cillum dolore eu fugiat nulla\
                     pariatur. Excepteur sint occaecat cupidatat non proid\
                    ent, sunt in culpa qui officia deserunt mollit anim id\
                     est laborum.".to_string());
    r.rebalance();
    assert!(r.is_balanced());
}

#[test]
fn big_rebalance() {
    let s: String = iter::repeat('a').take(10_000).collect();
    let mut r = Rope::from(s);
    r.rebalance();
    assert!(r.is_balanced());
}

#[test]
fn repeated_concat_left_rebalance() {
    let s: String = iter::repeat('a').take(10_000).collect();
    let mut r = Rope::from(s);
    for _ in 1..1000 {
        r = r + iter::repeat('a').take(100).collect::<String>();
    }
    assert!(r.is_balanced());
}

#[test]
fn repeated_concat_right_rebalance() {
    let s: String = iter::repeat('a').take(10_000).collect();
    let mut r = Rope::from(s);
    for _ in 1..1000 {
        let s2 = iter::repeat('a').take(100).collect::<String>();
        r = Rope::from(s2) + r;
    }
    assert!(r.is_balanced());
}

#[test]
fn merge_rebalance_test() {
    let s = "Lorem ipsum dolor sit amet, consectetur adipiscing eli\
             t, sed do eiusmod tempor incididunt ut labore et dolor\
             e magna aliqua. Ut enim ad minim veniam, quis nostrud \
             exercitation ullamco laboris nisi ut aliquip ex ea com\
             modo consequat. Duis aute irure dolor in reprehenderit\
              in voluptate velit esse cillum dolore eu fugiat nulla\
              pariatur. Excepteur sint occaecat cupidatat non proid\
             ent, sunt in culpa qui officia deserunt mollit anim id\
              est laborum.";

     let t = Rope::from(s.to_owned());
     let mut u = t + s;
     u.rebalance();
     assert!(u.is_balanced());
}

#[test]
fn with_insert_rope_balance_test() {
    let s: String = iter::repeat('a').take(10).collect();
    let mut r_1 = Rope::from(s);
    for _ in 0..99 {
        let t: String = iter::repeat('a').take(10).collect();
        r_1 = r_1.with_insert_rope(5, Rope::from(t));
    }
    //  this isn't necessary, with_insert_rope() will automatically rebalance
    //      - eliza, 12/18/2016
    // r_1.rebalance();
    assert!(r_1.is_balanced());
}

#[test]
fn with_insert_rope_test_1() {
    let s: String = iter::repeat('a').take(1_000).collect();
    let r_1 = Rope::from(s);
    let mut r_2 = Rope::new();
    for _ in 0..100 {
        let t: String = iter::repeat('a').take(10).collect();
        r_2 = r_2.with_insert_rope(0, Rope::from(t));
    }
    assert_eq!(r_1, r_2);
}

#[test]
fn with_insert_rope_test_2() {
    let s: String = iter::repeat('a').take(10).collect();
    let mut r_1 = Rope::from(s);
    for _ in 0..99 {
        let t: String = iter::repeat('a').take(10).collect();
        r_1 = r_1.with_insert_rope(5, Rope::from(t));
    }

    let q: String = iter::repeat('a').take(1_000).collect();
    let r_2 = Rope::from(q);
    assert_eq!(r_1, r_2);
}

#[test]
fn mutable_insert_rope_test_1() {
    let mut s_1 = Rope::from(String::from("aaaaa"));
    let mut s_2 = Rope::from(String::from("bbbbb"));
    let s_3 = Rope::from(String::from("ccccc"));
    s_2.insert_rope(0, s_3);
    s_1.insert_rope(0, s_2);
    assert_eq!(&s_1, "cccccbbbbbaaaaa");
}

#[test]
fn mutable_insert_str_test_1() {
    let mut s = Rope::from("aaaaa");
    s.insert_str(0, "bbbbb");
    s.insert_str(10, "ccccc");
    assert_eq!(&s, "bbbbbaaaaaccccc");
}

#[test]
fn mutable_insert_char_test_1() {
    let mut s = Rope::from("aaaaa");
    for _ in 0..5 { s.insert(0, 'b')}
    for _ in 0..5 { s.insert(10, 'c')}
    assert_eq!(&s, "bbbbbaaaaaccccc");
}

#[test]
fn mutable_insert_char_test_2() {
    // this is the same as with_insert_char_test1() except mutable
    let mut s = Rope::from("aaaaa");
    assert_eq!(&s, "aaaaa");
    s.insert(5, 'b');
    assert_eq!(&s, "aaaaab");
    s.insert(4, 'b');
    assert_eq!(&s, "aaaabab");
    s.insert(3, 'b');
    assert_eq!(&s, "aaababab");
    s.insert(2, 'b');
    assert_eq!(&s, "aabababab");
    s.insert(1, 'b');
    assert_eq!(&s, "ababababab");

}

#[test]
fn with_insert_char_test_1() {
    let s = Rope::from("aaaaa");
    let s_1 = s.with_insert(5, 'b');
    let s_2 = s_1.with_insert(4, 'b');
    let s_3 = s_2.with_insert(3, 'b');
    let s_4 = s_3.with_insert(2, 'b');
    let s_5 = s_4.with_insert(1, 'b');
    assert_eq!(&s, "aaaaa");
    assert_eq!(&s_1, "aaaaab");
    assert_eq!(&s_2, "aaaabab");
    assert_eq!(&s_3, "aaababab");
    assert_eq!(&s_4, "aabababab");
    assert_eq!(&s_5, "ababababab");

}

#[test]
fn with_insert_str_test_1() {
    let s = Rope::from("aaaaa");
    let s_1 = s.with_insert_str(5, "ccccc");
    let s_2 = s_1.with_insert_str(5, "bbbbb");
    assert_eq!(&s, "aaaaa");
    assert_eq!(&s_1, "aaaaaccccc");
    assert_eq!(&s_2, "aaaaabbbbbccccc");
}

#[test]
fn rope_char_indices() {
    let rope = Rope::from("aaaaa")
        .with_append(Rope::from("bbbbbb"))
        .with_append(Rope::from("cccccccccccc"))
        .with_append(Rope::from("defgdefgaabababab"));
    let string = String::from("aaaaabbbbbbccccccccccccdefgdefgaabababab");
    let indices = rope.char_indices().zip(string.char_indices());
    for ((ridx, rch), (sidx, sch)) in indices {
        assert_eq!(rch, sch);
        assert_eq!(ridx, sidx);
    }
}

#[test]
fn rope_slice_char_indices() {
    let string = "aaaaabbbbbbccccccccccccdefgdefgaabababab";
    let rope_slice = Rope::from(string).slice(4..8);
    let string_slice = &string[4..8];
    let indices = rope_slice.char_indices().zip(string_slice.char_indices());
    for ((ridx, rch), (sidx, sch)) in indices {
        assert_eq!(rch, sch);
        assert_eq!(ridx, sidx);
    }
}
<|endoftext|>


<commit_before>//! Config used by the language server.
//!
//! We currently get this config from `initialize` LSP request, which is not the
//! best way to do it, but was the simplest thing we could implement.
//!
//! Of particular interest is the `feature_flags` hash map: while other fields
//! configure the server itself, feature flags are passed into analysis, and
//! tweak things like automatic insertion of `()` in completions.

use std::{ffi::OsString, path::PathBuf};

use flycheck::FlycheckConfig;
use ide::{AssistConfig, CompletionConfig, DiagnosticsConfig, HoverConfig, InlayHintsConfig};
use lsp_types::ClientCapabilities;
use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest};
use rustc_hash::FxHashSet;
use serde::Deserialize;
use vfs::AbsPathBuf;

use crate::diagnostics::DiagnosticsMapConfig;

#[derive(Debug, Clone)]
pub struct Config {
    pub client_caps: ClientCapsConfig,

    pub publish_diagnostics: bool,
    pub diagnostics: DiagnosticsConfig,
    pub diagnostics_map: DiagnosticsMapConfig,
    pub lru_capacity: Option<usize>,
    pub proc_macro_srv: Option<(PathBuf, Vec<OsString>)>,
    pub files: FilesConfig,
    pub notifications: NotificationsConfig,

    pub cargo_autoreload: bool,
    pub cargo: CargoConfig,
    pub rustfmt: RustfmtConfig,
    pub flycheck: Option<FlycheckConfig>,

    pub inlay_hints: InlayHintsConfig,
    pub completion: CompletionConfig,
    pub assist: AssistConfig,
    pub call_info_full: bool,
    pub lens: LensConfig,
    pub hover: HoverConfig,

    pub with_sysroot: bool,
    pub linked_projects: Vec<LinkedProject>,
    pub root_path: AbsPathBuf,
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub enum LinkedProject {
    ProjectManifest(ProjectManifest),
    InlineJsonProject(ProjectJson),
}

impl From<ProjectManifest> for LinkedProject {
    fn from(v: ProjectManifest) -> Self {
        LinkedProject::ProjectManifest(v)
    }
}

impl From<ProjectJson> for LinkedProject {
    fn from(v: ProjectJson) -> Self {
        LinkedProject::InlineJsonProject(v)
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct LensConfig {
    pub run: bool,
    pub debug: bool,
    pub implementations: bool,
}

impl Default for LensConfig {
    fn default() -> Self {
        Self { run: true, debug: true, implementations: true }
    }
}

impl LensConfig {
    pub const NO_LENS: LensConfig = Self { run: false, debug: false, implementations: false };

    pub fn any(&self) -> bool {
        self.implementations || self.runnable()
    }

    pub fn none(&self) -> bool {
        !self.any()
    }

    pub fn runnable(&self) -> bool {
        self.run || self.debug
    }
}

#[derive(Debug, Clone)]
pub struct FilesConfig {
    pub watcher: FilesWatcher,
    pub exclude: Vec<String>,
}

#[derive(Debug, Clone)]
pub enum FilesWatcher {
    Client,
    Notify,
}

#[derive(Debug, Clone)]
pub struct NotificationsConfig {
    pub cargo_toml_not_found: bool,
}

#[derive(Debug, Clone)]
pub enum RustfmtConfig {
    Rustfmt { extra_args: Vec<String> },
    CustomCommand { command: String, args: Vec<String> },
}

#[derive(Debug, Clone, Default)]
pub struct ClientCapsConfig {
    pub location_link: bool,
    pub line_folding_only: bool,
    pub hierarchical_symbols: bool,
    pub code_action_literals: bool,
    pub work_done_progress: bool,
    pub code_action_group: bool,
    pub resolve_code_action: bool,
    pub hover_actions: bool,
    pub status_notification: bool,
    pub signature_help_label_offsets: bool,
}

impl Config {
    pub fn new(root_path: AbsPathBuf) -> Self {
        Config {
            client_caps: ClientCapsConfig::default(),

            with_sysroot: true,
            publish_diagnostics: true,
            diagnostics: DiagnosticsConfig::default(),
            diagnostics_map: DiagnosticsMapConfig::default(),
            lru_capacity: None,
            proc_macro_srv: None,
            files: FilesConfig { watcher: FilesWatcher::Notify, exclude: Vec::new() },
            notifications: NotificationsConfig { cargo_toml_not_found: true },

            cargo_autoreload: true,
            cargo: CargoConfig::default(),
            rustfmt: RustfmtConfig::Rustfmt { extra_args: Vec::new() },
            flycheck: Some(FlycheckConfig::CargoCommand {
                command: "check".to_string(),
                target_triple: None,
                no_default_features: false,
                all_targets: true,
                all_features: false,
                extra_args: Vec::new(),
                features: Vec::new(),
            }),

            inlay_hints: InlayHintsConfig {
                type_hints: true,
                parameter_hints: true,
                chaining_hints: true,
                max_length: None,
            },
            completion: CompletionConfig {
                enable_postfix_completions: true,
                add_call_parenthesis: true,
                add_call_argument_snippets: true,
                ..CompletionConfig::default()
            },
            assist: AssistConfig::default(),
            call_info_full: true,
            lens: LensConfig::default(),
            hover: HoverConfig::default(),
            linked_projects: Vec::new(),
            root_path,
        }
    }

    pub fn update(&mut self, json: serde_json::Value) {
        log::info!("Config::update({:#})", json);

        if json.is_null() || json.as_object().map_or(false, |it| it.is_empty()) {
            return;
        }

        let data = ConfigData::from_json(json);

        self.with_sysroot = data.withSysroot;
        self.publish_diagnostics = data.diagnostics_enable;
        self.diagnostics = DiagnosticsConfig {
            disable_experimental: !data.diagnostics_enableExperimental,
            disabled: data.diagnostics_disabled,
        };
        self.diagnostics_map = DiagnosticsMapConfig {
            warnings_as_info: data.diagnostics_warningsAsInfo,
            warnings_as_hint: data.diagnostics_warningsAsHint,
        };
        self.lru_capacity = data.lruCapacity;
        self.files.watcher = match data.files_watcher.as_str() {
            "notify" => FilesWatcher::Notify,
            "client" | _ => FilesWatcher::Client,
        };
        self.notifications =
            NotificationsConfig { cargo_toml_not_found: data.notifications_cargoTomlNotFound };
        self.cargo_autoreload = data.cargo_autoreload;
        self.cargo = CargoConfig {
            no_default_features: data.cargo_noDefaultFeatures,
            all_features: data.cargo_allFeatures,
            features: data.cargo_features.clone(),
            load_out_dirs_from_check: data.cargo_loadOutDirsFromCheck,
            target: data.cargo_target.clone(),
        };

        self.proc_macro_srv = if data.procMacro_enable {
            std::env::current_exe().ok().map(|path| (path, vec!["proc-macro".into()]))
        } else {
            None
        };

        self.rustfmt = match data.rustfmt_overrideCommand {
            Some(mut args) if !args.is_empty() => {
                let command = args.remove(0);
                RustfmtConfig::CustomCommand { command, args }
            }
            Some(_) | None => RustfmtConfig::Rustfmt { extra_args: data.rustfmt_extraArgs },
        };

        self.flycheck = if data.checkOnSave_enable {
            let flycheck_config = match data.checkOnSave_overrideCommand {
                Some(mut args) if !args.is_empty() => {
                    let command = args.remove(0);
                    FlycheckConfig::CustomCommand { command, args }
                }
                Some(_) | None => FlycheckConfig::CargoCommand {
                    command: data.checkOnSave_command,
                    target_triple: data.checkOnSave_target.or(data.cargo_target),
                    all_targets: data.checkOnSave_allTargets,
                    no_default_features: data
                        .checkOnSave_noDefaultFeatures
                        .unwrap_or(data.cargo_noDefaultFeatures),
                    all_features: data.checkOnSave_allFeatures.unwrap_or(data.cargo_allFeatures),
                    features: data.checkOnSave_features.unwrap_or(data.cargo_features),
                    extra_args: data.checkOnSave_extraArgs,
                },
            };
            Some(flycheck_config)
        } else {
            None
        };

        self.inlay_hints = InlayHintsConfig {
            type_hints: data.inlayHints_typeHints,
            parameter_hints: data.inlayHints_parameterHints,
            chaining_hints: data.inlayHints_chainingHints,
            max_length: data.inlayHints_maxLength,
        };

        self.completion.enable_postfix_completions = data.completion_postfix_enable;
        self.completion.add_call_parenthesis = data.completion_addCallParenthesis;
        self.completion.add_call_argument_snippets = data.completion_addCallArgumentSnippets;

        self.call_info_full = data.callInfo_full;

        self.lens = LensConfig {
            run: data.lens_enable && data.lens_run,
            debug: data.lens_enable && data.lens_debug,
            implementations: data.lens_enable && data.lens_implementations,
        };

        if !data.linkedProjects.is_empty() {
            self.linked_projects.clear();
            for linked_project in data.linkedProjects {
                let linked_project = match linked_project {
                    ManifestOrProjectJson::Manifest(it) => {
                        let path = self.root_path.join(it);
                        match ProjectManifest::from_manifest_file(path) {
                            Ok(it) => it.into(),
                            Err(_) => continue,
                        }
                    }
                    ManifestOrProjectJson::ProjectJson(it) => {
                        ProjectJson::new(&self.root_path, it).into()
                    }
                };
                self.linked_projects.push(linked_project);
            }
        }

        self.hover = HoverConfig {
            implementations: data.hoverActions_enable && data.hoverActions_implementations,
            run: data.hoverActions_enable && data.hoverActions_run,
            debug: data.hoverActions_enable && data.hoverActions_debug,
            goto_type_def: data.hoverActions_enable && data.hoverActions_gotoTypeDef,
        };

        log::info!("Config::update() = {:#?}", self);
    }

    pub fn update_caps(&mut self, caps: &ClientCapabilities) {
        if let Some(doc_caps) = caps.text_document.as_ref() {
            if let Some(value) = doc_caps.definition.as_ref().and_then(|it| it.link_support) {
                self.client_caps.location_link = value;
            }
            if let Some(value) = doc_caps.folding_range.as_ref().and_then(|it| it.line_folding_only)
            {
                self.client_caps.line_folding_only = value
            }
            if let Some(value) = doc_caps
                .document_symbol
                .as_ref()
                .and_then(|it| it.hierarchical_document_symbol_support)
            {
                self.client_caps.hierarchical_symbols = value
            }
            if let Some(value) =
                doc_caps.code_action.as_ref().map(|it| it.code_action_literal_support.is_some())
            {
                self.client_caps.code_action_literals = value;
            }
            if let Some(value) = doc_caps
                .signature_help
                .as_ref()
                .and_then(|it| it.signature_information.as_ref())
                .and_then(|it| it.parameter_information.as_ref())
                .and_then(|it| it.label_offset_support)
            {
                self.client_caps.signature_help_label_offsets = value;
            }

            self.completion.allow_snippets(false);
            if let Some(completion) = &doc_caps.completion {
                if let Some(completion_item) = &completion.completion_item {
                    if let Some(value) = completion_item.snippet_support {
                        self.completion.allow_snippets(value);
                    }
                }
            }
        }

        if let Some(window_caps) = caps.window.as_ref() {
            if let Some(value) = window_caps.work_done_progress {
                self.client_caps.work_done_progress = value;
            }
        }

        self.assist.allow_snippets(false);
        if let Some(experimental) = &caps.experimental {
            let get_bool =
                |index: &str| experimental.get(index).and_then(|it| it.as_bool()) == Some(true);

            let snippet_text_edit = get_bool("snippetTextEdit");
            self.assist.allow_snippets(snippet_text_edit);

            self.client_caps.code_action_group = get_bool("codeActionGroup");
            self.client_caps.resolve_code_action = get_bool("resolveCodeAction");
            self.client_caps.hover_actions = get_bool("hoverActions");
            self.client_caps.status_notification = get_bool("statusNotification");
        }
    }
}

#[derive(Deserialize)]
#[serde(untagged)]
enum ManifestOrProjectJson {
    Manifest(PathBuf),
    ProjectJson(ProjectJsonData),
}

macro_rules! config_data {
    (struct $name:ident { $($field:ident: $ty:ty = $default:expr,)*}) => {
        #[allow(non_snake_case)]
        struct $name { $($field: $ty,)* }
        impl $name {
            fn from_json(mut json: serde_json::Value) -> $name {
                $name {$(
                    $field: {
                        let pointer = stringify!($field).replace('_', "/");
                        let pointer = format!("/{}", pointer);
                        json.pointer_mut(&pointer)
                            .and_then(|it| serde_json::from_value(it.take()).ok())
                            .unwrap_or($default)
                    },
                )*}
            }
        }

    };
}

config_data! {
    struct ConfigData {
        callInfo_full: bool = true,

        cargo_autoreload: bool           = true,
        cargo_allFeatures: bool          = false,
        cargo_features: Vec<String>      = Vec::new(),
        cargo_loadOutDirsFromCheck: bool = false,
        cargo_noDefaultFeatures: bool    = false,
        cargo_target: Option<String>     = None,

        checkOnSave_enable: bool                         = false,
        checkOnSave_allFeatures: Option<bool>            = None,
        checkOnSave_allTargets: bool                     = true,
        checkOnSave_command: String                      = "check".into(),
        checkOnSave_noDefaultFeatures: Option<bool>      = None,
        checkOnSave_target: Option<String>               = None,
        checkOnSave_extraArgs: Vec<String>               = Vec::new(),
        checkOnSave_features: Option<Vec<String>>        = None,
        checkOnSave_overrideCommand: Option<Vec<String>> = None,

        completion_addCallArgumentSnippets: bool = true,
        completion_addCallParenthesis: bool      = true,
        completion_postfix_enable: bool          = true,

        diagnostics_enable: bool                = true,
        diagnostics_enableExperimental: bool    = true,
        diagnostics_disabled: FxHashSet<String> = FxHashSet::default(),
        diagnostics_warningsAsHint: Vec<String> = Vec::new(),
        diagnostics_warningsAsInfo: Vec<String> = Vec::new(),

        files_watcher: String = "client".into(),

        hoverActions_debug: bool           = true,
        hoverActions_enable: bool          = true,
        hoverActions_gotoTypeDef: bool     = true,
        hoverActions_implementations: bool = true,
        hoverActions_run: bool             = true,

        inlayHints_chainingHints: bool      = true,
        inlayHints_maxLength: Option<usize> = None,
        inlayHints_parameterHints: bool     = true,
        inlayHints_typeHints: bool          = true,

        lens_debug: bool           = true,
        lens_enable: bool          = true,
        lens_implementations: bool = true,
        lens_run: bool             = true,

        linkedProjects: Vec<ManifestOrProjectJson> = Vec::new(),
        lruCapacity: Option<usize>                 = None,
        notifications_cargoTomlNotFound: bool      = true,
        procMacro_enable: bool                     = false,

        rustfmt_extraArgs: Vec<String>               = Vec::new(),
        rustfmt_overrideCommand: Option<Vec<String>> = None,

        withSysroot: bool = true,
    }
}
<commit_msg>default `checkOnSave.enable` to `true` in server<commit_after>//! Config used by the language server.
//!
//! We currently get this config from `initialize` LSP request, which is not the
//! best way to do it, but was the simplest thing we could implement.
//!
//! Of particular interest is the `feature_flags` hash map: while other fields
//! configure the server itself, feature flags are passed into analysis, and
//! tweak things like automatic insertion of `()` in completions.

use std::{ffi::OsString, path::PathBuf};

use flycheck::FlycheckConfig;
use ide::{AssistConfig, CompletionConfig, DiagnosticsConfig, HoverConfig, InlayHintsConfig};
use lsp_types::ClientCapabilities;
use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest};
use rustc_hash::FxHashSet;
use serde::Deserialize;
use vfs::AbsPathBuf;

use crate::diagnostics::DiagnosticsMapConfig;

#[derive(Debug, Clone)]
pub struct Config {
    pub client_caps: ClientCapsConfig,

    pub publish_diagnostics: bool,
    pub diagnostics: DiagnosticsConfig,
    pub diagnostics_map: DiagnosticsMapConfig,
    pub lru_capacity: Option<usize>,
    pub proc_macro_srv: Option<(PathBuf, Vec<OsString>)>,
    pub files: FilesConfig,
    pub notifications: NotificationsConfig,

    pub cargo_autoreload: bool,
    pub cargo: CargoConfig,
    pub rustfmt: RustfmtConfig,
    pub flycheck: Option<FlycheckConfig>,

    pub inlay_hints: InlayHintsConfig,
    pub completion: CompletionConfig,
    pub assist: AssistConfig,
    pub call_info_full: bool,
    pub lens: LensConfig,
    pub hover: HoverConfig,

    pub with_sysroot: bool,
    pub linked_projects: Vec<LinkedProject>,
    pub root_path: AbsPathBuf,
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub enum LinkedProject {
    ProjectManifest(ProjectManifest),
    InlineJsonProject(ProjectJson),
}

impl From<ProjectManifest> for LinkedProject {
    fn from(v: ProjectManifest) -> Self {
        LinkedProject::ProjectManifest(v)
    }
}

impl From<ProjectJson> for LinkedProject {
    fn from(v: ProjectJson) -> Self {
        LinkedProject::InlineJsonProject(v)
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct LensConfig {
    pub run: bool,
    pub debug: bool,
    pub implementations: bool,
}

impl Default for LensConfig {
    fn default() -> Self {
        Self { run: true, debug: true, implementations: true }
    }
}

impl LensConfig {
    pub const NO_LENS: LensConfig = Self { run: false, debug: false, implementations: false };

    pub fn any(&self) -> bool {
        self.implementations || self.runnable()
    }

    pub fn none(&self) -> bool {
        !self.any()
    }

    pub fn runnable(&self) -> bool {
        self.run || self.debug
    }
}

#[derive(Debug, Clone)]
pub struct FilesConfig {
    pub watcher: FilesWatcher,
    pub exclude: Vec<String>,
}

#[derive(Debug, Clone)]
pub enum FilesWatcher {
    Client,
    Notify,
}

#[derive(Debug, Clone)]
pub struct NotificationsConfig {
    pub cargo_toml_not_found: bool,
}

#[derive(Debug, Clone)]
pub enum RustfmtConfig {
    Rustfmt { extra_args: Vec<String> },
    CustomCommand { command: String, args: Vec<String> },
}

#[derive(Debug, Clone, Default)]
pub struct ClientCapsConfig {
    pub location_link: bool,
    pub line_folding_only: bool,
    pub hierarchical_symbols: bool,
    pub code_action_literals: bool,
    pub work_done_progress: bool,
    pub code_action_group: bool,
    pub resolve_code_action: bool,
    pub hover_actions: bool,
    pub status_notification: bool,
    pub signature_help_label_offsets: bool,
}

impl Config {
    pub fn new(root_path: AbsPathBuf) -> Self {
        Config {
            client_caps: ClientCapsConfig::default(),

            with_sysroot: true,
            publish_diagnostics: true,
            diagnostics: DiagnosticsConfig::default(),
            diagnostics_map: DiagnosticsMapConfig::default(),
            lru_capacity: None,
            proc_macro_srv: None,
            files: FilesConfig { watcher: FilesWatcher::Notify, exclude: Vec::new() },
            notifications: NotificationsConfig { cargo_toml_not_found: true },

            cargo_autoreload: true,
            cargo: CargoConfig::default(),
            rustfmt: RustfmtConfig::Rustfmt { extra_args: Vec::new() },
            flycheck: Some(FlycheckConfig::CargoCommand {
                command: "check".to_string(),
                target_triple: None,
                no_default_features: false,
                all_targets: true,
                all_features: false,
                extra_args: Vec::new(),
                features: Vec::new(),
            }),

            inlay_hints: InlayHintsConfig {
                type_hints: true,
                parameter_hints: true,
                chaining_hints: true,
                max_length: None,
            },
            completion: CompletionConfig {
                enable_postfix_completions: true,
                add_call_parenthesis: true,
                add_call_argument_snippets: true,
                ..CompletionConfig::default()
            },
            assist: AssistConfig::default(),
            call_info_full: true,
            lens: LensConfig::default(),
            hover: HoverConfig::default(),
            linked_projects: Vec::new(),
            root_path,
        }
    }

    pub fn update(&mut self, json: serde_json::Value) {
        log::info!("Config::update({:#})", json);

        if json.is_null() || json.as_object().map_or(false, |it| it.is_empty()) {
            return;
        }

        let data = ConfigData::from_json(json);

        self.with_sysroot = data.withSysroot;
        self.publish_diagnostics = data.diagnostics_enable;
        self.diagnostics = DiagnosticsConfig {
            disable_experimental: !data.diagnostics_enableExperimental,
            disabled: data.diagnostics_disabled,
        };
        self.diagnostics_map = DiagnosticsMapConfig {
            warnings_as_info: data.diagnostics_warningsAsInfo,
            warnings_as_hint: data.diagnostics_warningsAsHint,
        };
        self.lru_capacity = data.lruCapacity;
        self.files.watcher = match data.files_watcher.as_str() {
            "notify" => FilesWatcher::Notify,
            "client" | _ => FilesWatcher::Client,
        };
        self.notifications =
            NotificationsConfig { cargo_toml_not_found: data.notifications_cargoTomlNotFound };
        self.cargo_autoreload = data.cargo_autoreload;
        self.cargo = CargoConfig {
            no_default_features: data.cargo_noDefaultFeatures,
            all_features: data.cargo_allFeatures,
            features: data.cargo_features.clone(),
            load_out_dirs_from_check: data.cargo_loadOutDirsFromCheck,
            target: data.cargo_target.clone(),
        };

        self.proc_macro_srv = if data.procMacro_enable {
            std::env::current_exe().ok().map(|path| (path, vec!["proc-macro".into()]))
        } else {
            None
        };

        self.rustfmt = match data.rustfmt_overrideCommand {
            Some(mut args) if !args.is_empty() => {
                let command = args.remove(0);
                RustfmtConfig::CustomCommand { command, args }
            }
            Some(_) | None => RustfmtConfig::Rustfmt { extra_args: data.rustfmt_extraArgs },
        };

        self.flycheck = if data.checkOnSave_enable {
            let flycheck_config = match data.checkOnSave_overrideCommand {
                Some(mut args) if !args.is_empty() => {
                    let command = args.remove(0);
                    FlycheckConfig::CustomCommand { command, args }
                }
                Some(_) | None => FlycheckConfig::CargoCommand {
                    command: data.checkOnSave_command,
                    target_triple: data.checkOnSave_target.or(data.cargo_target),
                    all_targets: data.checkOnSave_allTargets,
                    no_default_features: data
                        .checkOnSave_noDefaultFeatures
                        .unwrap_or(data.cargo_noDefaultFeatures),
                    all_features: data.checkOnSave_allFeatures.unwrap_or(data.cargo_allFeatures),
                    features: data.checkOnSave_features.unwrap_or(data.cargo_features),
                    extra_args: data.checkOnSave_extraArgs,
                },
            };
            Some(flycheck_config)
        } else {
            None
        };

        self.inlay_hints = InlayHintsConfig {
            type_hints: data.inlayHints_typeHints,
            parameter_hints: data.inlayHints_parameterHints,
            chaining_hints: data.inlayHints_chainingHints,
            max_length: data.inlayHints_maxLength,
        };

        self.completion.enable_postfix_completions = data.completion_postfix_enable;
        self.completion.add_call_parenthesis = data.completion_addCallParenthesis;
        self.completion.add_call_argument_snippets = data.completion_addCallArgumentSnippets;

        self.call_info_full = data.callInfo_full;

        self.lens = LensConfig {
            run: data.lens_enable && data.lens_run,
            debug: data.lens_enable && data.lens_debug,
            implementations: data.lens_enable && data.lens_implementations,
        };

        if !data.linkedProjects.is_empty() {
            self.linked_projects.clear();
            for linked_project in data.linkedProjects {
                let linked_project = match linked_project {
                    ManifestOrProjectJson::Manifest(it) => {
                        let path = self.root_path.join(it);
                        match ProjectManifest::from_manifest_file(path) {
                            Ok(it) => it.into(),
                            Err(_) => continue,
                        }
                    }
                    ManifestOrProjectJson::ProjectJson(it) => {
                        ProjectJson::new(&self.root_path, it).into()
                    }
                };
                self.linked_projects.push(linked_project);
            }
        }

        self.hover = HoverConfig {
            implementations: data.hoverActions_enable && data.hoverActions_implementations,
            run: data.hoverActions_enable && data.hoverActions_run,
            debug: data.hoverActions_enable && data.hoverActions_debug,
            goto_type_def: data.hoverActions_enable && data.hoverActions_gotoTypeDef,
        };

        log::info!("Config::update() = {:#?}", self);
    }

    pub fn update_caps(&mut self, caps: &ClientCapabilities) {
        if let Some(doc_caps) = caps.text_document.as_ref() {
            if let Some(value) = doc_caps.definition.as_ref().and_then(|it| it.link_support) {
                self.client_caps.location_link = value;
            }
            if let Some(value) = doc_caps.folding_range.as_ref().and_then(|it| it.line_folding_only)
            {
                self.client_caps.line_folding_only = value
            }
            if let Some(value) = doc_caps
                .document_symbol
                .as_ref()
                .and_then(|it| it.hierarchical_document_symbol_support)
            {
                self.client_caps.hierarchical_symbols = value
            }
            if let Some(value) =
                doc_caps.code_action.as_ref().map(|it| it.code_action_literal_support.is_some())
            {
                self.client_caps.code_action_literals = value;
            }
            if let Some(value) = doc_caps
                .signature_help
                .as_ref()
                .and_then(|it| it.signature_information.as_ref())
                .and_then(|it| it.parameter_information.as_ref())
                .and_then(|it| it.label_offset_support)
            {
                self.client_caps.signature_help_label_offsets = value;
            }

            self.completion.allow_snippets(false);
            if let Some(completion) = &doc_caps.completion {
                if let Some(completion_item) = &completion.completion_item {
                    if let Some(value) = completion_item.snippet_support {
                        self.completion.allow_snippets(value);
                    }
                }
            }
        }

        if let Some(window_caps) = caps.window.as_ref() {
            if let Some(value) = window_caps.work_done_progress {
                self.client_caps.work_done_progress = value;
            }
        }

        self.assist.allow_snippets(false);
        if let Some(experimental) = &caps.experimental {
            let get_bool =
                |index: &str| experimental.get(index).and_then(|it| it.as_bool()) == Some(true);

            let snippet_text_edit = get_bool("snippetTextEdit");
            self.assist.allow_snippets(snippet_text_edit);

            self.client_caps.code_action_group = get_bool("codeActionGroup");
            self.client_caps.resolve_code_action = get_bool("resolveCodeAction");
            self.client_caps.hover_actions = get_bool("hoverActions");
            self.client_caps.status_notification = get_bool("statusNotification");
        }
    }
}

#[derive(Deserialize)]
#[serde(untagged)]
enum ManifestOrProjectJson {
    Manifest(PathBuf),
    ProjectJson(ProjectJsonData),
}

macro_rules! config_data {
    (struct $name:ident { $($field:ident: $ty:ty = $default:expr,)*}) => {
        #[allow(non_snake_case)]
        struct $name { $($field: $ty,)* }
        impl $name {
            fn from_json(mut json: serde_json::Value) -> $name {
                $name {$(
                    $field: {
                        let pointer = stringify!($field).replace('_', "/");
                        let pointer = format!("/{}", pointer);
                        json.pointer_mut(&pointer)
                            .and_then(|it| serde_json::from_value(it.take()).ok())
                            .unwrap_or($default)
                    },
                )*}
            }
        }

    };
}

config_data! {
    struct ConfigData {
        callInfo_full: bool = true,

        cargo_autoreload: bool           = true,
        cargo_allFeatures: bool          = false,
        cargo_features: Vec<String>      = Vec::new(),
        cargo_loadOutDirsFromCheck: bool = false,
        cargo_noDefaultFeatures: bool    = false,
        cargo_target: Option<String>     = None,

        checkOnSave_enable: bool                         = true,
        checkOnSave_allFeatures: Option<bool>            = None,
        checkOnSave_allTargets: bool                     = true,
        checkOnSave_command: String                      = "check".into(),
        checkOnSave_noDefaultFeatures: Option<bool>      = None,
        checkOnSave_target: Option<String>               = None,
        checkOnSave_extraArgs: Vec<String>               = Vec::new(),
        checkOnSave_features: Option<Vec<String>>        = None,
        checkOnSave_overrideCommand: Option<Vec<String>> = None,

        completion_addCallArgumentSnippets: bool = true,
        completion_addCallParenthesis: bool      = true,
        completion_postfix_enable: bool          = true,

        diagnostics_enable: bool                = true,
        diagnostics_enableExperimental: bool    = true,
        diagnostics_disabled: FxHashSet<String> = FxHashSet::default(),
        diagnostics_warningsAsHint: Vec<String> = Vec::new(),
        diagnostics_warningsAsInfo: Vec<String> = Vec::new(),

        files_watcher: String = "client".into(),

        hoverActions_debug: bool           = true,
        hoverActions_enable: bool          = true,
        hoverActions_gotoTypeDef: bool     = true,
        hoverActions_implementations: bool = true,
        hoverActions_run: bool             = true,

        inlayHints_chainingHints: bool      = true,
        inlayHints_maxLength: Option<usize> = None,
        inlayHints_parameterHints: bool     = true,
        inlayHints_typeHints: bool          = true,

        lens_debug: bool           = true,
        lens_enable: bool          = true,
        lens_implementations: bool = true,
        lens_run: bool             = true,

        linkedProjects: Vec<ManifestOrProjectJson> = Vec::new(),
        lruCapacity: Option<usize>                 = None,
        notifications_cargoTomlNotFound: bool      = true,
        procMacro_enable: bool                     = false,

        rustfmt_extraArgs: Vec<String>               = Vec::new(),
        rustfmt_overrideCommand: Option<Vec<String>> = None,

        withSysroot: bool = true,
    }
}
<|endoftext|>


<commit_before>#[derive(Debug)]
pub enum ExpectedVersion {
    Number(i32),
    Empty,       // The stream should exist but should be empty.
    NoStream,    // The stream should not exist at the time of the writing.
    Any,         // This write should never conflict with anything and should always succeed.
    Exist,       // The stream should exist with any number of events in it.

    Unexpected(String) // Any string (including -3 OR less than or equal -5).
}

impl From<ExpectedVersion> for String {
    fn from(version: ExpectedVersion) -> String {
        match version {
            ExpectedVersion::Number(n) => n.to_string(),
            ExpectedVersion::Empty => "0".to_string(),
            ExpectedVersion::NoStream => "-1".to_string(),
            ExpectedVersion::Any => "-2".to_string(),
            ExpectedVersion::Exist => "-4".to_string(),
            ExpectedVersion::Unexpected(s) => s,
        }
    }
}

impl From<String> for ExpectedVersion {
    fn from(string: String) -> ExpectedVersion {
        match string.as_ref() {
            "-4" => ExpectedVersion::Exist,
            "-2" => ExpectedVersion::Any,
            "-1" => ExpectedVersion::NoStream,
            "0" => ExpectedVersion::Empty,
            _ => ExpectedVersion::parse_number_or_unexpected(string)
        }
    }
}

impl ExpectedVersion {
    fn parse_number_or_unexpected(string: String) -> ExpectedVersion {
        match string.parse::<i32>() {
            Ok(n) =>  ExpectedVersion::Number(n),
            Err(_) => ExpectedVersion::Unexpected(string)
        }
    }
}
<commit_msg>Introduce ExpectedVersion.Invalid<commit_after>#[derive(Debug)]
pub enum ExpectedVersion {
    Number(i32),
    Empty,              // The stream should exist but should be empty.
    NoStream,           // The stream should not exist at the time of the writing.
    Any,                // This write should never conflict with anything and should always succeed.
    Exist,              // The stream should exist with any number of events in it.

    Unexpected(String), // Any string (including -3 OR less than or equal -5).
    Invalid,            // Just for compatibility with related C# ExpectedVersion class.
}

impl From<ExpectedVersion> for String {
    fn from(version: ExpectedVersion) -> String {
        match version {
            ExpectedVersion::Number(n) => n.to_string(),
            ExpectedVersion::Empty => "0".to_string(),
            ExpectedVersion::NoStream => "-1".to_string(),
            ExpectedVersion::Any => "-2".to_string(),
            ExpectedVersion::Invalid => "-3".to_string(),
            ExpectedVersion::Exist => "-4".to_string(),
            ExpectedVersion::Unexpected(s) => s,
        }
    }
}

impl From<String> for ExpectedVersion {
    fn from(string: String) -> ExpectedVersion {
        match string.as_ref() {
            "-4" => ExpectedVersion::Exist,
            "-3" => ExpectedVersion::Invalid,
            "-2" => ExpectedVersion::Any,
            "-1" => ExpectedVersion::NoStream,
            "0" => ExpectedVersion::Empty,
            _ => ExpectedVersion::parse_number_or_unexpected(string)
        }
    }
}

impl ExpectedVersion {
    fn parse_number_or_unexpected(string: String) -> ExpectedVersion {
        match string.parse::<i32>() {
            Ok(n) =>  ExpectedVersion::Number(n),
            Err(_) => ExpectedVersion::Unexpected(string)
        }
    }
}
<|endoftext|>


<commit_before>use crate::language_client::LanguageClient;
use anyhow::Result;
use lsp_types::Command;
use serde::Deserialize;
use serde_json::Value;

pub mod command {
    // older versions of gopls send code lens commands without the `gopls` prefix
    // so probably a good idea to maintain both for a while.
    pub(super) const TEST: &str = "test";
    pub(super) const GOPLS_TEST: &str = "gopls.test";
    pub(super) const GENERATE: &str = "generate";
    pub(super) const GOPLS_GENERATE: &str = "gopls.generate";
}

impl LanguageClient {
    pub fn handle_gopls_command(&self, cmd: &Command) -> Result<bool> {
        match cmd.command.as_str() {
            command::TEST | command::GOPLS_TEST => {
                if let Some(args) = &cmd.arguments {
                    if let Some(file) = args.get(0) {
                        let file = String::deserialize(file)?;
                        let path = parse_package_path(file.as_str()).unwrap_or("./...".into());
                        let run = <Option<Vec<String>>>::deserialize(
                            args.get(1).unwrap_or(&Value::Null),
                        )?;

                        let bench = <Option<Vec<String>>>::deserialize(
                            args.get(2).unwrap_or(&Value::Null),
                        )?;

                        let run = run.unwrap_or_default();
                        let bench = bench.unwrap_or_default();

                        if run.len() > 0 {
                            let cmd = format!("term go test -run '{}' {}", run.join("|"), path);
                            self.vim()?.command(cmd)?;
                        } else if bench.len() > 0 {
                            let cmd = format!("term go test -bench '{}' {}", bench.join("|"), path);
                            self.vim()?.command(cmd)?;
                        } else {
                            self.vim()?.echoerr("No tests to run")?;
                        }
                    }
                }
            }
            command::GENERATE | command::GOPLS_GENERATE => {
                if let Some(arguments) = &cmd.arguments {
                    if let Some(package) = arguments.get(0) {
                        let package = String::deserialize(package)?;
                        let recursive =
                            bool::deserialize(arguments.get(1).unwrap_or(&Value::Bool(false)))?;
                        let cmd = match (package, recursive) {
                            (package, false) => format!("term go generate -x {}", package),
                            (_, true) => "term go generate -x ./...".into(),
                        };
                        self.vim()?.command(cmd)?;
                    }
                }
            }

            _ => return Ok(false),
        }

        Ok(true)
    }
}

fn parse_package_path(path: &str) -> Option<String> {
    let path = if path.starts_with("file://") {
        path.strip_prefix("file://")?
    } else {
        path
    };
    let path = std::path::PathBuf::from(path);
    Some(path.parent()?.to_str()?.to_owned())
}

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

    #[test]
    fn test_parse_package_path() {
        let folder = parse_package_path("file:///home/dev/someone/project/file.go");
        assert!(folder.is_some());
        assert_eq!("/home/dev/someone/project", folder.unwrap());

        let folder = parse_package_path("/home/dev/someone/project/file.go");
        assert!(folder.is_some());
        assert_eq!("/home/dev/someone/project", folder.unwrap());
    }
}
<commit_msg>Handle gopls generate commands server-side (#1142)<commit_after>use crate::language_client::LanguageClient;
use anyhow::Result;
use lsp_types::Command;
use serde::Deserialize;
use serde_json::Value;

pub mod command {
    // older versions of gopls send code lens commands without the `gopls` prefix
    // so probably a good idea to maintain both for a while.
    pub(super) const TEST: &str = "test";
    pub(super) const GOPLS_TEST: &str = "gopls.test";
}

impl LanguageClient {
    pub fn handle_gopls_command(&self, cmd: &Command) -> Result<bool> {
        match cmd.command.as_str() {
            command::TEST | command::GOPLS_TEST => {
                if let Some(args) = &cmd.arguments {
                    if let Some(file) = args.get(0) {
                        let file = String::deserialize(file)?;
                        let path = parse_package_path(file.as_str()).unwrap_or("./...".into());
                        let run = <Option<Vec<String>>>::deserialize(
                            args.get(1).unwrap_or(&Value::Null),
                        )?;

                        let bench = <Option<Vec<String>>>::deserialize(
                            args.get(2).unwrap_or(&Value::Null),
                        )?;

                        let run = run.unwrap_or_default();
                        let bench = bench.unwrap_or_default();

                        if run.len() > 0 {
                            let cmd = format!("term go test -run '{}' {}", run.join("|"), path);
                            self.vim()?.command(cmd)?;
                        } else if bench.len() > 0 {
                            let cmd = format!("term go test -bench '{}' {}", bench.join("|"), path);
                            self.vim()?.command(cmd)?;
                        } else {
                            self.vim()?.echoerr("No tests to run")?;
                        }
                    }
                }
            }
            _ => return Ok(false),
        }

        Ok(true)
    }
}

fn parse_package_path(path: &str) -> Option<String> {
    let path = if path.starts_with("file://") {
        path.strip_prefix("file://")?
    } else {
        path
    };
    let path = std::path::PathBuf::from(path);
    Some(path.parent()?.to_str()?.to_owned())
}

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

    #[test]
    fn test_parse_package_path() {
        let folder = parse_package_path("file:///home/dev/someone/project/file.go");
        assert!(folder.is_some());
        assert_eq!("/home/dev/someone/project", folder.unwrap());

        let folder = parse_package_path("/home/dev/someone/project/file.go");
        assert!(folder.is_some());
        assert_eq!("/home/dev/someone/project", folder.unwrap());
    }
}
<|endoftext|>


<commit_before>use serialize::json::{Json};
use anymap::AnyMap;
use std::os;

use server::{Request, Response};
use errors::{Error, FileError};
use middleware::{Application, HandleResult};
use framework::endpoint::Endpoint;
use framework::media::Media;
use server_backend::status;
use server_backend::mime;
use server_backend::header::{Header, HeaderFormat};
use server_backend::header::common::{ContentType, Location};
use {Extensible};

pub struct Client<'a> {
    pub app: &'a Application,
    pub endpoint: &'a Endpoint,
    pub request: &'a mut Request,
    pub media: &'a Media,
    pub ext: AnyMap,
    pub response: Response
}

pub type ClientResult<'a> = HandleResult<Client<'a>>;

impl<'a> Client<'a> {

    pub fn new(app: &'a Application, endpoint: &'a Endpoint, request: &'a mut Request, media: &'a Media) -> Client<'a> {
        Client {
            app: app,
            endpoint: endpoint,
            request: request,
            media: media,
            ext: AnyMap::new(),
            response: Response::new(status::Ok)
        }
    }

    //
    // Work with status
    //

    pub fn status(&mut self) -> status::StatusCode {
        self.response.status
    }

    pub fn set_status(&mut self, status: status::StatusCode) {
        self.response.status = status;
    }

    pub fn unauthorized(&mut self) {
        self.response.status = status::Unauthorized;
    }

    pub fn forbidden(&mut self) {
        self.response.status = status::Forbidden;
    }

    pub fn not_found(&mut self) {
        self.response.status = status::NotFound;
    }

    pub fn internal_server_error(&mut self) {
        self.response.status = status::InternalServerError;
    }

    pub fn not_implemented(&mut self) {
        self.response.status = status::NotImplemented;
    }

    //

    pub fn set_header<H: Header + HeaderFormat>(&mut self, header: H) {
        self.response.set_header(header);
    }

    pub fn set_json_content_type(&mut self) {
        self.set_header(ContentType(mime::Mime(mime::Application, mime::Json, vec![])));
    }

    pub fn set_content_type(&mut self, mime: mime::Mime) {
        self.set_header(ContentType(mime));
    }

    pub fn error<T: Error>(self, error: T) -> ClientResult<'a> {
        Err(box error as Box<Error>)
    }

    pub fn json(mut self, result: &Json) -> ClientResult<'a> {
        self.set_json_content_type();
        self.response.push_string(result.to_string());

        Ok(self)
    }

    pub fn text(mut self, result: String) -> ClientResult<'a> {
        self.response.push_string(result);

        Ok(self)
    }

    pub fn file(mut self, path: &Path) -> ClientResult<'a> {
       match self.response.push_file(&os::make_absolute(path)) {
            Ok(()) => Ok(self),
            Err(err) => {
                return Err(box FileError(err) as Box<Error>);
            }
        }
    }

    pub fn redirect(mut self, to: &str) -> ClientResult<'a> {
        self.set_status(status::Found);
        self.set_header(Location(to.to_string()));

        Ok(self)
    }

    pub fn permanent_redirect(mut self, to: &str) -> ClientResult<'a> {
        self.set_status(status::MovedPermanently);
        self.set_header(Location(to.to_string()));

        Ok(self)
    }

    pub fn move_response(self) -> Response {
        self.response
    }

    pub fn ext(&self) -> &AnyMap {
        &self.ext
    }

    pub fn ext_mut(&mut self) -> &mut AnyMap {
        &mut self.ext
    }

}

impl<'a> Extensible for Client<'a> {
    fn ext(&self) -> &::anymap::AnyMap { &self.ext }
    fn ext_mut(&mut self) -> &mut ::anymap::AnyMap { &mut self.ext }
}<commit_msg>feat: add `empty()` to `Client`<commit_after>use serialize::json::{Json};
use anymap::AnyMap;
use std::os;

use server::{Request, Response};
use errors::{Error, FileError};
use middleware::{Application, HandleResult};
use framework::endpoint::Endpoint;
use framework::media::Media;
use server_backend::status;
use server_backend::mime;
use server_backend::header::{Header, HeaderFormat};
use server_backend::header::common::{ContentType, Location};
use {Extensible};

pub struct Client<'a> {
    pub app: &'a Application,
    pub endpoint: &'a Endpoint,
    pub request: &'a mut Request,
    pub media: &'a Media,
    pub ext: AnyMap,
    pub response: Response
}

pub type ClientResult<'a> = HandleResult<Client<'a>>;

impl<'a> Client<'a> {

    pub fn new(app: &'a Application, endpoint: &'a Endpoint, request: &'a mut Request, media: &'a Media) -> Client<'a> {
        Client {
            app: app,
            endpoint: endpoint,
            request: request,
            media: media,
            ext: AnyMap::new(),
            response: Response::new(status::Ok)
        }
    }

    //
    // Work with status
    //

    pub fn status(&mut self) -> status::StatusCode {
        self.response.status
    }

    pub fn set_status(&mut self, status: status::StatusCode) {
        self.response.status = status;
    }

    pub fn unauthorized(&mut self) {
        self.response.status = status::Unauthorized;
    }

    pub fn forbidden(&mut self) {
        self.response.status = status::Forbidden;
    }

    pub fn not_found(&mut self) {
        self.response.status = status::NotFound;
    }

    pub fn internal_server_error(&mut self) {
        self.response.status = status::InternalServerError;
    }

    pub fn not_implemented(&mut self) {
        self.response.status = status::NotImplemented;
    }

    //

    pub fn set_header<H: Header + HeaderFormat>(&mut self, header: H) {
        self.response.set_header(header);
    }

    pub fn set_json_content_type(&mut self) {
        self.set_header(ContentType(mime::Mime(mime::Application, mime::Json, vec![])));
    }

    pub fn set_content_type(&mut self, mime: mime::Mime) {
        self.set_header(ContentType(mime));
    }

    pub fn error<T: Error>(self, error: T) -> ClientResult<'a> {
        Err(box error as Box<Error>)
    }

    pub fn json(mut self, result: &Json) -> ClientResult<'a> {
        self.set_json_content_type();
        self.response.push_string(result.to_string());

        Ok(self)
    }

    pub fn text(mut self, result: String) -> ClientResult<'a> {
        self.response.push_string(result);

        Ok(self)
    }

    pub fn file(mut self, path: &Path) -> ClientResult<'a> {
       match self.response.push_file(&os::make_absolute(path)) {
            Ok(()) => Ok(self),
            Err(err) => {
                return Err(box FileError(err) as Box<Error>);
            }
        }
    }

    pub fn empty(self) -> ClientResult<'a> {
        Ok(self)
    }

    pub fn redirect(mut self, to: &str) -> ClientResult<'a> {
        self.set_status(status::Found);
        self.set_header(Location(to.to_string()));

        Ok(self)
    }

    pub fn permanent_redirect(mut self, to: &str) -> ClientResult<'a> {
        self.set_status(status::MovedPermanently);
        self.set_header(Location(to.to_string()));

        Ok(self)
    }

    pub fn move_response(self) -> Response {
        self.response
    }

    pub fn ext(&self) -> &AnyMap {
        &self.ext
    }

    pub fn ext_mut(&mut self) -> &mut AnyMap {
        &mut self.ext
    }

}

impl<'a> Extensible for Client<'a> {
    fn ext(&self) -> &::anymap::AnyMap { &self.ext }
    fn ext_mut(&mut self) -> &mut ::anymap::AnyMap { &mut self.ext }
}<|endoftext|>


<commit_before>use std::fmt;
use std::hash::{Hash, Hasher, SipHasher};
use std::ptr;

use fst::{NONE_STATE, CompiledAddr, Output, Transition};
use fst::build::BuilderNode;

#[derive(Debug)]
pub struct Registry {
    table: Vec<RegistryCell>,
    table_size: usize, // number of rows
    lru_size: usize, // number of columns
}

#[derive(Debug)]
struct RegistryLru<'a> {
    cells: &'a mut [RegistryCell],
}

#[derive(Clone, Copy, Debug)]
pub struct RegistryCell {
    addr: CompiledAddr,
    node: RegistryNode,
}

#[derive(Copy)]
struct RegistryNode {
    is_final: bool,
    final_output: Output,
    trans: [Transition; 256],
}

#[derive(Debug)]
pub enum RegistryEntry<'a> {
    Found(CompiledAddr),
    NotFound(&'a mut RegistryCell),
    Rejected,
}

impl Registry {
    pub fn new(table_size: usize, lru_size: usize) -> Registry {
        let empty_cell = RegistryCell::none();
        let ncells = table_size.checked_mul(lru_size).unwrap();
        Registry {
            table: vec![empty_cell; ncells],
            table_size: table_size,
            lru_size: lru_size,
        }
    }

    pub fn entry<'a>(&'a mut self, bnode: &BuilderNode) -> RegistryEntry<'a> {
        if self.table.is_empty() {
            return RegistryEntry::Rejected;
        }
        let bucket = self.hash(bnode);
        let node = RegistryNode::from_builder_node(bnode);
        let start = self.lru_size * bucket;
        let end = start + self.lru_size;
        RegistryLru { cells: &mut self.table[start..end] }.entry(node)
    }

    fn hash(&self, node: &BuilderNode) -> usize {
        // The overhead of std's SipHasher is just so not worth it.
        // (In my unscientific tests, this dumb hash function has no
        // observable impact on compression ratio, but results in a perf
        // boost of ~38%.)
        // let mut n: usize = 0;
        // n = n.wrapping_add(2 * node.is_final as usize);
        // n = n.wrapping_add(7 * node.trans[0] as usize);
        // n = n.wrapping_add(11 * node.trans[1] as usize);
        // n = n.wrapping_add(3 * node.inputs[0] as usize);
        // n = n.wrapping_add(5 * node.inputs[1] as usize);
        // n = n.wrapping_add(13 * node.outputs[0].encode() as usize);
        // n = n.wrapping_add(17 * node.outputs[1].encode() as usize);
        // n = n.wrapping_add(19 * node.final_output.encode() as usize);
        // n % self.table_size
        let mut hasher = SipHasher::new();
        node.hash(&mut hasher);
        hasher.finish() as usize % self.table_size
    }
}

impl<'a> RegistryLru<'a> {
    fn entry(mut self, node: RegistryNode) -> RegistryEntry<'a> {
        if let Some(i) = self.cells.iter().position(|c| c.node == node) {
            let addr = self.cells[i].addr;
            self.promote(i); // most recently used
            RegistryEntry::Found(addr)
        } else {
            if self.cells[self.cells.len()-1].is_none() {
                let p = self.cells.as_mut_ptr();
                unsafe { ptr::copy(p, p.offset(1), self.cells.len()-1); }
            }
            self.cells[0].node = node; // discard MRU
            RegistryEntry::NotFound(&mut self.cells[0])
        }
    }

    fn promote(&mut self, i: usize) {
        assert!(i < self.cells.len());
        let cell = self.cells[i];
        let p = self.cells.as_mut_ptr();
        unsafe { ptr::copy(p, p.offset(1), i); }
        self.cells[0] = cell;
    }
}

impl RegistryCell {
    fn none() -> RegistryCell {
        let t = Transition {
            inp: 0,
            out: Output::zero(),
            addr: NONE_STATE,
        };
        RegistryCell {
            addr: NONE_STATE,
            node: RegistryNode {
                is_final: false,
                final_output: Output::zero(),
                trans: [t; 256],
            },
        }
    }

    pub fn insert(&mut self, addr: CompiledAddr) {
        self.addr = addr;
    }

    fn is_none(&self) -> bool {
        self.addr == NONE_STATE
    }
}

impl RegistryNode {
    fn from_builder_node(bnode: &BuilderNode) -> RegistryNode {
        let t = Transition {
            inp: 0,
            out: Output::zero(),
            addr: NONE_STATE,
        };
        let mut node = RegistryNode {
            is_final: bnode.is_final,
            final_output: bnode.final_output,
            trans: [t; 256],
        };
        for (i, &t) in bnode.trans.iter().enumerate() {
            node.trans[i] = t;
        }
        node
    }
}

impl fmt::Debug for RegistryNode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "RegistryNode {{ is_final: {:?}, \
                   final_output: {:?}, trans: ?? }}",
               self.is_final, self.final_output)
    }
}

impl Clone for RegistryNode {
    fn clone(&self) -> RegistryNode {
        RegistryNode {
            is_final: self.is_final,
            final_output: self.final_output,
            trans: self.trans,
        }
    }
}

impl PartialEq for RegistryNode {
    fn eq(&self, other: &RegistryNode) -> bool {
        self.is_final == other.is_final
        && self.final_output == other.final_output
        && self.trans[..] == other.trans[..]
    }
}
<commit_msg>Remove unused registry.<commit_after><|endoftext|>


<commit_before>use audio::Effects;
use character::controls::CharacterControl;
use crossbeam_channel as channel;
use gfx_app::mouse_controls::MouseControl;
use graphics::camera::CameraControl;

#[derive(Debug, Clone)]
pub struct TilemapControls {
  audio_control: channel::Sender<Effects>,
  terrain_control: channel::Sender<CameraControl>,
  character_control: channel::Sender<CharacterControl>,
  mouse_control: channel::Sender<(MouseControl, Option<(f64, f64)>)>,
}

impl TilemapControls {
  pub fn new(atc: channel::Sender<Effects>,
             ttc: channel::Sender<CameraControl>,
             ctc: channel::Sender<CharacterControl>,
             mtc: channel::Sender<(MouseControl, Option<(f64, f64)>)>) -> TilemapControls {
    TilemapControls {
      audio_control: atc,
      terrain_control: ttc,
      character_control: ctc,
      mouse_control: mtc,
    }
  }

  fn ac(&mut self, value: Effects) {
    self.audio_control.send(value);
  }

  fn tc(&mut self, value: CameraControl) {
    self.terrain_control.send(value);
  }
  fn cc(&mut self, value: CharacterControl) {
    self.character_control.send(value);
  }
  fn mc(&mut self, contol_value: MouseControl, value: Option<(f64, f64)>) {
    self.mouse_control.send((contol_value, value));
  }

  pub fn zoom_in(&mut self) {
    self.tc(CameraControl::ZoomIn)
  }
  pub fn zoom_out(&mut self) {
    self.tc(CameraControl::ZoomOut)
  }
  pub fn zoom_stop(&mut self) {
    self.tc(CameraControl::ZoomStop)
  }
  pub fn ctrl_pressed(&mut self) {
    self.cc(CharacterControl::CtrlPressed)
  }
  pub fn ctrl_released(&mut self) {
    self.cc(CharacterControl::CtrlReleased)
  }

  pub fn move_character_left(&mut self) {
    self.cc(CharacterControl::Left)
  }
  pub fn move_character_right(&mut self) {
    self.cc(CharacterControl::Right)
  }
  pub fn stop_character_x(&mut self) { self.cc(CharacterControl::XMoveStop) }
  pub fn move_character_up(&mut self) {
    self.cc(CharacterControl::Up)
  }
  pub fn move_character_down(&mut self) {
    self.cc(CharacterControl::Down)
  }
  pub fn stop_character_y(&mut self) { self.cc(CharacterControl::YMoveStop) }

  pub fn mouse_left_click(&mut self, mouse_pos: Option<(f64, f64)>) {
    self.mc(MouseControl::LeftClick, mouse_pos);
    match mouse_pos {
      Some(_) => self.ac(Effects::PistolFire),
      _ => self.ac(Effects::None),
    }
  }
  pub fn mouse_right_click(&mut self, mouse_pos: Option<(f64, f64)>) {
    self.mc(MouseControl::RightClick, mouse_pos)
  }
}
<commit_msg>Rename control functions<commit_after>use audio::Effects;
use character::controls::CharacterControl;
use crossbeam_channel as channel;
use gfx_app::mouse_controls::MouseControl;
use graphics::camera::CameraControl;

#[derive(Debug, Clone)]
pub struct TilemapControls {
  audio_control: channel::Sender<Effects>,
  terrain_control: channel::Sender<CameraControl>,
  character_control: channel::Sender<CharacterControl>,
  mouse_control: channel::Sender<(MouseControl, Option<(f64, f64)>)>,
}

impl TilemapControls {
  pub fn new(atc: channel::Sender<Effects>,
             ttc: channel::Sender<CameraControl>,
             ctc: channel::Sender<CharacterControl>,
             mtc: channel::Sender<(MouseControl, Option<(f64, f64)>)>) -> TilemapControls {
    TilemapControls {
      audio_control: atc,
      terrain_control: ttc,
      character_control: ctc,
      mouse_control: mtc,
    }
  }

  fn audio(&mut self, value: Effects) {
    self.audio_control.send(value);
  }

  fn terrain(&mut self, value: CameraControl) {
    self.terrain_control.send(value);
  }
  fn character(&mut self, value: CharacterControl) {
    self.character_control.send(value);
  }
  fn mouse(&mut self, contol_value: MouseControl, value: Option<(f64, f64)>) {
    self.mouse_control.send((contol_value, value));
  }

  pub fn zoom_in(&mut self) {
    self.terrain(CameraControl::ZoomIn)
  }
  pub fn zoom_out(&mut self) {
    self.terrain(CameraControl::ZoomOut)
  }
  pub fn zoom_stop(&mut self) {
    self.terrain(CameraControl::ZoomStop)
  }
  pub fn ctrl_pressed(&mut self) {
    self.character(CharacterControl::CtrlPressed)
  }
  pub fn ctrl_released(&mut self) {
    self.character(CharacterControl::CtrlReleased)
  }

  pub fn move_character_left(&mut self) {
    self.character(CharacterControl::Left)
  }
  pub fn move_character_right(&mut self) {
    self.character(CharacterControl::Right)
  }
  pub fn stop_character_x(&mut self) { self.character(CharacterControl::XMoveStop) }
  pub fn move_character_up(&mut self) {
    self.character(CharacterControl::Up)
  }
  pub fn move_character_down(&mut self) {
    self.character(CharacterControl::Down)
  }
  pub fn stop_character_y(&mut self) { self.character(CharacterControl::YMoveStop) }

  pub fn mouse_left_click(&mut self, mouse_pos: Option<(f64, f64)>) {
    self.mouse(MouseControl::LeftClick, mouse_pos);
    match mouse_pos {
      Some(_) => self.audio(Effects::PistolFire),
      _ => self.audio(Effects::None),
    }
  }
  pub fn mouse_right_click(&mut self, mouse_pos: Option<(f64, f64)>) {
    self.mouse(MouseControl::RightClick, mouse_pos)
  }
}
<|endoftext|>