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

use crate::{
    common::{
        types::{CliCommand, CliError, CliResult, CliTypedResult, TransactionOptions},
        utils::read_from_file,
    },
    genesis::git::from_yaml,
};
use aptos_crypto::{ed25519::Ed25519PublicKey, x25519, PrivateKey, ValidCryptoMaterialStringExt};
use aptos_genesis::{config::HostAndPort, keys::PrivateIdentity};
use aptos_rest_client::Transaction;
use aptos_types::account_address::AccountAddress;
use async_trait::async_trait;
use clap::Parser;
use std::{
    path::PathBuf,
    time::{SystemTime, UNIX_EPOCH},
};

/// Tool for manipulating nodes
///
#[derive(Parser)]
pub enum NodeTool {
    AddStake(AddStake),
    UnlockStake(UnlockStake),
    WithdrawStake(WithdrawStake),
    IncreaseLockup(IncreaseLockup),
    RegisterValidatorCandidate(RegisterValidatorCandidate),
    JoinValidatorSet(JoinValidatorSet),
    LeaveValidatorSet(LeaveValidatorSet),
}

impl NodeTool {
    pub async fn execute(self) -> CliResult {
        use NodeTool::*;
        match self {
            AddStake(tool) => tool.execute_serialized().await,
            UnlockStake(tool) => tool.execute_serialized().await,
            WithdrawStake(tool) => tool.execute_serialized().await,
            IncreaseLockup(tool) => tool.execute_serialized().await,
            RegisterValidatorCandidate(tool) => tool.execute_serialized().await,
            JoinValidatorSet(tool) => tool.execute_serialized().await,
            LeaveValidatorSet(tool) => tool.execute_serialized().await,
        }
    }
}

/// Stake coins for an account to the stake pool
#[derive(Parser)]
pub struct AddStake {
    #[clap(flatten)]
    pub(crate) txn_options: TransactionOptions,
    /// Amount of coins to add to stake
    #[clap(long)]
    pub amount: u64,
}

#[async_trait]
impl CliCommand<Transaction> for AddStake {
    fn command_name(&self) -> &'static str {
        "AddStake"
    }

    async fn execute(mut self) -> CliTypedResult<Transaction> {
        self.txn_options
            .submit_script_function(
                AccountAddress::ONE,
                "Stake",
                "add_stake",
                vec![],
                vec![bcs::to_bytes(&self.amount)?],
            )
            .await
    }
}

/// Unlock staked coins
///
/// Coins can only be unlocked if they no longer have an applied lockup period
#[derive(Parser)]
pub struct UnlockStake {
    #[clap(flatten)]
    pub(crate) txn_options: TransactionOptions,
    /// Amount of coins to unlock
    #[clap(long)]
    pub amount: u64,
}

#[async_trait]
impl CliCommand<Transaction> for UnlockStake {
    fn command_name(&self) -> &'static str {
        "UnlockStake"
    }

    async fn execute(mut self) -> CliTypedResult<Transaction> {
        self.txn_options
            .submit_script_function(
                AccountAddress::ONE,
                "Stake",
                "unlock",
                vec![],
                vec![bcs::to_bytes(&self.amount)?],
            )
            .await
    }
}

/// Withdraw all unlocked staked coins
///
/// Before calling `WithdrawStake`, `UnlockStake` must be called first.
#[derive(Parser)]
pub struct WithdrawStake {
    #[clap(flatten)]
    pub(crate) node_op_options: TransactionOptions,
}

#[async_trait]
impl CliCommand<Transaction> for WithdrawStake {
    fn command_name(&self) -> &'static str {
        "WithdrawStake"
    }

    async fn execute(mut self) -> CliTypedResult<Transaction> {
        self.node_op_options
            .submit_script_function(AccountAddress::ONE, "Stake", "withdraw", vec![], vec![])
            .await
    }
}

/// Increase lockup of all staked coins in an account
#[derive(Parser)]
pub struct IncreaseLockup {
    #[clap(flatten)]
    pub(crate) txn_options: TransactionOptions,
    /// Number of seconds to increase the lockup period by
    #[clap(long)]
    pub(crate) lockup_timestamp_secs: u64,
}

#[async_trait]
impl CliCommand<Transaction> for IncreaseLockup {
    fn command_name(&self) -> &'static str {
        "IncreaseLockup"
    }

    async fn execute(mut self) -> CliTypedResult<Transaction> {
        if self.lockup_timestamp_secs
            <= SystemTime::now()
                .duration_since(UNIX_EPOCH)
                .unwrap()
                .as_secs()
        {
            return Err(CliError::CommandArgumentError(
                "--lockup-timestamp-secs is in the past".to_string(),
            ));
        }

        self.txn_options
            .submit_script_function(
                AccountAddress::ONE,
                "Stake",
                "increase_lockup",
                vec![],
                vec![bcs::to_bytes(&self.lockup_timestamp_secs)?],
            )
            .await
    }
}

/// Register the current account as a Validator candidate
#[derive(Parser)]
pub struct RegisterValidatorCandidate {
    #[clap(flatten)]
    pub(crate) txn_options: TransactionOptions,
    /// Private keys file, created from the `genesis keys` command
    #[clap(long)]
    pub(crate) private_keys_file: Option<PathBuf>,
    /// Hex encoded Consensus public key
    #[clap(long, parse(try_from_str = Ed25519PublicKey::from_encoded_string))]
    pub(crate) consensus_public_key: Option<Ed25519PublicKey>,
    /// Host and port pair for the validator e.g. 127.0.0.1:6180
    #[clap(long)]
    pub(crate) validator_host: HostAndPort,
    /// Validator x25519 public network key
    #[clap(long, parse(try_from_str = x25519::PublicKey::from_encoded_string))]
    pub(crate) validator_network_public_key: Option<x25519::PublicKey>,
    /// Host and port pair for the fullnode e.g. 127.0.0.1:6180.  Optional
    #[clap(long)]
    pub(crate) full_node_host: Option<HostAndPort>,
    /// Full node x25519 public network key
    #[clap(long, parse(try_from_str = x25519::PublicKey::from_encoded_string))]
    pub(crate) full_node_network_public_key: Option<x25519::PublicKey>,
}

impl RegisterValidatorCandidate {
    fn consensus_public_key(&self) -> CliTypedResult<Ed25519PublicKey> {
        if let Some(ref consensus_public_key) = self.consensus_public_key {
            Ok(consensus_public_key.clone())
        } else if let Some(ref file) = self.private_keys_file {
            let identity: PrivateIdentity =
                from_yaml(&String::from_utf8(read_from_file(file)?).map_err(CliError::from)?)?;
            Ok(identity.consensus_private_key.public_key())
        } else {
            Err(CliError::CommandArgumentError(
                "Must provide either --validator-identity-file or --consensus-public-key"
                    .to_string(),
            ))
        }
    }

    fn network_keys(&self) -> CliTypedResult<(x25519::PublicKey, Option<x25519::PublicKey>)> {
        let identity: Option<PrivateIdentity> = if let Some(ref file) = self.private_keys_file {
            from_yaml(&String::from_utf8(read_from_file(file)?).map_err(CliError::from)?)?
        } else {
            None
        };

        let validator_network_public_key =
            if let Some(public_key) = self.validator_network_public_key {
                Ok(public_key)
            } else if let Some(ref identity) = identity {
                Ok(identity.validator_network_private_key.public_key())
            } else {
                Err(CliError::CommandArgumentError(
                    "Must provide either --validator-identity-file or --consensus-public-key"
                        .to_string(),
                ))
            }?;

        let full_node_network_public_key =
            if let Some(public_key) = self.full_node_network_public_key {
                Some(public_key)
            } else {
                identity.map(|identity| identity.full_node_network_private_key.public_key())
            };

        Ok((validator_network_public_key, full_node_network_public_key))
    }
}

#[async_trait]
impl CliCommand<Transaction> for RegisterValidatorCandidate {
    fn command_name(&self) -> &'static str {
        "RegisterValidatorCandidate"
    }

    async fn execute(mut self) -> CliTypedResult<Transaction> {
        let consensus_public_key = self.consensus_public_key()?;
        let (validator_network_public_key, full_node_network_public_key) = self.network_keys()?;
        let validator_network_addresses = vec![self
            .validator_host
            .as_network_address(validator_network_public_key)?];
        let full_node_network_addresses =
            match (self.full_node_host.as_ref(), full_node_network_public_key) {
                (Some(host), Some(public_key)) => vec![host.as_network_address(public_key)?],
                _ => vec![],
            };

        self.txn_options
            .submit_script_function(
                AccountAddress::ONE,
                "Stake",
                "register_validator_candidate",
                vec![],
                vec![
                    bcs::to_bytes(&consensus_public_key)?,
                    // Double BCS encode, so that we can hide the original type
                    bcs::to_bytes(&bcs::to_bytes(&validator_network_addresses)?)?,
                    bcs::to_bytes(&bcs::to_bytes(&full_node_network_addresses)?)?,
                ],
            )
            .await
    }
}

/// Arguments used for operator of the staking pool
#[derive(Parser)]
pub struct OperatorArgs {
    /// Address of the Staking pool
    #[clap(long)]
    pub(crate) pool_address: Option<AccountAddress>,
}

/// Join the validator set after meeting staking requirements
#[derive(Parser)]
pub struct JoinValidatorSet {
    #[clap(flatten)]
    pub(crate) txn_options: TransactionOptions,
    #[clap(flatten)]
    pub(crate) operator_args: OperatorArgs,
}

#[async_trait]
impl CliCommand<Transaction> for JoinValidatorSet {
    fn command_name(&self) -> &'static str {
        "JoinValidatorSet"
    }

    async fn execute(mut self) -> CliTypedResult<Transaction> {
        let address = if let Some(address) = self.operator_args.pool_address {
            address
        } else {
            self.txn_options.profile_options.account_address()?
        };

        self.txn_options
            .submit_script_function(
                AccountAddress::ONE,
                "Stake",
                "join_validator_set",
                vec![],
                vec![bcs::to_bytes(&address)?],
            )
            .await
    }
}

/// Leave the validator set
#[derive(Parser)]
pub struct LeaveValidatorSet {
    #[clap(flatten)]
    pub(crate) txn_options: TransactionOptions,
    #[clap(flatten)]
    pub(crate) operator_args: OperatorArgs,
}

#[async_trait]
impl CliCommand<Transaction> for LeaveValidatorSet {
    fn command_name(&self) -> &'static str {
        "LeaveValidatorSet"
    }

    async fn execute(mut self) -> CliTypedResult<Transaction> {
        let address = if let Some(address) = self.operator_args.pool_address {
            address
        } else {
            self.txn_options.profile_options.account_address()?
        };

        self.txn_options
            .submit_script_function(
                AccountAddress::ONE,
                "Stake",
                "leave_validator_set",
                vec![],
                vec![bcs::to_bytes(&address)?],
            )
            .await
    }
}
<commit_msg>[aptos-cli] Add validator configuration file as input for register validator candidate<commit_after>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::{
    common::{
        types::{CliCommand, CliError, CliResult, CliTypedResult, TransactionOptions},
        utils::read_from_file,
    },
    genesis::git::from_yaml,
};
use aptos_crypto::{ed25519::Ed25519PublicKey, x25519, ValidCryptoMaterialStringExt};
use aptos_genesis::config::{HostAndPort, ValidatorConfiguration};
use aptos_rest_client::Transaction;
use aptos_types::account_address::AccountAddress;
use async_trait::async_trait;
use clap::Parser;
use std::{
    path::PathBuf,
    time::{SystemTime, UNIX_EPOCH},
};

/// Tool for manipulating nodes
///
#[derive(Parser)]
pub enum NodeTool {
    AddStake(AddStake),
    UnlockStake(UnlockStake),
    WithdrawStake(WithdrawStake),
    IncreaseLockup(IncreaseLockup),
    RegisterValidatorCandidate(RegisterValidatorCandidate),
    JoinValidatorSet(JoinValidatorSet),
    LeaveValidatorSet(LeaveValidatorSet),
}

impl NodeTool {
    pub async fn execute(self) -> CliResult {
        use NodeTool::*;
        match self {
            AddStake(tool) => tool.execute_serialized().await,
            UnlockStake(tool) => tool.execute_serialized().await,
            WithdrawStake(tool) => tool.execute_serialized().await,
            IncreaseLockup(tool) => tool.execute_serialized().await,
            RegisterValidatorCandidate(tool) => tool.execute_serialized().await,
            JoinValidatorSet(tool) => tool.execute_serialized().await,
            LeaveValidatorSet(tool) => tool.execute_serialized().await,
        }
    }
}

/// Stake coins for an account to the stake pool
#[derive(Parser)]
pub struct AddStake {
    #[clap(flatten)]
    pub(crate) txn_options: TransactionOptions,
    /// Amount of coins to add to stake
    #[clap(long)]
    pub amount: u64,
}

#[async_trait]
impl CliCommand<Transaction> for AddStake {
    fn command_name(&self) -> &'static str {
        "AddStake"
    }

    async fn execute(mut self) -> CliTypedResult<Transaction> {
        self.txn_options
            .submit_script_function(
                AccountAddress::ONE,
                "Stake",
                "add_stake",
                vec![],
                vec![bcs::to_bytes(&self.amount)?],
            )
            .await
    }
}

/// Unlock staked coins
///
/// Coins can only be unlocked if they no longer have an applied lockup period
#[derive(Parser)]
pub struct UnlockStake {
    #[clap(flatten)]
    pub(crate) txn_options: TransactionOptions,
    /// Amount of coins to unlock
    #[clap(long)]
    pub amount: u64,
}

#[async_trait]
impl CliCommand<Transaction> for UnlockStake {
    fn command_name(&self) -> &'static str {
        "UnlockStake"
    }

    async fn execute(mut self) -> CliTypedResult<Transaction> {
        self.txn_options
            .submit_script_function(
                AccountAddress::ONE,
                "Stake",
                "unlock",
                vec![],
                vec![bcs::to_bytes(&self.amount)?],
            )
            .await
    }
}

/// Withdraw all unlocked staked coins
///
/// Before calling `WithdrawStake`, `UnlockStake` must be called first.
#[derive(Parser)]
pub struct WithdrawStake {
    #[clap(flatten)]
    pub(crate) node_op_options: TransactionOptions,
}

#[async_trait]
impl CliCommand<Transaction> for WithdrawStake {
    fn command_name(&self) -> &'static str {
        "WithdrawStake"
    }

    async fn execute(mut self) -> CliTypedResult<Transaction> {
        self.node_op_options
            .submit_script_function(AccountAddress::ONE, "Stake", "withdraw", vec![], vec![])
            .await
    }
}

/// Increase lockup of all staked coins in an account
#[derive(Parser)]
pub struct IncreaseLockup {
    #[clap(flatten)]
    pub(crate) txn_options: TransactionOptions,
    /// Number of seconds to increase the lockup period by
    #[clap(long)]
    pub(crate) lockup_timestamp_secs: u64,
}

#[async_trait]
impl CliCommand<Transaction> for IncreaseLockup {
    fn command_name(&self) -> &'static str {
        "IncreaseLockup"
    }

    async fn execute(mut self) -> CliTypedResult<Transaction> {
        if self.lockup_timestamp_secs
            <= SystemTime::now()
                .duration_since(UNIX_EPOCH)
                .unwrap()
                .as_secs()
        {
            return Err(CliError::CommandArgumentError(
                "--lockup-timestamp-secs is in the past".to_string(),
            ));
        }

        self.txn_options
            .submit_script_function(
                AccountAddress::ONE,
                "Stake",
                "increase_lockup",
                vec![],
                vec![bcs::to_bytes(&self.lockup_timestamp_secs)?],
            )
            .await
    }
}

/// Register the current account as a Validator candidate
#[derive(Parser)]
pub struct RegisterValidatorCandidate {
    #[clap(flatten)]
    pub(crate) txn_options: TransactionOptions,
    /// Validator Configuration file, created from the `genesis set-validator-configuration` command
    #[clap(long)]
    pub(crate) validator_config_file: Option<PathBuf>,
    /// Hex encoded Consensus public key
    #[clap(long, parse(try_from_str = Ed25519PublicKey::from_encoded_string))]
    pub(crate) consensus_public_key: Option<Ed25519PublicKey>,
    /// Host and port pair for the validator e.g. 127.0.0.1:6180
    #[clap(long)]
    pub(crate) validator_host: Option<HostAndPort>,
    /// Validator x25519 public network key
    #[clap(long, parse(try_from_str = x25519::PublicKey::from_encoded_string))]
    pub(crate) validator_network_public_key: Option<x25519::PublicKey>,
    /// Host and port pair for the fullnode e.g. 127.0.0.1:6180.  Optional
    #[clap(long)]
    pub(crate) full_node_host: Option<HostAndPort>,
    /// Full node x25519 public network key
    #[clap(long, parse(try_from_str = x25519::PublicKey::from_encoded_string))]
    pub(crate) full_node_network_public_key: Option<x25519::PublicKey>,
}

impl RegisterValidatorCandidate {
    fn process_inputs(
        &self,
    ) -> CliTypedResult<(
        Ed25519PublicKey,
        x25519::PublicKey,
        Option<x25519::PublicKey>,
        HostAndPort,
        Option<HostAndPort>,
    )> {
        let validator_config = self.read_validator_config()?;

        let consensus_public_key = if let Some(ref consensus_public_key) = self.consensus_public_key
        {
            consensus_public_key.clone()
        } else if let Some(ref validator_config) = validator_config {
            validator_config.consensus_public_key.clone()
        } else {
            return Err(CliError::CommandArgumentError(
                "Must provide either --validator-config-file or --consensus-public-key".to_string(),
            ));
        };

        let validator_network_public_key =
            if let Some(public_key) = self.validator_network_public_key {
                public_key
            } else if let Some(ref validator_config) = validator_config {
                validator_config.validator_network_public_key
            } else {
                return Err(CliError::CommandArgumentError(
                    "Must provide either --validator-config-file or --validator-network-public-key"
                        .to_string(),
                ));
            };

        let full_node_network_public_key =
            if let Some(public_key) = self.full_node_network_public_key {
                Some(public_key)
            } else if let Some(ref validator_config) = validator_config {
                validator_config.full_node_network_public_key
            } else {
                None
            };

        let validator_host = if let Some(ref host) = self.validator_host {
            host.clone()
        } else if let Some(ref validator_config) = validator_config {
            validator_config.validator_host.clone()
        } else {
            return Err(CliError::CommandArgumentError(
                "Must provide either --validator-config-file or --validator-host".to_string(),
            ));
        };

        let full_node_host = if let Some(ref host) = self.full_node_host {
            Some(host.clone())
        } else if let Some(ref validator_config) = validator_config {
            validator_config.full_node_host.clone()
        } else {
            None
        };

        Ok((
            consensus_public_key,
            validator_network_public_key,
            full_node_network_public_key,
            validator_host,
            full_node_host,
        ))
    }

    fn read_validator_config(&self) -> CliTypedResult<Option<ValidatorConfiguration>> {
        if let Some(ref file) = self.validator_config_file {
            Ok(from_yaml(
                &String::from_utf8(read_from_file(file)?).map_err(CliError::from)?,
            )?)
        } else {
            Ok(None)
        }
    }
}

#[async_trait]
impl CliCommand<Transaction> for RegisterValidatorCandidate {
    fn command_name(&self) -> &'static str {
        "RegisterValidatorCandidate"
    }

    async fn execute(mut self) -> CliTypedResult<Transaction> {
        let (
            consensus_public_key,
            validator_network_public_key,
            full_node_network_public_key,
            validator_host,
            full_node_host,
        ) = self.process_inputs()?;
        let validator_network_addresses =
            vec![validator_host.as_network_address(validator_network_public_key)?];
        let full_node_network_addresses =
            match (full_node_host.as_ref(), full_node_network_public_key) {
                (Some(host), Some(public_key)) => vec![host.as_network_address(public_key)?],
                _ => vec![],
            };

        self.txn_options
            .submit_script_function(
                AccountAddress::ONE,
                "Stake",
                "register_validator_candidate",
                vec![],
                vec![
                    bcs::to_bytes(&consensus_public_key)?,
                    // Double BCS encode, so that we can hide the original type
                    bcs::to_bytes(&bcs::to_bytes(&validator_network_addresses)?)?,
                    bcs::to_bytes(&bcs::to_bytes(&full_node_network_addresses)?)?,
                ],
            )
            .await
    }
}

/// Arguments used for operator of the staking pool
#[derive(Parser)]
pub struct OperatorArgs {
    /// Address of the Staking pool
    #[clap(long)]
    pub(crate) pool_address: Option<AccountAddress>,
}

/// Join the validator set after meeting staking requirements
#[derive(Parser)]
pub struct JoinValidatorSet {
    #[clap(flatten)]
    pub(crate) txn_options: TransactionOptions,
    #[clap(flatten)]
    pub(crate) operator_args: OperatorArgs,
}

#[async_trait]
impl CliCommand<Transaction> for JoinValidatorSet {
    fn command_name(&self) -> &'static str {
        "JoinValidatorSet"
    }

    async fn execute(mut self) -> CliTypedResult<Transaction> {
        let address = if let Some(address) = self.operator_args.pool_address {
            address
        } else {
            self.txn_options.profile_options.account_address()?
        };

        self.txn_options
            .submit_script_function(
                AccountAddress::ONE,
                "Stake",
                "join_validator_set",
                vec![],
                vec![bcs::to_bytes(&address)?],
            )
            .await
    }
}

/// Leave the validator set
#[derive(Parser)]
pub struct LeaveValidatorSet {
    #[clap(flatten)]
    pub(crate) txn_options: TransactionOptions,
    #[clap(flatten)]
    pub(crate) operator_args: OperatorArgs,
}

#[async_trait]
impl CliCommand<Transaction> for LeaveValidatorSet {
    fn command_name(&self) -> &'static str {
        "LeaveValidatorSet"
    }

    async fn execute(mut self) -> CliTypedResult<Transaction> {
        let address = if let Some(address) = self.operator_args.pool_address {
            address
        } else {
            self.txn_options.profile_options.account_address()?
        };

        self.txn_options
            .submit_script_function(
                AccountAddress::ONE,
                "Stake",
                "leave_validator_set",
                vec![],
                vec![bcs::to_bytes(&address)?],
            )
            .await
    }
}
<|endoftext|>


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

use dom::bindings::codegen::Bindings::TextEncoderBinding;
use dom::bindings::codegen::Bindings::TextEncoderBinding::TextEncoderMethods;
use dom::bindings::global::GlobalRef;
use dom::bindings::error::Fallible;
use dom::bindings::error::Error::Range;
use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::str::USVString;
use dom::bindings::utils::{Reflector, reflect_dom_object};

use util::str::DOMString;

use std::borrow::ToOwned;
use std::ascii::AsciiExt;
use std::ptr;

use encoding::types::EncodingRef;
use encoding::{Encoding, EncoderTrap};
use encoding::label::encoding_from_whatwg_label;

use libc::uint8_t;
use js::jsapi::{JSContext, JSObject};
use js::jsfriendapi::bindgen::{JS_NewUint8Array, JS_GetUint8ArrayData};

#[dom_struct]
pub struct TextEncoder {
    reflector_: Reflector,
    encoding: DOMString,
    encoder: EncodingRef,
}

impl TextEncoder {
    fn new_inherited(encoding: DOMString, encoder: EncodingRef) -> TextEncoder {
        TextEncoder {
            reflector_: Reflector::new(),
            encoding: encoding,
            encoder: encoder,
        }
    }

    pub fn new(global: GlobalRef, encoding: DOMString, encoder: EncodingRef) -> Temporary<TextEncoder> {
        reflect_dom_object(box TextEncoder::new_inherited(encoding, encoder),
                           global,
                           TextEncoderBinding::Wrap)
    }

    // https://encoding.spec.whatwg.org/#dom-textencoder
    pub fn Constructor(global: GlobalRef,
                       label: DOMString) -> Fallible<Temporary<TextEncoder>> {
        let encoding = match encoding_from_whatwg_label(&label.trim().to_ascii_lowercase()) {
            Some(enc) => enc,
            None => {
                debug!("Encoding Label Not Supported");
                return Err(Range("The given encoding is not supported.".to_owned()))
            }
        };

        match encoding.name() {
            "utf-8" | "utf-16be" | "utf-16le" => {
                Ok(TextEncoder::new(global, encoding.name().to_owned(), encoding))
            }
            _ => {
                debug!("Encoding Not UTF");
                return Err(Range("The encoding must be utf-8, utf-16le, or utf-16be.".to_owned()))
            }
        }
    }
}

impl<'a> TextEncoderMethods for JSRef<'a, TextEncoder> {
    // https://encoding.spec.whatwg.org/#dom-textencoder-encoding
    fn Encoding(self) -> DOMString {
        self.encoding.clone()
    }

    // https://encoding.spec.whatwg.org/#dom-textencoder-encode
    #[allow(unsafe_code)]
    fn Encode(self, cx: *mut JSContext, input: USVString) -> *mut JSObject {
        unsafe {
            let output = self.encoder.encode(&input.0, EncoderTrap::Strict).unwrap();
            let length = output.len() as u32;
            let js_object: *mut JSObject = JS_NewUint8Array(cx, length);

            let js_object_data: *mut uint8_t = JS_GetUint8ArrayData(js_object, cx);
            ptr::copy_nonoverlapping(js_object_data, output.as_ptr(), length as usize);
            return js_object;
        }
    }
}
<commit_msg>Use a better name in TextEncoder::Encode.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::Bindings::TextEncoderBinding;
use dom::bindings::codegen::Bindings::TextEncoderBinding::TextEncoderMethods;
use dom::bindings::global::GlobalRef;
use dom::bindings::error::Fallible;
use dom::bindings::error::Error::Range;
use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::str::USVString;
use dom::bindings::utils::{Reflector, reflect_dom_object};

use util::str::DOMString;

use std::borrow::ToOwned;
use std::ascii::AsciiExt;
use std::ptr;

use encoding::types::EncodingRef;
use encoding::{Encoding, EncoderTrap};
use encoding::label::encoding_from_whatwg_label;

use libc::uint8_t;
use js::jsapi::{JSContext, JSObject};
use js::jsfriendapi::bindgen::{JS_NewUint8Array, JS_GetUint8ArrayData};

#[dom_struct]
pub struct TextEncoder {
    reflector_: Reflector,
    encoding: DOMString,
    encoder: EncodingRef,
}

impl TextEncoder {
    fn new_inherited(encoding: DOMString, encoder: EncodingRef) -> TextEncoder {
        TextEncoder {
            reflector_: Reflector::new(),
            encoding: encoding,
            encoder: encoder,
        }
    }

    pub fn new(global: GlobalRef, encoding: DOMString, encoder: EncodingRef) -> Temporary<TextEncoder> {
        reflect_dom_object(box TextEncoder::new_inherited(encoding, encoder),
                           global,
                           TextEncoderBinding::Wrap)
    }

    // https://encoding.spec.whatwg.org/#dom-textencoder
    pub fn Constructor(global: GlobalRef,
                       label: DOMString) -> Fallible<Temporary<TextEncoder>> {
        let encoding = match encoding_from_whatwg_label(&label.trim().to_ascii_lowercase()) {
            Some(enc) => enc,
            None => {
                debug!("Encoding Label Not Supported");
                return Err(Range("The given encoding is not supported.".to_owned()))
            }
        };

        match encoding.name() {
            "utf-8" | "utf-16be" | "utf-16le" => {
                Ok(TextEncoder::new(global, encoding.name().to_owned(), encoding))
            }
            _ => {
                debug!("Encoding Not UTF");
                return Err(Range("The encoding must be utf-8, utf-16le, or utf-16be.".to_owned()))
            }
        }
    }
}

impl<'a> TextEncoderMethods for JSRef<'a, TextEncoder> {
    // https://encoding.spec.whatwg.org/#dom-textencoder-encoding
    fn Encoding(self) -> DOMString {
        self.encoding.clone()
    }

    // https://encoding.spec.whatwg.org/#dom-textencoder-encode
    #[allow(unsafe_code)]
    fn Encode(self, cx: *mut JSContext, input: USVString) -> *mut JSObject {
        unsafe {
            let encoded = self.encoder.encode(&input.0, EncoderTrap::Strict).unwrap();
            let length = encoded.len() as u32;
            let js_object: *mut JSObject = JS_NewUint8Array(cx, length);

            let js_object_data: *mut uint8_t = JS_GetUint8ArrayData(js_object, cx);
            ptr::copy_nonoverlapping(js_object_data, encoded.as_ptr(), length as usize);
            return js_object;
        }
    }
}
<|endoftext|>


<commit_before>use bindgen::callbacks::*;

#[derive(Debug)]
pub struct RemoveFunctionPrefixParseCallback {
    pub remove_function_prefix: Option<String>,
}

impl RemoveFunctionPrefixParseCallback {
    pub fn new(prefix: &str) -> Self {
        RemoveFunctionPrefixParseCallback {
            remove_function_prefix: Some(prefix.to_string()),
        }
    }
}

impl ParseCallbacks for RemoveFunctionPrefixParseCallback {
    fn generated_name_override(&self, function_name: &str) -> Option<String> {
        if let Some(prefix) = &self.remove_function_prefix {
            if let Some(name) = function_name.strip_prefix(prefix) {
                return Some(name.to_string())
            }
        }
        None
    }
}

#[derive(Debug)]
struct EnumVariantRename;

impl ParseCallbacks for EnumVariantRename {
    fn enum_variant_name(
        &self,
        _enum_name: Option<&str>,
        original_variant_name: &str,
        _variant_value: EnumVariantValue,
    ) -> Option<String> {
        Some(format!("RENAMED_{}", original_variant_name))
    }
}

#[derive(Debug)]
struct BlocklistedTypeImplementsTrait;

impl ParseCallbacks for BlocklistedTypeImplementsTrait {
    fn blocklisted_type_implements_trait(
        &self,
        _name: &str,
        derive_trait: DeriveTrait,
    ) -> Option<ImplementsTrait> {
        if derive_trait == DeriveTrait::Hash {
            Some(ImplementsTrait::No)
        } else {
            Some(ImplementsTrait::Yes)
        }
    }
}

pub fn lookup(cb: &str) -> Box<dyn ParseCallbacks> {
    match cb {
        "enum-variant-rename" => Box::new(EnumVariantRename),
        "blocklisted-type-implements-trait" => {
            Box::new(BlocklistedTypeImplementsTrait)
        }
        call_back => {
            if call_back.starts_with("remove-function-prefix-") {
                let prefix = call_back
                    .split("remove-function-prefix-")
                    .last()
                    .to_owned();
                let lnopc =
                    RemoveFunctionPrefixParseCallback::new(prefix.unwrap());
                Box::new(lnopc)
            } else {
                panic!("Couldn't find name ParseCallbacks: {}", cb)
            }
        }
    }
}
<commit_msg>Fix formatting<commit_after>use bindgen::callbacks::*;

#[derive(Debug)]
pub struct RemoveFunctionPrefixParseCallback {
    pub remove_function_prefix: Option<String>,
}

impl RemoveFunctionPrefixParseCallback {
    pub fn new(prefix: &str) -> Self {
        RemoveFunctionPrefixParseCallback {
            remove_function_prefix: Some(prefix.to_string()),
        }
    }
}

impl ParseCallbacks for RemoveFunctionPrefixParseCallback {
    fn generated_name_override(&self, function_name: &str) -> Option<String> {
        if let Some(prefix) = &self.remove_function_prefix {
            if let Some(name) = function_name.strip_prefix(prefix) {
                return Some(name.to_string());
            }
        }
        None
    }
}

#[derive(Debug)]
struct EnumVariantRename;

impl ParseCallbacks for EnumVariantRename {
    fn enum_variant_name(
        &self,
        _enum_name: Option<&str>,
        original_variant_name: &str,
        _variant_value: EnumVariantValue,
    ) -> Option<String> {
        Some(format!("RENAMED_{}", original_variant_name))
    }
}

#[derive(Debug)]
struct BlocklistedTypeImplementsTrait;

impl ParseCallbacks for BlocklistedTypeImplementsTrait {
    fn blocklisted_type_implements_trait(
        &self,
        _name: &str,
        derive_trait: DeriveTrait,
    ) -> Option<ImplementsTrait> {
        if derive_trait == DeriveTrait::Hash {
            Some(ImplementsTrait::No)
        } else {
            Some(ImplementsTrait::Yes)
        }
    }
}

pub fn lookup(cb: &str) -> Box<dyn ParseCallbacks> {
    match cb {
        "enum-variant-rename" => Box::new(EnumVariantRename),
        "blocklisted-type-implements-trait" => {
            Box::new(BlocklistedTypeImplementsTrait)
        }
        call_back => {
            if call_back.starts_with("remove-function-prefix-") {
                let prefix = call_back
                    .split("remove-function-prefix-")
                    .last()
                    .to_owned();
                let lnopc =
                    RemoveFunctionPrefixParseCallback::new(prefix.unwrap());
                Box::new(lnopc)
            } else {
                panic!("Couldn't find name ParseCallbacks: {}", cb)
            }
        }
    }
}
<|endoftext|>


<commit_before>//! cargo_check provides the functionality needed to run `cargo check` or
//! another compatible command (f.x. clippy) in a background thread and provide
//! LSP diagnostics based on the output of the command.
use cargo_metadata::Message;
use crossbeam_channel::{never, select, unbounded, Receiver, RecvError, Sender};
use lsp_types::{
    CodeAction, CodeActionOrCommand, Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin,
    WorkDoneProgressEnd, WorkDoneProgressReport,
};
use std::{
    error, fmt,
    io::{BufRead, BufReader},
    path::{Path, PathBuf},
    process::{Command, Stdio},
    time::Instant,
};

mod conv;

use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic};

pub use crate::conv::url_from_path_with_drive_lowercasing;

#[derive(Clone, Debug)]
pub struct CheckConfig {
    pub enable: bool,
    pub args: Vec<String>,
    pub command: String,
    pub all_targets: bool,
}

/// CheckWatcher wraps the shared state and communication machinery used for
/// running `cargo check` (or other compatible command) and providing
/// diagnostics based on the output.
/// The spawned thread is shut down when this struct is dropped.
#[derive(Debug)]
pub struct CheckWatcher {
    // XXX: drop order is significant
    cmd_send: Sender<CheckCommand>,
    handle: Option<jod_thread::JoinHandle<()>>,
    pub task_recv: Receiver<CheckTask>,
}

impl CheckWatcher {
    pub fn new(config: CheckConfig, workspace_root: PathBuf) -> CheckWatcher {
        let (task_send, task_recv) = unbounded::<CheckTask>();
        let (cmd_send, cmd_recv) = unbounded::<CheckCommand>();
        let handle = jod_thread::spawn(move || {
            let mut check = CheckWatcherThread::new(config, workspace_root);
            check.run(&task_send, &cmd_recv);
        });
        CheckWatcher { task_recv, cmd_send, handle: Some(handle) }
    }

    /// Schedule a re-start of the cargo check worker.
    pub fn update(&self) {
        self.cmd_send.send(CheckCommand::Update).unwrap();
    }
}

#[derive(Debug)]
pub enum CheckTask {
    /// Request a clearing of all cached diagnostics from the check watcher
    ClearDiagnostics,

    /// Request adding a diagnostic with fixes included to a file
    AddDiagnostic { url: Url, diagnostic: Diagnostic, fixes: Vec<CodeActionOrCommand> },

    /// Request check progress notification to client
    Status(WorkDoneProgress),
}

pub enum CheckCommand {
    /// Request re-start of check thread
    Update,
}

struct CheckWatcherThread {
    options: CheckConfig,
    workspace_root: PathBuf,
    watcher: WatchThread,
    last_update_req: Option<Instant>,
}

impl CheckWatcherThread {
    fn new(options: CheckConfig, workspace_root: PathBuf) -> CheckWatcherThread {
        CheckWatcherThread {
            options,
            workspace_root,
            watcher: WatchThread::dummy(),
            last_update_req: None,
        }
    }

    fn run(&mut self, task_send: &Sender<CheckTask>, cmd_recv: &Receiver<CheckCommand>) {
        // If we rerun the thread, we need to discard the previous check results first
        self.clean_previous_results(task_send);

        loop {
            select! {
                recv(&cmd_recv) -> cmd => match cmd {
                    Ok(cmd) => self.handle_command(cmd),
                    Err(RecvError) => {
                        // Command channel has closed, so shut down
                        break;
                    },
                },
                recv(self.watcher.message_recv) -> msg => match msg {
                    Ok(msg) => self.handle_message(msg, task_send),
                    Err(RecvError) => {
                        // Watcher finished, replace it with a never channel to
                        // avoid busy-waiting.
                        std::mem::replace(&mut self.watcher.message_recv, never());
                    },
                }
            };

            if self.should_recheck() {
                self.last_update_req.take();
                task_send.send(CheckTask::ClearDiagnostics).unwrap();

                // Replace with a dummy watcher first so we drop the original and wait for completion
                std::mem::replace(&mut self.watcher, WatchThread::dummy());

                // Then create the actual new watcher
                self.watcher = WatchThread::new(&self.options, &self.workspace_root);
            }
        }
    }

    fn clean_previous_results(&self, task_send: &Sender<CheckTask>) {
        task_send.send(CheckTask::ClearDiagnostics).unwrap();
        task_send
            .send(CheckTask::Status(WorkDoneProgress::End(WorkDoneProgressEnd { message: None })))
            .unwrap();
    }

    fn should_recheck(&mut self) -> bool {
        if let Some(_last_update_req) = &self.last_update_req {
            // We currently only request an update on save, as we need up to
            // date source on disk for cargo check to do it's magic, so we
            // don't really need to debounce the requests at this point.
            return true;
        }
        false
    }

    fn handle_command(&mut self, cmd: CheckCommand) {
        match cmd {
            CheckCommand::Update => self.last_update_req = Some(Instant::now()),
        }
    }

    fn handle_message(&self, msg: CheckEvent, task_send: &Sender<CheckTask>) {
        match msg {
            CheckEvent::Begin => {
                task_send
                    .send(CheckTask::Status(WorkDoneProgress::Begin(WorkDoneProgressBegin {
                        title: "Running 'cargo check'".to_string(),
                        cancellable: Some(false),
                        message: None,
                        percentage: None,
                    })))
                    .unwrap();
            }

            CheckEvent::End => {
                task_send
                    .send(CheckTask::Status(WorkDoneProgress::End(WorkDoneProgressEnd {
                        message: None,
                    })))
                    .unwrap();
            }

            CheckEvent::Msg(Message::CompilerArtifact(msg)) => {
                task_send
                    .send(CheckTask::Status(WorkDoneProgress::Report(WorkDoneProgressReport {
                        cancellable: Some(false),
                        message: Some(msg.target.name),
                        percentage: None,
                    })))
                    .unwrap();
            }

            CheckEvent::Msg(Message::CompilerMessage(msg)) => {
                let map_result = map_rust_diagnostic_to_lsp(&msg.message, &self.workspace_root);
                if map_result.is_empty() {
                    return;
                }

                for MappedRustDiagnostic { location, diagnostic, fixes } in map_result {
                    let fixes = fixes
                        .into_iter()
                        .map(|fix| {
                            CodeAction { diagnostics: Some(vec![diagnostic.clone()]), ..fix }.into()
                        })
                        .collect();

                    task_send
                        .send(CheckTask::AddDiagnostic { url: location.uri, diagnostic, fixes })
                        .unwrap();
                }
            }

            CheckEvent::Msg(Message::BuildScriptExecuted(_msg)) => {}
            CheckEvent::Msg(Message::Unknown) => {}
        }
    }
}

#[derive(Debug)]
pub struct DiagnosticWithFixes {
    diagnostic: Diagnostic,
    fixes: Vec<CodeAction>,
}

/// WatchThread exists to wrap around the communication needed to be able to
/// run `cargo check` without blocking. Currently the Rust standard library
/// doesn't provide a way to read sub-process output without blocking, so we
/// have to wrap sub-processes output handling in a thread and pass messages
/// back over a channel.
/// The correct way to dispose of the thread is to drop it, on which the
/// sub-process will be killed, and the thread will be joined.
struct WatchThread {
    // XXX: drop order is significant
    message_recv: Receiver<CheckEvent>,
    _handle: Option<jod_thread::JoinHandle<()>>,
}

enum CheckEvent {
    Begin,
    Msg(cargo_metadata::Message),
    End,
}

#[derive(Debug)]
pub struct CargoError(String);

impl fmt::Display for CargoError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Cargo failed: {}", self.0)
    }
}
impl error::Error for CargoError {}

fn run_cargo(
    args: &[String],
    current_dir: Option<&Path>,
    on_message: &mut dyn FnMut(cargo_metadata::Message) -> bool,
) -> Result<(), CargoError> {
    let mut command = Command::new("cargo");
    if let Some(current_dir) = current_dir {
        command.current_dir(current_dir);
    }

    let mut child = command
        .args(args)
        .stdout(Stdio::piped())
        .stderr(Stdio::null())
        .stdin(Stdio::null())
        .spawn()
        .expect("couldn't launch cargo");

    // We manually read a line at a time, instead of using serde's
    // stream deserializers, because the deserializer cannot recover
    // from an error, resulting in it getting stuck, because we try to
    // be resillient against failures.
    //
    // Because cargo only outputs one JSON object per line, we can
    // simply skip a line if it doesn't parse, which just ignores any
    // erroneus output.
    let stdout = BufReader::new(child.stdout.take().unwrap());
    let mut read_at_least_one_message = false;

    for line in stdout.lines() {
        let line = match line {
            Ok(line) => line,
            Err(err) => {
                log::error!("Couldn't read line from cargo: {}", err);
                continue;
            }
        };

        let message = serde_json::from_str::<cargo_metadata::Message>(&line);
        let message = match message {
            Ok(message) => message,
            Err(err) => {
                log::error!("Invalid json from cargo check, ignoring ({}): {:?} ", err, line);
                continue;
            }
        };

        read_at_least_one_message = true;

        if !on_message(message) {
            break;
        }
    }

    // It is okay to ignore the result, as it only errors if the process is already dead
    let _ = child.kill();

    let err_msg = match child.wait() {
        Ok(exit_code) if !exit_code.success() && !read_at_least_one_message => {
            // FIXME: Read the stderr to display the reason, see `read2()` reference in PR comment:
            // https://github.com/rust-analyzer/rust-analyzer/pull/3632#discussion_r395605298
            format!(
                "the command produced no valid metadata (exit code: {:?}): cargo {}",
                exit_code,
                args.join(" ")
            )
        }
        Err(err) => format!("io error: {:?}", err),
        Ok(_) => return Ok(()),
    };

    Err(CargoError(err_msg))
}

impl WatchThread {
    fn dummy() -> WatchThread {
        WatchThread { message_recv: never(), _handle: None }
    }

    fn new(options: &CheckConfig, workspace_root: &Path) -> WatchThread {
        let mut args: Vec<String> = vec![
            options.command.clone(),
            "--workspace".to_string(),
            "--message-format=json".to_string(),
            "--manifest-path".to_string(),
            format!("{}/Cargo.toml", workspace_root.display()),
        ];
        if options.all_targets {
            args.push("--all-targets".to_string());
        }
        args.extend(options.args.iter().cloned());

        let (message_send, message_recv) = unbounded();
        let workspace_root = workspace_root.to_owned();
        let handle = if options.enable {
            Some(jod_thread::spawn(move || {
                // If we trigger an error here, we will do so in the loop instead,
                // which will break out of the loop, and continue the shutdown
                let _ = message_send.send(CheckEvent::Begin);

                let res = run_cargo(&args, Some(&workspace_root), &mut |message| {
                    // Skip certain kinds of messages to only spend time on what's useful
                    match &message {
                        Message::CompilerArtifact(artifact) if artifact.fresh => return true,
                        Message::BuildScriptExecuted(_) => return true,
                        Message::Unknown => return true,
                        _ => {}
                    }

                    // if the send channel was closed, we want to shutdown
                    message_send.send(CheckEvent::Msg(message)).is_ok()
                });

                if let Err(err) = res {
                    // FIXME: make the `message_send` to be `Sender<Result<CheckEvent, CargoError>>`
                    // to display user-caused misconfiguration errors instead of just logging them here
                    log::error!("Cargo watcher failed {:?}", err);
                }

                // We can ignore any error here, as we are already in the progress
                // of shutting down.
                let _ = message_send.send(CheckEvent::End);
            }))
        } else {
            None
        };
        WatchThread { message_recv, _handle: handle }
    }
}
<commit_msg>sort imports<commit_after>//! cargo_check provides the functionality needed to run `cargo check` or
//! another compatible command (f.x. clippy) in a background thread and provide
//! LSP diagnostics based on the output of the command.
mod conv;

use std::{
    error, fmt,
    io::{BufRead, BufReader},
    path::{Path, PathBuf},
    process::{Command, Stdio},
    time::Instant,
};

use cargo_metadata::Message;
use crossbeam_channel::{never, select, unbounded, Receiver, RecvError, Sender};
use lsp_types::{
    CodeAction, CodeActionOrCommand, Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin,
    WorkDoneProgressEnd, WorkDoneProgressReport,
};

use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic};

pub use crate::conv::url_from_path_with_drive_lowercasing;

#[derive(Clone, Debug)]
pub struct CheckConfig {
    pub enable: bool,
    pub args: Vec<String>,
    pub command: String,
    pub all_targets: bool,
}

/// CheckWatcher wraps the shared state and communication machinery used for
/// running `cargo check` (or other compatible command) and providing
/// diagnostics based on the output.
/// The spawned thread is shut down when this struct is dropped.
#[derive(Debug)]
pub struct CheckWatcher {
    // XXX: drop order is significant
    cmd_send: Sender<CheckCommand>,
    handle: Option<jod_thread::JoinHandle<()>>,
    pub task_recv: Receiver<CheckTask>,
}

impl CheckWatcher {
    pub fn new(config: CheckConfig, workspace_root: PathBuf) -> CheckWatcher {
        let (task_send, task_recv) = unbounded::<CheckTask>();
        let (cmd_send, cmd_recv) = unbounded::<CheckCommand>();
        let handle = jod_thread::spawn(move || {
            let mut check = CheckWatcherThread::new(config, workspace_root);
            check.run(&task_send, &cmd_recv);
        });
        CheckWatcher { task_recv, cmd_send, handle: Some(handle) }
    }

    /// Schedule a re-start of the cargo check worker.
    pub fn update(&self) {
        self.cmd_send.send(CheckCommand::Update).unwrap();
    }
}

#[derive(Debug)]
pub enum CheckTask {
    /// Request a clearing of all cached diagnostics from the check watcher
    ClearDiagnostics,

    /// Request adding a diagnostic with fixes included to a file
    AddDiagnostic { url: Url, diagnostic: Diagnostic, fixes: Vec<CodeActionOrCommand> },

    /// Request check progress notification to client
    Status(WorkDoneProgress),
}

pub enum CheckCommand {
    /// Request re-start of check thread
    Update,
}

struct CheckWatcherThread {
    options: CheckConfig,
    workspace_root: PathBuf,
    watcher: WatchThread,
    last_update_req: Option<Instant>,
}

impl CheckWatcherThread {
    fn new(options: CheckConfig, workspace_root: PathBuf) -> CheckWatcherThread {
        CheckWatcherThread {
            options,
            workspace_root,
            watcher: WatchThread::dummy(),
            last_update_req: None,
        }
    }

    fn run(&mut self, task_send: &Sender<CheckTask>, cmd_recv: &Receiver<CheckCommand>) {
        // If we rerun the thread, we need to discard the previous check results first
        self.clean_previous_results(task_send);

        loop {
            select! {
                recv(&cmd_recv) -> cmd => match cmd {
                    Ok(cmd) => self.handle_command(cmd),
                    Err(RecvError) => {
                        // Command channel has closed, so shut down
                        break;
                    },
                },
                recv(self.watcher.message_recv) -> msg => match msg {
                    Ok(msg) => self.handle_message(msg, task_send),
                    Err(RecvError) => {
                        // Watcher finished, replace it with a never channel to
                        // avoid busy-waiting.
                        std::mem::replace(&mut self.watcher.message_recv, never());
                    },
                }
            };

            if self.should_recheck() {
                self.last_update_req.take();
                task_send.send(CheckTask::ClearDiagnostics).unwrap();

                // Replace with a dummy watcher first so we drop the original and wait for completion
                std::mem::replace(&mut self.watcher, WatchThread::dummy());

                // Then create the actual new watcher
                self.watcher = WatchThread::new(&self.options, &self.workspace_root);
            }
        }
    }

    fn clean_previous_results(&self, task_send: &Sender<CheckTask>) {
        task_send.send(CheckTask::ClearDiagnostics).unwrap();
        task_send
            .send(CheckTask::Status(WorkDoneProgress::End(WorkDoneProgressEnd { message: None })))
            .unwrap();
    }

    fn should_recheck(&mut self) -> bool {
        if let Some(_last_update_req) = &self.last_update_req {
            // We currently only request an update on save, as we need up to
            // date source on disk for cargo check to do it's magic, so we
            // don't really need to debounce the requests at this point.
            return true;
        }
        false
    }

    fn handle_command(&mut self, cmd: CheckCommand) {
        match cmd {
            CheckCommand::Update => self.last_update_req = Some(Instant::now()),
        }
    }

    fn handle_message(&self, msg: CheckEvent, task_send: &Sender<CheckTask>) {
        match msg {
            CheckEvent::Begin => {
                task_send
                    .send(CheckTask::Status(WorkDoneProgress::Begin(WorkDoneProgressBegin {
                        title: "Running 'cargo check'".to_string(),
                        cancellable: Some(false),
                        message: None,
                        percentage: None,
                    })))
                    .unwrap();
            }

            CheckEvent::End => {
                task_send
                    .send(CheckTask::Status(WorkDoneProgress::End(WorkDoneProgressEnd {
                        message: None,
                    })))
                    .unwrap();
            }

            CheckEvent::Msg(Message::CompilerArtifact(msg)) => {
                task_send
                    .send(CheckTask::Status(WorkDoneProgress::Report(WorkDoneProgressReport {
                        cancellable: Some(false),
                        message: Some(msg.target.name),
                        percentage: None,
                    })))
                    .unwrap();
            }

            CheckEvent::Msg(Message::CompilerMessage(msg)) => {
                let map_result = map_rust_diagnostic_to_lsp(&msg.message, &self.workspace_root);
                if map_result.is_empty() {
                    return;
                }

                for MappedRustDiagnostic { location, diagnostic, fixes } in map_result {
                    let fixes = fixes
                        .into_iter()
                        .map(|fix| {
                            CodeAction { diagnostics: Some(vec![diagnostic.clone()]), ..fix }.into()
                        })
                        .collect();

                    task_send
                        .send(CheckTask::AddDiagnostic { url: location.uri, diagnostic, fixes })
                        .unwrap();
                }
            }

            CheckEvent::Msg(Message::BuildScriptExecuted(_msg)) => {}
            CheckEvent::Msg(Message::Unknown) => {}
        }
    }
}

#[derive(Debug)]
pub struct DiagnosticWithFixes {
    diagnostic: Diagnostic,
    fixes: Vec<CodeAction>,
}

/// WatchThread exists to wrap around the communication needed to be able to
/// run `cargo check` without blocking. Currently the Rust standard library
/// doesn't provide a way to read sub-process output without blocking, so we
/// have to wrap sub-processes output handling in a thread and pass messages
/// back over a channel.
/// The correct way to dispose of the thread is to drop it, on which the
/// sub-process will be killed, and the thread will be joined.
struct WatchThread {
    // XXX: drop order is significant
    message_recv: Receiver<CheckEvent>,
    _handle: Option<jod_thread::JoinHandle<()>>,
}

enum CheckEvent {
    Begin,
    Msg(cargo_metadata::Message),
    End,
}

#[derive(Debug)]
pub struct CargoError(String);

impl fmt::Display for CargoError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Cargo failed: {}", self.0)
    }
}
impl error::Error for CargoError {}

fn run_cargo(
    args: &[String],
    current_dir: Option<&Path>,
    on_message: &mut dyn FnMut(cargo_metadata::Message) -> bool,
) -> Result<(), CargoError> {
    let mut command = Command::new("cargo");
    if let Some(current_dir) = current_dir {
        command.current_dir(current_dir);
    }

    let mut child = command
        .args(args)
        .stdout(Stdio::piped())
        .stderr(Stdio::null())
        .stdin(Stdio::null())
        .spawn()
        .expect("couldn't launch cargo");

    // We manually read a line at a time, instead of using serde's
    // stream deserializers, because the deserializer cannot recover
    // from an error, resulting in it getting stuck, because we try to
    // be resillient against failures.
    //
    // Because cargo only outputs one JSON object per line, we can
    // simply skip a line if it doesn't parse, which just ignores any
    // erroneus output.
    let stdout = BufReader::new(child.stdout.take().unwrap());
    let mut read_at_least_one_message = false;

    for line in stdout.lines() {
        let line = match line {
            Ok(line) => line,
            Err(err) => {
                log::error!("Couldn't read line from cargo: {}", err);
                continue;
            }
        };

        let message = serde_json::from_str::<cargo_metadata::Message>(&line);
        let message = match message {
            Ok(message) => message,
            Err(err) => {
                log::error!("Invalid json from cargo check, ignoring ({}): {:?} ", err, line);
                continue;
            }
        };

        read_at_least_one_message = true;

        if !on_message(message) {
            break;
        }
    }

    // It is okay to ignore the result, as it only errors if the process is already dead
    let _ = child.kill();

    let err_msg = match child.wait() {
        Ok(exit_code) if !exit_code.success() && !read_at_least_one_message => {
            // FIXME: Read the stderr to display the reason, see `read2()` reference in PR comment:
            // https://github.com/rust-analyzer/rust-analyzer/pull/3632#discussion_r395605298
            format!(
                "the command produced no valid metadata (exit code: {:?}): cargo {}",
                exit_code,
                args.join(" ")
            )
        }
        Err(err) => format!("io error: {:?}", err),
        Ok(_) => return Ok(()),
    };

    Err(CargoError(err_msg))
}

impl WatchThread {
    fn dummy() -> WatchThread {
        WatchThread { message_recv: never(), _handle: None }
    }

    fn new(options: &CheckConfig, workspace_root: &Path) -> WatchThread {
        let mut args: Vec<String> = vec![
            options.command.clone(),
            "--workspace".to_string(),
            "--message-format=json".to_string(),
            "--manifest-path".to_string(),
            format!("{}/Cargo.toml", workspace_root.display()),
        ];
        if options.all_targets {
            args.push("--all-targets".to_string());
        }
        args.extend(options.args.iter().cloned());

        let (message_send, message_recv) = unbounded();
        let workspace_root = workspace_root.to_owned();
        let handle = if options.enable {
            Some(jod_thread::spawn(move || {
                // If we trigger an error here, we will do so in the loop instead,
                // which will break out of the loop, and continue the shutdown
                let _ = message_send.send(CheckEvent::Begin);

                let res = run_cargo(&args, Some(&workspace_root), &mut |message| {
                    // Skip certain kinds of messages to only spend time on what's useful
                    match &message {
                        Message::CompilerArtifact(artifact) if artifact.fresh => return true,
                        Message::BuildScriptExecuted(_) => return true,
                        Message::Unknown => return true,
                        _ => {}
                    }

                    // if the send channel was closed, we want to shutdown
                    message_send.send(CheckEvent::Msg(message)).is_ok()
                });

                if let Err(err) = res {
                    // FIXME: make the `message_send` to be `Sender<Result<CheckEvent, CargoError>>`
                    // to display user-caused misconfiguration errors instead of just logging them here
                    log::error!("Cargo watcher failed {:?}", err);
                }

                // We can ignore any error here, as we are already in the progress
                // of shutting down.
                let _ = message_send.send(CheckEvent::End);
            }))
        } else {
            None
        };
        WatchThread { message_recv, _handle: handle }
    }
}
<|endoftext|>


<commit_before>use std;

type id = libc::intptr_t;
type Class = libc::intptr_t;
type IMP = *u8;
type SEL = libc::intptr_t;
type Ivar = libc::intptr_t;

const nil : id = 0 as id;

extern mod objc {
    fn class_addMethod(cls : Class,
                       name : SEL,
                       imp : IMP,
                       types : *libc::c_char) -> bool;
    fn class_addIvar(cls : Class,
                     name : *libc::c_char,
                     size : libc::size_t,
                     alignment: u8,
		     types: *libc::c_char) -> bool;
    fn object_setInstanceVariable(obj : id,
                                  name : *libc::c_char,
                                  value : *libc::c_void);
    fn object_getInstanceVariable(obj : id,
                                  name : *libc::c_char,
                                  outValue : **libc::c_void);
    fn objc_allocateClassPair(superclass : Class,
                              name : *libc::c_char,
                              extraBytes : libc::size_t) -> Class;
    fn objc_getClass(name : *libc::c_char) -> id;
    fn objc_msgSend(theReceiver : id, theSelector : SEL) -> id;
    fn objc_registerClassPair(cls : Class);
    fn sel_registerName(name : *libc::c_char) -> SEL;
}

#[test]
fn test_nsapp() {
    let klass = str::as_c_str("NSApplication", |s|
        objc::objc_getClass(s)
    );

    let sel = str::as_c_str("sharedApplication", |s|
        objc::sel_registerName(s)
    );

    let nsapp = objc::objc_msgSend(klass, sel);

    io::println(#fmt("nsapp: %d", (nsapp as int)));
}

#[test]
fn test_custom_obj() {
    extern fn MyObject_doSomething(this : id, _sel : SEL) -> id {
        io::println("doSomething");
        ret this;
    }

    let NSObject = str::as_c_str("NSObject", |s|
        objc::objc_getClass(s)
    );
    let MyObject = str::as_c_str("MyObject", |s|
        objc::objc_allocateClassPair(NSObject, s, 0 as libc::size_t)
    );
    let doSomething = str::as_c_str("doSomething", |s|
        objc::sel_registerName(s)
    );
    let _ = str::as_c_str("@@:", |types|
        objc::class_addMethod(MyObject, doSomething, MyObject_doSomething,
                              types)
    );
    objc::objc_registerClassPair(MyObject);

    let alloc = str::as_c_str("alloc", |s| objc::sel_registerName(s));
    let init = str::as_c_str("init", |s| objc::sel_registerName(s));

    let mut obj = objc::objc_msgSend(MyObject, alloc);
    obj = objc::objc_msgSend(obj, init);
    objc::objc_msgSend(obj, doSomething);
}

<commit_msg>Fix syntax in tests<commit_after>use std;

type id = libc::intptr_t;
type Class = libc::intptr_t;
type IMP = *u8;
type SEL = libc::intptr_t;
type Ivar = libc::intptr_t;

const nil : id = 0 as id;

extern mod objc {
    fn class_addMethod(cls : Class,
                       name : SEL,
                       imp : IMP,
                       types : *libc::c_char) -> bool;
    fn class_addIvar(cls : Class,
                     name : *libc::c_char,
                     size : libc::size_t,
                     alignment: u8,
		     types: *libc::c_char) -> bool;
    fn object_setInstanceVariable(obj : id,
                                  name : *libc::c_char,
                                  value : *libc::c_void);
    fn object_getInstanceVariable(obj : id,
                                  name : *libc::c_char,
                                  outValue : **libc::c_void);
    fn objc_allocateClassPair(superclass : Class,
                              name : *libc::c_char,
                              extraBytes : libc::size_t) -> Class;
    fn objc_getClass(name : *libc::c_char) -> id;
    fn objc_msgSend(theReceiver : id, theSelector : SEL) -> id;
    fn objc_registerClassPair(cls : Class);
    fn sel_registerName(name : *libc::c_char) -> SEL;
}

#[test]
fn test_nsapp() {
    let klass = str::as_c_str(~"NSApplication", |s|
        objc::objc_getClass(s)
    );

    let sel = str::as_c_str(~"sharedApplication", |s|
        objc::sel_registerName(s)
    );

    let nsapp = objc::objc_msgSend(klass, sel);

    io::println(#fmt(~"nsapp: %d", (nsapp as int)));
}

#[test]
fn test_custom_obj() {
    extern fn MyObject_doSomething(this : id, _sel : SEL) -> id {
        io::println("doSomething");
        ret this;
    }

    let NSObject = str::as_c_str(~"NSObject", |s|
        objc::objc_getClass(s)
    );
    let MyObject = str::as_c_str(~"MyObject", |s|
        objc::objc_allocateClassPair(NSObject, s, 0 as libc::size_t)
    );
    let doSomething = str::as_c_str(~"doSomething", |s|
        objc::sel_registerName(s)
    );
    let _ = str::as_c_str(~"@@:", |types|
        objc::class_addMethod(MyObject, doSomething, MyObject_doSomething,
                              types)
    );
    objc::objc_registerClassPair(MyObject);

    let alloc = str::as_c_str(~"alloc", |s| objc::sel_registerName(s));
    let init = str::as_c_str(~"init", |s| objc::sel_registerName(s));

    let mut obj = objc::objc_msgSend(MyObject, alloc);
    obj = objc::objc_msgSend(obj, init);
    objc::objc_msgSend(obj, doSomething);
}

<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
extern crate rls_analysis as analysis;
extern crate rls_data as data;
extern crate serde;
#[macro_use]
extern crate serde_json;

use analysis::{AnalysisHost, AnalysisLoader};
use std::collections::HashMap;
use std::fs::{self, File};
use std::path::{Path, PathBuf};

#[derive(Clone)]
pub struct Loader {
    deps_dir: PathBuf,
}

impl Loader {
    pub fn new(deps_dir: PathBuf) -> Self {
        Self { deps_dir }
    }
}

impl AnalysisLoader for Loader {
    fn needs_hard_reload(&self, _: &Path) -> bool {
        true
    }

    fn fresh_host(&self) -> AnalysisHost<Self> {
        AnalysisHost::new_with_loader(self.clone())
    }

    fn set_path_prefix(&mut self, _: &Path) {}

    fn abs_path_prefix(&self) -> Option<PathBuf> {
        None
    }
    fn search_directories(&self) -> Vec<PathBuf> {
        vec![self.deps_dir.clone()]
    }
}

// Searchfox uses 1-indexed lines, 0-indexed columns.
fn span_to_string(span: &data::SpanData) -> String {
    // Rust spans are multi-line... So we just set the start column if it spans
    // multiple rows, searchfox has fallback code to handle this.
    if span.line_start != span.line_end {
        return format!("{}:{}", span.line_start.0, span.column_start.0 - 1);
    }
    if span.column_start == span.column_end {
        return format!("{}:{}", span.line_start.0, span.column_start.0 - 1);
    }
    let len = span.column_end.0 - span.column_end.0;
    format!("{}:{}-{}", span.line_start.0, span.column_start.0 - 1, len)
}

fn visit(
    file: &mut File,
    kind: &'static str,
    location: &data::SpanData,
    qualname: &str,
    context: Option<&str>,
) {
    use serde_json::map::Map;
    use serde_json::value::Value;
    use std::io::Write;

    let mut out = Map::new();
    out.insert("loc".into(), Value::String(span_to_string(location)));
    out.insert("target".into(), json!(1));
    out.insert("kind".into(), Value::String(kind.into()));
    out.insert("pretty".into(), Value::String(qualname.into()));
    out.insert("sym".into(), Value::String(qualname.into()));
    if let Some(context) = context {
        out.insert("context".into(), Value::String(context.into()));
        out.insert("contextsym".into(), Value::String(context.into()));
    }

    let object = serde_json::to_string(&Value::Object(out)).unwrap();
    file.write_all(object.as_bytes()).unwrap();
    write!(file, "\n").unwrap();

    let mut out = Map::new();
    out.insert("loc".into(), Value::String(span_to_string(location)));
    out.insert("source".into(), json!(1));
    out.insert("kind".into(), Value::String(kind.into()));
    out.insert("pretty".into(), Value::String(qualname.into()));
    out.insert("sym".into(), Value::String(qualname.into()));

    let object = serde_json::to_string(&Value::Object(out)).unwrap();
    file.write_all(object.as_bytes()).unwrap();
    write!(file, "\n").unwrap();
}

fn analyze_file(
    file_name: &PathBuf,
    defs: &HashMap<data::Id, data::Def>,
    file_analysis: &data::Analysis,
    src_dir: &Path,
    output_dir: &Path,
) {
    let file = match file_name.strip_prefix(src_dir) {
        Ok(f) => f,
        Err(..) => {
            eprintln!("File not in the source directory: {}", file_name.display());
            return;
        }
    };

    let output_file = output_dir.join(file);
    let mut output_dir = output_file.clone();
    output_dir.pop();
    if let Err(err) = fs::create_dir_all(output_dir) {
        eprintln!(
            "Couldn't create dir for: {}, {:?}",
            output_file.display(),
            err
        );
        return;
    }
    let mut file = match File::create(&output_file) {
        Ok(f) => f,
        Err(err) => {
            eprintln!(
                "Couldn't open output file: {}, {:?}",
                output_file.display(),
                err
            );
            return;
        }
    };

    for import in &file_analysis.imports {
        let id = match import.ref_id {
            Some(id) => id,
            None => continue,
        };

        let def = match defs.get(&id) {
            Some(def) => def,
            None => continue,
        };

        visit(&mut file, "import", &import.span, &def.qualname, None)
    }

    for def in &file_analysis.defs {
        let parent = def.parent
            .and_then(|parent_id| defs.get(&parent_id).map(|d| &*d.qualname));

        visit(&mut file, "def", &def.span, &def.qualname, parent)
    }

    for ref_ in &file_analysis.refs {
        let def = match defs.get(&ref_.ref_id) {
            Some(d) => d,
            None => continue,
        };
        visit(
            &mut file,
            "use",
            &ref_.span,
            &def.qualname,
            /* context = */ None, // TODO
        )
    }
}

fn analyze_crate(
    analysis: &data::Analysis,
    defs: &HashMap<data::Id, data::Def>,
    src_dir: &Path,
    output_dir: &Path,
) {
    let mut per_file = HashMap::new();

    macro_rules! flat_map_per_file {
        ($field:ident) => {
            for item in &analysis.$field {
                let mut file_analysis =
                    per_file.entry(item.span.file_name.clone())
                        .or_insert_with(|| {
                            data::Analysis::new(analysis.config.clone())
                        });
                file_analysis.$field.push(item.clone());
            }
        }
    }

    flat_map_per_file!(imports);
    flat_map_per_file!(defs);
    flat_map_per_file!(impls);
    flat_map_per_file!(refs);
    flat_map_per_file!(macro_refs);
    flat_map_per_file!(relations);

    for (mut name, analysis) in per_file.drain() {
        if name.is_relative() {
            name = src_dir.join(name);
        }
        analyze_file(&name, defs, &analysis, src_dir, output_dir);
    }
}

fn main() {
    let matches = app_from_crate!()
        .args_from_usage(
            "<src>    'Points to the source root'
             <input>  'Points to the deps/save-analysis directory'
             <output> 'Points to the directory where searchfox metadata should'",
        )
        .get_matches();

    let src_dir = Path::new(matches.value_of("src").unwrap());
    let input_dir = Path::new(matches.value_of("input").unwrap());
    let output_dir = Path::new(matches.value_of("output").unwrap());

    let loader = Loader::new(PathBuf::from(input_dir));

    let crates = analysis::read_analysis_from_files(&loader, Default::default(), &[]);

    let mut defs = HashMap::new();
    for krate in &crates {
        for def in &krate.analysis.defs {
            defs.insert(def.id, def.clone());
        }
    }

    for krate in crates {
        analyze_crate(&krate.analysis, &defs, &src_dir, &output_dir);
    }
}
<commit_msg>rust-indexer: Get refs across crates working.<commit_after>#[macro_use]
extern crate clap;
extern crate rls_analysis as analysis;
extern crate rls_data as data;
extern crate serde;
#[macro_use]
extern crate serde_json;

use data::GlobalCrateId;
use analysis::{AnalysisHost, AnalysisLoader};
use std::collections::HashMap;
use std::fs::{self, File};
use std::path::{Path, PathBuf};

/// A global definition id in a crate.
///
/// FIXME(emilio): This key is kind of slow, because GlobalCrateId contains a
/// String. There's a "disambiguator" field which may be more than enough for
/// our purposes.
#[derive(Clone, Hash, Debug, Eq, PartialEq)]
pub struct DefId(GlobalCrateId, u32);

/// A map from global definition ids to the actual definition.
pub struct Defs {
    map: HashMap<DefId, data::Def>,
}

impl Defs {
    fn new() -> Self {
        Self { map: HashMap::new() }
    }

    fn insert(&mut self, analysis: &data::Analysis, def: &data::Def) {
        let crate_id = analysis.prelude.as_ref().unwrap().crate_id.clone();
        let previous = self.map.insert(DefId(crate_id, def.id.index), def.clone());
        assert!(
            previous.is_none(),
            "Found a definition with the same ID twice? {:?}",
            def,
        );
    }

    /// Getter for a given local id, which takes care of converting to a global
    /// ID and returning the definition if present.
    fn get(&self, analysis: &data::Analysis, id: data::Id) -> Option<data::Def> {
        let prelude = analysis.prelude.as_ref().unwrap();
        let krate_id = if id.krate == 0 {
            prelude.crate_id.clone()
        } else {
            // TODO(emilio): This escales with the number of crates in this
            // particular crate, but it's probably not too bad, since it should
            // be a pretty fast linear search.
            let krate = prelude.external_crates.iter().find(|krate| {
                krate.num == id.krate
            });

            let krate = match krate {
                Some(k) => k,
                None => {
                    eprintln!("Crate not found: {:?}", id);
                    return None;
                }
            };

            krate.id.clone()
        };

        let id = DefId(krate_id, id.index);
        self.map.get(&id).cloned()
    }
}

#[derive(Clone)]
pub struct Loader {
    deps_dir: PathBuf,
}

impl Loader {
    pub fn new(deps_dir: PathBuf) -> Self {
        Self { deps_dir }
    }
}

impl AnalysisLoader for Loader {
    fn needs_hard_reload(&self, _: &Path) -> bool {
        true
    }

    fn fresh_host(&self) -> AnalysisHost<Self> {
        AnalysisHost::new_with_loader(self.clone())
    }

    fn set_path_prefix(&mut self, _: &Path) {}

    fn abs_path_prefix(&self) -> Option<PathBuf> {
        None
    }
    fn search_directories(&self) -> Vec<PathBuf> {
        vec![self.deps_dir.clone()]
    }
}

// Searchfox uses 1-indexed lines, 0-indexed columns.
fn span_to_string(span: &data::SpanData) -> String {
    // Rust spans are multi-line... So we just set the start column if it spans
    // multiple rows, searchfox has fallback code to handle this.
    if span.line_start != span.line_end {
        return format!("{}:{}", span.line_start.0, span.column_start.0 - 1);
    }
    if span.column_start == span.column_end {
        return format!("{}:{}", span.line_start.0, span.column_start.0 - 1);
    }
    let len = span.column_end.0 - span.column_end.0;
    format!("{}:{}-{}", span.line_start.0, span.column_start.0 - 1, len)
}

fn visit(
    file: &mut File,
    kind: &'static str,
    location: &data::SpanData,
    qualname: &str,
    context: Option<&str>,
) {
    use serde_json::map::Map;
    use serde_json::value::Value;
    use std::io::Write;

    let mut out = Map::new();
    out.insert("loc".into(), Value::String(span_to_string(location)));
    out.insert("target".into(), json!(1));
    out.insert("kind".into(), Value::String(kind.into()));
    out.insert("pretty".into(), Value::String(qualname.into()));
    out.insert("sym".into(), Value::String(qualname.into()));
    if let Some(context) = context {
        out.insert("context".into(), Value::String(context.into()));
        out.insert("contextsym".into(), Value::String(context.into()));
    }

    let object = serde_json::to_string(&Value::Object(out)).unwrap();
    file.write_all(object.as_bytes()).unwrap();
    write!(file, "\n").unwrap();

    let mut out = Map::new();
    out.insert("loc".into(), Value::String(span_to_string(location)));
    out.insert("source".into(), json!(1));
    out.insert("kind".into(), Value::String(kind.into()));
    out.insert("pretty".into(), Value::String(qualname.into()));
    out.insert("sym".into(), Value::String(qualname.into()));

    let object = serde_json::to_string(&Value::Object(out)).unwrap();
    file.write_all(object.as_bytes()).unwrap();
    write!(file, "\n").unwrap();
}

fn analyze_file(
    file_name: &PathBuf,
    defs: &Defs,
    file_analysis: &data::Analysis,
    src_dir: &Path,
    output_dir: &Path,
) {
    let file = match file_name.strip_prefix(src_dir) {
        Ok(f) => f,
        Err(..) => {
            eprintln!("File not in the source directory: {}", file_name.display());
            return;
        }
    };

    let output_file = output_dir.join(file);
    let mut output_dir = output_file.clone();
    output_dir.pop();
    if let Err(err) = fs::create_dir_all(output_dir) {
        eprintln!(
            "Couldn't create dir for: {}, {:?}",
            output_file.display(),
            err
        );
        return;
    }
    let mut file = match File::create(&output_file) {
        Ok(f) => f,
        Err(err) => {
            eprintln!(
                "Couldn't open output file: {}, {:?}",
                output_file.display(),
                err
            );
            return;
        }
    };

    for import in &file_analysis.imports {
        let id = match import.ref_id {
            Some(id) => id,
            None => {
                eprintln!("Dropping import {}: {}, no ref", import.name, import.value);
                continue;
            }
        };

        let def = match defs.get(file_analysis, id) {
            Some(def) => def,
            None => {
                eprintln!("Dropping import {}: {}, no def for ref {:?}", import.name, import.value, id);
                continue;
            }
        };

        visit(&mut file, "use", &import.span, &def.qualname, None)
    }

    for def in &file_analysis.defs {
        let parent = def.parent
            .and_then(|parent_id| defs.get(file_analysis, parent_id).map(|d| d.qualname));

        visit(&mut file, "def", &def.span, &def.qualname, parent.as_ref().map(|p| &**p))
    }

    for ref_ in &file_analysis.refs {
        let def = match defs.get(file_analysis, ref_.ref_id) {
            Some(d) => d,
            None => {
                eprintln!("Dropping ref {:?}, kind {:?}, no def", ref_.ref_id, ref_.kind);
                continue;
            }
        };
        visit(
            &mut file,
            "use",
            &ref_.span,
            &def.qualname,
            /* context = */ None, // TODO
        )
    }
}

fn analyze_crate(
    analysis: &data::Analysis,
    defs: &Defs,
    src_dir: &Path,
    output_dir: &Path,
) {
    let mut per_file = HashMap::new();

    println!("Analyzing crate: {:?}", analysis.prelude);

    macro_rules! flat_map_per_file {
        ($field:ident) => {
            for item in &analysis.$field {
                let mut file_analysis =
                    per_file.entry(item.span.file_name.clone())
                        .or_insert_with(|| {
                            let prelude = analysis.prelude.clone();
                            let mut analysis = data::Analysis::new(analysis.config.clone());
                            analysis.prelude = prelude;
                            analysis
                        });
                file_analysis.$field.push(item.clone());
            }
        }
    }

    flat_map_per_file!(imports);
    flat_map_per_file!(defs);
    flat_map_per_file!(impls);
    flat_map_per_file!(refs);
    flat_map_per_file!(macro_refs);
    flat_map_per_file!(relations);

    for (mut name, analysis) in per_file.drain() {
        if name.is_relative() {
            name = src_dir.join(name);
        }
        analyze_file(&name, defs, &analysis, src_dir, output_dir);
    }
}

fn main() {
    let matches = app_from_crate!()
        .args_from_usage(
            "<src>    'Points to the source root'
             <input>  'Points to the deps/save-analysis directory'
             <output> 'Points to the directory where searchfox metadata should'",
        )
        .get_matches();

    let src_dir = Path::new(matches.value_of("src").unwrap());
    let input_dir = Path::new(matches.value_of("input").unwrap());
    let output_dir = Path::new(matches.value_of("output").unwrap());

    let loader = Loader::new(PathBuf::from(input_dir));

    let crates = analysis::read_analysis_from_files(&loader, Default::default(), &[]);

    let mut defs = Defs::new();
    for krate in &crates {
        for def in &krate.analysis.defs {
            println!("Indexing def: {:?}", def);
            defs.insert(&krate.analysis, def);
        }
    }

    for krate in crates {
        analyze_crate(&krate.analysis, &defs, &src_dir, &output_dir);
    }
}
<|endoftext|>


<commit_before>#![crate_name = "tournament_test"]
#![crate_type = "lib"]

#![feature(slice_patterns)]
#![feature(convert)]

use std::fs::File;
use std::path::Path;
use std::io::Read;

mod tournament;

fn file_equal(output_file: &str, expected_file: &str) {
    let output = match File::open(&Path::new(output_file)) {
        Err(e) => panic!("Couldn't open {}: {}", output_file, e),
        Ok(mut f) => {
            let mut buf = String::new();
            match f.read_to_string(&mut buf) {
                Err(e) => panic!("Couldn't read {}: {}", output_file, e),
                Ok(_) => buf
            }
        }
    };
    let expected = match File::open(&Path::new(expected_file)) {
        Err(e) => panic!("Couldn't open {}: {}", expected_file, e),
        Ok(mut f) => {
            let mut buf = String::new();
            match f.read_to_string(&mut buf) {
                Err(e) => panic!("Couldn't read {}: {}", expected_file, e),
                Ok(_) => buf
            }
        }
    };
    assert_eq!("\n".to_string() + output.as_ref(), "\n".to_string() + expected.as_ref());

}

#[test]
#[ignore]
fn test_good() {
    assert_eq!(tournament::tally(&Path::new("input1.txt"), &Path::new("output1.txt")), Ok(6));
    file_equal("output1.txt", "expected1.txt");
}

#[test]
#[ignore]
fn test_ignore_bad_lines() {
    assert_eq!(tournament::tally(&Path::new("input2.txt"), &Path::new("output2.txt")), Ok(6));
    file_equal("output2.txt", "expected2.txt");
}

#[test]
#[ignore]
fn test_incomplete_competition() {
    assert_eq!(tournament::tally(&Path::new("input3.txt"), &Path::new("output3.txt")), Ok(4));
    file_equal("output3.txt", "expected3.txt");
}
<commit_msg>Unwrap before comparison (std::io::Error no longer implements Eq)<commit_after>#![crate_name = "tournament_test"]
#![crate_type = "lib"]

#![feature(slice_patterns)]

use std::fs::File;
use std::path::Path;
use std::io::Read;

mod tournament;

fn file_equal(output_file: &str, expected_file: &str) {
    let output = match File::open(&Path::new(output_file)) {
        Err(e) => panic!("Couldn't open {}: {}", output_file, e),
        Ok(mut f) => {
            let mut buf = String::new();
            match f.read_to_string(&mut buf) {
                Err(e) => panic!("Couldn't read {}: {}", output_file, e),
                Ok(_) => buf
            }
        }
    };
    let expected = match File::open(&Path::new(expected_file)) {
        Err(e) => panic!("Couldn't open {}: {}", expected_file, e),
        Ok(mut f) => {
            let mut buf = String::new();
            match f.read_to_string(&mut buf) {
                Err(e) => panic!("Couldn't read {}: {}", expected_file, e),
                Ok(_) => buf
            }
        }
    };
    assert_eq!("\n".to_string() + output.as_ref(), "\n".to_string() + expected.as_ref());

}

#[test]
#[ignore]
fn test_good() {
    assert_eq!(tournament::tally(&Path::new("input1.txt"), &Path::new("output1.txt")).unwrap(), 6);
    file_equal("output1.txt", "expected1.txt");
}

#[test]
#[ignore]
fn test_ignore_bad_lines() {
    assert_eq!(tournament::tally(&Path::new("input2.txt"), &Path::new("output2.txt")).unwrap(), 6);
    file_equal("output2.txt", "expected2.txt");
}

#[test]
#[ignore]
fn test_incomplete_competition() {
    assert_eq!(tournament::tally(&Path::new("input3.txt"), &Path::new("output3.txt")).unwrap(), 4);
    file_equal("output3.txt", "expected3.txt");
}
<|endoftext|>