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

use crate::shared::{get_shuffle_dir, send, MAIN_PKG_PATH};
use anyhow::{anyhow, Result};
use diem_crypto::PrivateKey;
use diem_sdk::{
    client::BlockingClient,
    transaction_builder::TransactionFactory,
    types::{
        transaction::{Module, TransactionPayload},
        LocalAccount,
    },
};
use diem_types::{
    account_state::AccountState, account_state_blob::AccountStateBlob, chain_id::ChainId,
    transaction::authenticator::AuthenticationKey,
};
use generate_key::load_key;
use move_binary_format::{file_format::CompiledModule, normalized};
use move_lang::compiled_unit::{CompiledUnit, NamedCompiledModule};
use move_package::compilation::compiled_package::CompiledPackage;
use std::{collections::HashSet, convert::TryFrom, path::Path};

/// Deploys shuffle's main Move Package to the sender's address.
pub fn handle(project_path: &Path) -> Result<()> {
    let account_key_path = get_shuffle_dir()
        .join("accounts")
        .join("latest")
        .join("dev.key");
    if !account_key_path.exists() {
        return Err(anyhow!(
            "An account hasn't been created yet! Run shuffle account first."
        ));
    }
    let compiled_package = build_move_packages(project_path)?;
    publish_packages_as_transaction(&account_key_path, compiled_package)
}

/// Builds the packages in the shuffle project using the move package system.
fn build_move_packages(project_path: &Path) -> Result<CompiledPackage> {
    println!("Building Examples...");
    let pkgdir = project_path.join(MAIN_PKG_PATH);
    let config = move_package::BuildConfig {
        dev_mode: true,
        test_mode: false,
        generate_docs: false,
        generate_abis: true,
    };

    config.compile_package(pkgdir.as_path(), &mut std::io::stdout())
}

fn publish_packages_as_transaction(
    account_key_path: &Path,
    compiled_package: CompiledPackage,
) -> Result<()> {
    let new_account_key = load_key(account_key_path);
    let json_rpc_url = format!("http://0.0.0.0:{}", 8080); // TODO: Hardcoded to local devnet
    let factory = TransactionFactory::new(ChainId::test());
    println!("Connecting to {}", json_rpc_url);

    let client = BlockingClient::new(json_rpc_url);

    println!("Using Public Key {}", &new_account_key.public_key());
    let derived_address =
        AuthenticationKey::ed25519(&new_account_key.public_key()).derived_address();
    println!(
        "Sending txn from address {}",
        derived_address.to_hex_literal()
    );

    // ================= Send a module transaction ========================
    let seq_number = client
        .get_account(derived_address)
        .unwrap()
        .into_inner()
        .unwrap()
        .sequence_number;
    let mut new_account = LocalAccount::new(derived_address, new_account_key, seq_number);
    let compiled_units = compiled_package.compiled_units;
    let mut uniq_modules: HashSet<String> = HashSet::new(); // Apparently modules can appear twice in compiled units, ensure uniq
    for unit in compiled_units {
        match unit {
            CompiledUnit::Module(NamedCompiledModule { name, module, .. }) => {
                let namecpy = name.to_string();
                if uniq_modules.contains(&namecpy) {
                    continue;
                }
                println!("Deploying Module: {}", namecpy);
                uniq_modules.insert(namecpy);
                let mut binary = vec![];
                module.serialize(&mut binary)?;
                let publish_txn = new_account.sign_with_transaction_builder(
                    factory.payload(TransactionPayload::Module(Module::new(binary))),
                );

                send(&client, publish_txn)?;
            }
            _ => {
                continue;
            }
        }
    }
    println!("Success!");

    // ================= Get modules in the account  ========================
    // Assumes we've deployed to the shuffle developer's address.

    let account_state_blob: AccountStateBlob = {
        let blob = client
            .get_account_state_with_proof(new_account.address(), None, None)
            .unwrap()
            .into_inner()
            .blob
            .ok_or_else(|| anyhow::anyhow!("missing account state blob"))?;
        bcs::from_bytes(&blob)?
    };
    let account_state = AccountState::try_from(&account_state_blob)?;
    let mut modules = vec![];
    for module_bytes in account_state.get_modules() {
        modules.push(normalized::Module::new(
            &CompiledModule::deserialize(module_bytes)
                .map_err(|e| anyhow!("Failure deserializing module: {:?}", e))?,
        ));
    }
    println!("move modules length: {}", modules.len());
    println!("move modules name: {}", modules[0].name);

    Ok(())
}
<commit_msg>replaced unwrap with methods to handle possible errors<commit_after>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::shared::{get_shuffle_dir, send, MAIN_PKG_PATH};
use anyhow::{anyhow, Result};
use diem_crypto::PrivateKey;
use diem_sdk::{
    client::BlockingClient,
    transaction_builder::TransactionFactory,
    types::{
        transaction::{Module, TransactionPayload},
        LocalAccount,
    },
};
use diem_types::{
    account_state::AccountState, account_state_blob::AccountStateBlob, chain_id::ChainId,
    transaction::authenticator::AuthenticationKey,
};
use generate_key::load_key;
use move_binary_format::{file_format::CompiledModule, normalized};
use move_lang::compiled_unit::{CompiledUnit, NamedCompiledModule};
use move_package::compilation::compiled_package::CompiledPackage;
use std::{collections::HashSet, convert::TryFrom, path::Path};

/// Deploys shuffle's main Move Package to the sender's address.
pub fn handle(project_path: &Path) -> Result<()> {
    let account_key_path = get_shuffle_dir()
        .join("accounts")
        .join("latest")
        .join("dev.key");
    if !account_key_path.exists() {
        return Err(anyhow!(
            "An account hasn't been created yet! Run shuffle account first."
        ));
    }
    let compiled_package = build_move_packages(project_path)?;
    publish_packages_as_transaction(&account_key_path, compiled_package)
}

/// Builds the packages in the shuffle project using the move package system.
fn build_move_packages(project_path: &Path) -> Result<CompiledPackage> {
    println!("Building Examples...");
    let pkgdir = project_path.join(MAIN_PKG_PATH);
    let config = move_package::BuildConfig {
        dev_mode: true,
        test_mode: false,
        generate_docs: false,
        generate_abis: true,
    };

    config.compile_package(pkgdir.as_path(), &mut std::io::stdout())
}

fn publish_packages_as_transaction(
    account_key_path: &Path,
    compiled_package: CompiledPackage,
) -> Result<()> {
    let new_account_key = load_key(account_key_path);
    let json_rpc_url = format!("http://0.0.0.0:{}", 8080); // TODO: Hardcoded to local devnet
    let factory = TransactionFactory::new(ChainId::test());
    println!("Connecting to {}", json_rpc_url);

    let client = BlockingClient::new(json_rpc_url);

    println!("Using Public Key {}", &new_account_key.public_key());
    let derived_address =
        AuthenticationKey::ed25519(&new_account_key.public_key()).derived_address();
    println!(
        "Sending txn from address {}",
        derived_address.to_hex_literal()
    );

    // ================= Send a module transaction ========================
    let seq_number = client
        .get_account(derived_address)?
        .into_inner()
        .ok_or_else(|| anyhow::anyhow!("missing AccountView"))?
        .sequence_number;
    let mut new_account = LocalAccount::new(derived_address, new_account_key, seq_number);
    let compiled_units = compiled_package.compiled_units;
    let mut uniq_modules: HashSet<String> = HashSet::new(); // Apparently modules can appear twice in compiled units, ensure uniq
    for unit in compiled_units {
        match unit {
            CompiledUnit::Module(NamedCompiledModule { name, module, .. }) => {
                let namecpy = name.to_string();
                if uniq_modules.contains(&namecpy) {
                    continue;
                }
                println!("Deploying Module: {}", namecpy);
                uniq_modules.insert(namecpy);
                let mut binary = vec![];
                module.serialize(&mut binary)?;
                let publish_txn = new_account.sign_with_transaction_builder(
                    factory.payload(TransactionPayload::Module(Module::new(binary))),
                );

                send(&client, publish_txn)?;
            }
            _ => {
                continue;
            }
        }
    }
    println!("Success!");

    // ================= Get modules in the account  ========================
    // Assumes we've deployed to the shuffle developer's address.

    let account_state_blob: AccountStateBlob = {
        let blob = client
            .get_account_state_with_proof(new_account.address(), None, None)?
            .into_inner()
            .blob
            .ok_or_else(|| anyhow::anyhow!("missing account state blob"))?;
        bcs::from_bytes(&blob)?
    };
    let account_state = AccountState::try_from(&account_state_blob)?;
    let mut modules = vec![];
    for module_bytes in account_state.get_modules() {
        modules.push(normalized::Module::new(
            &CompiledModule::deserialize(module_bytes)
                .map_err(|e| anyhow!("Failure deserializing module: {:?}", e))?,
        ));
    }
    println!("move modules length: {}", modules.len());
    println!("move modules name: {}", modules[0].name);

    Ok(())
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

use crate::internal::prelude::*;
use crate::model::channel::ChannelType;

#[derive(Debug, Clone, Default)]
pub struct CreateThread(pub HashMap<&'static str, Value>);

impl CreateThread {
    /// The name of the thread.
    ///
    /// **Note**: Must be between 2 and 100 characters long.
    pub fn name<D: ToString>(&mut self, name: D) -> &mut Self {
        self.0.insert("name", Value::String(name.to_string()));

        self
    }

    /// Duration in minutes to automatically archive the thread after recent activity.
    ///
    /// **Note**: Can only be set to 60, 1440, 4320, 10080 currently.
    pub fn auto_archive_duration(&mut self, duration: u16) -> &mut Self {
        self.0.insert("auto_archive_duration", Value::Number(Number::from(duration)));

        self
    }

    /// The thread type, which can be [`ChannelType::PublicThread`] or [`ChannelType::PrivateThread`].
    ///
    /// **Note**: This defaults to [`ChannelType::PrivateThread`] in order to match the behavior
    /// when thread documentation was first published. This is a bit of a weird default though,
    /// and thus is highly likely to change in the future, so it is recommended to always
    /// explicitly setting it to avoid any breaking change.
    ///
    /// [`ChannelType::PublicThread`]: crate::model::channel::ChannelType::PublicThread
    /// [`ChannelType::PrivateThread`]: crate::model::channel::ChannelType::PrivateThread
    pub fn kind(&mut self, kind: ChannelType) -> &mut Self {
        self.0.insert("type", Value::Number(Number::from(kind as u8)));

        self
    }
}
<commit_msg>Add support for enabling slowmode on thread creation (#1682)<commit_after>use std::collections::HashMap;

use crate::internal::prelude::*;
use crate::model::channel::ChannelType;

#[derive(Debug, Clone, Default)]
pub struct CreateThread(pub HashMap<&'static str, Value>);

impl CreateThread {
    /// The name of the thread.
    ///
    /// **Note**: Must be between 2 and 100 characters long.
    pub fn name<D: ToString>(&mut self, name: D) -> &mut Self {
        self.0.insert("name", Value::String(name.to_string()));

        self
    }

    /// Duration in minutes to automatically archive the thread after recent activity.
    ///
    /// **Note**: Can only be set to 60, 1440, 4320, 10080 currently.
    pub fn auto_archive_duration(&mut self, duration: u16) -> &mut Self {
        self.0.insert("auto_archive_duration", Value::Number(Number::from(duration)));

        self
    }

    /// How many seconds must a user wait before sending another message.
    ///
    /// Bots, or users with the [`MANAGE_MESSAGES`] and/or [`MANAGE_CHANNELS`] permissions are exempt
    /// from this restriction.
    ///
    /// **Note**: Must be between 0 and 21600 seconds (360 minutes or 6 hours).
    ///
    /// [`MANAGE_MESSAGES`]: crate::model::permissions::Permissions::MANAGE_MESSAGES
    /// [`MANAGE_CHANNELS`]: crate::model::permissions::Permissions::MANAGE_CHANNELS
    #[doc(alias = "slowmode")]
    pub fn rate_limit_per_user(&mut self, seconds: u64) -> &mut Self {
        self.0.insert("rate_limit_per_user", Value::Number(Number::from(seconds)));

        self
    }

    /// The thread type, which can be [`ChannelType::PublicThread`] or [`ChannelType::PrivateThread`].
    ///
    /// **Note**: This defaults to [`ChannelType::PrivateThread`] in order to match the behavior
    /// when thread documentation was first published. This is a bit of a weird default though,
    /// and thus is highly likely to change in the future, so it is recommended to always
    /// explicitly setting it to avoid any breaking change.
    ///
    /// [`ChannelType::PublicThread`]: crate::model::channel::ChannelType::PublicThread
    /// [`ChannelType::PrivateThread`]: crate::model::channel::ChannelType::PrivateThread
    pub fn kind(&mut self, kind: ChannelType) -> &mut Self {
        self.0.insert("type", Value::Number(Number::from(kind as u8)));

        self
    }
}
<|endoftext|>


<commit_before>use crate::core::compiler::{CompileKind, CompileMode, Layout, RustcTargetData};
use crate::core::profiles::Profiles;
use crate::core::{PackageIdSpec, TargetKind, Workspace};
use crate::ops;
use crate::util::errors::CargoResult;
use crate::util::interning::InternedString;
use crate::util::lev_distance;
use crate::util::{Config, Progress, ProgressStyle};

use anyhow::Context as _;
use cargo_util::paths;
use std::fs;
use std::path::Path;

pub struct CleanOptions<'a> {
    pub config: &'a Config,
    /// A list of packages to clean. If empty, everything is cleaned.
    pub spec: Vec<String>,
    /// The target arch triple to clean, or None for the host arch
    pub targets: Vec<String>,
    /// Whether to clean the release directory
    pub profile_specified: bool,
    /// Whether to clean the directory of a certain build profile
    pub requested_profile: InternedString,
    /// Whether to just clean the doc directory
    pub doc: bool,
}

/// Cleans the package's build artifacts.
pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> {
    let mut target_dir = ws.target_dir();
    let config = ws.config();

    // If the doc option is set, we just want to delete the doc directory.
    if opts.doc {
        let mut progress = Progress::with_style("Cleaning", ProgressStyle::Percentage, config);
        target_dir = target_dir.join("doc");
        return rm_rf_with_progress(&target_dir.into_path_unlocked(), &mut progress);
    }

    let profiles = Profiles::new(ws, opts.requested_profile)?;

    if opts.profile_specified {
        // After parsing profiles we know the dir-name of the profile, if a profile
        // was passed from the command line. If so, delete only the directory of
        // that profile.
        let dir_name = profiles.get_dir_name();
        target_dir = target_dir.join(dir_name);
    }

    // If we have a spec, then we need to delete some packages, otherwise, just
    // remove the whole target directory and be done with it!
    //
    // Note that we don't bother grabbing a lock here as we're just going to
    // blow it all away anyway.
    if opts.spec.is_empty() {
        let mut progress = Progress::with_style("Cleaning", ProgressStyle::Percentage, config);
        return rm_rf_with_progress(&target_dir.into_path_unlocked(), &mut progress);
    }

    // Clean specific packages.
    let requested_kinds = CompileKind::from_requested_targets(config, &opts.targets)?;
    let target_data = RustcTargetData::new(ws, &requested_kinds)?;
    let (pkg_set, resolve) = ops::resolve_ws(ws)?;
    let prof_dir_name = profiles.get_dir_name();
    let host_layout = Layout::new(ws, None, &prof_dir_name)?;
    // Convert requested kinds to a Vec of layouts.
    let target_layouts: Vec<(CompileKind, Layout)> = requested_kinds
        .into_iter()
        .filter_map(|kind| match kind {
            CompileKind::Target(target) => match Layout::new(ws, Some(target), &prof_dir_name) {
                Ok(layout) => Some(Ok((kind, layout))),
                Err(e) => Some(Err(e)),
            },
            CompileKind::Host => None,
        })
        .collect::<CargoResult<_>>()?;
    // A Vec of layouts. This is a little convoluted because there can only be
    // one host_layout.
    let layouts = if opts.targets.is_empty() {
        vec![(CompileKind::Host, &host_layout)]
    } else {
        target_layouts
            .iter()
            .map(|(kind, layout)| (*kind, layout))
            .collect()
    };
    // Create a Vec that also includes the host for things that need to clean both.
    let layouts_with_host: Vec<(CompileKind, &Layout)> =
        std::iter::once((CompileKind::Host, &host_layout))
            .chain(layouts.iter().map(|(k, l)| (*k, *l)))
            .collect();

    // Cleaning individual rustdoc crates is currently not supported.
    // For example, the search index would need to be rebuilt to fully
    // remove it (otherwise you're left with lots of broken links).
    // Doc tests produce no output.

    // Get Packages for the specified specs.
    let mut pkg_ids = Vec::new();
    for spec_str in opts.spec.iter() {
        // Translate the spec to a Package.
        let spec = PackageIdSpec::parse(spec_str)?;
        if spec.version().is_some() {
            config.shell().warn(&format!(
                "version qualifier in `-p {}` is ignored, \
                cleaning all versions of `{}` found",
                spec_str,
                spec.name()
            ))?;
        }
        if spec.url().is_some() {
            config.shell().warn(&format!(
                "url qualifier in `-p {}` ignored, \
                cleaning all versions of `{}` found",
                spec_str,
                spec.name()
            ))?;
        }
        let matches: Vec<_> = resolve.iter().filter(|id| spec.matches(*id)).collect();
        if matches.is_empty() {
            let mut suggestion = String::new();
            suggestion.push_str(&lev_distance::closest_msg(
                &spec.name(),
                resolve.iter(),
                |id| id.name().as_str(),
            ));
            anyhow::bail!(
                "package ID specification `{}` did not match any packages{}",
                spec,
                suggestion
            );
        }
        pkg_ids.extend(matches);
    }
    let packages = pkg_set.get_many(pkg_ids)?;

    let mut progress = Progress::with_style("Cleaning", ProgressStyle::Ratio, config);
    for (pkg_idx, pkg) in packages.iter().enumerate() {
        let pkg_dir = format!("{}-*", pkg.name());
        progress.tick_now(pkg_idx + 1, packages.len(), &format!(": {}", pkg.name()))?;

        // Clean fingerprints.
        for (_, layout) in &layouts_with_host {
            let dir = escape_glob_path(layout.fingerprint())?;
            rm_rf_glob(&Path::new(&dir).join(&pkg_dir), config)?;
        }

        for target in pkg.targets() {
            if target.is_custom_build() {
                // Get both the build_script_build and the output directory.
                for (_, layout) in &layouts_with_host {
                    let dir = escape_glob_path(layout.build())?;
                    rm_rf_glob(&Path::new(&dir).join(&pkg_dir), config)?;
                }
                continue;
            }
            let crate_name = target.crate_name();
            for &mode in &[
                CompileMode::Build,
                CompileMode::Test,
                CompileMode::Check { test: false },
            ] {
                for (compile_kind, layout) in &layouts {
                    let triple = target_data.short_name(compile_kind);

                    let (file_types, _unsupported) = target_data
                        .info(*compile_kind)
                        .rustc_outputs(mode, target.kind(), triple)?;
                    let (dir, uplift_dir) = match target.kind() {
                        TargetKind::ExampleBin | TargetKind::ExampleLib(..) => {
                            (layout.examples(), Some(layout.examples()))
                        }
                        // Tests/benchmarks are never uplifted.
                        TargetKind::Test | TargetKind::Bench => (layout.deps(), None),
                        _ => (layout.deps(), Some(layout.dest())),
                    };
                    for file_type in file_types {
                        // Some files include a hash in the filename, some don't.
                        let hashed_name = file_type.output_filename(target, Some("*"));
                        let unhashed_name = file_type.output_filename(target, None);
                        let dir_glob = escape_glob_path(dir)?;
                        let dir_glob = Path::new(&dir_glob);

                        rm_rf_glob(&dir_glob.join(&hashed_name), config)?;
                        rm_rf(&dir.join(&unhashed_name), config)?;
                        // Remove dep-info file generated by rustc. It is not tracked in
                        // file_types. It does not have a prefix.
                        let hashed_dep_info = dir_glob.join(format!("{}-*.d", crate_name));
                        rm_rf_glob(&hashed_dep_info, config)?;
                        let unhashed_dep_info = dir.join(format!("{}.d", crate_name));
                        rm_rf(&unhashed_dep_info, config)?;
                        // Remove split-debuginfo files generated by rustc.
                        let split_debuginfo_obj = dir_glob.join(format!("{}.*.o", crate_name));
                        rm_rf_glob(&split_debuginfo_obj, config)?;
                        let split_debuginfo_dwo = dir_glob.join(format!("{}.*.dwo", crate_name));
                        rm_rf_glob(&split_debuginfo_dwo, config)?;

                        // Remove the uplifted copy.
                        if let Some(uplift_dir) = uplift_dir {
                            let uplifted_path = uplift_dir.join(file_type.uplift_filename(target));
                            rm_rf(&uplifted_path, config)?;
                            // Dep-info generated by Cargo itself.
                            let dep_info = uplifted_path.with_extension("d");
                            rm_rf(&dep_info, config)?;
                        }
                    }
                    // TODO: what to do about build_script_build?
                    let dir = escape_glob_path(layout.incremental())?;
                    let incremental = Path::new(&dir).join(format!("{}-*", crate_name));
                    rm_rf_glob(&incremental, config)?;
                }
            }
        }
    }

    Ok(())
}

fn escape_glob_path(pattern: &Path) -> CargoResult<String> {
    let pattern = pattern
        .to_str()
        .ok_or_else(|| anyhow::anyhow!("expected utf-8 path"))?;
    Ok(glob::Pattern::escape(pattern))
}

fn rm_rf_glob(pattern: &Path, config: &Config) -> CargoResult<()> {
    // TODO: Display utf8 warning to user?  Or switch to globset?
    let pattern = pattern
        .to_str()
        .ok_or_else(|| anyhow::anyhow!("expected utf-8 path"))?;
    for path in glob::glob(pattern)? {
        rm_rf(&path?, config)?;
    }
    Ok(())
}

fn rm_rf_with_progress(path: &Path, progress: &mut Progress<'_>) -> CargoResult<()> {
    let num_paths = walkdir::WalkDir::new(path).into_iter().count();
    for (idx, entry) in walkdir::WalkDir::new(path)
        .contents_first(true)
        .into_iter()
        .enumerate()
    {
        progress.tick(std::cmp::min(idx + 1, num_paths), num_paths, "")?;
        if let Ok(entry) = entry {
            if entry.file_type().is_dir() {
                paths::remove_dir(entry.path())?;
            } else {
                paths::remove_file(entry.path())?;
            }
        }
    }
    Ok(())
}

fn rm_rf(path: &Path, config: &Config) -> CargoResult<()> {
    let m = fs::symlink_metadata(path);
    if m.as_ref().map(|s| s.is_dir()).unwrap_or(false) {
        config
            .shell()
            .verbose(|shell| shell.status("Removing", path.display()))?;
        paths::remove_dir_all(path).with_context(|| "could not remove build directory")?;
    } else if m.is_ok() {
        config
            .shell()
            .verbose(|shell| shell.status("Removing", path.display()))?;
        paths::remove_file(path).with_context(|| "failed to remove build artifact")?;
    }
    Ok(())
}
<commit_msg>Print the bar in verbose mode, keep count of the number of cleaned files<commit_after>use crate::core::compiler::{CompileKind, CompileMode, Layout, RustcTargetData};
use crate::core::profiles::Profiles;
use crate::core::{PackageIdSpec, TargetKind, Workspace};
use crate::ops;
use crate::util::errors::CargoResult;
use crate::util::interning::InternedString;
use crate::util::lev_distance;
use crate::util::{Config, Progress, ProgressStyle};

use anyhow::Context as _;
use cargo_util::paths;
use std::fs;
use std::path::Path;

pub struct CleanOptions<'a> {
    pub config: &'a Config,
    /// A list of packages to clean. If empty, everything is cleaned.
    pub spec: Vec<String>,
    /// The target arch triple to clean, or None for the host arch
    pub targets: Vec<String>,
    /// Whether to clean the release directory
    pub profile_specified: bool,
    /// Whether to clean the directory of a certain build profile
    pub requested_profile: InternedString,
    /// Whether to just clean the doc directory
    pub doc: bool,
}

/// Cleans the package's build artifacts.
pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> {
    let mut target_dir = ws.target_dir();
    let config = ws.config();

    // If the doc option is set, we just want to delete the doc directory.
    if opts.doc {
        target_dir = target_dir.join("doc");
        return clean_entire_folder(&target_dir.into_path_unlocked(), &config);
    }

    let profiles = Profiles::new(ws, opts.requested_profile)?;

    if opts.profile_specified {
        // After parsing profiles we know the dir-name of the profile, if a profile
        // was passed from the command line. If so, delete only the directory of
        // that profile.
        let dir_name = profiles.get_dir_name();
        target_dir = target_dir.join(dir_name);
    }

    // If we have a spec, then we need to delete some packages, otherwise, just
    // remove the whole target directory and be done with it!
    //
    // Note that we don't bother grabbing a lock here as we're just going to
    // blow it all away anyway.
    if opts.spec.is_empty() {
        return clean_entire_folder(&target_dir.into_path_unlocked(), &config);
    }

    // Clean specific packages.
    let requested_kinds = CompileKind::from_requested_targets(config, &opts.targets)?;
    let target_data = RustcTargetData::new(ws, &requested_kinds)?;
    let (pkg_set, resolve) = ops::resolve_ws(ws)?;
    let prof_dir_name = profiles.get_dir_name();
    let host_layout = Layout::new(ws, None, &prof_dir_name)?;
    // Convert requested kinds to a Vec of layouts.
    let target_layouts: Vec<(CompileKind, Layout)> = requested_kinds
        .into_iter()
        .filter_map(|kind| match kind {
            CompileKind::Target(target) => match Layout::new(ws, Some(target), &prof_dir_name) {
                Ok(layout) => Some(Ok((kind, layout))),
                Err(e) => Some(Err(e)),
            },
            CompileKind::Host => None,
        })
        .collect::<CargoResult<_>>()?;
    // A Vec of layouts. This is a little convoluted because there can only be
    // one host_layout.
    let layouts = if opts.targets.is_empty() {
        vec![(CompileKind::Host, &host_layout)]
    } else {
        target_layouts
            .iter()
            .map(|(kind, layout)| (*kind, layout))
            .collect()
    };
    // Create a Vec that also includes the host for things that need to clean both.
    let layouts_with_host: Vec<(CompileKind, &Layout)> =
        std::iter::once((CompileKind::Host, &host_layout))
            .chain(layouts.iter().map(|(k, l)| (*k, *l)))
            .collect();

    // Cleaning individual rustdoc crates is currently not supported.
    // For example, the search index would need to be rebuilt to fully
    // remove it (otherwise you're left with lots of broken links).
    // Doc tests produce no output.

    // Get Packages for the specified specs.
    let mut pkg_ids = Vec::new();
    for spec_str in opts.spec.iter() {
        // Translate the spec to a Package.
        let spec = PackageIdSpec::parse(spec_str)?;
        if spec.version().is_some() {
            config.shell().warn(&format!(
                "version qualifier in `-p {}` is ignored, \
                cleaning all versions of `{}` found",
                spec_str,
                spec.name()
            ))?;
        }
        if spec.url().is_some() {
            config.shell().warn(&format!(
                "url qualifier in `-p {}` ignored, \
                cleaning all versions of `{}` found",
                spec_str,
                spec.name()
            ))?;
        }
        let matches: Vec<_> = resolve.iter().filter(|id| spec.matches(*id)).collect();
        if matches.is_empty() {
            let mut suggestion = String::new();
            suggestion.push_str(&lev_distance::closest_msg(
                &spec.name(),
                resolve.iter(),
                |id| id.name().as_str(),
            ));
            anyhow::bail!(
                "package ID specification `{}` did not match any packages{}",
                spec,
                suggestion
            );
        }
        pkg_ids.extend(matches);
    }
    let packages = pkg_set.get_many(pkg_ids)?;

    let mut progress = CleaningPackagesBar::new(config, packages.len());
    for pkg in packages {
        let pkg_dir = format!("{}-*", pkg.name());
        progress.on_cleaning_package(&pkg.name())?;

        // Clean fingerprints.
        for (_, layout) in &layouts_with_host {
            let dir = escape_glob_path(layout.fingerprint())?;
            rm_rf_glob(&Path::new(&dir).join(&pkg_dir), config, &mut progress)?;
        }

        for target in pkg.targets() {
            if target.is_custom_build() {
                // Get both the build_script_build and the output directory.
                for (_, layout) in &layouts_with_host {
                    let dir = escape_glob_path(layout.build())?;
                    rm_rf_glob(&Path::new(&dir).join(&pkg_dir), config, &mut progress)?;
                }
                continue;
            }
            let crate_name = target.crate_name();
            for &mode in &[
                CompileMode::Build,
                CompileMode::Test,
                CompileMode::Check { test: false },
            ] {
                for (compile_kind, layout) in &layouts {
                    let triple = target_data.short_name(compile_kind);

                    let (file_types, _unsupported) = target_data
                        .info(*compile_kind)
                        .rustc_outputs(mode, target.kind(), triple)?;
                    let (dir, uplift_dir) = match target.kind() {
                        TargetKind::ExampleBin | TargetKind::ExampleLib(..) => {
                            (layout.examples(), Some(layout.examples()))
                        }
                        // Tests/benchmarks are never uplifted.
                        TargetKind::Test | TargetKind::Bench => (layout.deps(), None),
                        _ => (layout.deps(), Some(layout.dest())),
                    };
                    for file_type in file_types {
                        // Some files include a hash in the filename, some don't.
                        let hashed_name = file_type.output_filename(target, Some("*"));
                        let unhashed_name = file_type.output_filename(target, None);
                        let dir_glob = escape_glob_path(dir)?;
                        let dir_glob = Path::new(&dir_glob);

                        rm_rf_glob(&dir_glob.join(&hashed_name), config, &mut progress)?;
                        rm_rf(&dir.join(&unhashed_name), config, &mut progress)?;
                        // Remove dep-info file generated by rustc. It is not tracked in
                        // file_types. It does not have a prefix.
                        let hashed_dep_info = dir_glob.join(format!("{}-*.d", crate_name));
                        rm_rf_glob(&hashed_dep_info, config, &mut progress)?;
                        let unhashed_dep_info = dir.join(format!("{}.d", crate_name));
                        rm_rf(&unhashed_dep_info, config, &mut progress)?;
                        // Remove split-debuginfo files generated by rustc.
                        let split_debuginfo_obj = dir_glob.join(format!("{}.*.o", crate_name));
                        rm_rf_glob(&split_debuginfo_obj, config, &mut progress)?;
                        let split_debuginfo_dwo = dir_glob.join(format!("{}.*.dwo", crate_name));
                        rm_rf_glob(&split_debuginfo_dwo, config, &mut progress)?;

                        // Remove the uplifted copy.
                        if let Some(uplift_dir) = uplift_dir {
                            let uplifted_path = uplift_dir.join(file_type.uplift_filename(target));
                            rm_rf(&uplifted_path, config, &mut progress)?;
                            // Dep-info generated by Cargo itself.
                            let dep_info = uplifted_path.with_extension("d");
                            rm_rf(&dep_info, config, &mut progress)?;
                        }
                    }
                    // TODO: what to do about build_script_build?
                    let dir = escape_glob_path(layout.incremental())?;
                    let incremental = Path::new(&dir).join(format!("{}-*", crate_name));
                    rm_rf_glob(&incremental, config, &mut progress)?;
                }
            }
        }
    }

    Ok(())
}

fn escape_glob_path(pattern: &Path) -> CargoResult<String> {
    let pattern = pattern
        .to_str()
        .ok_or_else(|| anyhow::anyhow!("expected utf-8 path"))?;
    Ok(glob::Pattern::escape(pattern))
}

fn rm_rf_glob(
    pattern: &Path,
    config: &Config,
    progress: &mut impl CleaningProgressBar,
) -> CargoResult<()> {
    // TODO: Display utf8 warning to user?  Or switch to globset?
    let pattern = pattern
        .to_str()
        .ok_or_else(|| anyhow::anyhow!("expected utf-8 path"))?;
    for path in glob::glob(pattern)? {
        rm_rf(&path?, config, progress)?;
    }
    Ok(())
}

fn rm_rf(path: &Path, config: &Config, progress: &mut impl CleaningProgressBar) -> CargoResult<()> {
    if fs::symlink_metadata(path).is_err() {
        return Ok(());
    }

    config
        .shell()
        .verbose(|shell| shell.status("Removing", path.display()))?;
    progress.display_now()?;

    for entry in walkdir::WalkDir::new(path).contents_first(true) {
        let entry = entry?;
        progress.on_clean()?;
        if entry.file_type().is_dir() {
            paths::remove_dir(entry.path()).with_context(|| "could not remove build directory")?;
        } else {
            paths::remove_file(entry.path()).with_context(|| "failed to remove build artifact")?;
        }
    }

    Ok(())
}

fn clean_entire_folder(path: &Path, config: &Config) -> CargoResult<()> {
    let num_paths = walkdir::WalkDir::new(path).into_iter().count();
    let mut progress = CleaningFolderBar::new(config, num_paths);
    rm_rf(path, config, &mut progress)
}

trait CleaningProgressBar {
    fn display_now(&mut self) -> CargoResult<()>;
    fn on_clean(&mut self) -> CargoResult<()>;
}

struct CleaningFolderBar<'cfg> {
    bar: Progress<'cfg>,
    max: usize,
    cur: usize,
}

impl<'cfg> CleaningFolderBar<'cfg> {
    fn new(cfg: &'cfg Config, max: usize) -> Self {
        Self {
            bar: Progress::with_style("Cleaning", ProgressStyle::Percentage, cfg),
            max,
            cur: 0,
        }
    }

    fn cur_progress(&self) -> usize {
        std::cmp::min(self.cur, self.max)
    }
}

impl<'cfg> CleaningProgressBar for CleaningFolderBar<'cfg> {
    fn display_now(&mut self) -> CargoResult<()> {
        self.bar.tick_now(self.cur_progress(), self.max, "")
    }

    fn on_clean(&mut self) -> CargoResult<()> {
        self.cur += 1;
        self.bar.tick(self.cur_progress(), self.max, "")
    }
}

struct CleaningPackagesBar<'cfg> {
    bar: Progress<'cfg>,
    max: usize,
    cur: usize,
    num_files_folders_cleaned: usize,
    package_being_cleaned: String,
}

impl<'cfg> CleaningPackagesBar<'cfg> {
    fn new(cfg: &'cfg Config, max: usize) -> Self {
        Self {
            bar: Progress::with_style("Cleaning", ProgressStyle::Ratio, cfg),
            max,
            cur: 0,
            num_files_folders_cleaned: 0,
            package_being_cleaned: String::new(),
        }
    }

    fn on_cleaning_package(&mut self, package: &str) -> CargoResult<()> {
        self.cur += 1;
        self.package_being_cleaned = String::from(package);
        self.bar
            .tick(self.cur_progress(), self.max, &self.format_message())
    }

    fn cur_progress(&self) -> usize {
        std::cmp::min(self.cur, self.max)
    }

    fn format_message(&self) -> String {
        format!(
            ": {}, {} files/folders cleaned",
            self.package_being_cleaned, self.num_files_folders_cleaned
        )
    }
}

impl<'cfg> CleaningProgressBar for CleaningPackagesBar<'cfg> {
    fn display_now(&mut self) -> CargoResult<()> {
        self.bar
            .tick_now(self.cur_progress(), self.max, &self.format_message())
    }

    fn on_clean(&mut self) -> CargoResult<()> {
        self.bar
            .tick(self.cur_progress(), self.max, &self.format_message())?;
        self.num_files_folders_cleaned += 1;
        Ok(())
    }
}
<|endoftext|>


<commit_before>// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::collections::BTreeMap;
use std::fmt::{self, Display};
use std::fs::File;
use std::io::{self, stdin, stdout};
use std::os::unix::io::{AsRawFd, RawFd};
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::Arc;

use devices::{Bus, ProxyDevice, Serial};
use io_jail::Minijail;
use sync::Mutex;
use sys_util::{read_raw_stdin, syslog, EventFd};

use crate::DeviceRegistrationError;

#[derive(Debug)]
pub enum Error {
    CloneEventFd(sys_util::Error),
    FileError(std::io::Error),
    InvalidSerialType(String),
    PathRequired,
    Unimplemented(SerialType),
}

impl Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Error::*;

        match self {
            CloneEventFd(e) => write!(f, "unable to clone an EventFd: {}", e),
            FileError(e) => write!(f, "unable to open/create file: {}", e),
            InvalidSerialType(e) => write!(f, "invalid serial type: {}", e),
            PathRequired => write!(f, "serial device type file requires a path"),
            Unimplemented(e) => write!(f, "serial device type {} not implemented", e.to_string()),
        }
    }
}

/// Enum for possible type of serial devices
#[derive(Debug)]
pub enum SerialType {
    File,
    Stdout,
    Sink,
    Syslog,
    UnixSocket, // NOT IMPLEMENTED
}

impl Display for SerialType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let s = match &self {
            SerialType::File => "File".to_string(),
            SerialType::Stdout => "Stdout".to_string(),
            SerialType::Sink => "Sink".to_string(),
            SerialType::Syslog => "Syslog".to_string(),
            SerialType::UnixSocket => "UnixSocket".to_string(),
        };

        write!(f, "{}", s)
    }
}

impl FromStr for SerialType {
    type Err = Error;
    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
        match s {
            "file" | "File" => Ok(SerialType::File),
            "stdout" | "Stdout" => Ok(SerialType::Stdout),
            "sink" | "Sink" => Ok(SerialType::Sink),
            "syslog" | "Syslog" => Ok(SerialType::Syslog),
            "unix" | "UnixSocket" => Ok(SerialType::UnixSocket),
            _ => Err(Error::InvalidSerialType(s.to_string())),
        }
    }
}

/// Holds the parameters for a serial device
#[derive(Debug)]
pub struct SerialParameters {
    pub type_: SerialType,
    pub path: Option<PathBuf>,
    pub input: Option<PathBuf>,
    pub num: u8,
    pub console: bool,
    pub stdin: bool,
}

impl SerialParameters {
    /// Helper function to create a serial device from the defined parameters.
    ///
    /// # Arguments
    /// * `evt_fd` - eventfd used for interrupt events
    /// * `keep_fds` - Vector of FDs required by this device if it were sandboxed in a child
    ///                process. `evt_fd` will always be added to this vector by this function.
    pub fn create_serial_device(
        &self,
        evt_fd: &EventFd,
        keep_fds: &mut Vec<RawFd>,
    ) -> std::result::Result<Serial, Error> {
        let evt_fd = evt_fd.try_clone().map_err(Error::CloneEventFd)?;
        keep_fds.push(evt_fd.as_raw_fd());
        let input: Option<Box<dyn io::Read + Send>> = if let Some(input_path) = &self.input {
            let input_file = File::open(input_path.as_path()).map_err(Error::FileError)?;
            keep_fds.push(input_file.as_raw_fd());
            Some(Box::new(input_file))
        } else if self.stdin {
            keep_fds.push(stdin().as_raw_fd());
            // This wrapper is used in place of the libstd native version because we don't want
            // buffering for stdin.
            struct StdinWrapper;
            impl io::Read for StdinWrapper {
                fn read(&mut self, out: &mut [u8]) -> io::Result<usize> {
                    read_raw_stdin(out).map_err(|e| e.into())
                }
            }
            Some(Box::new(StdinWrapper))
        } else {
            None
        };
        match self.type_ {
            SerialType::Stdout => {
                keep_fds.push(stdout().as_raw_fd());
                Ok(Serial::new(evt_fd, input, Some(Box::new(stdout()))))
            }
            SerialType::Sink => Ok(Serial::new(evt_fd, input, None)),
            SerialType::Syslog => {
                syslog::push_fds(keep_fds);
                Ok(Serial::new(
                    evt_fd,
                    input,
                    Some(Box::new(syslog::Syslogger::new(
                        syslog::Priority::Info,
                        syslog::Facility::Daemon,
                    ))),
                ))
            }
            SerialType::File => match &self.path {
                Some(path) => {
                    let file = File::create(path.as_path()).map_err(Error::FileError)?;
                    keep_fds.push(file.as_raw_fd());
                    Ok(Serial::new(evt_fd, input, Some(Box::new(file))))
                }
                _ => Err(Error::PathRequired),
            },
            SerialType::UnixSocket => Err(Error::Unimplemented(SerialType::UnixSocket)),
        }
    }
}

// Structure for holding the default configuration of the serial devices.
const DEFAULT_SERIAL_PARAMS: [SerialParameters; 4] = [
    SerialParameters {
        type_: SerialType::Stdout,
        path: None,
        input: None,
        num: 1,
        console: true,
        stdin: true,
    },
    SerialParameters {
        type_: SerialType::Sink,
        path: None,
        input: None,
        num: 2,
        console: false,
        stdin: false,
    },
    SerialParameters {
        type_: SerialType::Sink,
        path: None,
        input: None,
        num: 3,
        console: false,
        stdin: false,
    },
    SerialParameters {
        type_: SerialType::Sink,
        path: None,
        input: None,
        num: 4,
        console: false,
        stdin: false,
    },
];

/// Address for Serial ports in x86
pub const SERIAL_ADDR: [u64; 4] = [0x3f8, 0x2f8, 0x3e8, 0x2e8];

/// String representations of serial devices
const SERIAL_TTY_STRINGS: [&str; 4] = ["ttyS0", "ttyS1", "ttyS2", "ttyS3"];

/// Helper function to get the tty string of a serial device based on the port number. Will default
///  to ttyS0 if an invalid number is given.
pub fn get_serial_tty_string(stdio_serial_num: u8) -> String {
    stdio_serial_num
        .checked_sub(1)
        .and_then(|i| SERIAL_TTY_STRINGS.get(i as usize))
        .unwrap_or(&SERIAL_TTY_STRINGS[0])
        .to_string()
}

/// Adds serial devices to the provided bus based on the serial parameters given. Returns the serial
///  port number and serial device to be used for stdout if defined.
///
/// # Arguments
///
/// * `io_bus` - Bus to add the devices to
/// * `com_evt_1_3` - eventfd for com1 and com3
/// * `com_evt_1_4` - eventfd for com2 and com4
/// * `io_bus` - Bus to add the devices to
/// * `serial_parameters` - definitions of serial parameter configuationis. If a setting is not
///     provided for a port, then it will use the default configuation.
pub fn add_serial_devices(
    io_bus: &mut Bus,
    com_evt_1_3: &EventFd,
    com_evt_2_4: &EventFd,
    serial_parameters: &BTreeMap<u8, SerialParameters>,
    serial_jail: Option<Minijail>,
) -> Result<Option<u8>, DeviceRegistrationError> {
    let mut stdio_serial_num = None;

    for x in 0..=3 {
        let com_evt = match x {
            0 => com_evt_1_3,
            1 => com_evt_2_4,
            2 => com_evt_1_3,
            3 => com_evt_2_4,
            _ => com_evt_1_3,
        };

        let param = serial_parameters
            .get(&(x + 1))
            .unwrap_or(&DEFAULT_SERIAL_PARAMS[x as usize]);

        if param.console {
            stdio_serial_num = Some(x + 1);
        }

        let mut preserved_fds = Vec::new();
        let com = param
            .create_serial_device(&com_evt, &mut preserved_fds)
            .map_err(DeviceRegistrationError::CreateSerialDevice)?;

        match serial_jail.as_ref() {
            Some(jail) => {
                let com = Arc::new(Mutex::new(
                    ProxyDevice::new(com, &jail, preserved_fds)
                        .map_err(DeviceRegistrationError::ProxyDeviceCreation)?,
                ));
                io_bus
                    .insert(com.clone(), SERIAL_ADDR[x as usize], 0x8, false)
                    .unwrap();
            }
            None => {
                let com = Arc::new(Mutex::new(com));
                io_bus
                    .insert(com.clone(), SERIAL_ADDR[x as usize], 0x8, false)
                    .unwrap();
            }
        }
    }

    Ok(stdio_serial_num)
}
<commit_msg>arch: serial: simplify device creation<commit_after>// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::collections::BTreeMap;
use std::fmt::{self, Display};
use std::fs::File;
use std::io::{self, stdin, stdout};
use std::os::unix::io::{AsRawFd, RawFd};
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::Arc;

use devices::{Bus, ProxyDevice, Serial};
use io_jail::Minijail;
use sync::Mutex;
use sys_util::{read_raw_stdin, syslog, EventFd};

use crate::DeviceRegistrationError;

#[derive(Debug)]
pub enum Error {
    CloneEventFd(sys_util::Error),
    FileError(std::io::Error),
    InvalidSerialType(String),
    PathRequired,
    Unimplemented(SerialType),
}

impl Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Error::*;

        match self {
            CloneEventFd(e) => write!(f, "unable to clone an EventFd: {}", e),
            FileError(e) => write!(f, "unable to open/create file: {}", e),
            InvalidSerialType(e) => write!(f, "invalid serial type: {}", e),
            PathRequired => write!(f, "serial device type file requires a path"),
            Unimplemented(e) => write!(f, "serial device type {} not implemented", e.to_string()),
        }
    }
}

/// Enum for possible type of serial devices
#[derive(Debug)]
pub enum SerialType {
    File,
    Stdout,
    Sink,
    Syslog,
    UnixSocket, // NOT IMPLEMENTED
}

impl Display for SerialType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let s = match &self {
            SerialType::File => "File".to_string(),
            SerialType::Stdout => "Stdout".to_string(),
            SerialType::Sink => "Sink".to_string(),
            SerialType::Syslog => "Syslog".to_string(),
            SerialType::UnixSocket => "UnixSocket".to_string(),
        };

        write!(f, "{}", s)
    }
}

impl FromStr for SerialType {
    type Err = Error;
    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
        match s {
            "file" | "File" => Ok(SerialType::File),
            "stdout" | "Stdout" => Ok(SerialType::Stdout),
            "sink" | "Sink" => Ok(SerialType::Sink),
            "syslog" | "Syslog" => Ok(SerialType::Syslog),
            "unix" | "UnixSocket" => Ok(SerialType::UnixSocket),
            _ => Err(Error::InvalidSerialType(s.to_string())),
        }
    }
}

/// Holds the parameters for a serial device
#[derive(Debug)]
pub struct SerialParameters {
    pub type_: SerialType,
    pub path: Option<PathBuf>,
    pub input: Option<PathBuf>,
    pub num: u8,
    pub console: bool,
    pub stdin: bool,
}

impl SerialParameters {
    /// Helper function to create a serial device from the defined parameters.
    ///
    /// # Arguments
    /// * `evt_fd` - eventfd used for interrupt events
    /// * `keep_fds` - Vector of FDs required by this device if it were sandboxed in a child
    ///                process. `evt_fd` will always be added to this vector by this function.
    pub fn create_serial_device(
        &self,
        evt_fd: &EventFd,
        keep_fds: &mut Vec<RawFd>,
    ) -> std::result::Result<Serial, Error> {
        let evt_fd = evt_fd.try_clone().map_err(Error::CloneEventFd)?;
        keep_fds.push(evt_fd.as_raw_fd());
        let input: Option<Box<dyn io::Read + Send>> = if let Some(input_path) = &self.input {
            let input_file = File::open(input_path.as_path()).map_err(Error::FileError)?;
            keep_fds.push(input_file.as_raw_fd());
            Some(Box::new(input_file))
        } else if self.stdin {
            keep_fds.push(stdin().as_raw_fd());
            // This wrapper is used in place of the libstd native version because we don't want
            // buffering for stdin.
            struct StdinWrapper;
            impl io::Read for StdinWrapper {
                fn read(&mut self, out: &mut [u8]) -> io::Result<usize> {
                    read_raw_stdin(out).map_err(|e| e.into())
                }
            }
            Some(Box::new(StdinWrapper))
        } else {
            None
        };
        let output: Option<Box<dyn io::Write + Send>> = match self.type_ {
            SerialType::Stdout => {
                keep_fds.push(stdout().as_raw_fd());
                Some(Box::new(stdout()))
            }
            SerialType::Sink => None,
            SerialType::Syslog => {
                syslog::push_fds(keep_fds);
                Some(Box::new(syslog::Syslogger::new(
                    syslog::Priority::Info,
                    syslog::Facility::Daemon,
                )))
            }
            SerialType::File => match &self.path {
                Some(path) => {
                    let file = File::create(path.as_path()).map_err(Error::FileError)?;
                    keep_fds.push(file.as_raw_fd());
                    Some(Box::new(file))
                }
                None => return Err(Error::PathRequired),
            },
            SerialType::UnixSocket => return Err(Error::Unimplemented(SerialType::UnixSocket)),
        };
        Ok(Serial::new(evt_fd, input, output))
    }
}

// Structure for holding the default configuration of the serial devices.
const DEFAULT_SERIAL_PARAMS: [SerialParameters; 4] = [
    SerialParameters {
        type_: SerialType::Stdout,
        path: None,
        input: None,
        num: 1,
        console: true,
        stdin: true,
    },
    SerialParameters {
        type_: SerialType::Sink,
        path: None,
        input: None,
        num: 2,
        console: false,
        stdin: false,
    },
    SerialParameters {
        type_: SerialType::Sink,
        path: None,
        input: None,
        num: 3,
        console: false,
        stdin: false,
    },
    SerialParameters {
        type_: SerialType::Sink,
        path: None,
        input: None,
        num: 4,
        console: false,
        stdin: false,
    },
];

/// Address for Serial ports in x86
pub const SERIAL_ADDR: [u64; 4] = [0x3f8, 0x2f8, 0x3e8, 0x2e8];

/// String representations of serial devices
const SERIAL_TTY_STRINGS: [&str; 4] = ["ttyS0", "ttyS1", "ttyS2", "ttyS3"];

/// Helper function to get the tty string of a serial device based on the port number. Will default
///  to ttyS0 if an invalid number is given.
pub fn get_serial_tty_string(stdio_serial_num: u8) -> String {
    stdio_serial_num
        .checked_sub(1)
        .and_then(|i| SERIAL_TTY_STRINGS.get(i as usize))
        .unwrap_or(&SERIAL_TTY_STRINGS[0])
        .to_string()
}

/// Adds serial devices to the provided bus based on the serial parameters given. Returns the serial
///  port number and serial device to be used for stdout if defined.
///
/// # Arguments
///
/// * `io_bus` - Bus to add the devices to
/// * `com_evt_1_3` - eventfd for com1 and com3
/// * `com_evt_1_4` - eventfd for com2 and com4
/// * `io_bus` - Bus to add the devices to
/// * `serial_parameters` - definitions of serial parameter configuationis. If a setting is not
///     provided for a port, then it will use the default configuation.
pub fn add_serial_devices(
    io_bus: &mut Bus,
    com_evt_1_3: &EventFd,
    com_evt_2_4: &EventFd,
    serial_parameters: &BTreeMap<u8, SerialParameters>,
    serial_jail: Option<Minijail>,
) -> Result<Option<u8>, DeviceRegistrationError> {
    let mut stdio_serial_num = None;

    for x in 0..=3 {
        let com_evt = match x {
            0 => com_evt_1_3,
            1 => com_evt_2_4,
            2 => com_evt_1_3,
            3 => com_evt_2_4,
            _ => com_evt_1_3,
        };

        let param = serial_parameters
            .get(&(x + 1))
            .unwrap_or(&DEFAULT_SERIAL_PARAMS[x as usize]);

        if param.console {
            stdio_serial_num = Some(x + 1);
        }

        let mut preserved_fds = Vec::new();
        let com = param
            .create_serial_device(&com_evt, &mut preserved_fds)
            .map_err(DeviceRegistrationError::CreateSerialDevice)?;

        match serial_jail.as_ref() {
            Some(jail) => {
                let com = Arc::new(Mutex::new(
                    ProxyDevice::new(com, &jail, preserved_fds)
                        .map_err(DeviceRegistrationError::ProxyDeviceCreation)?,
                ));
                io_bus
                    .insert(com.clone(), SERIAL_ADDR[x as usize], 0x8, false)
                    .unwrap();
            }
            None => {
                let com = Arc::new(Mutex::new(com));
                io_bus
                    .insert(com.clone(), SERIAL_ADDR[x as usize], 0x8, false)
                    .unwrap();
            }
        }
    }

    Ok(stdio_serial_num)
}
<|endoftext|>


<commit_before>use std::env;
use std::fs::{self, DirEntry, File};
use std::io::{self, Read};
use std::path::{Path, PathBuf};

pub fn get_template_source(tpl_file: &str) -> String {
    let root = env::var("CARGO_MANIFEST_DIR").unwrap();
    let mut path = PathBuf::from(root);
    path.push("templates");
    path.push(Path::new(tpl_file));
    let mut f = File::open(path).unwrap();
    let mut s = String::new();
    f.read_to_string(&mut s).unwrap();
    s
}

fn visit_dirs(dir: &Path, cb: &Fn(&DirEntry)) -> io::Result<()> {
    if dir.is_dir() {
        for entry in try!(fs::read_dir(dir)) {
            let entry = try!(entry);
            let path = entry.path();
            if path.is_dir() {
                try!(visit_dirs(&path, cb));
            } else {
                cb(&entry);
            }
        }
    }
    Ok(())
}

pub fn rerun_if_templates_changed() {
    let root = env::var("CARGO_MANIFEST_DIR").unwrap();
    visit_dirs(&Path::new(&root).join("templates"), &|e: &DirEntry| {
        println!("cargo:rerun-if-changed={}", e.path().to_str().unwrap());
    }).unwrap();
}
<commit_msg>Extract template_dir() helper method<commit_after>use std::env;
use std::fs::{self, DirEntry, File};
use std::io::{self, Read};
use std::path::{Path, PathBuf};

fn template_dir() -> PathBuf {
    let mut path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
    path.push("templates");
    path
}

pub fn get_template_source(tpl_file: &str) -> String {
    let mut path = template_dir();
    path.push(Path::new(tpl_file));
    let mut f = File::open(path).unwrap();
    let mut s = String::new();
    f.read_to_string(&mut s).unwrap();
    s
}

fn visit_dirs(dir: &Path, cb: &Fn(&DirEntry)) -> io::Result<()> {
    if dir.is_dir() {
        for entry in try!(fs::read_dir(dir)) {
            let entry = try!(entry);
            let path = entry.path();
            if path.is_dir() {
                try!(visit_dirs(&path, cb));
            } else {
                cb(&entry);
            }
        }
    }
    Ok(())
}

pub fn rerun_if_templates_changed() {
    visit_dirs(&template_dir(), &|e: &DirEntry| {
        println!("cargo:rerun-if-changed={}", e.path().to_str().unwrap());
    }).unwrap();
}
<|endoftext|>


<commit_before>extern crate csv;
extern crate rustc_serialize;

mod distance;

use std::fs::File;
use csv::Reader;
use distance::*;
use std::collections::HashMap;

#[derive(RustcDecodable)]
struct User {
    id: u32,
    location: String,
    age: u32,
}

#[derive(RustcDecodable)]
struct Book {
    isbn: String,
    title: String,
    author: String,
    pub_year: u32,
    publisher: String,
}

#[derive(RustcDecodable)]
struct Rating {
    user_id: u32,
    book_isbn: String,
    rating: u32,
}

fn init_db(path: &str) -> Reader<File> {
    let mut rdr = csv::Reader::from_file(path).unwrap();
    rdr
}

// fn get_vector(db: &Value, name: &str) -> Vec<f32> {
//     let mut vec: Vec<f32> = vec![];
//     for elem in db["scores"][name].as_array().unwrap().iter() {
//         vec.push(elem.as_f64().unwrap() as f32);
//     }

//     vec
// }

// fn evaluate_movie_ratings() {
//     let mut rdr = init_db("./data/Movie_Ratings.csv");
// }

fn load_book_ratings() {
    /*********************LOAD USERS*********************/
    // let mut rdr = init_db("./data/BX-Dump/BX-Users.csv").has_headers(false);

    // let mut i = 0;
    // for record in rdr.decode() {
    //     if i > 10 {
    //         break;
    //     }
    //     let user: User = record.unwrap();
    //     println!("{}: {} [{}]", user.id, user.location, user.age);

    //     i += 1;
    // }
    /****************************************************/

    let mut rdr = init_db("./data/BX-Dump/BX-Book-Ratings.csv").has_headers(false);

    let mut i = 0;
    let mut ratings: HashMap<u32, HashMap<String, u32>> = HashMap::new();

    for record in rdr.decode() {
        if i > 10 {
            break;
        }
        i += 1;

        let mut rating: Rating = record.unwrap();

        let mut user_ratings = HashMap::new();
        user_ratings.insert(rating.book_isbn, rating.rating);

        ratings.insert(rating.user_id, user_ratings);
    }

    i = 0;

    for record in rdr.decode() {
        if i > 10 {
            break;
        }
        i += 1;

        let mut rating: Rating = record.unwrap();

        print!("{}: ", rating.user_id);

        let isbn = String::from("0100000X");
        let rate = 7;

        match ratings.get_mut(&rating.user_id) {
            Some(mut usr_rtngs) => {
                // let variable: () = usr_rtngs;
                &usr_rtngs.insert(isbn, rate);
                for (book_name, &rating_num) in usr_rtngs.iter() {
                    println!("{} -> {}", book_name, rating_num);
                }
                // usr_rtngs.insert(String::from("AAAAAAA"), 9);
                // println!("Found");
            }
            None => {
                println!("Not found");
            }
        }
    }
}

fn main() {
    load_book_ratings();
    println!("Hello, world!");
}
<commit_msg>Bad code but working, now inserting all of them<commit_after>extern crate csv;
extern crate rustc_serialize;

mod distance;

use std::fs::File;
use csv::Reader;
use distance::*;
use std::collections::HashMap;

#[derive(RustcDecodable)]
struct User {
    id: u32,
    location: String,
    age: u32,
}

#[derive(RustcDecodable)]
struct Book {
    isbn: String,
    title: String,
    author: String,
    pub_year: u32,
    publisher: String,
}

#[derive(RustcDecodable)]
struct Rating {
    user_id: u32,
    book_isbn: String,
    rating: u32,
}

fn init_db(path: &str) -> Reader<File> {
    let mut rdr = csv::Reader::from_file(path).unwrap();
    rdr
}

// fn get_vector(db: &Value, name: &str) -> Vec<f32> {
//     let mut vec: Vec<f32> = vec![];
//     for elem in db["scores"][name].as_array().unwrap().iter() {
//         vec.push(elem.as_f64().unwrap() as f32);
//     }

//     vec
// }

// fn evaluate_movie_ratings() {
//     let mut rdr = init_db("./data/Movie_Ratings.csv");
// }

fn load_book_ratings() {
    /*********************LOAD USERS*********************/
    // let mut rdr = init_db("./data/BX-Dump/BX-Users.csv").has_headers(false);

    // let mut i = 0;
    // for record in rdr.decode() {
    //     if i > 10 {
    //         break;
    //     }
    //     let user: User = record.unwrap();
    //     println!("{}: {} [{}]", user.id, user.location, user.age);

    //     i += 1;
    // }
    /****************************************************/

    let mut rdr = init_db("./data/BX-Dump/BX-Book-Ratings.csv").has_headers(false);

    let mut i = 0;
    let mut ratings: HashMap<u32, HashMap<String, u32>> = HashMap::new();

    for record in rdr.decode() {
        if i > 100 {
            break;
        }
        i += 1;

        let mut rating: Rating = record.unwrap();
        let mut found = false;
        let mut new_user_ratings = HashMap::new();

        match ratings.get_mut(&rating.user_id) {
            Some(mut usr_rtngs) => {
                &usr_rtngs.insert(rating.book_isbn, rating.rating);
                found = true;
            }
            None => {
                new_user_ratings.insert(rating.book_isbn, rating.rating);
            }
        }
        if !found {
            ratings.insert(rating.user_id, new_user_ratings);
        }
    }

    i = 0;

    for record in rdr.decode() {
        if i > 100 {
            break;
        }
        i += 1;

        let mut rating: Rating = record.unwrap();

        print!("{}: ", rating.user_id);

        // let isbn = String::from("0100000X");
        // let rate = 7;

        match ratings.get_mut(&rating.user_id) {
            Some(mut usr_rtngs) => {
                // let variable: () = usr_rtngs;
                // &usr_rtngs.insert(isbn, rate);
                for (book_name, &rating_num) in usr_rtngs.iter() {
                    println!("{} -> {}", book_name, rating_num);
                }
                // usr_rtngs.insert(String::from("AAAAAAA"), 9);
                // println!("Found");
            }
            None => {
                println!("Not found");
            }
        }
    }
}

fn main() {
    load_book_ratings();
    println!("Hello, world!");
}
<|endoftext|>


<commit_before>use std::io::{Result, Write};

use codegen::general::version_condition;
use config::gobjects::GObject;
use env::Env;
use library;
use nameutil;
use super::ffi_type::*;
use traits::*;
use regex::Regex;

//used as glib:get-type in GLib-2.0.gir
const INTERN: &'static str = "intern";

lazy_static! {
    static ref DEFAULT_OBJ: GObject = Default::default();
}

pub fn generate_records_funcs(
    w: &mut Write,
    env: &Env,
    records: &[&library::Record],
) -> Result<()> {
    let intern_str = INTERN.to_string();
    for record in records {
        // Nested structs tend to generate a duplicate function name,
        // this catches the nested struct and ignores function gen
        let s_regex = Regex::new(r"^\w+_s\d+$").unwrap();
        if !s_regex.is_match(&record.name) {
            let name = format!("{}.{}", env.config.library_name, record.name);
            let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
            let glib_get_type = record.glib_get_type.as_ref().unwrap_or(&intern_str);
            try!(generate_object_funcs(
                w,
                env,
                obj,
                &record.c_type,
                glib_get_type,
                &record.functions,
            ));
        }
    }

    Ok(())
}

pub fn generate_classes_funcs(w: &mut Write, env: &Env, classes: &[&library::Class]) -> Result<()> {
    for klass in classes {
        let name = format!("{}.{}", env.config.library_name, klass.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        try!(generate_object_funcs(
            w,
            env,
            obj,
            &klass.c_type,
            &klass.glib_get_type,
            &klass.functions,
        ));
    }

    Ok(())
}

pub fn generate_bitfields_funcs(
    w: &mut Write,
    env: &Env,
    bitfields: &[&library::Bitfield],
) -> Result<()> {
    let intern_str = INTERN.to_string();
    for bitfield in bitfields {
        let name = format!("{}.{}", env.config.library_name, bitfield.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        let glib_get_type = bitfield.glib_get_type.as_ref().unwrap_or(&intern_str);
        try!(generate_object_funcs(
            w,
            env,
            obj,
            &bitfield.c_type,
            &glib_get_type,
            &bitfield.functions,
        ));
    }

    Ok(())
}

pub fn generate_enums_funcs(
    w: &mut Write,
    env: &Env,
    enums: &[&library::Enumeration],
) -> Result<()> {
    let intern_str = INTERN.to_string();
    for en in enums {
        let name = format!("{}.{}", env.config.library_name, en.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        let glib_get_type = en.glib_get_type.as_ref().unwrap_or(&intern_str);
        try!(generate_object_funcs(
            w,
            env,
            obj,
            &en.c_type,
            &glib_get_type,
            &en.functions,
        ));
    }

    Ok(())
}

pub fn generate_unions_funcs(w: &mut Write, env: &Env, unions: &[&library::Union]) -> Result<()> {
    let intern_str = INTERN.to_string();
    for union in unions {
        let c_type = match union.c_type {
            Some(ref x) => x,
            None => return Ok(()),
        };
        let name = format!("{}.{}", env.config.library_name, union.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        let glib_get_type = union.glib_get_type.as_ref().unwrap_or(&intern_str);
        try!(generate_object_funcs(
            w,
            env,
            obj,
            c_type,
            &glib_get_type,
            &union.functions,
        ));
    }

    Ok(())
}

pub fn generate_interfaces_funcs(
    w: &mut Write,
    env: &Env,
    interfaces: &[&library::Interface],
) -> Result<()> {
    for interface in interfaces {
        let name = format!("{}.{}", env.config.library_name, interface.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        try!(generate_object_funcs(
            w,
            env,
            obj,
            &interface.c_type,
            &interface.glib_get_type,
            &interface.functions,
        ));
    }

    Ok(())
}

pub fn generate_other_funcs(
    w: &mut Write,
    env: &Env,
    functions: &[library::Function],
) -> Result<()> {
    let name = format!("{}.*", env.config.library_name);
    let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
    generate_object_funcs(w, env, obj, "Other functions", INTERN, functions)
}

fn generate_object_funcs(
    w: &mut Write,
    env: &Env,
    obj: &GObject,
    c_type: &str,
    glib_get_type: &str,
    functions: &[library::Function],
) -> Result<()> {
    let write_get_type = glib_get_type != INTERN;
    if write_get_type || !functions.is_empty() {
        try!(writeln!(w, ""));
        try!(writeln!(
            w,
            "    //========================================================================="
        ));
        try!(writeln!(w, "    // {}", c_type));
        try!(writeln!(
            w,
            "    //========================================================================="
        ));
    }
    if write_get_type {
        try!(writeln!(w, "    pub fn {}() -> GType;", glib_get_type));
    }

    for func in functions {
        let configured_functions = obj.functions.matched(&func.name);
        if configured_functions.iter().any(|f| f.ignore) {
            continue;
        }
        let is_windows_utf8 = configured_functions.iter().any(|f| f.is_windows_utf8);

        let (commented, sig) = function_signature(env, func, false);
        let comment = if commented { "//" } else { "" };
        try!(version_condition(w, env, func.version, commented, 1));
        let name = func.c_identifier.as_ref().unwrap();
        // since we work with gir-files from Linux, some function names need to be adjusted
        if is_windows_utf8 {
            try!(writeln!(w, "    {}#[cfg(windows)]", comment));
            try!(writeln!(w, "    {}pub fn {}_utf8{};", comment, name, sig));
            try!(version_condition(w, env, func.version, commented, 1));
        }
        try!(writeln!(w, "    {}pub fn {}{};", comment, name, sig));
    }

    Ok(())
}

pub fn generate_callbacks(
    w: &mut Write,
    env: &Env,
    callbacks: &[&library::Function],
) -> Result<()> {
    if !callbacks.is_empty() {
        try!(writeln!(w, "// Callbacks"));
    }
    for func in callbacks {
        let (commented, sig) = function_signature(env, func, true);
        let comment = if commented { "//" } else { "" };
        try!(writeln!(
            w,
            "{}pub type {} = Option<unsafe extern \"C\" fn{}>;",
            comment,
            func.c_identifier.as_ref().unwrap(),
            sig
        ));
    }
    if !callbacks.is_empty() {
        try!(writeln!(w, ""));
    }

    Ok(())
}

pub fn function_signature(env: &Env, func: &library::Function, bare: bool) -> (bool, String) {
    let (mut commented, ret_str) = function_return_value(env, func);

    let mut parameter_strs: Vec<String> = Vec::new();
    for par in &func.parameters {
        let (c, par_str) = function_parameter(env, par, bare);
        parameter_strs.push(par_str);
        if c {
            commented = true;
        }
    }

    (
        commented,
        format!("({}){}", parameter_strs.join(", "), ret_str),
    )
}

fn function_return_value(env: &Env, func: &library::Function) -> (bool, String) {
    if func.ret.typ == Default::default() {
        return (false, String::new());
    }
    let ffi_type = ffi_type(env, func.ret.typ, &func.ret.c_type);
    let commented = ffi_type.is_err();
    (commented, format!(" -> {}", ffi_type.into_string()))
}

fn function_parameter(env: &Env, par: &library::Parameter, bare: bool) -> (bool, String) {
    if let library::Type::Fundamental(library::Fundamental::VarArgs) = *env.library.type_(par.typ) {
        return (false, "...".into());
    }
    let ffi_type = ffi_type(env, par.typ, &par.c_type);
    let commented = ffi_type.is_err();
    let res = if bare {
        ffi_type.into_string()
    } else {
        format!(
            "{}: {}",
            nameutil::mangle_keywords(&*par.name),
            ffi_type.into_string()
        )
    };
    (commented, res)
}
<commit_msg>Don't create double-references but use the references directly<commit_after>use std::io::{Result, Write};

use codegen::general::version_condition;
use config::gobjects::GObject;
use env::Env;
use library;
use nameutil;
use super::ffi_type::*;
use traits::*;
use regex::Regex;

//used as glib:get-type in GLib-2.0.gir
const INTERN: &'static str = "intern";

lazy_static! {
    static ref DEFAULT_OBJ: GObject = Default::default();
}

pub fn generate_records_funcs(
    w: &mut Write,
    env: &Env,
    records: &[&library::Record],
) -> Result<()> {
    let intern_str = INTERN.to_string();
    for record in records {
        // Nested structs tend to generate a duplicate function name,
        // this catches the nested struct and ignores function gen
        let s_regex = Regex::new(r"^\w+_s\d+$").unwrap();
        if !s_regex.is_match(&record.name) {
            let name = format!("{}.{}", env.config.library_name, record.name);
            let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
            let glib_get_type = record.glib_get_type.as_ref().unwrap_or(&intern_str);
            try!(generate_object_funcs(
                w,
                env,
                obj,
                &record.c_type,
                glib_get_type,
                &record.functions,
            ));
        }
    }

    Ok(())
}

pub fn generate_classes_funcs(w: &mut Write, env: &Env, classes: &[&library::Class]) -> Result<()> {
    for klass in classes {
        let name = format!("{}.{}", env.config.library_name, klass.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        try!(generate_object_funcs(
            w,
            env,
            obj,
            &klass.c_type,
            &klass.glib_get_type,
            &klass.functions,
        ));
    }

    Ok(())
}

pub fn generate_bitfields_funcs(
    w: &mut Write,
    env: &Env,
    bitfields: &[&library::Bitfield],
) -> Result<()> {
    let intern_str = INTERN.to_string();
    for bitfield in bitfields {
        let name = format!("{}.{}", env.config.library_name, bitfield.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        let glib_get_type = bitfield.glib_get_type.as_ref().unwrap_or(&intern_str);
        try!(generate_object_funcs(
            w,
            env,
            obj,
            &bitfield.c_type,
            glib_get_type,
            &bitfield.functions,
        ));
    }

    Ok(())
}

pub fn generate_enums_funcs(
    w: &mut Write,
    env: &Env,
    enums: &[&library::Enumeration],
) -> Result<()> {
    let intern_str = INTERN.to_string();
    for en in enums {
        let name = format!("{}.{}", env.config.library_name, en.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        let glib_get_type = en.glib_get_type.as_ref().unwrap_or(&intern_str);
        try!(generate_object_funcs(
            w,
            env,
            obj,
            &en.c_type,
            glib_get_type,
            &en.functions,
        ));
    }

    Ok(())
}

pub fn generate_unions_funcs(w: &mut Write, env: &Env, unions: &[&library::Union]) -> Result<()> {
    let intern_str = INTERN.to_string();
    for union in unions {
        let c_type = match union.c_type {
            Some(ref x) => x,
            None => return Ok(()),
        };
        let name = format!("{}.{}", env.config.library_name, union.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        let glib_get_type = union.glib_get_type.as_ref().unwrap_or(&intern_str);
        try!(generate_object_funcs(
            w,
            env,
            obj,
            c_type,
            glib_get_type,
            &union.functions,
        ));
    }

    Ok(())
}

pub fn generate_interfaces_funcs(
    w: &mut Write,
    env: &Env,
    interfaces: &[&library::Interface],
) -> Result<()> {
    for interface in interfaces {
        let name = format!("{}.{}", env.config.library_name, interface.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        try!(generate_object_funcs(
            w,
            env,
            obj,
            &interface.c_type,
            &interface.glib_get_type,
            &interface.functions,
        ));
    }

    Ok(())
}

pub fn generate_other_funcs(
    w: &mut Write,
    env: &Env,
    functions: &[library::Function],
) -> Result<()> {
    let name = format!("{}.*", env.config.library_name);
    let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
    generate_object_funcs(w, env, obj, "Other functions", INTERN, functions)
}

fn generate_object_funcs(
    w: &mut Write,
    env: &Env,
    obj: &GObject,
    c_type: &str,
    glib_get_type: &str,
    functions: &[library::Function],
) -> Result<()> {
    let write_get_type = glib_get_type != INTERN;
    if write_get_type || !functions.is_empty() {
        try!(writeln!(w, ""));
        try!(writeln!(
            w,
            "    //========================================================================="
        ));
        try!(writeln!(w, "    // {}", c_type));
        try!(writeln!(
            w,
            "    //========================================================================="
        ));
    }
    if write_get_type {
        try!(writeln!(w, "    pub fn {}() -> GType;", glib_get_type));
    }

    for func in functions {
        let configured_functions = obj.functions.matched(&func.name);
        if configured_functions.iter().any(|f| f.ignore) {
            continue;
        }
        let is_windows_utf8 = configured_functions.iter().any(|f| f.is_windows_utf8);

        let (commented, sig) = function_signature(env, func, false);
        let comment = if commented { "//" } else { "" };
        try!(version_condition(w, env, func.version, commented, 1));
        let name = func.c_identifier.as_ref().unwrap();
        // since we work with gir-files from Linux, some function names need to be adjusted
        if is_windows_utf8 {
            try!(writeln!(w, "    {}#[cfg(windows)]", comment));
            try!(writeln!(w, "    {}pub fn {}_utf8{};", comment, name, sig));
            try!(version_condition(w, env, func.version, commented, 1));
        }
        try!(writeln!(w, "    {}pub fn {}{};", comment, name, sig));
    }

    Ok(())
}

pub fn generate_callbacks(
    w: &mut Write,
    env: &Env,
    callbacks: &[&library::Function],
) -> Result<()> {
    if !callbacks.is_empty() {
        try!(writeln!(w, "// Callbacks"));
    }
    for func in callbacks {
        let (commented, sig) = function_signature(env, func, true);
        let comment = if commented { "//" } else { "" };
        try!(writeln!(
            w,
            "{}pub type {} = Option<unsafe extern \"C\" fn{}>;",
            comment,
            func.c_identifier.as_ref().unwrap(),
            sig
        ));
    }
    if !callbacks.is_empty() {
        try!(writeln!(w, ""));
    }

    Ok(())
}

pub fn function_signature(env: &Env, func: &library::Function, bare: bool) -> (bool, String) {
    let (mut commented, ret_str) = function_return_value(env, func);

    let mut parameter_strs: Vec<String> = Vec::new();
    for par in &func.parameters {
        let (c, par_str) = function_parameter(env, par, bare);
        parameter_strs.push(par_str);
        if c {
            commented = true;
        }
    }

    (
        commented,
        format!("({}){}", parameter_strs.join(", "), ret_str),
    )
}

fn function_return_value(env: &Env, func: &library::Function) -> (bool, String) {
    if func.ret.typ == Default::default() {
        return (false, String::new());
    }
    let ffi_type = ffi_type(env, func.ret.typ, &func.ret.c_type);
    let commented = ffi_type.is_err();
    (commented, format!(" -> {}", ffi_type.into_string()))
}

fn function_parameter(env: &Env, par: &library::Parameter, bare: bool) -> (bool, String) {
    if let library::Type::Fundamental(library::Fundamental::VarArgs) = *env.library.type_(par.typ) {
        return (false, "...".into());
    }
    let ffi_type = ffi_type(env, par.typ, &par.c_type);
    let commented = ffi_type.is_err();
    let res = if bare {
        ffi_type.into_string()
    } else {
        format!(
            "{}: {}",
            nameutil::mangle_keywords(&*par.name),
            ffi_type.into_string()
        )
    };
    (commented, res)
}
<|endoftext|>