<commit_before>// run-pass

use std::num::Wrapping;

macro_rules! wrapping_operation {
    ($result:expr, $lhs:ident $op:tt $rhs:expr) => {
        assert_eq!($result, $lhs $op $rhs);
        assert_eq!($result, &$lhs $op $rhs);
        assert_eq!($result, $lhs $op &$rhs);
        assert_eq!($result, &$lhs $op &$rhs);
    };
    ($result:expr, $op:tt $expr:expr) => {
        assert_eq!($result, $op $expr);
        assert_eq!($result, $op &$expr);
    };
}

macro_rules! wrapping_assignment {
    ($result:expr, $lhs:ident $op:tt $rhs:expr) => {
        let mut lhs1 = $lhs;
        lhs1 $op $rhs;
        assert_eq!($result, lhs1);

        let mut lhs2 = $lhs;
        lhs2 $op &$rhs;
        assert_eq!($result, lhs2);
    };
}

macro_rules! wrapping_test {
    ($type:ty, $min:expr, $max:expr) => {
        let zero: Wrapping<$type> = Wrapping(0);
        let one: Wrapping<$type> = Wrapping(1);
        let min: Wrapping<$type> = Wrapping($min);
        let max: Wrapping<$type> = Wrapping($max);

        wrapping_operation!(min, max + one);
        wrapping_assignment!(min, max += one);
        wrapping_operation!(max, min - one);
        wrapping_assignment!(max, min -= one);
        wrapping_operation!(max, max * one);
        wrapping_assignment!(max, max *= one);
        wrapping_operation!(max, max / one);
        wrapping_assignment!(max, max /= one);
        wrapping_operation!(zero, max % one);
        wrapping_assignment!(zero, max %= one);
        wrapping_operation!(zero, zero & max);
        wrapping_assignment!(zero, zero &= max);
        wrapping_operation!(max, zero | max);
        wrapping_assignment!(max, zero |= max);
        wrapping_operation!(zero, max ^ max);
        wrapping_assignment!(zero, max ^= max);
        wrapping_operation!(zero, zero << 1usize);
        wrapping_assignment!(zero, zero <<= 1usize);
        wrapping_operation!(zero, zero >> 1usize);
        wrapping_assignment!(zero, zero >>= 1usize);
        wrapping_operation!(zero, -zero);
        wrapping_operation!(max, !min);
    };
}

fn main() {
    wrapping_test!(i8, std::i8::MIN, std::i8::MAX);
    wrapping_test!(i16, std::i16::MIN, std::i16::MAX);
    wrapping_test!(i32, std::i32::MIN, std::i32::MAX);
    wrapping_test!(i64, std::i64::MIN, std::i64::MAX);
    wrapping_test!(i128, std::i128::MIN, std::i128::MAX);
    wrapping_test!(u8, std::u8::MIN, std::u8::MAX);
    wrapping_test!(u16, std::u16::MIN, std::u16::MAX);
    wrapping_test!(u32, std::u32::MIN, std::u32::MAX);
    wrapping_test!(u64, std::u64::MIN, std::u64::MAX);
    wrapping_test!(u128, std::u128::MIN, std::u128::MAX);
}
<commit_msg>Also test isize and usize in wrapping arithmetics<commit_after>// run-pass

use std::num::Wrapping;

macro_rules! wrapping_operation {
    ($result:expr, $lhs:ident $op:tt $rhs:expr) => {
        assert_eq!($result, $lhs $op $rhs);
        assert_eq!($result, &$lhs $op $rhs);
        assert_eq!($result, $lhs $op &$rhs);
        assert_eq!($result, &$lhs $op &$rhs);
    };
    ($result:expr, $op:tt $expr:expr) => {
        assert_eq!($result, $op $expr);
        assert_eq!($result, $op &$expr);
    };
}

macro_rules! wrapping_assignment {
    ($result:expr, $lhs:ident $op:tt $rhs:expr) => {
        let mut lhs1 = $lhs;
        lhs1 $op $rhs;
        assert_eq!($result, lhs1);

        let mut lhs2 = $lhs;
        lhs2 $op &$rhs;
        assert_eq!($result, lhs2);
    };
}

macro_rules! wrapping_test {
    ($type:ty, $min:expr, $max:expr) => {
        let zero: Wrapping<$type> = Wrapping(0);
        let one: Wrapping<$type> = Wrapping(1);
        let min: Wrapping<$type> = Wrapping($min);
        let max: Wrapping<$type> = Wrapping($max);

        wrapping_operation!(min, max + one);
        wrapping_assignment!(min, max += one);
        wrapping_operation!(max, min - one);
        wrapping_assignment!(max, min -= one);
        wrapping_operation!(max, max * one);
        wrapping_assignment!(max, max *= one);
        wrapping_operation!(max, max / one);
        wrapping_assignment!(max, max /= one);
        wrapping_operation!(zero, max % one);
        wrapping_assignment!(zero, max %= one);
        wrapping_operation!(zero, zero & max);
        wrapping_assignment!(zero, zero &= max);
        wrapping_operation!(max, zero | max);
        wrapping_assignment!(max, zero |= max);
        wrapping_operation!(zero, max ^ max);
        wrapping_assignment!(zero, max ^= max);
        wrapping_operation!(zero, zero << 1usize);
        wrapping_assignment!(zero, zero <<= 1usize);
        wrapping_operation!(zero, zero >> 1usize);
        wrapping_assignment!(zero, zero >>= 1usize);
        wrapping_operation!(zero, -zero);
        wrapping_operation!(max, !min);
    };
}

fn main() {
    wrapping_test!(i8, std::i8::MIN, std::i8::MAX);
    wrapping_test!(i16, std::i16::MIN, std::i16::MAX);
    wrapping_test!(i32, std::i32::MIN, std::i32::MAX);
    wrapping_test!(i64, std::i64::MIN, std::i64::MAX);
    wrapping_test!(i128, std::i128::MIN, std::i128::MAX);
    wrapping_test!(isize, std::isize::MIN, std::isize::MAX);
    wrapping_test!(u8, std::u8::MIN, std::u8::MAX);
    wrapping_test!(u16, std::u16::MIN, std::u16::MAX);
    wrapping_test!(u32, std::u32::MIN, std::u32::MAX);
    wrapping_test!(u64, std::u64::MIN, std::u64::MAX);
    wrapping_test!(u128, std::u128::MIN, std::u128::MAX);
    wrapping_test!(usize, std::usize::MIN, std::usize::MAX);
}
<|endoftext|>


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

use crate::{counters, SynchronizerState};
use anyhow::{format_err, Result};
use executor_types::{ChunkExecutor, ExecutedTrees};
use itertools::Itertools;
use libra_types::{
    account_state::AccountState,
    contract_event::ContractEvent,
    ledger_info::LedgerInfoWithSignatures,
    move_resource::MoveStorage,
    on_chain_config::{config_address, OnChainConfigPayload, ON_CHAIN_CONFIG_REGISTRY},
    transaction::TransactionListWithProof,
};
use std::{collections::HashSet, convert::TryFrom, sync::Arc};
use storage_interface::DbReader;
use subscription_service::ReconfigSubscription;

/// Proxies interactions with execution and storage for state synchronization
pub trait ExecutorProxyTrait: Send {
    /// Sync the local state with the latest in storage.
    fn get_local_storage_state(&self) -> Result<SynchronizerState>;

    /// Execute and commit a batch of transactions
    fn execute_chunk(
        &mut self,
        txn_list_with_proof: TransactionListWithProof,
        verified_target_li: LedgerInfoWithSignatures,
        intermediate_end_of_epoch_li: Option<LedgerInfoWithSignatures>,
    ) -> Result<()>;

    /// Gets chunk of transactions given the known version, target version and the max limit.
    fn get_chunk(
        &self,
        known_version: u64,
        limit: u64,
        target_version: u64,
    ) -> Result<TransactionListWithProof>;

    /// Get the epoch change ledger info for epoch so that we can move to next epoch.
    fn get_epoch_proof(&self, epoch: u64) -> Result<LedgerInfoWithSignatures>;

    /// Get ledger info at an epoch boundary version.
    fn get_epoch_ending_ledger_info(&self, version: u64) -> Result<LedgerInfoWithSignatures>;

    /// Load all on-chain configs from storage
    /// Note: this method is being exposed as executor proxy trait temporarily because storage read is currently
    /// using the tonic storage read client, which needs the tokio runtime to block on with no runtime/async issues
    /// Once we make storage reads sync (by replacing the storage read client with direct LibraDB),
    /// we can make this entirely internal to `ExecutorProxy`'s initialization procedure
    fn load_on_chain_configs(&mut self) -> Result<()>;

    /// publishes on-chain config updates to subscribed components
    fn publish_on_chain_config_updates(&mut self, events: Vec<ContractEvent>) -> Result<()>;
}

pub(crate) struct ExecutorProxy {
    storage: Arc<dyn DbReader>,
    executor: Box<dyn ChunkExecutor>,
    reconfig_subscriptions: Vec<ReconfigSubscription>,
    on_chain_configs: OnChainConfigPayload,
}

impl ExecutorProxy {
    pub(crate) fn new(
        storage: Arc<dyn DbReader>,
        executor: Box<dyn ChunkExecutor>,
        mut reconfig_subscriptions: Vec<ReconfigSubscription>,
    ) -> Self {
        let on_chain_configs = Self::fetch_all_configs(&*storage)
            .expect("[state sync] Failed initial read of on-chain configs");
        for subscription in reconfig_subscriptions.iter_mut() {
            subscription
                .publish(on_chain_configs.clone())
                .expect("[state sync] Failed to publish initial on-chain config");
        }
        Self {
            storage,
            executor,
            reconfig_subscriptions,
            on_chain_configs,
        }
    }

    // TODO make this into more general trait method in `on_chain_config`
    // once `StorageRead` trait is replaced with `DbReader` and `batch_fetch_config` method is no longer async
    fn fetch_all_configs(storage: &dyn DbReader) -> Result<OnChainConfigPayload> {
        let access_paths = ON_CHAIN_CONFIG_REGISTRY
            .iter()
            .map(|config_id| config_id.access_path())
            .collect();
        let configs = storage.batch_fetch_resources(access_paths)?;
        let epoch = storage
            .get_account_state_with_proof_by_version(
                config_address(),
                storage.fetch_synced_version()?,
            )?
            .0
            .map(|blob| {
                AccountState::try_from(&blob).and_then(|state| {
                    Ok(state
                        .get_configuration_resource()?
                        .ok_or_else(|| format_err!("ConfigurationResource does not exist"))?
                        .epoch())
                })
            })
            .ok_or_else(|| format_err!("Failed to fetch ConfigurationResource"))??;

        Ok(OnChainConfigPayload::new(
            epoch,
            Arc::new(
                ON_CHAIN_CONFIG_REGISTRY
                    .iter()
                    .cloned()
                    .zip_eq(configs)
                    .collect(),
            ),
        ))
    }
}

impl ExecutorProxyTrait for ExecutorProxy {
    fn get_local_storage_state(&self) -> Result<SynchronizerState> {
        let storage_info = self
            .storage
            .get_startup_info()?
            .ok_or_else(|| format_err!("[state sync] Failed to access storage info"))?;

        let current_epoch_state = storage_info.get_epoch_state().clone();

        let synced_trees = if let Some(synced_tree_state) = storage_info.synced_tree_state {
            ExecutedTrees::from(synced_tree_state)
        } else {
            ExecutedTrees::from(storage_info.committed_tree_state)
        };

        Ok(SynchronizerState::new(
            storage_info.latest_ledger_info,
            synced_trees,
            current_epoch_state,
        ))
    }

    fn execute_chunk(
        &mut self,
        txn_list_with_proof: TransactionListWithProof,
        verified_target_li: LedgerInfoWithSignatures,
        intermediate_end_of_epoch_li: Option<LedgerInfoWithSignatures>,
    ) -> Result<()> {
        // track chunk execution time
        let timer = counters::EXECUTE_CHUNK_DURATION.start_timer();
        let reconfig_events = self.executor.execute_and_commit_chunk(
            txn_list_with_proof,
            verified_target_li,
            intermediate_end_of_epoch_li,
        )?;
        timer.stop_and_record();
        self.publish_on_chain_config_updates(reconfig_events)
    }

    fn get_chunk(
        &self,
        known_version: u64,
        limit: u64,
        target_version: u64,
    ) -> Result<TransactionListWithProof> {
        self.storage
            .get_transactions(known_version + 1, limit, target_version, false)
    }

    fn get_epoch_proof(&self, epoch: u64) -> Result<LedgerInfoWithSignatures> {
        self.storage
            .get_epoch_ending_ledger_infos(epoch, epoch + 1)?
            .ledger_info_with_sigs
            .pop()
            .ok_or_else(|| format_err!("Empty EpochChangeProof"))
    }

    fn get_epoch_ending_ledger_info(&self, version: u64) -> Result<LedgerInfoWithSignatures> {
        self.storage.get_epoch_ending_ledger_info(version)
    }

    fn load_on_chain_configs(&mut self) -> Result<()> {
        self.on_chain_configs = Self::fetch_all_configs(&*self.storage)?;
        Ok(())
    }

    fn publish_on_chain_config_updates(&mut self, events: Vec<ContractEvent>) -> Result<()> {
        if events.is_empty() {
            return Ok(());
        }
        let event_keys = events
            .iter()
            .map(|event| *event.key())
            .collect::<HashSet<_>>();

        // calculate deltas
        let new_configs = Self::fetch_all_configs(&*self.storage)?;
        let changed_configs = new_configs
            .configs()
            .iter()
            .filter(|(id, cfg)| {
                &self
                    .on_chain_configs
                    .configs()
                    .get(id)
                    .expect("missing on-chain config value in local copy")
                    != cfg
            })
            .map(|(id, _)| *id)
            .collect::<HashSet<_>>();

        // notify subscribers
        for subscription in self.reconfig_subscriptions.iter_mut() {
            // publish updates if *any* of the subscribed configs changed
            // or any of the subscribed events were emitted
            let subscribed_items = subscription.subscribed_items();
            if !changed_configs.is_disjoint(&subscribed_items.configs)
                || !event_keys.is_disjoint(&subscribed_items.events)
            {
                subscription.publish(new_configs.clone())?;
            }
        }

        self.on_chain_configs = new_configs;
        Ok(())
    }
}
<commit_msg>[state sync] log instead of throw error on reconfig notification failure<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{counters, SynchronizerState};
use anyhow::{format_err, Result};
use executor_types::{ChunkExecutor, ExecutedTrees};
use itertools::Itertools;
use libra_logger::prelude::*;
use libra_types::{
    account_state::AccountState,
    contract_event::ContractEvent,
    ledger_info::LedgerInfoWithSignatures,
    move_resource::MoveStorage,
    on_chain_config::{config_address, OnChainConfigPayload, ON_CHAIN_CONFIG_REGISTRY},
    transaction::TransactionListWithProof,
};
use std::{collections::HashSet, convert::TryFrom, sync::Arc};
use storage_interface::DbReader;
use subscription_service::ReconfigSubscription;

/// Proxies interactions with execution and storage for state synchronization
pub trait ExecutorProxyTrait: Send {
    /// Sync the local state with the latest in storage.
    fn get_local_storage_state(&self) -> Result<SynchronizerState>;

    /// Execute and commit a batch of transactions
    fn execute_chunk(
        &mut self,
        txn_list_with_proof: TransactionListWithProof,
        verified_target_li: LedgerInfoWithSignatures,
        intermediate_end_of_epoch_li: Option<LedgerInfoWithSignatures>,
    ) -> Result<()>;

    /// Gets chunk of transactions given the known version, target version and the max limit.
    fn get_chunk(
        &self,
        known_version: u64,
        limit: u64,
        target_version: u64,
    ) -> Result<TransactionListWithProof>;

    /// Get the epoch change ledger info for epoch so that we can move to next epoch.
    fn get_epoch_proof(&self, epoch: u64) -> Result<LedgerInfoWithSignatures>;

    /// Get ledger info at an epoch boundary version.
    fn get_epoch_ending_ledger_info(&self, version: u64) -> Result<LedgerInfoWithSignatures>;

    /// Load all on-chain configs from storage
    /// Note: this method is being exposed as executor proxy trait temporarily because storage read is currently
    /// using the tonic storage read client, which needs the tokio runtime to block on with no runtime/async issues
    /// Once we make storage reads sync (by replacing the storage read client with direct LibraDB),
    /// we can make this entirely internal to `ExecutorProxy`'s initialization procedure
    fn load_on_chain_configs(&mut self) -> Result<()>;

    /// publishes on-chain config updates to subscribed components
    fn publish_on_chain_config_updates(&mut self, events: Vec<ContractEvent>) -> Result<()>;
}

pub(crate) struct ExecutorProxy {
    storage: Arc<dyn DbReader>,
    executor: Box<dyn ChunkExecutor>,
    reconfig_subscriptions: Vec<ReconfigSubscription>,
    on_chain_configs: OnChainConfigPayload,
}

impl ExecutorProxy {
    pub(crate) fn new(
        storage: Arc<dyn DbReader>,
        executor: Box<dyn ChunkExecutor>,
        mut reconfig_subscriptions: Vec<ReconfigSubscription>,
    ) -> Self {
        let on_chain_configs = Self::fetch_all_configs(&*storage)
            .expect("[state sync] Failed initial read of on-chain configs");
        for subscription in reconfig_subscriptions.iter_mut() {
            subscription
                .publish(on_chain_configs.clone())
                .expect("[state sync] Failed to publish initial on-chain config");
        }
        Self {
            storage,
            executor,
            reconfig_subscriptions,
            on_chain_configs,
        }
    }

    // TODO make this into more general trait method in `on_chain_config`
    // once `StorageRead` trait is replaced with `DbReader` and `batch_fetch_config` method is no longer async
    fn fetch_all_configs(storage: &dyn DbReader) -> Result<OnChainConfigPayload> {
        let access_paths = ON_CHAIN_CONFIG_REGISTRY
            .iter()
            .map(|config_id| config_id.access_path())
            .collect();
        let configs = storage.batch_fetch_resources(access_paths)?;
        let epoch = storage
            .get_account_state_with_proof_by_version(
                config_address(),
                storage.fetch_synced_version()?,
            )?
            .0
            .map(|blob| {
                AccountState::try_from(&blob).and_then(|state| {
                    Ok(state
                        .get_configuration_resource()?
                        .ok_or_else(|| format_err!("ConfigurationResource does not exist"))?
                        .epoch())
                })
            })
            .ok_or_else(|| format_err!("Failed to fetch ConfigurationResource"))??;

        Ok(OnChainConfigPayload::new(
            epoch,
            Arc::new(
                ON_CHAIN_CONFIG_REGISTRY
                    .iter()
                    .cloned()
                    .zip_eq(configs)
                    .collect(),
            ),
        ))
    }
}

impl ExecutorProxyTrait for ExecutorProxy {
    fn get_local_storage_state(&self) -> Result<SynchronizerState> {
        let storage_info = self
            .storage
            .get_startup_info()?
            .ok_or_else(|| format_err!("[state sync] Failed to access storage info"))?;

        let current_epoch_state = storage_info.get_epoch_state().clone();

        let synced_trees = if let Some(synced_tree_state) = storage_info.synced_tree_state {
            ExecutedTrees::from(synced_tree_state)
        } else {
            ExecutedTrees::from(storage_info.committed_tree_state)
        };

        Ok(SynchronizerState::new(
            storage_info.latest_ledger_info,
            synced_trees,
            current_epoch_state,
        ))
    }

    fn execute_chunk(
        &mut self,
        txn_list_with_proof: TransactionListWithProof,
        verified_target_li: LedgerInfoWithSignatures,
        intermediate_end_of_epoch_li: Option<LedgerInfoWithSignatures>,
    ) -> Result<()> {
        // track chunk execution time
        let timer = counters::EXECUTE_CHUNK_DURATION.start_timer();
        let reconfig_events = self.executor.execute_and_commit_chunk(
            txn_list_with_proof,
            verified_target_li,
            intermediate_end_of_epoch_li,
        )?;
        timer.stop_and_record();
        if let Err(e) = self.publish_on_chain_config_updates(reconfig_events) {
            error!(
                "[state sync] failed to publish reconfig notification in execute_chunk: {}",
                e
            );
        }
        Ok(())
    }

    fn get_chunk(
        &self,
        known_version: u64,
        limit: u64,
        target_version: u64,
    ) -> Result<TransactionListWithProof> {
        self.storage
            .get_transactions(known_version + 1, limit, target_version, false)
    }

    fn get_epoch_proof(&self, epoch: u64) -> Result<LedgerInfoWithSignatures> {
        self.storage
            .get_epoch_ending_ledger_infos(epoch, epoch + 1)?
            .ledger_info_with_sigs
            .pop()
            .ok_or_else(|| format_err!("Empty EpochChangeProof"))
    }

    fn get_epoch_ending_ledger_info(&self, version: u64) -> Result<LedgerInfoWithSignatures> {
        self.storage.get_epoch_ending_ledger_info(version)
    }

    fn load_on_chain_configs(&mut self) -> Result<()> {
        self.on_chain_configs = Self::fetch_all_configs(&*self.storage)?;
        Ok(())
    }

    fn publish_on_chain_config_updates(&mut self, events: Vec<ContractEvent>) -> Result<()> {
        if events.is_empty() {
            return Ok(());
        }
        let event_keys = events
            .iter()
            .map(|event| *event.key())
            .collect::<HashSet<_>>();

        // calculate deltas
        let new_configs = Self::fetch_all_configs(&*self.storage)?;
        let changed_configs = new_configs
            .configs()
            .iter()
            .filter(|(id, cfg)| {
                &self
                    .on_chain_configs
                    .configs()
                    .get(id)
                    .expect("missing on-chain config value in local copy")
                    != cfg
            })
            .map(|(id, _)| *id)
            .collect::<HashSet<_>>();

        // notify subscribers
        for subscription in self.reconfig_subscriptions.iter_mut() {
            // publish updates if *any* of the subscribed configs changed
            // or any of the subscribed events were emitted
            let subscribed_items = subscription.subscribed_items();
            if !changed_configs.is_disjoint(&subscribed_items.configs)
                || !event_keys.is_disjoint(&subscribed_items.events)
            {
                if let Err(e) = subscription.publish(new_configs.clone()) {
                    error!(
                        "[state sync] failed to publish individual reconfig subscription: {}",
                        e
                    );
                }
            }
        }

        self.on_chain_configs = new_configs;
        Ok(())
    }
}
<|endoftext|>


<commit_before>//! Internal client
//!
//! Do not use directly - use the [`Algorithmia`](../struct.Algorithmia.html) struct instead
use http::header::HeaderValue;
use headers_ext::{Authorization, HeaderMapExt, UserAgent, authorization::Credentials};
use http::header::HeaderMap;
use reqwest::{Client, IntoUrl, Method, RequestBuilder, Url};
use std::str::FromStr;
use std::sync::Arc;

use crate::error::{ErrorKind, Result, ResultExt};
pub use reqwest::Body;

struct Simple(HeaderValue);
impl Credentials for Simple {
    const SCHEME: &'static str = "Simple";
    fn decode(value: &HeaderValue) -> Option<Self> {
        debug_assert!(
            value.as_bytes().starts_with(b"Simple "),
            "HeaderValue to decode should start with \"Simple ..\", received = {:?}",
            value,
        );

        Some(Simple(value.clone()))
    }
    fn encode(&self) -> HeaderValue {
        self.0.clone()
    }
}

impl Simple {
    /// Try to create a `Simple` authorization header.
    pub fn new(token: &str) -> Result<Self> {
        HeaderValue::from_str(&format!("Simple {}", token))
            .map(Simple)
            .map_err(|_| ErrorKind::InvalidApiKey.into())
    }
}
/// Represent the different ways to auth with the API
#[derive(Clone)]
pub enum ApiAuth {
    /// Algorithmia API key to use for authentication
    ApiKey(String),
    /// Use unauthenticated request (common for on-platform algorithms)
    None,
}

/// Internal `HttpClient` to build requests: wraps `reqwest` client
#[derive(Clone)]
pub struct HttpClient {
    pub base_url: Url,
    api_auth: ApiAuth,
    inner_client: Arc<Client>,
    user_agent: String,
}

impl HttpClient {
    /// Instantiate an `HttpClient` - creates a new `reqwest` client
    pub fn new<U: IntoUrl>(api_auth: ApiAuth, base_url: U) -> Result<HttpClient> {
        Ok(HttpClient {
            api_auth: api_auth,
            base_url: base_url
                .into_url()
                .chain_err(|| ErrorKind::InvalidBaseUrl)?,
            inner_client: Arc::new(Client::new()),
            user_agent: format!(
                "algorithmia-rust/{} (Rust {}",
                option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"),
                crate::version::RUSTC_VERSION
            ),
        })
    }

    /// Helper to make Algorithmia GET requests with the API key
    pub fn get(&self, url: Url) -> RequestBuilder {
        self.build_request(Method::GET, url)
    }

    /// Helper to make Algorithmia GET requests with the API key
    pub fn head(&self, url: Url) -> RequestBuilder {
        self.build_request(Method::HEAD, url)
    }

    /// Helper to make Algorithmia POST requests with the API key
    pub fn post(&self, url: Url) -> RequestBuilder {
        self.build_request(Method::POST, url)
    }

    /// Helper to make Algorithmia PUT requests with the API key
    pub fn put(&self, url: Url) -> RequestBuilder {
        self.build_request(Method::PUT, url)
    }

    /// Helper to make Algorithmia POST requests with the API key
    pub fn delete(&self, url: Url) -> RequestBuilder {
        self.build_request(Method::DELETE, url)
    }

    fn build_request(&self, verb: Method, url: Url) -> RequestBuilder {
        let mut headers = HeaderMap::new();
        headers.typed_insert(
            UserAgent::from_str(&self.user_agent).expect("User Agent not valid ASCII"),
        );
        if let ApiAuth::ApiKey(ref api_key) = self.api_auth {
            headers.typed_insert(Authorization(
                Simple::new(api_key).expect("API Key not valid ASCII"),
            ));
        }

        self.inner_client
            .request(verb, url.clone())
            .headers(headers)
    }
}

impl<'a> From<&'a str> for ApiAuth {
    fn from(api_key: &'a str) -> Self {
        match api_key.len() {
            0 => ApiAuth::None,
            _ => ApiAuth::ApiKey(api_key.into()),
        }
    }
}

impl From<String> for ApiAuth {
    fn from(api_key: String) -> Self {
        match api_key.len() {
            0 => ApiAuth::None,
            _ => ApiAuth::ApiKey(api_key),
        }
    }
}

impl From<()> for ApiAuth {
    fn from(_: ()) -> Self {
        ApiAuth::None
    }
}
<commit_msg>cargo fmt<commit_after>//! Internal client
//!
//! Do not use directly - use the [`Algorithmia`](../struct.Algorithmia.html) struct instead
use headers_ext::{authorization::Credentials, Authorization, HeaderMapExt, UserAgent};
use http::header::HeaderMap;
use http::header::HeaderValue;
use reqwest::{Client, IntoUrl, Method, RequestBuilder, Url};
use std::str::FromStr;
use std::sync::Arc;

use crate::error::{ErrorKind, Result, ResultExt};
pub use reqwest::Body;

struct Simple(HeaderValue);
impl Credentials for Simple {
    const SCHEME: &'static str = "Simple";
    fn decode(value: &HeaderValue) -> Option<Self> {
        debug_assert!(
            value.as_bytes().starts_with(b"Simple "),
            "HeaderValue to decode should start with \"Simple ..\", received = {:?}",
            value,
        );

        Some(Simple(value.clone()))
    }
    fn encode(&self) -> HeaderValue {
        self.0.clone()
    }
}

impl Simple {
    /// Try to create a `Simple` authorization header.
    pub fn new(token: &str) -> Result<Self> {
        HeaderValue::from_str(&format!("Simple {}", token))
            .map(Simple)
            .map_err(|_| ErrorKind::InvalidApiKey.into())
    }
}
/// Represent the different ways to auth with the API
#[derive(Clone)]
pub enum ApiAuth {
    /// Algorithmia API key to use for authentication
    ApiKey(String),
    /// Use unauthenticated request (common for on-platform algorithms)
    None,
}

/// Internal `HttpClient` to build requests: wraps `reqwest` client
#[derive(Clone)]
pub struct HttpClient {
    pub base_url: Url,
    api_auth: ApiAuth,
    inner_client: Arc<Client>,
    user_agent: String,
}

impl HttpClient {
    /// Instantiate an `HttpClient` - creates a new `reqwest` client
    pub fn new<U: IntoUrl>(api_auth: ApiAuth, base_url: U) -> Result<HttpClient> {
        Ok(HttpClient {
            api_auth: api_auth,
            base_url: base_url
                .into_url()
                .chain_err(|| ErrorKind::InvalidBaseUrl)?,
            inner_client: Arc::new(Client::new()),
            user_agent: format!(
                "algorithmia-rust/{} (Rust {}",
                option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"),
                crate::version::RUSTC_VERSION
            ),
        })
    }

    /// Helper to make Algorithmia GET requests with the API key
    pub fn get(&self, url: Url) -> RequestBuilder {
        self.build_request(Method::GET, url)
    }

    /// Helper to make Algorithmia GET requests with the API key
    pub fn head(&self, url: Url) -> RequestBuilder {
        self.build_request(Method::HEAD, url)
    }

    /// Helper to make Algorithmia POST requests with the API key
    pub fn post(&self, url: Url) -> RequestBuilder {
        self.build_request(Method::POST, url)
    }

    /// Helper to make Algorithmia PUT requests with the API key
    pub fn put(&self, url: Url) -> RequestBuilder {
        self.build_request(Method::PUT, url)
    }

    /// Helper to make Algorithmia POST requests with the API key
    pub fn delete(&self, url: Url) -> RequestBuilder {
        self.build_request(Method::DELETE, url)
    }

    fn build_request(&self, verb: Method, url: Url) -> RequestBuilder {
        let mut headers = HeaderMap::new();
        headers.typed_insert(
            UserAgent::from_str(&self.user_agent).expect("User Agent not valid ASCII"),
        );
        if let ApiAuth::ApiKey(ref api_key) = self.api_auth {
            headers.typed_insert(Authorization(
                Simple::new(api_key).expect("API Key not valid ASCII"),
            ));
        }

        self.inner_client
            .request(verb, url.clone())
            .headers(headers)
    }
}

impl<'a> From<&'a str> for ApiAuth {
    fn from(api_key: &'a str) -> Self {
        match api_key.len() {
            0 => ApiAuth::None,
            _ => ApiAuth::ApiKey(api_key.into()),
        }
    }
}

impl From<String> for ApiAuth {
    fn from(api_key: String) -> Self {
        match api_key.len() {
            0 => ApiAuth::None,
            _ => ApiAuth::ApiKey(api_key),
        }
    }
}

impl From<()> for ApiAuth {
    fn from(_: ()) -> Self {
        ApiAuth::None
    }
}
<|endoftext|>


<commit_before>//! Types for user flags which can appear in rooms.
use std::fmt;

use serde::de::{Deserializer, Visitor, Error, Unexpected};

/// Single flag.
#[derive(Clone, Debug, Hash)]
pub struct Flag {
    /// The name of the flag, unique per user.
    pub name: String,
    /// The primary color of the flag.
    pub primary_color: FlagColor,
    /// The secondary color of the flag.
    pub secondary_color: FlagColor,
    /// The X position of the flag.
    pub x: i8,
    /// The Y position of the flag.
    pub y: i8,
}

/// All possible colors a flag can have.test
#[repr(u8)]
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub enum FlagColor {
    /// COLOR_RED = 1
    Red = 1,
    /// COLOR_PURPLE = 2
    Purple = 2,
    /// COLOR_BLUE = 3
    Blue = 3,
    /// COLOR_CYAN = 4
    Cyan = 4,
    /// COLOR_GREEN = 5
    Green = 5,
    /// COLOR_YELLOW = 6
    Yellow = 6,
    /// COLOR_ORANGE = 7
    Orange = 7,
    /// COLOR_BROWN = 8
    Brown = 8,
    /// COLOR_GREY = 9
    Grey = 9,
    /// COLOR_WHITE = 10
    White = 10,
}

/// An error resulting from trying to convert a FlagColor that's out of bounds.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct FlagColorError;

impl FlagColor {
    /// Converts an integer color code into a flag color.
    #[inline]
    pub fn from(v: u8) -> Result<Self, FlagColorError> {
        match v {
            1 => Ok(FlagColor::Red),
            2 => Ok(FlagColor::Purple),
            3 => Ok(FlagColor::Blue),
            4 => Ok(FlagColor::Cyan),
            5 => Ok(FlagColor::Green),
            6 => Ok(FlagColor::Yellow),
            7 => Ok(FlagColor::Orange),
            8 => Ok(FlagColor::Brown),
            9 => Ok(FlagColor::Grey),
            10 => Ok(FlagColor::White),
            _ => Err(FlagColorError),
        }
    }

    #[inline]
    fn from_serde<E: Error>(v: u8) -> Result<Self, E> {
        match v {
            1 => Ok(FlagColor::Red),
            2 => Ok(FlagColor::Purple),
            3 => Ok(FlagColor::Blue),
            4 => Ok(FlagColor::Cyan),
            5 => Ok(FlagColor::Green),
            6 => Ok(FlagColor::Yellow),
            7 => Ok(FlagColor::Orange),
            8 => Ok(FlagColor::Brown),
            9 => Ok(FlagColor::Grey),
            10 => Ok(FlagColor::White),
            other => {
                Err(E::invalid_value(Unexpected::Unsigned(other as u64),
                                     &"an integer between 1 and 10"))
            }
        }
    }
}

struct FlagStringVisitor;

impl<'de> Visitor<'de> for FlagStringVisitor {
    type Value = Vec<Flag>;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("a string with flag formatting (`Flag571~2~3~14~7|`...)")
    }

    #[inline]
    fn visit_unit<E>(self) -> Result<Self::Value, E>
        where E: Error
    {
        Ok(Vec::new())
    }

    #[inline]
    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
        where E: Error
    {
        // TODO: nocopy version of this maybe?
        v.split("|")
            .map(|flag_str| {
                let mut iter = flag_str.split("~");

                macro_rules! next {
                    () => ({
                        iter.next().ok_or_else(|| {
                            E::invalid_type(Unexpected::Other(flag_str),
                                            &"a string in the format of name~4~2~4~2")
                        })?
                    })
                }

                macro_rules! next_u8 {
                    () => ({
                        let next = next!();
                        next.parse().map_err(|_| E::invalid_type(Unexpected::Other(next),
                            &"an unsigned integer"))?
                    })
                }

                macro_rules! next_color {
                    () => ({
                        FlagColor::from_serde(next_u8!())?
                    })
                }

                Ok(Flag {
                    name: next!().to_owned(),
                    primary_color: next_color!(),
                    secondary_color: next_color!(),
                    x: next_u8!(),
                    y: next_u8!(),
                })
            })
            .collect::<Result<Vec<_>, _>>()
    }
}

pub(super) fn deserialize_flags<'de, D>(deserializer: D) -> Result<Vec<Flag>, D::Error>
    where D: Deserializer<'de>
{
    deserializer.deserialize_str(FlagStringVisitor)
}
<commit_msg>Fix another edge case in flag parsing<commit_after>//! Types for user flags which can appear in rooms.
use std::fmt;

use serde::de::{Deserializer, Visitor, Error, Unexpected};

/// Single flag.
#[derive(Clone, Debug, Hash)]
pub struct Flag {
    /// The name of the flag, unique per user.
    pub name: String,
    /// The primary color of the flag.
    pub primary_color: FlagColor,
    /// The secondary color of the flag.
    pub secondary_color: FlagColor,
    /// The X position of the flag.
    pub x: i8,
    /// The Y position of the flag.
    pub y: i8,
}

/// All possible colors a flag can have.test
#[repr(u8)]
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub enum FlagColor {
    /// COLOR_RED = 1
    Red = 1,
    /// COLOR_PURPLE = 2
    Purple = 2,
    /// COLOR_BLUE = 3
    Blue = 3,
    /// COLOR_CYAN = 4
    Cyan = 4,
    /// COLOR_GREEN = 5
    Green = 5,
    /// COLOR_YELLOW = 6
    Yellow = 6,
    /// COLOR_ORANGE = 7
    Orange = 7,
    /// COLOR_BROWN = 8
    Brown = 8,
    /// COLOR_GREY = 9
    Grey = 9,
    /// COLOR_WHITE = 10
    White = 10,
}

/// An error resulting from trying to convert a FlagColor that's out of bounds.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct FlagColorError;

impl FlagColor {
    /// Converts an integer color code into a flag color.
    #[inline]
    pub fn from(v: u8) -> Result<Self, FlagColorError> {
        match v {
            1 => Ok(FlagColor::Red),
            2 => Ok(FlagColor::Purple),
            3 => Ok(FlagColor::Blue),
            4 => Ok(FlagColor::Cyan),
            5 => Ok(FlagColor::Green),
            6 => Ok(FlagColor::Yellow),
            7 => Ok(FlagColor::Orange),
            8 => Ok(FlagColor::Brown),
            9 => Ok(FlagColor::Grey),
            10 => Ok(FlagColor::White),
            _ => Err(FlagColorError),
        }
    }

    #[inline]
    fn from_serde<E: Error>(v: u8) -> Result<Self, E> {
        match v {
            1 => Ok(FlagColor::Red),
            2 => Ok(FlagColor::Purple),
            3 => Ok(FlagColor::Blue),
            4 => Ok(FlagColor::Cyan),
            5 => Ok(FlagColor::Green),
            6 => Ok(FlagColor::Yellow),
            7 => Ok(FlagColor::Orange),
            8 => Ok(FlagColor::Brown),
            9 => Ok(FlagColor::Grey),
            10 => Ok(FlagColor::White),
            other => {
                Err(E::invalid_value(Unexpected::Unsigned(other as u64),
                                     &"an integer between 1 and 10"))
            }
        }
    }
}

struct FlagStringVisitor;

impl<'de> Visitor<'de> for FlagStringVisitor {
    type Value = Vec<Flag>;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("a string with flag formatting (`Flag571~2~3~14~7|`...)")
    }

    #[inline]
    fn visit_unit<E>(self) -> Result<Self::Value, E>
        where E: Error
    {
        Ok(Vec::new())
    }

    #[inline]
    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
        where E: Error
    {
        if v.is_empty() {
            return Ok(Vec::new());
        }

        // TODO: nocopy version of this maybe?
        v.split("|")
            .map(|flag_str| {
                let mut iter = flag_str.split("~");

                macro_rules! next {
                    () => ({
                        iter.next().ok_or_else(|| {
                            E::invalid_type(Unexpected::Str(flag_str),
                                            &"a string in the format of name~4~2~4~2")
                        })?
                    })
                }

                macro_rules! next_u8 {
                    () => ({
                        let next = next!();
                        next.parse().map_err(|_| E::invalid_type(Unexpected::Str(next),
                            &"an unsigned integer"))?
                    })
                }

                macro_rules! next_color {
                    () => ({
                        FlagColor::from_serde(next_u8!())?
                    })
                }

                Ok(Flag {
                    name: next!().to_owned(),
                    primary_color: next_color!(),
                    secondary_color: next_color!(),
                    x: next_u8!(),
                    y: next_u8!(),
                })
            })
            .collect::<Result<Vec<_>, _>>()
    }
}

pub(super) fn deserialize_flags<'de, D>(deserializer: D) -> Result<Vec<Flag>, D::Error>
    where D: Deserializer<'de>
{
    deserializer.deserialize_str(FlagStringVisitor)
}
<|endoftext|>


<commit_before>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Martin Kysel <code@martinkysel.com>
//  *
//  * For the full copyright and license information, please view the LICENSE file
//  * that was distributed with this source code.

// spell-checker:ignore (methods) isnt

#[macro_use]
extern crate uucore;

use std::{
    fs::File,
    io::{stdin, stdout, BufReader, Read, Stdout, Write},
    path::Path,
    time::Duration,
};

#[cfg(all(unix, not(target_os = "fuchsia")))]
extern crate nix;

use clap::{crate_version, App, Arg};
use crossterm::{
    event::{self, Event, KeyCode, KeyEvent, KeyModifiers},
    execute, queue,
    style::Attribute,
    terminal,
};

use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr;
use uucore::display::Quotable;

const BELL: &str = "\x07";

pub mod options {
    pub const SILENT: &str = "silent";
    pub const LOGICAL: &str = "logical";
    pub const NO_PAUSE: &str = "no-pause";
    pub const PRINT_OVER: &str = "print-over";
    pub const CLEAN_PRINT: &str = "clean-print";
    pub const SQUEEZE: &str = "squeeze";
    pub const PLAIN: &str = "plain";
    pub const LINES: &str = "lines";
    pub const NUMBER: &str = "number";
    pub const PATTERN: &str = "pattern";
    pub const FROM_LINE: &str = "from-line";
    pub const FILES: &str = "files";
}

const MULTI_FILE_TOP_PROMPT: &str = "::::::::::::::\n{}\n::::::::::::::\n";

pub fn uumain(args: impl uucore::Args) -> i32 {
    let matches = uu_app().get_matches_from(args);

    let mut buff = String::new();
    let silent = matches.is_present(options::SILENT);
    if let Some(files) = matches.values_of(options::FILES) {
        let mut stdout = setup_term();
        let length = files.len();

        let mut files_iter = files.peekable();
        while let (Some(file), next_file) = (files_iter.next(), files_iter.peek()) {
            let file = Path::new(file);
            if file.is_dir() {
                terminal::disable_raw_mode().unwrap();
                show_usage_error!("{} is a directory.", file.quote());
                return 1;
            }
            if !file.exists() {
                terminal::disable_raw_mode().unwrap();
                show_error!("cannot open {}: No such file or directory", file.quote());
                return 1;
            }
            if length > 1 {
                buff.push_str(&MULTI_FILE_TOP_PROMPT.replace("{}", file.to_str().unwrap()));
            }
            let mut reader = BufReader::new(File::open(file).unwrap());
            reader.read_to_string(&mut buff).unwrap();
            more(&buff, &mut stdout, next_file.copied(), silent);
            buff.clear();
        }
        reset_term(&mut stdout);
    } else if atty::isnt(atty::Stream::Stdin) {
        stdin().read_to_string(&mut buff).unwrap();
        let mut stdout = setup_term();
        more(&buff, &mut stdout, None, silent);
        reset_term(&mut stdout);
    } else {
        show_usage_error!("bad usage");
    }
    0
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(uucore::util_name())
        .about("A file perusal filter for CRT viewing.")
        .version(crate_version!())
        .arg(
            Arg::with_name(options::SILENT)
                .short("d")
                .long(options::SILENT)
                .help("Display help instead of ringing bell"),
        )
        // The commented arguments below are unimplemented:
        /*
        .arg(
            Arg::with_name(options::LOGICAL)
                .short("f")
                .long(options::LOGICAL)
                .help("Count logical rather than screen lines"),
        )
        .arg(
            Arg::with_name(options::NO_PAUSE)
                .short("l")
                .long(options::NO_PAUSE)
                .help("Suppress pause after form feed"),
        )
        .arg(
            Arg::with_name(options::PRINT_OVER)
                .short("c")
                .long(options::PRINT_OVER)
                .help("Do not scroll, display text and clean line ends"),
        )
        .arg(
            Arg::with_name(options::CLEAN_PRINT)
                .short("p")
                .long(options::CLEAN_PRINT)
                .help("Do not scroll, clean screen and display text"),
        )
        .arg(
            Arg::with_name(options::SQUEEZE)
                .short("s")
                .long(options::SQUEEZE)
                .help("Squeeze multiple blank lines into one"),
        )
        .arg(
            Arg::with_name(options::PLAIN)
                .short("u")
                .long(options::PLAIN)
                .help("Suppress underlining and bold"),
        )
        .arg(
            Arg::with_name(options::LINES)
                .short("n")
                .long(options::LINES)
                .value_name("number")
                .takes_value(true)
                .help("The number of lines per screen full"),
        )
        .arg(
            Arg::with_name(options::NUMBER)
                .allow_hyphen_values(true)
                .long(options::NUMBER)
                .required(false)
                .takes_value(true)
                .help("Same as --lines"),
        )
        .arg(
            Arg::with_name(options::FROM_LINE)
                .short("F")
                .allow_hyphen_values(true)
                .required(false)
                .takes_value(true)
                .value_name("number")
                .help("Display file beginning from line number"),
        )
        .arg(
            Arg::with_name(options::PATTERN)
                .short("P")
                .allow_hyphen_values(true)
                .required(false)
                .takes_value(true)
                .help("Display file beginning from pattern match"),
        )
        */
        .arg(
            Arg::with_name(options::FILES)
                .required(false)
                .multiple(true)
                .help("Path to the files to be read"),
        )
}

#[cfg(not(target_os = "fuchsia"))]
fn setup_term() -> std::io::Stdout {
    let stdout = stdout();
    terminal::enable_raw_mode().unwrap();
    stdout
}

#[cfg(target_os = "fuchsia")]
#[inline(always)]
fn setup_term() -> usize {
    0
}

#[cfg(not(target_os = "fuchsia"))]
fn reset_term(stdout: &mut std::io::Stdout) {
    terminal::disable_raw_mode().unwrap();
    // Clear the prompt
    queue!(stdout, terminal::Clear(terminal::ClearType::CurrentLine)).unwrap();
    // Move cursor to the beginning without printing new line
    print!("\r");
    stdout.flush().unwrap();
}

#[cfg(target_os = "fuchsia")]
#[inline(always)]
fn reset_term(_: &mut usize) {}

fn more(buff: &str, stdout: &mut Stdout, next_file: Option<&str>, silent: bool) {
    let (cols, rows) = terminal::size().unwrap();
    let lines = break_buff(buff, usize::from(cols));

    let mut pager = Pager::new(rows, lines, next_file, silent);
    pager.draw(stdout, None);
    if pager.should_close() {
        return;
    }

    loop {
        let mut wrong_key = None;
        if event::poll(Duration::from_millis(10)).unwrap() {
            match event::read().unwrap() {
                Event::Key(KeyEvent {
                    code: KeyCode::Char('q'),
                    modifiers: KeyModifiers::NONE,
                })
                | Event::Key(KeyEvent {
                    code: KeyCode::Char('c'),
                    modifiers: KeyModifiers::CONTROL,
                }) => {
                    reset_term(stdout);
                    std::process::exit(0);
                }
                Event::Key(KeyEvent {
                    code: KeyCode::Down,
                    modifiers: KeyModifiers::NONE,
                })
                | Event::Key(KeyEvent {
                    code: KeyCode::Char(' '),
                    modifiers: KeyModifiers::NONE,
                }) => {
                    if pager.should_close() {
                        return;
                    } else {
                        pager.page_down();
                    }
                }
                Event::Key(KeyEvent {
                    code: KeyCode::Up,
                    modifiers: KeyModifiers::NONE,
                }) => {
                    pager.page_up();
                }
                Event::Resize(col, row) => {
                    pager.page_resize(col, row);
                }
                Event::Key(KeyEvent {
                    code: KeyCode::Char(k),
                    ..
                }) => wrong_key = Some(k),
                _ => continue,
            }

            pager.draw(stdout, wrong_key);
        }
    }
}

struct Pager<'a> {
    // The current line at the top of the screen
    upper_mark: usize,
    // The number of rows that fit on the screen
    content_rows: u16,
    lines: Vec<String>,
    next_file: Option<&'a str>,
    line_count: usize,
    silent: bool,
}

impl<'a> Pager<'a> {
    fn new(rows: u16, lines: Vec<String>, next_file: Option<&'a str>, silent: bool) -> Self {
        let line_count = lines.len();
        Self {
            upper_mark: 0,
            content_rows: rows.saturating_sub(1),
            lines,
            next_file,
            line_count,
            silent,
        }
    }

    fn should_close(&mut self) -> bool {
        self.upper_mark
            .saturating_add(self.content_rows.into())
            .ge(&self.line_count)
    }

    fn page_down(&mut self) {
        self.upper_mark = self.upper_mark.saturating_add(self.content_rows.into());
    }

    fn page_up(&mut self) {
        self.upper_mark = self.upper_mark.saturating_sub(self.content_rows.into());
    }

    // TODO: Deal with column size changes.
    fn page_resize(&mut self, _: u16, row: u16) {
        self.content_rows = row.saturating_sub(1);
    }

    fn draw(&self, stdout: &mut std::io::Stdout, wrong_key: Option<char>) {
        let lower_mark = self
            .line_count
            .min(self.upper_mark.saturating_add(self.content_rows.into()));
        self.draw_lines(stdout);
        self.draw_prompt(stdout, lower_mark, wrong_key);
        stdout.flush().unwrap();
    }

    fn draw_lines(&self, stdout: &mut std::io::Stdout) {
        execute!(stdout, terminal::Clear(terminal::ClearType::CurrentLine)).unwrap();
        let displayed_lines = self
            .lines
            .iter()
            .skip(self.upper_mark)
            .take(self.content_rows.into());

        for line in displayed_lines {
            stdout
                .write_all(format!("\r{}\n", line).as_bytes())
                .unwrap();
        }
    }

    fn draw_prompt(&self, stdout: &mut Stdout, lower_mark: usize, wrong_key: Option<char>) {
        let status_inner = if lower_mark == self.line_count {
            format!("Next file: {}", self.next_file.unwrap_or_default())
        } else {
            format!(
                "{}%",
                (lower_mark as f64 / self.line_count as f64 * 100.0).round() as u16
            )
        };

        let status = format!("--More--({})", status_inner);

        let banner = match (self.silent, wrong_key) {
            (true, Some(key)) => format!(
                "{} [Unknown key: '{}'. Press 'h' for instructions. (unimplemented)]",
                status, key
            ),
            (true, None) => format!("{}[Press space to continue, 'q' to quit.]", status),
            (false, Some(_)) => format!("{}{}", status, BELL),
            (false, None) => status,
        };

        write!(
            stdout,
            "\r{}{}{}",
            Attribute::Reverse,
            banner,
            Attribute::Reset
        )
        .unwrap();
    }
}

// Break the lines on the cols of the terminal
fn break_buff(buff: &str, cols: usize) -> Vec<String> {
    let mut lines = Vec::with_capacity(buff.lines().count());

    for l in buff.lines() {
        lines.append(&mut break_line(l, cols));
    }
    lines
}

fn break_line(line: &str, cols: usize) -> Vec<String> {
    let width = UnicodeWidthStr::width(line);
    let mut lines = Vec::new();
    if width < cols {
        lines.push(line.to_string());
        return lines;
    }

    let gr_idx = UnicodeSegmentation::grapheme_indices(line, true);
    let mut last_index = 0;
    let mut total_width = 0;
    for (index, grapheme) in gr_idx {
        let width = UnicodeWidthStr::width(grapheme);
        total_width += width;

        if total_width > cols {
            lines.push(line[last_index..index].to_string());
            last_index = index;
            total_width = width;
        }
    }

    if last_index != line.len() {
        lines.push(line[last_index..].to_string());
    }
    lines
}

#[cfg(test)]
mod tests {
    use super::break_line;
    use unicode_width::UnicodeWidthStr;

    #[test]
    fn test_break_lines_long() {
        let mut test_string = String::with_capacity(100);
        for _ in 0..200 {
            test_string.push('#');
        }

        let lines = break_line(&test_string, 80);
        let widths: Vec<usize> = lines
            .iter()
            .map(|s| UnicodeWidthStr::width(&s[..]))
            .collect();

        assert_eq!((80, 80, 40), (widths[0], widths[1], widths[2]));
    }

    #[test]
    fn test_break_lines_short() {
        let mut test_string = String::with_capacity(100);
        for _ in 0..20 {
            test_string.push('#');
        }

        let lines = break_line(&test_string, 80);

        assert_eq!(20, lines[0].len());
    }

    #[test]
    fn test_break_line_zwj() {
        let mut test_string = String::with_capacity(1100);
        for _ in 0..20 {
            test_string.push_str("👩🏻🔬");
        }

        let lines = break_line(&test_string, 80);

        let widths: Vec<usize> = lines
            .iter()
            .map(|s| UnicodeWidthStr::width(&s[..]))
            .collect();

        // Each 👩🏻🔬 is 6 character width it break line to the closest number to 80 => 6 * 13 = 78
        assert_eq!((78, 42), (widths[0], widths[1]));
    }
}
<commit_msg>more: use UResult<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Martin Kysel <code@martinkysel.com>
//  *
//  * For the full copyright and license information, please view the LICENSE file
//  * that was distributed with this source code.

// spell-checker:ignore (methods) isnt

use std::{
    fs::File,
    io::{stdin, stdout, BufReader, Read, Stdout, Write},
    path::Path,
    time::Duration,
};

#[cfg(all(unix, not(target_os = "fuchsia")))]
extern crate nix;

use clap::{crate_version, App, Arg};
use crossterm::{
    event::{self, Event, KeyCode, KeyEvent, KeyModifiers},
    execute, queue,
    style::Attribute,
    terminal,
};

use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr;
use uucore::display::Quotable;
use uucore::error::{UResult, USimpleError, UUsageError};

const BELL: &str = "\x07";

pub mod options {
    pub const SILENT: &str = "silent";
    pub const LOGICAL: &str = "logical";
    pub const NO_PAUSE: &str = "no-pause";
    pub const PRINT_OVER: &str = "print-over";
    pub const CLEAN_PRINT: &str = "clean-print";
    pub const SQUEEZE: &str = "squeeze";
    pub const PLAIN: &str = "plain";
    pub const LINES: &str = "lines";
    pub const NUMBER: &str = "number";
    pub const PATTERN: &str = "pattern";
    pub const FROM_LINE: &str = "from-line";
    pub const FILES: &str = "files";
}

const MULTI_FILE_TOP_PROMPT: &str = "::::::::::::::\n{}\n::::::::::::::\n";

#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let matches = uu_app().get_matches_from(args);

    let mut buff = String::new();
    let silent = matches.is_present(options::SILENT);
    if let Some(files) = matches.values_of(options::FILES) {
        let mut stdout = setup_term();
        let length = files.len();

        let mut files_iter = files.peekable();
        while let (Some(file), next_file) = (files_iter.next(), files_iter.peek()) {
            let file = Path::new(file);
            if file.is_dir() {
                terminal::disable_raw_mode().unwrap();
                return Err(UUsageError::new(
                    1,
                    format!("{} is a directory.", file.quote()),
                ));
            }
            if !file.exists() {
                terminal::disable_raw_mode().unwrap();
                return Err(USimpleError::new(
                    1,
                    format!("cannot open {}: No such file or directory", file.quote()),
                ));
            }
            if length > 1 {
                buff.push_str(&MULTI_FILE_TOP_PROMPT.replace("{}", file.to_str().unwrap()));
            }
            let mut reader = BufReader::new(File::open(file).unwrap());
            reader.read_to_string(&mut buff).unwrap();
            more(&buff, &mut stdout, next_file.copied(), silent)?;
            buff.clear();
        }
        reset_term(&mut stdout);
    } else if atty::isnt(atty::Stream::Stdin) {
        stdin().read_to_string(&mut buff).unwrap();
        let mut stdout = setup_term();
        more(&buff, &mut stdout, None, silent)?;
        reset_term(&mut stdout);
    } else {
        return Err(UUsageError::new(1, "bad usage"));
    }
    Ok(())
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(uucore::util_name())
        .about("A file perusal filter for CRT viewing.")
        .version(crate_version!())
        .arg(
            Arg::with_name(options::SILENT)
                .short("d")
                .long(options::SILENT)
                .help("Display help instead of ringing bell"),
        )
        // The commented arguments below are unimplemented:
        /*
        .arg(
            Arg::with_name(options::LOGICAL)
                .short("f")
                .long(options::LOGICAL)
                .help("Count logical rather than screen lines"),
        )
        .arg(
            Arg::with_name(options::NO_PAUSE)
                .short("l")
                .long(options::NO_PAUSE)
                .help("Suppress pause after form feed"),
        )
        .arg(
            Arg::with_name(options::PRINT_OVER)
                .short("c")
                .long(options::PRINT_OVER)
                .help("Do not scroll, display text and clean line ends"),
        )
        .arg(
            Arg::with_name(options::CLEAN_PRINT)
                .short("p")
                .long(options::CLEAN_PRINT)
                .help("Do not scroll, clean screen and display text"),
        )
        .arg(
            Arg::with_name(options::SQUEEZE)
                .short("s")
                .long(options::SQUEEZE)
                .help("Squeeze multiple blank lines into one"),
        )
        .arg(
            Arg::with_name(options::PLAIN)
                .short("u")
                .long(options::PLAIN)
                .help("Suppress underlining and bold"),
        )
        .arg(
            Arg::with_name(options::LINES)
                .short("n")
                .long(options::LINES)
                .value_name("number")
                .takes_value(true)
                .help("The number of lines per screen full"),
        )
        .arg(
            Arg::with_name(options::NUMBER)
                .allow_hyphen_values(true)
                .long(options::NUMBER)
                .required(false)
                .takes_value(true)
                .help("Same as --lines"),
        )
        .arg(
            Arg::with_name(options::FROM_LINE)
                .short("F")
                .allow_hyphen_values(true)
                .required(false)
                .takes_value(true)
                .value_name("number")
                .help("Display file beginning from line number"),
        )
        .arg(
            Arg::with_name(options::PATTERN)
                .short("P")
                .allow_hyphen_values(true)
                .required(false)
                .takes_value(true)
                .help("Display file beginning from pattern match"),
        )
        */
        .arg(
            Arg::with_name(options::FILES)
                .required(false)
                .multiple(true)
                .help("Path to the files to be read"),
        )
}

#[cfg(not(target_os = "fuchsia"))]
fn setup_term() -> std::io::Stdout {
    let stdout = stdout();
    terminal::enable_raw_mode().unwrap();
    stdout
}

#[cfg(target_os = "fuchsia")]
#[inline(always)]
fn setup_term() -> usize {
    0
}

#[cfg(not(target_os = "fuchsia"))]
fn reset_term(stdout: &mut std::io::Stdout) {
    terminal::disable_raw_mode().unwrap();
    // Clear the prompt
    queue!(stdout, terminal::Clear(terminal::ClearType::CurrentLine)).unwrap();
    // Move cursor to the beginning without printing new line
    print!("\r");
    stdout.flush().unwrap();
}

#[cfg(target_os = "fuchsia")]
#[inline(always)]
fn reset_term(_: &mut usize) {}

fn more(buff: &str, stdout: &mut Stdout, next_file: Option<&str>, silent: bool) -> UResult<()> {
    let (cols, rows) = terminal::size().unwrap();
    let lines = break_buff(buff, usize::from(cols));

    let mut pager = Pager::new(rows, lines, next_file, silent);
    pager.draw(stdout, None);
    if pager.should_close() {
        return Ok(());
    }

    loop {
        let mut wrong_key = None;
        if event::poll(Duration::from_millis(10)).unwrap() {
            match event::read().unwrap() {
                Event::Key(KeyEvent {
                    code: KeyCode::Char('q'),
                    modifiers: KeyModifiers::NONE,
                })
                | Event::Key(KeyEvent {
                    code: KeyCode::Char('c'),
                    modifiers: KeyModifiers::CONTROL,
                }) => {
                    reset_term(stdout);
                    std::process::exit(0);
                }
                Event::Key(KeyEvent {
                    code: KeyCode::Down,
                    modifiers: KeyModifiers::NONE,
                })
                | Event::Key(KeyEvent {
                    code: KeyCode::Char(' '),
                    modifiers: KeyModifiers::NONE,
                }) => {
                    if pager.should_close() {
                        return Ok(());
                    } else {
                        pager.page_down();
                    }
                }
                Event::Key(KeyEvent {
                    code: KeyCode::Up,
                    modifiers: KeyModifiers::NONE,
                }) => {
                    pager.page_up();
                }
                Event::Resize(col, row) => {
                    pager.page_resize(col, row);
                }
                Event::Key(KeyEvent {
                    code: KeyCode::Char(k),
                    ..
                }) => wrong_key = Some(k),
                _ => continue,
            }

            pager.draw(stdout, wrong_key);
        }
    }
}

struct Pager<'a> {
    // The current line at the top of the screen
    upper_mark: usize,
    // The number of rows that fit on the screen
    content_rows: u16,
    lines: Vec<String>,
    next_file: Option<&'a str>,
    line_count: usize,
    silent: bool,
}

impl<'a> Pager<'a> {
    fn new(rows: u16, lines: Vec<String>, next_file: Option<&'a str>, silent: bool) -> Self {
        let line_count = lines.len();
        Self {
            upper_mark: 0,
            content_rows: rows.saturating_sub(1),
            lines,
            next_file,
            line_count,
            silent,
        }
    }

    fn should_close(&mut self) -> bool {
        self.upper_mark
            .saturating_add(self.content_rows.into())
            .ge(&self.line_count)
    }

    fn page_down(&mut self) {
        self.upper_mark = self.upper_mark.saturating_add(self.content_rows.into());
    }

    fn page_up(&mut self) {
        self.upper_mark = self.upper_mark.saturating_sub(self.content_rows.into());
    }

    // TODO: Deal with column size changes.
    fn page_resize(&mut self, _: u16, row: u16) {
        self.content_rows = row.saturating_sub(1);
    }

    fn draw(&self, stdout: &mut std::io::Stdout, wrong_key: Option<char>) {
        let lower_mark = self
            .line_count
            .min(self.upper_mark.saturating_add(self.content_rows.into()));
        self.draw_lines(stdout);
        self.draw_prompt(stdout, lower_mark, wrong_key);
        stdout.flush().unwrap();
    }

    fn draw_lines(&self, stdout: &mut std::io::Stdout) {
        execute!(stdout, terminal::Clear(terminal::ClearType::CurrentLine)).unwrap();
        let displayed_lines = self
            .lines
            .iter()
            .skip(self.upper_mark)
            .take(self.content_rows.into());

        for line in displayed_lines {
            stdout
                .write_all(format!("\r{}\n", line).as_bytes())
                .unwrap();
        }
    }

    fn draw_prompt(&self, stdout: &mut Stdout, lower_mark: usize, wrong_key: Option<char>) {
        let status_inner = if lower_mark == self.line_count {
            format!("Next file: {}", self.next_file.unwrap_or_default())
        } else {
            format!(
                "{}%",
                (lower_mark as f64 / self.line_count as f64 * 100.0).round() as u16
            )
        };

        let status = format!("--More--({})", status_inner);

        let banner = match (self.silent, wrong_key) {
            (true, Some(key)) => format!(
                "{} [Unknown key: '{}'. Press 'h' for instructions. (unimplemented)]",
                status, key
            ),
            (true, None) => format!("{}[Press space to continue, 'q' to quit.]", status),
            (false, Some(_)) => format!("{}{}", status, BELL),
            (false, None) => status,
        };

        write!(
            stdout,
            "\r{}{}{}",
            Attribute::Reverse,
            banner,
            Attribute::Reset
        )
        .unwrap();
    }
}

// Break the lines on the cols of the terminal
fn break_buff(buff: &str, cols: usize) -> Vec<String> {
    let mut lines = Vec::with_capacity(buff.lines().count());

    for l in buff.lines() {
        lines.append(&mut break_line(l, cols));
    }
    lines
}

fn break_line(line: &str, cols: usize) -> Vec<String> {
    let width = UnicodeWidthStr::width(line);
    let mut lines = Vec::new();
    if width < cols {
        lines.push(line.to_string());
        return lines;
    }

    let gr_idx = UnicodeSegmentation::grapheme_indices(line, true);
    let mut last_index = 0;
    let mut total_width = 0;
    for (index, grapheme) in gr_idx {
        let width = UnicodeWidthStr::width(grapheme);
        total_width += width;

        if total_width > cols {
            lines.push(line[last_index..index].to_string());
            last_index = index;
            total_width = width;
        }
    }

    if last_index != line.len() {
        lines.push(line[last_index..].to_string());
    }
    lines
}

#[cfg(test)]
mod tests {
    use super::break_line;
    use unicode_width::UnicodeWidthStr;

    #[test]
    fn test_break_lines_long() {
        let mut test_string = String::with_capacity(100);
        for _ in 0..200 {
            test_string.push('#');
        }

        let lines = break_line(&test_string, 80);
        let widths: Vec<usize> = lines
            .iter()
            .map(|s| UnicodeWidthStr::width(&s[..]))
            .collect();

        assert_eq!((80, 80, 40), (widths[0], widths[1], widths[2]));
    }

    #[test]
    fn test_break_lines_short() {
        let mut test_string = String::with_capacity(100);
        for _ in 0..20 {
            test_string.push('#');
        }

        let lines = break_line(&test_string, 80);

        assert_eq!(20, lines[0].len());
    }

    #[test]
    fn test_break_line_zwj() {
        let mut test_string = String::with_capacity(1100);
        for _ in 0..20 {
            test_string.push_str("👩🏻🔬");
        }

        let lines = break_line(&test_string, 80);

        let widths: Vec<usize> = lines
            .iter()
            .map(|s| UnicodeWidthStr::width(&s[..]))
            .collect();

        // Each 👩🏻🔬 is 6 character width it break line to the closest number to 80 => 6 * 13 = 78
        assert_eq!((78, 42), (widths[0], widths[1]));
    }
}
<|endoftext|>


<commit_before>fn main() {
    let v: Vec<i32> = Vec::new();

    // Macro usage for vector
    let v_from_macro = vec![1, 2, 3];

    let mut mutable_vector = Vec::new();

    mutable_vector.push(1);
    mutable_vector.push(2);
}
<commit_msg>Two examples of getting a reference to vector element<commit_after>fn main() {
    let v: Vec<i32> = Vec::new();

    // Macro usage for vector
    let v_from_macro = vec![1, 2, 3];

    let third: &i32 = &v_from_macro[2];
    let third_2: Option<&i32> = v_from_macro.get(2);

    let mut mutable_vector = Vec::new();

    mutable_vector.push(1);
    mutable_vector.push(2);
}
<|endoftext|>


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

extern crate gtk;
#[macro_use]
extern crate relm;
#[macro_use]
extern crate relm_derive;

use gtk::{Button, ButtonExt, ContainerExt, EditableSignals, Entry, EntryExt, Label, WidgetExt, Window, WindowType};
use gtk::Orientation::{Horizontal, Vertical};
use relm::{ContainerWidget, QuitFuture, Relm, Widget};

use self::CounterMsg::*;
use self::Msg::*;
use self::TextMsg::*;

#[derive(Clone)]
struct TextModel {
    content: String,
}

#[derive(Msg)]
enum TextMsg {
    Change,
}

struct TextWidgets {
    input: Entry,
    label: Label,
    vbox: gtk::Box,
}

struct Text {
    model: TextModel,
    widgets: TextWidgets,
}

impl Text {
    fn view(relm: &Relm<TextMsg>) -> TextWidgets {
        let vbox = gtk::Box::new(Vertical, 0);

        let input = Entry::new();
        vbox.add(&input);

        let label = Label::new(None);
        vbox.add(&label);

        connect!(relm, input, connect_changed(_), Change);

        TextWidgets {
            input: input,
            label: label,
            vbox: vbox,
        }
    }
}

impl Widget<TextMsg> for Text {
    type Container = gtk::Box;

    fn container(&self) -> &Self::Container {
        &self.widgets.vbox
    }

    fn new(relm: Relm<TextMsg>) -> Self {
        let widgets = Self::view(&relm);
        Text {
            model: TextModel {
                content: String::new(),
            },
            widgets: widgets,
        }
    }

    fn update(&mut self, event: TextMsg) {
        match event {
            Change => {
                self.model.content = self.widgets.input.get_text().unwrap().chars().rev().collect();
                self.widgets.label.set_text(&self.model.content);
            },
        }
    }
}

#[derive(Clone)]
struct CounterModel {
    counter: i32,
}

#[derive(Msg)]
enum CounterMsg {
    Decrement,
    Increment,
}

struct Counter {
    model: CounterModel,
    widgets: CounterWidgets,
}

impl Counter {
    fn view(relm: &Relm<CounterMsg>) -> CounterWidgets {
        let vbox = gtk::Box::new(Vertical, 0);

        let plus_button = Button::new_with_label("+");
        vbox.add(&plus_button);

        let counter_label = Label::new(Some("0"));
        vbox.add(&counter_label);

        let minus_button = Button::new_with_label("-");
        vbox.add(&minus_button);

        connect!(relm, plus_button, connect_clicked(_), Increment);
        connect!(relm, minus_button, connect_clicked(_), Decrement);

        CounterWidgets {
            counter_label: counter_label,
            vbox: vbox,
        }
    }
}

impl Widget<CounterMsg> for Counter {
    type Container = gtk::Box;

    fn container(&self) -> &Self::Container {
        &self.widgets.vbox
    }

    fn new(relm: Relm<CounterMsg>) -> Self {
        let widgets = Self::view(&relm);
        Counter {
            model: CounterModel {
                counter: 0,
            },
            widgets: widgets,
        }
    }

    fn update(&mut self, event: CounterMsg) {
        let label = &self.widgets.counter_label;

        match event {
            Decrement => {
                self.model.counter -= 1;
                label.set_text(&self.model.counter.to_string());
            },
            Increment => {
                self.model.counter += 1;
                label.set_text(&self.model.counter.to_string());
            },
        }
    }
}

struct CounterWidgets {
    counter_label: Label,
    vbox: gtk::Box,
}

struct Model {
    counter: i32,
}

#[derive(Msg)]
enum Msg {
    TextChange,
    Quit,
}

struct Widgets {
    label: Label,
    window: Window,
}

struct Win {
    model: Model,
    relm: Relm<Msg>,
    widgets: Widgets,
}

impl Win {
    fn view(relm: &Relm<Msg>) -> Widgets {
        let handle = relm.handle();

        let window = Window::new(WindowType::Toplevel);

        let hbox = gtk::Box::new(Horizontal, 0);

        let button = Button::new_with_label("Decrement");
        hbox.add(&button);

        let counter1 = hbox.add_widget::<Counter, _>(handle);
        let counter2 = hbox.add_widget::<Counter, _>(handle);
        let text = hbox.add_widget::<Text, _>(handle);
        connect!(text, Change, relm, TextChange); // TODO: get the text in the TextChange.
        connect!(text, Change, counter1, Increment);
        connect!(counter1, Increment, counter2, Decrement);
        connect!(button, connect_clicked(_), counter1, Decrement);

        let label = Label::new(None);
        hbox.add(&label);

        window.add(&hbox);

        window.show_all();

        connect_no_inhibit!(relm, window, connect_delete_event(_, _), Quit);

        Widgets {
            label: label,
            window: window,
        }
    }
}

impl Widget<Msg> for Win {
    type Container = Window;

    fn container(&self) -> &Self::Container {
        &self.widgets.window
    }

    fn new(relm: Relm<Msg>) -> Self {
        let widgets = Self::view(&relm);
        Win {
            model: Model {
                counter: 0,
            },
            relm: relm,
            widgets: widgets,
        }
    }

    fn update(&mut self, event: Msg) {
        match event {
            TextChange => {
                self.model.counter += 1;
                self.widgets.label.set_text(&self.model.counter.to_string());
            },
            Quit => self.relm.exec(QuitFuture),
        }
    }
}

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

extern crate gtk;
#[macro_use]
extern crate relm;
#[macro_use]
extern crate relm_derive;

use gtk::{Button, ButtonExt, ContainerExt, EditableSignals, Entry, EntryExt, Label, WidgetExt, Window, WindowType};
use gtk::Orientation::{Horizontal, Vertical};
use relm::{Component, ContainerWidget, Relm, RemoteRelm, Widget};

use self::CounterMsg::*;
use self::Msg::*;
use self::TextMsg::*;

#[derive(Clone)]
struct TextModel {
    content: String,
}

#[derive(Msg)]
enum TextMsg {
    Change,
}

struct TextWidgets {
    input: Entry,
    label: Label,
    vbox: gtk::Box,
}

struct Text {
    widgets: TextWidgets,
}

impl Text {
    fn view(relm: &RemoteRelm<TextMsg>) -> TextWidgets {
        let vbox = gtk::Box::new(Vertical, 0);

        let input = Entry::new();
        vbox.add(&input);

        let label = Label::new(None);
        vbox.add(&label);

        connect!(relm, input, connect_changed(_), Change);

        TextWidgets {
            input: input,
            label: label,
            vbox: vbox,
        }
    }
}

impl Widget<TextMsg> for Text {
    type Container = gtk::Box;
    type Model = TextModel;

    fn container(&self) -> &Self::Container {
        &self.widgets.vbox
    }

    fn new(relm: RemoteRelm<TextMsg>) -> (Self, TextModel) {
        let widgets = Self::view(&relm);
        let widget = Text {
            widgets: widgets,
        };
        let model = TextModel {
            content: String::new(),
        };
        (widget, model)
    }

    fn update(&mut self, event: TextMsg, model: &mut TextModel) {
        match event {
            Change => {
                model.content = self.widgets.input.get_text().unwrap().chars().rev().collect();
                self.widgets.label.set_text(&model.content);
            },
        }
    }
}

#[derive(Clone)]
struct CounterModel {
    counter: i32,
}

#[derive(Msg)]
enum CounterMsg {
    Decrement,
    Increment,
}

struct Counter {
    widgets: CounterWidgets,
}

impl Counter {
    fn view(relm: &RemoteRelm<CounterMsg>) -> CounterWidgets {
        let vbox = gtk::Box::new(Vertical, 0);

        let plus_button = Button::new_with_label("+");
        vbox.add(&plus_button);

        let counter_label = Label::new(Some("0"));
        vbox.add(&counter_label);

        let minus_button = Button::new_with_label("-");
        vbox.add(&minus_button);

        connect!(relm, plus_button, connect_clicked(_), Increment);
        connect!(relm, minus_button, connect_clicked(_), Decrement);

        CounterWidgets {
            counter_label: counter_label,
            vbox: vbox,
        }
    }
}

impl Widget<CounterMsg> for Counter {
    type Container = gtk::Box;
    type Model = CounterModel;

    fn container(&self) -> &Self::Container {
        &self.widgets.vbox
    }

    fn new(relm: RemoteRelm<CounterMsg>) -> (Self, CounterModel) {
        let widgets = Self::view(&relm);
        let widget = Counter {
            widgets: widgets,
        };
        let model = CounterModel {
            counter: 0,
        };
        (widget, model)
    }

    fn update(&mut self, event: CounterMsg, model: &mut CounterModel) {
        let label = &self.widgets.counter_label;

        match event {
            Decrement => {
                model.counter -= 1;
                label.set_text(&model.counter.to_string());
            },
            Increment => {
                model.counter += 1;
                label.set_text(&model.counter.to_string());
            },
        }
    }
}

struct CounterWidgets {
    counter_label: Label,
    vbox: gtk::Box,
}

struct Model {
    counter: i32,
}

#[derive(Msg)]
enum Msg {
    TextChange,
    Quit,
}

struct Widgets {
    _counter1: Component<CounterModel, CounterMsg, gtk::Box>,
    _counter2: Component<CounterModel, CounterMsg, gtk::Box>,
    label: Label,
    _text: Component<TextModel, TextMsg, gtk::Box>,
    window: Window,
}

struct Win {
    widgets: Widgets,
}

impl Win {
    fn view(relm: &RemoteRelm<Msg>) -> Widgets {
        let window = Window::new(WindowType::Toplevel);

        let hbox = gtk::Box::new(Horizontal, 0);

        let button = Button::new_with_label("Decrement");
        hbox.add(&button);

        let counter1 = hbox.add_widget::<Counter, _, _>(relm);
        let counter2 = hbox.add_widget::<Counter, _, _>(relm);
        let text = hbox.add_widget::<Text, _, _>(relm);
        connect!(text, Change, relm, TextChange); // TODO: get the text in the TextChange.
        connect!(text, Change, counter1, Increment);
        connect!(counter1, Increment, counter2, Decrement);
        connect!(button, connect_clicked(_), counter1, Decrement);

        let label = Label::new(None);
        hbox.add(&label);

        window.add(&hbox);

        window.show_all();

        connect_no_inhibit!(relm, window, connect_delete_event(_, _), Quit);

        Widgets {
            _counter1: counter1,
            _counter2: counter2,
            label: label,
            _text: text,
            window: window,
        }
    }
}

impl Widget<Msg> for Win {
    type Container = Window;
    type Model = Model;

    fn container(&self) -> &Self::Container {
        &self.widgets.window
    }

    fn new(relm: RemoteRelm<Msg>) -> (Self, Model) {
        let widgets = Self::view(&relm);
        let window = Win {
            widgets: widgets,
        };
        let model = Model {
            counter: 0,
        };
        (window, model)
    }

    fn update(&mut self, event: Msg, model: &mut Model) {
        match event {
            TextChange => {
                model.counter += 1;
                self.widgets.label.set_text(&model.counter.to_string());
            },
            Quit => gtk::main_quit(),
        }
    }
}

fn main() {
    Relm::run::<Win>().unwrap();
}
<|endoftext|>