<commit_before>use std::{
    env,
    ffi::OsString,
    fs::{copy, create_dir_all, read_to_string, remove_dir_all, write, OpenOptions},
    io::{BufWriter, Write},
    path::{Path, PathBuf},
};

use binary::{http_utils::download_file_sync, Binary, BinaryTrait};
use buildpack::{
    eyre,
    hash_utils::str_sha256_hex,
    libcnb::{
        self,
        build::{BuildContext, BuildResult, BuildResultBuilder},
        data::{build_plan::BuildPlan, layer_content_metadata::LayerTypes, layer_name},
        detect::{DetectContext, DetectResult, DetectResultBuilder},
        generic::{GenericMetadata, GenericPlatform},
        layer::{ExistingLayerStrategy, Layer, LayerResult, LayerResultBuilder},
        layer_env::{LayerEnv, ModificationBehavior, Scope},
        Buildpack,
    },
    maplit::hashmap,
    tracing, BuildpackTrait, LayerOptions,
};
use serde::{Deserialize, Serialize};
use utils::vec_string;

pub struct AptBuildpack;

impl BuildpackTrait for AptBuildpack {
    fn toml() -> &'static str {
        include_str!("../buildpack.toml")
    }
}

const APTFILE: &str = "Aptfile";

impl Buildpack for AptBuildpack {
    type Platform = GenericPlatform;
    type Metadata = GenericMetadata;
    type Error = eyre::Report;

    fn detect(&self, _context: DetectContext<Self>) -> libcnb::Result<DetectResult, Self::Error> {
        // Detect `Aptfile`
        let aptfile = PathBuf::from(APTFILE);

        // Get the Linux release for reuse below
        let linux_flavour = sys_info::linux_os_release().ok();

        // Fail if no `Aptfile` or not on Ubuntu Linux
        let pass = if !aptfile.exists() {
            false
        } else if env::consts::OS != "linux" {
            tracing::warn!("Aptfile detected but will be ignored because not on Linux");
            false
        } else if linux_flavour
            .as_ref()
            .map_or_else(|| "".to_string(), |rel| rel.id().to_string())
            != "ubuntu"
        {
            tracing::warn!("Aptfile detected but will be ignored because not on Ubuntu Linux");
            false
        } else {
            true
        };
        if !pass {
            //return DetectResultBuilder::fail().build();
        }

        let version = linux_flavour
            .expect("Should have returned by now if not on Linux")
            .version_codename
            .expect("Should have an Ubuntu version codename");

        let (require, provide) = Self::require_and_provide(
            "apt_packages",
            APTFILE,
            format!("Install `apt` packages for Ubuntu '{}'", version).trim(),
            Some(hashmap! {
                "version" => version,
                "file" => APTFILE.to_string()
            }),
        );

        let mut build_plan = BuildPlan::new();
        build_plan.requires = vec![require];
        build_plan.provides = vec![provide];
        DetectResultBuilder::pass().build_plan(build_plan).build()
    }

    fn build(&self, context: BuildContext<Self>) -> libcnb::Result<BuildResult, Self::Error> {
        let entries = self.buildpack_plan_entries(&context.buildpack_plan);

        if let Some(options) = entries.get("apt_packages") {
            context.handle_layer(
                layer_name!("apt_packages"),
                AptPackagesLayer::new(options, &context.app_dir),
            )?;
        }

        BuildResultBuilder::new().build()
    }
}

#[derive(Clone, Deserialize, Serialize)]
struct AptPackagesLayer {
    /// The version of Ubuntu that packages will be installed for e.g `bionic`, `focal`
    version: String,

    /// The path to the `Aptfile` (or similar name) that specifies packages to be installed
    file: Option<PathBuf>,

    /// A list of package names, or deb URLs to be installed
    ///
    /// Usually instead of an `Aptfile` but can be specified in addition to it
    packages: Vec<String>,

    /// A list of repos to be used
    ///
    /// Usually instead of `:repo:` entries in an `Aptfile` but can be specified in addition to it
    repos: Vec<String>,
}

impl AptPackagesLayer {
    fn new(options: &LayerOptions, app_path: &Path) -> Self {
        let version = match options.get("version") {
            Some(version) => version.to_string(),
            None => sys_info::linux_os_release()
                .ok()
                .and_then(|info| info.version_codename)
                .unwrap_or_default(),
        };

        let file = options.get("file").map(PathBuf::from);

        // Split `Aptfile` into  packages and repos
        let mut repos = Vec::new();
        let mut packages = match &file {
            Some(file) => read_to_string(app_path.join(file))
                .unwrap_or_default()
                .lines()
                .filter_map(|line| {
                    let line = line.trim();
                    if line.is_empty() || line.starts_with('#') {
                        None
                    } else if let Some(repo) = line.strip_prefix(":repo:") {
                        repos.push(repo.to_string());
                        None
                    } else {
                        Some(line.to_string())
                    }
                })
                .collect(),
            None => Vec::new(),
        };

        // Add any other packages
        if let Some(list) = options.get("packages") {
            packages.append(&mut list.split(',').map(|pkg| pkg.trim().to_string()).collect());
        }

        // Add any other repos
        if let Some(list) = options.get("repos") {
            repos.append(&mut list.split(',').map(|pkg| pkg.trim().to_string()).collect());
        }

        Self {
            version,
            file,
            packages,
            repos,
        }
    }
}

impl Layer for AptPackagesLayer {
    type Buildpack = AptBuildpack;
    type Metadata = AptPackagesLayer;

    fn types(&self) -> LayerTypes {
        LayerTypes {
            build: true,
            launch: true,
            cache: true,
        }
    }

    fn existing_layer_strategy(
        &self,
        _context: &BuildContext<Self::Buildpack>,
        layer_data: &libcnb::layer::LayerData<Self::Metadata>,
    ) -> Result<libcnb::layer::ExistingLayerStrategy, <Self::Buildpack as Buildpack>::Error> {
        let metadata = &layer_data.content_metadata.metadata;
        let strategy = if self.version != metadata.version {
            tracing::info!(
                "Existing `apt_packages` layer is for different Ubuntu version; will recreate",
            );
            ExistingLayerStrategy::Recreate
        } else if self.packages != metadata.packages || self.repos != metadata.repos {
            tracing::info!(
                "Existing `apt_packages` layer has different packages and/or repos; will update"
            );
            ExistingLayerStrategy::Update
        } else {
            tracing::info!("Existing `apt_packages` layer has no changes; will keep",);
            ExistingLayerStrategy::Keep
        };
        Ok(strategy)
    }

    fn create(
        &self,
        context: &BuildContext<Self::Buildpack>,
        layer_path: &Path,
    ) -> Result<LayerResult<Self::Metadata>, eyre::Report> {
        tracing::info!("Creating `apt_packages` layer");
        self.install(context, layer_path)
    }

    fn update(
        &self,
        context: &BuildContext<Self::Buildpack>,
        layer_data: &libcnb::layer::LayerData<Self::Metadata>,
    ) -> Result<LayerResult<Self::Metadata>, <Self::Buildpack as Buildpack>::Error> {
        tracing::info!("Updating `apt_packages` layer");
        self.install(context, &layer_data.path)
    }
}

impl AptPackagesLayer {
    fn install(
        &self,
        _context: &BuildContext<AptBuildpack>,
        layer_path: &Path,
    ) -> Result<LayerResult<AptPackagesLayer>, eyre::Report> {
        let layer_path = &layer_path.canonicalize()?;

        tracing::info!("Installing apt packages: {}", self.packages.join(", "));

        let apt_cache_dir = layer_path.join("cache");
        let apt_archives_dir = apt_cache_dir.join("archives");
        let apt_state_dir = layer_path.join("state");
        let apt_sources_dir = layer_path.join("sources");
        let apt_downloads_dir = layer_path.join("downloads");

        // Create the directories that `apt-get` expects
        create_dir_all(apt_archives_dir.join("partial"))?;
        create_dir_all(apt_state_dir.join("lists").join("partial"))?;
        create_dir_all(&apt_sources_dir)?;
        create_dir_all(&apt_downloads_dir)?;

        // Copy over the system sources list
        let apt_sources_list = apt_sources_dir.join("sources.list");
        copy(
            PathBuf::from("/")
                .join("etc")
                .join("apt")
                .join("sources.list"),
            &apt_sources_list,
        )?;

        // Add any repositories added in the `Aptfile`
        if !self.repos.is_empty() {
            let mut file = BufWriter::new(
                OpenOptions::new()
                    .write(true)
                    .append(true)
                    .open(&apt_sources_list)
                    .expect("Should be able to open file"),
            );
            for repo in &self.repos {
                writeln!(file, "{}", repo)?;
            }
        }

        let apt = Binary::named("apt-get").require_sync()?;
        let apt_options: Vec<String> = vec![
            "debug::nolocking=true",
            &format!("dir::cache={}", apt_cache_dir.display()),
            &format!("dir::state={}", apt_state_dir.display()),
            &format!("dir::etc::sourcelist={}", apt_sources_list.display()),
            "dir::etc::sourceparts=/dev/null",
        ]
        .into_iter()
        .map(|option| ["-o", option].concat())
        .collect();

        tracing::info!("Updating apt caches");
        apt.run_sync([apt_options.clone(), vec_string!["update"]].concat())?;

        // Remove everything in  the layers `usr` dir (😱) because we don't want artifacts
        // of packages that were previous installed but have been removed from the list
        tracing::info!("Removing previous installs");
        let layer_usr_dir = layer_path.join("usr");
        if layer_usr_dir.exists() {
            remove_dir_all(layer_usr_dir)?;
        }

        let dpkg = Binary::named("dpkg").require_sync()?;

        // Get deb files
        for package in &self.packages {
            // Use hash of URL as package name for remote debs
            let package_id = if package.starts_with("http") && package.ends_with(".deb") {
                str_sha256_hex(package)
            } else {
                package.to_string()
            };

            // Use record of downloaded debs for a package or download them
            let downloads_for_package = apt_downloads_dir.join(&package_id);
            let downloaded_debs = if downloads_for_package.exists() {
                tracing::info!("Package `{}` already downloaded", package);

                read_to_string(downloads_for_package)?
                    .lines()
                    .map(OsString::from)
                    .collect()
            } else {
                // Get a list of the debs in archive
                let get_debs = || -> Vec<OsString> {
                    apt_archives_dir
                        .read_dir()
                        .expect("Archives directory should be readable")
                        .flatten()
                        .filter_map(|entry| {
                            let path = entry.path();
                            if path.extension() == Some(&OsString::from("deb")) {
                                path.file_name().map(|name| name.to_os_string())
                            } else {
                                None
                            }
                        })
                        .collect()
                };

                // Record debs before
                let debs_before = get_debs();

                if package.starts_with("http") && package.ends_with(".deb") {
                    tracing::info!("Downloading `{}`", package);

                    let path = apt_archives_dir.join(format!("{}.deb", package_id));
                    download_file_sync(package, &path)?;
                } else {
                    tracing::info!("Fetching deb files for package `{}`", package);

                    // Assumes using `apt-get` >= 1.1 which replaced `--force-yes` with
                    // `--allow-*` options
                    apt.run_sync(
                        [
                            apt_options.clone(),
                            vec_string![
                                "-y",
                                "--allow-downgrades",
                                "--allow-remove-essential",
                                "--allow-change-held-packages",
                                "-d",
                                "install",
                                "--reinstall",
                                package
                            ],
                        ]
                        .concat(),
                    )?;
                }

                let debs_after = get_debs();
                let debs_downloaded: Vec<OsString> = debs_after
                    .into_iter()
                    .filter(|item| !debs_before.contains(item))
                    .collect();

                // Record the debs that were downloaded for the package
                write(
                    apt_downloads_dir.join(package_id),
                    debs_downloaded
                        .iter()
                        .map(|deb| deb.to_string_lossy().to_string())
                        .collect::<Vec<String>>()
                        .join("\n"),
                )?;

                debs_downloaded
            };

            // Install the downloaded deb files into layer using `dpkg`
            tracing::info!("Installing debs for package `{}`", package);
            for deb in downloaded_debs {
                let deb_path = apt_archives_dir.join(deb);
                dpkg.run_sync([
                    "-x",
                    &deb_path.display().to_string(),
                    &layer_path.display().to_string(),
                ])?;
            }
        }

        // Prepend a lot of env vars

        let prefix_paths = |paths: &[&str]| {
            // The trailing colon here is important to separate what we prepend
            // from the existing path
            env::join_paths(paths.iter().map(|path| layer_path.join(path)))
                .map(|joined| format!("{}:", joined.to_string_lossy()))
        };

        let mut layer_env = LayerEnv::new().chainable_insert(
            Scope::All,
            ModificationBehavior::Prepend,
            "PATH",
            prefix_paths(&["usr/bin"])?,
        );

        let include_path_prepend = prefix_paths(&[
            "usr/include/x86_64-linux-gnu",
            "usr/include/i386-linux-gnu",
            "usr/include",
        ])?;
        for var in ["INCLUDE_PATH", "CPATH", "CPPPATH"] {
            layer_env.insert(
                Scope::All,
                ModificationBehavior::Prepend,
                var,
                &include_path_prepend,
            );
        }

        let library_paths = prefix_paths(&[
            "usr/lib/x86_64-linux-gnu",
            "usr/lib/i386-linux-gnu",
            "usr/lib",
        ])?;
        for var in ["LD_LIBRARY_PATH", "LIBRARY_PATH"] {
            layer_env.insert(
                Scope::All,
                ModificationBehavior::Prepend,
                var,
                &library_paths,
            );
        }

        layer_env.insert(
            Scope::All,
            ModificationBehavior::Prepend,
            "PKG_CONFIG_PATH",
            prefix_paths(&[
                "usr/lib/x86_64-linux-gnu/pkgconfig",
                "usr/lib/i386-linux-gnu/pkgconfig",
                "usr/lib/pkgconfig",
            ])?,
        );

        LayerResultBuilder::new(self.clone()).env(layer_env).build()
    }
}
<commit_msg>fix(APT buildpack): Various fixes including recreating layer on changes<commit_after>use std::{
    env,
    ffi::OsString,
    fs::{copy, create_dir_all, read_to_string, remove_dir_all, write, OpenOptions},
    io::{BufWriter, Write},
    path::{Path, PathBuf},
};

use binary::{http_utils::download_file_sync, Binary, BinaryTrait};
use buildpack::{
    eyre,
    fs_utils::clear_dir_all,
    hash_utils::str_sha256_hex,
    libcnb::{
        self,
        build::{BuildContext, BuildResult, BuildResultBuilder},
        data::{
            build_plan::{BuildPlan, Provide},
            layer_content_metadata::LayerTypes,
            layer_name,
        },
        detect::{DetectContext, DetectResult, DetectResultBuilder},
        generic::{GenericMetadata, GenericPlatform},
        layer::{ExistingLayerStrategy, Layer, LayerResult, LayerResultBuilder},
        layer_env::{LayerEnv, ModificationBehavior, Scope},
        Buildpack,
    },
    maplit::hashmap,
    tracing, BuildpackTrait, LayerOptions,
};
use serde::{Deserialize, Serialize};
use utils::vec_string;

pub struct AptBuildpack;

impl BuildpackTrait for AptBuildpack {
    fn toml() -> &'static str {
        include_str!("../buildpack.toml")
    }
}

// The name of the file that is detected in the app dir
const APT_FILE: &str = "Aptfile";

// The name of the layer that the buildpack creates
const APT_PACKAGES: &str = "apt_packages";

impl Buildpack for AptBuildpack {
    type Platform = GenericPlatform;
    type Metadata = GenericMetadata;
    type Error = eyre::Report;

    fn detect(&self, _context: DetectContext<Self>) -> libcnb::Result<DetectResult, Self::Error> {
        // Detect `Aptfile`
        let aptfile = PathBuf::from(APT_FILE);

        // Get the Linux release for reuse below
        let linux_flavour = sys_info::linux_os_release().ok();

        // Fail if not on Ubuntu Linux
        if env::consts::OS != "linux"
            || linux_flavour
                .as_ref()
                .map_or_else(|| "".to_string(), |rel| rel.id().to_string())
                != "ubuntu"
        {
            if aptfile.exists() {
                tracing::warn!("Aptfile detected but will be ignored because not on Ubuntu Linux");
            }
            return DetectResultBuilder::fail().build();
        }

        let mut build_plan = BuildPlan::new();

        // Because this buildpack may be used by others, always indicates that is provides
        // `apt_packages`
        build_plan.provides.push(Provide::new(APT_PACKAGES));

        // Require `apt_packages` layer if there is an `Aptfile`
        if aptfile.exists() {
            let version = linux_flavour
                .expect("Should have returned by now if not on Linux")
                .version_codename
                .expect("Should have an Ubuntu version codename");

            build_plan.requires.push(Self::require(
                APT_PACKAGES,
                APT_FILE,
                format!("Install `apt` packages for Ubuntu '{}'", version).trim(),
                Some(hashmap! {
                    "version" => version,
                    "file" => APT_FILE.to_string()
                }),
            ))
        }

        DetectResultBuilder::pass().build_plan(build_plan).build()
    }

    fn build(&self, context: BuildContext<Self>) -> libcnb::Result<BuildResult, Self::Error> {
        let entries = self.buildpack_plan_entries(&context.buildpack_plan);

        if let Some(options) = entries.get(APT_PACKAGES) {
            context.handle_layer(
                layer_name!("apt_packages"),
                AptPackagesLayer::new(options, &context.app_dir),
            )?;
        }

        BuildResultBuilder::new().build()
    }
}

#[derive(Clone, Deserialize, Serialize)]
struct AptPackagesLayer {
    /// The version of Ubuntu that packages will be installed for e.g `bionic`, `focal`
    version: String,

    /// The path to the `Aptfile` (or similar name) that specifies packages to be installed
    file: Option<PathBuf>,

    /// A list of package names, or deb URLs to be installed
    ///
    /// Usually instead of an `Aptfile` but can be specified in addition to it
    packages: Vec<String>,

    /// A list of repos to be used
    ///
    /// Usually instead of `:repo:` entries in an `Aptfile` but can be specified in addition to it
    repos: Vec<String>,
}

impl AptPackagesLayer {
    fn new(options: &LayerOptions, app_path: &Path) -> Self {
        let version = match options.get("version") {
            Some(version) => version.to_string(),
            None => sys_info::linux_os_release()
                .ok()
                .and_then(|info| info.version_codename)
                .unwrap_or_default(),
        };

        let file = options.get("file").map(PathBuf::from);

        // Split `Aptfile` into  packages and repos
        let mut repos = Vec::new();
        let mut packages = match &file {
            Some(file) => read_to_string(app_path.join(file))
                .unwrap_or_default()
                .lines()
                .filter_map(|line| {
                    let line = line.trim();
                    if line.is_empty() || line.starts_with('#') {
                        None
                    } else if let Some(repo) = line.strip_prefix(":repo:") {
                        repos.push(repo.to_string());
                        None
                    } else {
                        Some(line.to_string())
                    }
                })
                .collect(),
            None => Vec::new(),
        };

        // Add any other packages
        if let Some(list) = options.get("packages") {
            packages.append(&mut list.split(',').map(|pkg| pkg.trim().to_string()).collect());
        }

        // Add any other repos
        if let Some(list) = options.get("repos") {
            repos.append(&mut list.split(',').map(|pkg| pkg.trim().to_string()).collect());
        }

        Self {
            version,
            file,
            packages,
            repos,
        }
    }
}

impl Layer for AptPackagesLayer {
    type Buildpack = AptBuildpack;
    type Metadata = AptPackagesLayer;

    fn types(&self) -> LayerTypes {
        LayerTypes {
            build: true,
            launch: true,
            cache: true,
        }
    }

    fn existing_layer_strategy(
        &self,
        _context: &BuildContext<Self::Buildpack>,
        layer_data: &libcnb::layer::LayerData<Self::Metadata>,
    ) -> Result<libcnb::layer::ExistingLayerStrategy, <Self::Buildpack as Buildpack>::Error> {
        let metadata = &layer_data.content_metadata.metadata;
        let strategy = if self.version != metadata.version {
            tracing::info!(
                "Existing `apt_packages` layer is for different Ubuntu version (`{}` => `{}`); will recreate",
                metadata.version,
                self.version,
            );
            ExistingLayerStrategy::Recreate
        } else if self.repos != metadata.repos {
            tracing::info!(
                "Existing `apt_packages` layer has different repos (`{}` => `{}`); will recreate",
                metadata.repos.join(","),
                self.repos.join(","),
            );
            ExistingLayerStrategy::Recreate
        } else if self.packages != metadata.packages {
            tracing::info!(
                "Existing `apt_packages` layer has different packages (`{}` => `{}`); will recreate",
                metadata.packages.join(","),
                self.packages.join(",")
            );
            ExistingLayerStrategy::Recreate
        } else {
            tracing::info!("Existing `apt_packages` layer meets requirements; will keep",);
            ExistingLayerStrategy::Keep
        };
        Ok(strategy)
    }

    fn create(
        &self,
        context: &BuildContext<Self::Buildpack>,
        layer_path: &Path,
    ) -> Result<LayerResult<Self::Metadata>, eyre::Report> {
        tracing::info!("Creating `apt_packages` layer");
        self.install(context, layer_path)
    }

    fn update(
        &self,
        context: &BuildContext<Self::Buildpack>,
        layer_data: &libcnb::layer::LayerData<Self::Metadata>,
    ) -> Result<LayerResult<Self::Metadata>, <Self::Buildpack as Buildpack>::Error> {
        tracing::info!("Updating `apt_packages` layer");
        self.install(context, &layer_data.path)
    }
}

impl AptPackagesLayer {
    fn install(
        &self,
        _context: &BuildContext<AptBuildpack>,
        layer_path: &Path,
    ) -> Result<LayerResult<AptPackagesLayer>, eyre::Report> {
        let layer_path = &layer_path.canonicalize()?;

        tracing::info!("Installing apt packages: {}", self.packages.join(", "));

        let apt_cache_dir = layer_path.join("cache");
        let apt_archives_dir = apt_cache_dir.join("archives");
        let apt_state_dir = layer_path.join("state");
        let apt_sources_dir = layer_path.join("sources");
        let apt_downloads_dir = layer_path.join("downloads");

        // Create the directories that `apt-get` expects
        create_dir_all(apt_archives_dir.join("partial"))?;
        create_dir_all(apt_state_dir.join("lists").join("partial"))?;
        create_dir_all(&apt_sources_dir)?;
        create_dir_all(&apt_downloads_dir)?;

        // Copy over the system sources list
        let apt_sources_list = apt_sources_dir.join("sources.list");
        copy(
            PathBuf::from("/")
                .join("etc")
                .join("apt")
                .join("sources.list"),
            &apt_sources_list,
        )?;

        // Add any repositories added in the `Aptfile`
        if !self.repos.is_empty() {
            let mut file = BufWriter::new(
                OpenOptions::new()
                    .write(true)
                    .append(true)
                    .open(&apt_sources_list)
                    .expect("Should be able to open file"),
            );
            for repo in &self.repos {
                writeln!(file, "{}", repo)?;
            }
        }

        // WARN: This function has logic for updating an existing layer (with the view to making updates
        // related to changes in the list of packages faster). However, that seems to be unreliable and
        // so for now at least, the layer is recreated on any detected changes

        // Remove everything in the layer's `state`, `usr` and `etc` dirs so we don't have artifacts
        // of packages that were previous installed but have been removed from the list
        tracing::info!("Removing previous installs");
        clear_dir_all(&apt_state_dir)?;
        let layer_usr_dir = layer_path.join("usr");
        if layer_usr_dir.exists() {
            remove_dir_all(layer_usr_dir)?;
        }
        let layer_etc_dir = layer_path.join("etc");
        if layer_etc_dir.exists() {
            remove_dir_all(layer_etc_dir)?;
        }

        let apt = Binary::named("apt-get").require_sync()?;
        let apt_options: Vec<String> = vec![
            "debug::nolocking=true",
            &format!("dir::cache={}", apt_cache_dir.display()),
            &format!("dir::state={}", apt_state_dir.display()),
            &format!("dir::etc::sourcelist={}", apt_sources_list.display()),
            "dir::etc::sourceparts=/dev/null",
        ]
        .into_iter()
        .map(|option| ["-o", option].concat())
        .collect();

        tracing::info!("Updating apt caches");
        apt.run_sync([apt_options.clone(), vec_string!["update"]].concat())?;

        let dpkg = Binary::named("dpkg").require_sync()?;

        // Get deb files
        for package in &self.packages {
            // Use hash of URL as package name for remote debs
            let package_id = if package.starts_with("http") && package.ends_with(".deb") {
                str_sha256_hex(package)
            } else {
                package.to_string()
            };

            // Use record of downloaded debs for a package or download them
            let downloads_for_package = apt_downloads_dir.join(&package_id);
            let downloaded_debs = if downloads_for_package.exists() {
                tracing::info!("Package `{}` already downloaded", package);

                read_to_string(downloads_for_package)?
                    .lines()
                    .map(OsString::from)
                    .collect()
            } else {
                // Get a list of the debs in archive
                let get_debs = || -> Vec<OsString> {
                    apt_archives_dir
                        .read_dir()
                        .expect("Archives directory should be readable")
                        .flatten()
                        .filter_map(|entry| {
                            let path = entry.path();
                            if path.extension() == Some(&OsString::from("deb")) {
                                path.file_name().map(|name| name.to_os_string())
                            } else {
                                None
                            }
                        })
                        .collect()
                };

                // Record debs before
                let debs_before = get_debs();

                if package.starts_with("http") && package.ends_with(".deb") {
                    tracing::info!("Downloading `{}`", package);

                    let path = apt_archives_dir.join(format!("{}.deb", package_id));
                    download_file_sync(package, &path)?;
                } else {
                    tracing::info!("Fetching deb files for package `{}`", package);

                    // Assumes using `apt-get` >= 1.1 which replaced `--force-yes` with
                    // `--allow-*` options
                    apt.run_sync(
                        [
                            apt_options.clone(),
                            vec_string![
                                "-y",
                                "--allow-downgrades",
                                "--allow-remove-essential",
                                "--allow-change-held-packages",
                                "-d",
                                "install",
                                "--reinstall",
                                package
                            ],
                        ]
                        .concat(),
                    )?;
                }

                let debs_after = get_debs();
                let debs_downloaded: Vec<OsString> = debs_after
                    .into_iter()
                    .filter(|item| !debs_before.contains(item))
                    .collect();

                // Record the debs that were downloaded for the package
                write(
                    apt_downloads_dir.join(package_id),
                    debs_downloaded
                        .iter()
                        .map(|deb| deb.to_string_lossy().to_string())
                        .collect::<Vec<String>>()
                        .join("\n"),
                )?;

                debs_downloaded
            };

            // Install the downloaded deb files into layer using `dpkg`
            tracing::info!("Installing debs for package `{}`", package);
            for deb in downloaded_debs {
                let deb_path = apt_archives_dir.join(deb);
                dpkg.run_sync([
                    "-x",
                    &deb_path.display().to_string(),
                    &layer_path.display().to_string(),
                ])?;
            }
        }

        // Prepend a lot of env vars

        let prefix_paths = |paths: &[&str]| {
            // The trailing colon here is important to separate what we prepend
            // from the existing path
            env::join_paths(paths.iter().map(|path| layer_path.join(path)))
                .map(|joined| format!("{}:", joined.to_string_lossy()))
        };

        let mut layer_env = LayerEnv::new().chainable_insert(
            Scope::All,
            ModificationBehavior::Prepend,
            "PATH",
            prefix_paths(&["usr/bin"])?,
        );

        let include_path_prepend = prefix_paths(&[
            "usr/include/x86_64-linux-gnu",
            "usr/include/i386-linux-gnu",
            "usr/include",
        ])?;
        for var in ["INCLUDE_PATH", "CPATH", "CPPPATH"] {
            layer_env.insert(
                Scope::All,
                ModificationBehavior::Prepend,
                var,
                &include_path_prepend,
            );
        }

        let library_paths = prefix_paths(&[
            "usr/lib/x86_64-linux-gnu",
            "usr/lib/i386-linux-gnu",
            "usr/lib",
        ])?;
        for var in ["LD_LIBRARY_PATH", "LIBRARY_PATH"] {
            layer_env.insert(
                Scope::All,
                ModificationBehavior::Prepend,
                var,
                &library_paths,
            );
        }

        layer_env.insert(
            Scope::All,
            ModificationBehavior::Prepend,
            "PKG_CONFIG_PATH",
            prefix_paths(&[
                "usr/lib/x86_64-linux-gnu/pkgconfig",
                "usr/lib/i386-linux-gnu/pkgconfig",
                "usr/lib/pkgconfig",
            ])?,
        );

        LayerResultBuilder::new(self.clone()).env(layer_env).build()
    }
}
<|endoftext|>


<commit_before>extern crate luar;

use std::collections::HashMap;
use luar::State;

// rewrite of http://lua-users.org/wiki/SimpleLuaApiExample
fn main() {
    let mut state = State::new();

    let mut table = HashMap::new();
    for i in range(1i, 6) {
        //table[i] = i * 2;
        table.insert(i, i * 2);
    }

    // this is seemingly hard to implement:
    //state["foo"] = table;
    // meanwhile this is the norm
    state.insert("foo", table);

    // evaluate a script
    state.eval(&Path::new("./script.lua")).unwrap();

    // get its return either one will do:
    let sum: f64 = state.pop().unwrap();
    //let sum = state.pop::<f64>().unwrap();

    println!("Script returned {:0.f}", sum);
}
<commit_msg>Fixing format, no more `f`.<commit_after>extern crate luar;

use std::collections::HashMap;
use luar::State;

// rewrite of http://lua-users.org/wiki/SimpleLuaApiExample
fn main() {
    let mut state = State::new();

    let mut table = HashMap::new();
    for i in range(1i, 6) {
        //table[i] = i * 2;
        table.insert(i, i * 2);
    }

    // this is seemingly hard to implement:
    //state["foo"] = table;
    // meanwhile this is the norm
    state.insert("foo", table);

    // evaluate a script
    state.eval(&Path::new("./script.lua")).unwrap();

    // get its return either one will do:
    let sum: f64 = state.pop().unwrap();
    //let sum = state.pop::<f64>().unwrap();

    println!("Script returned {:0.}", sum);
}
<|endoftext|>


<commit_before>extern crate peg;

peg::parser!( grammar ra() for str {
    use peg::ParseLiteral;

    rule number() -> i64
        = n:$(['0'..='9']+) { n.parse().unwrap() }

    rule commasep<T>(x: rule<T>) -> Vec<T> = v:(x() ** ",") ","? {v}
    rule bracketed<T>(x: rule<T>) -> T = "[" v:x() "]" {v}

    pub rule list() -> Vec<i64> = commasep(<number()>)
    pub rule array() -> Vec<i64> = bracketed(<commasep(<number()>)>)

    rule keyword(id: &'static str) = ##parse_string_literal(id) !['0'..='9' | 'a'..='z' | 'A'..='Z' | '_']
    rule ident() = ['a'..='z']+
    rule _() = [' ']*
    pub rule ifelse() = keyword("if") _ ident() _ keyword("then") _ ident() _ keyword("else") _ ident()

    pub rule repeated_a(i: usize) = ['a']*<{i}>
});

use ra::*;

fn main() {
    assert_eq!(list("1,2,3,4"), Ok(vec![1,2,3,4]));
    assert_eq!(array("[1,1,2,3,5,]"), Ok(vec![1,1,2,3,5]));

    assert!(ifelse("if foo then x else y").is_ok());
    assert!(ifelse("iffoothenxelsey").is_err());

    assert!(repeated_a("aa", 2).is_ok());
    assert!(repeated_a("aaa", 2).is_err());
    assert!(repeated_a("aaaaa", 5).is_ok());
}<commit_msg>Add another rule-args example<commit_after>extern crate peg;

peg::parser!( grammar ra() for str {
    use peg::ParseLiteral;

    rule number() -> i64
        = n:$(['0'..='9']+) { n.parse().unwrap() }

    rule commasep<T>(x: rule<T>) -> Vec<T> = v:(x() ** ",") ","? {v}
    rule bracketed<T>(x: rule<T>) -> T = "[" v:x() "]" {v}

    pub rule list() -> Vec<i64> = commasep(<number()>)
    pub rule array() -> Vec<i64> = bracketed(<commasep(<number()>)>)

    rule keyword(id: &'static str) = ##parse_string_literal(id) !['0'..='9' | 'a'..='z' | 'A'..='Z' | '_']
    rule ident() = ['a'..='z']+
    rule _() = [' ']*
    pub rule ifelse() = keyword("if") _ ident() _ keyword("then") _ ident() _ keyword("else") _ ident()

    pub rule repeated_a(i: usize) = ['a']*<{i}>

    rule i(literal: &'static str) = input:$([_]*<{literal.len()}>) {? if input.eq_ignore_ascii_case(literal) { Ok(()) } else { Err(literal) } }

    pub rule test_i() = i("foo") i("bar")
});

use ra::*;

fn main() {
    assert_eq!(list("1,2,3,4"), Ok(vec![1,2,3,4]));
    assert_eq!(array("[1,1,2,3,5,]"), Ok(vec![1,1,2,3,5]));

    assert!(ifelse("if foo then x else y").is_ok());
    assert!(ifelse("iffoothenxelsey").is_err());

    assert!(repeated_a("aa", 2).is_ok());
    assert!(repeated_a("aaa", 2).is_err());
    assert!(repeated_a("aaaaa", 5).is_ok());

    assert!(test_i("fOoBaR").is_ok());
    assert!(test_i("fOoBaZ").is_err());
    assert!(test_i("fOoX").is_err());
}<|endoftext|>


<commit_before>#![feature(test)]

extern crate test;
extern crate bf;

#[cfg(feature = "jit")]
mod jit_only {
    use bf::ast;
    use bf::rle;
    use bf::peephole;
    use bf::jit;

    use bf::traits::Interpretable;
    use bf::test_helpers;

    use test::Bencher;

    #[bench]
    fn compile_factor(b: &mut Bencher) {
        let program = ast::parse_program(test_helpers::FACTOR_SRC).unwrap();

        b.iter(|| {
            let program = rle::compile(&program);
            let program = peephole::compile(&program);
            jit::compile(&program, true)
        });
    }

    #[bench]
    fn run_factor_million(b: &mut Bencher) {
        let program = ast::parse_program(test_helpers::FACTOR_SRC).unwrap();
        let program = rle::compile(&program);
        let program = peephole::compile(&program);
        let program = jit::compile(&program, true);

        b.iter(|| {
            program.interpret_memory(None, b"1000000\n").unwrap()
        });
    }

    #[bench]
    fn run_factor_million_unchecked(b: &mut Bencher) {
        let program = ast::parse_program(test_helpers::FACTOR_SRC).unwrap();
        let program = rle::compile(&program);
        let program = peephole::compile(&program);
        let program = jit::compile(&program, false);

        b.iter(|| {
            program.interpret_memory(None, b"1000000\n").unwrap()
        });
    }
}<commit_msg>Benchmark for unchecked compiling (which runs less analysis).<commit_after>#![feature(test)]

extern crate test;
extern crate bf;

#[cfg(feature = "jit")]
mod jit_only {
    use bf::ast;
    use bf::rle;
    use bf::peephole;
    use bf::jit;

    use bf::traits::Interpretable;
    use bf::test_helpers;

    use test::Bencher;

    #[bench]
    fn compile_factor(b: &mut Bencher) {
        let program = ast::parse_program(test_helpers::FACTOR_SRC).unwrap();

        b.iter(|| {
            let program = rle::compile(&program);
            let program = peephole::compile(&program);
            jit::compile(&program, true)
        });
    }

    #[bench]
    fn compile_factor_unchecked(b: &mut Bencher) {
        let program = ast::parse_program(test_helpers::FACTOR_SRC).unwrap();

        b.iter(|| {
            let program = rle::compile(&program);
            let program = peephole::compile(&program);
            jit::compile(&program, false)
        });
    }

    #[bench]
    fn run_factor_million(b: &mut Bencher) {
        let program = ast::parse_program(test_helpers::FACTOR_SRC).unwrap();
        let program = rle::compile(&program);
        let program = peephole::compile(&program);
        let program = jit::compile(&program, true);

        b.iter(|| {
            program.interpret_memory(None, b"1000000\n").unwrap()
        });
    }

    #[bench]
    fn run_factor_million_unchecked(b: &mut Bencher) {
        let program = ast::parse_program(test_helpers::FACTOR_SRC).unwrap();
        let program = rle::compile(&program);
        let program = peephole::compile(&program);
        let program = jit::compile(&program, false);

        b.iter(|| {
            program.interpret_memory(None, b"1000000\n").unwrap()
        });
    }
}<|endoftext|>


<commit_before>#![feature(test)]
extern crate test;

extern crate rusqlite;

use rusqlite::Connection;
use rusqlite::cache::StatementCache;
use test::Bencher;

#[bench]
fn bench_no_cache(b: &mut Bencher) {
    let db = Connection::open_in_memory().unwrap();
    let sql = "SELECT 1, 'test', 3.14 UNION SELECT 2, 'exp', 2.71";
    b.iter(|| db.prepare(sql).unwrap());
}

#[bench]
fn bench_cache(b: &mut Bencher) {
    let db = Connection::open_in_memory().unwrap();
    let cache = StatementCache::new(&db, 15);
    let sql = "SELECT 1, 'test', 3.14 UNION SELECT 2, 'exp', 2.71";
    b.iter(|| cache.get(sql).unwrap());
}
<commit_msg>Fix benches<commit_after>#![feature(test)]
extern crate test;

extern crate rusqlite;

use rusqlite::Connection;
use test::Bencher;

#[bench]
fn bench_no_cache(b: &mut Bencher) {
    let db = Connection::open_in_memory().unwrap();
    let sql = "SELECT 1, 'test', 3.14 UNION SELECT 2, 'exp', 2.71";
    b.iter(|| db.prepare(sql).unwrap());
}

#[bench]
fn bench_cache(b: &mut Bencher) {
    let db = Connection::open_in_memory().unwrap();
    let sql = "SELECT 1, 'test', 3.14 UNION SELECT 2, 'exp', 2.71";
    b.iter(|| db.prepare_cached(sql).unwrap());
}
<|endoftext|>


<commit_before>use {localhost, TryRead, TryWrite};
use mio::*;
use mio::deprecated::{EventLoop, Handler};
use mio::net::{TcpListener, TcpStream};
use bytes::{Buf, ByteBuf, MutByteBuf, SliceBuf};
use slab;
use std::io;

const SERVER: Token = Token(10_000_000);
const CLIENT: Token = Token(10_000_001);

struct EchoConn {
    sock: TcpStream,
    buf: Option<ByteBuf>,
    mut_buf: Option<MutByteBuf>,
    token: Option<Token>,
    interest: Ready
}

type Slab<T> = slab::Slab<T, Token>;

impl EchoConn {
    fn new(sock: TcpStream) -> EchoConn {
        EchoConn {
            sock: sock,
            buf: None,
            mut_buf: Some(ByteBuf::mut_with_capacity(2048)),
            token: None,
            interest: Ready::hup()
        }
    }

    fn writable(&mut self, event_loop: &mut EventLoop<Echo>) -> io::Result<()> {
        let mut buf = self.buf.take().unwrap();

        match self.sock.try_write_buf(&mut buf) {
            Ok(None) => {
                debug!("client flushing buf; WOULDBLOCK");

                self.buf = Some(buf);
                self.interest.insert(Ready::writable());
            }
            Ok(Some(r)) => {
                debug!("CONN : we wrote {} bytes!", r);

                self.mut_buf = Some(buf.flip());

                self.interest.insert(Ready::readable());
                self.interest.remove(Ready::writable());
            }
            Err(e) => debug!("not implemented; client err={:?}", e),
        }

        assert!(self.interest.is_readable() || self.interest.is_writable(), "actual={:?}", self.interest);
        event_loop.reregister(&self.sock, self.token.unwrap(), self.interest,
                              PollOpt::edge() | PollOpt::oneshot())
    }

    fn readable(&mut self, event_loop: &mut EventLoop<Echo>) -> io::Result<()> {
        let mut buf = self.mut_buf.take().unwrap();

        match self.sock.try_read_buf(&mut buf) {
            Ok(None) => {
                debug!("CONN : spurious read wakeup");
                self.mut_buf = Some(buf);
            }
            Ok(Some(r)) => {
                debug!("CONN : we read {} bytes!", r);

                // prepare to provide this to writable
                self.buf = Some(buf.flip());

                self.interest.remove(Ready::readable());
                self.interest.insert(Ready::writable());
            }
            Err(e) => {
                debug!("not implemented; client err={:?}", e);
                self.interest.remove(Ready::readable());
            }

        };

        assert!(self.interest.is_readable() || self.interest.is_writable(), "actual={:?}", self.interest);
        event_loop.reregister(&self.sock, self.token.unwrap(), self.interest,
                              PollOpt::edge())
    }
}

struct EchoServer {
    sock: TcpListener,
    conns: Slab<EchoConn>
}

impl EchoServer {
    fn accept(&mut self, event_loop: &mut EventLoop<Echo>) -> io::Result<()> {
        debug!("server accepting socket");

        let sock = self.sock.accept().unwrap().0;
        let conn = EchoConn::new(sock,);
        let tok = self.conns.insert(conn)
            .ok().expect("could not add connection to slab");

        // Register the connection
        self.conns[tok].token = Some(tok);
        event_loop.register(&self.conns[tok].sock, tok, Ready::readable(),
                                PollOpt::edge() | PollOpt::oneshot())
            .ok().expect("could not register socket with event loop");

        Ok(())
    }

    fn conn_readable(&mut self, event_loop: &mut EventLoop<Echo>,
                     tok: Token) -> io::Result<()> {
        debug!("server conn readable; tok={:?}", tok);
        self.conn(tok).readable(event_loop)
    }

    fn conn_writable(&mut self, event_loop: &mut EventLoop<Echo>,
                     tok: Token) -> io::Result<()> {
        debug!("server conn writable; tok={:?}", tok);
        self.conn(tok).writable(event_loop)
    }

    fn conn<'a>(&'a mut self, tok: Token) -> &'a mut EchoConn {
        &mut self.conns[tok]
    }
}

struct EchoClient {
    sock: TcpStream,
    msgs: Vec<&'static str>,
    tx: SliceBuf<'static>,
    rx: SliceBuf<'static>,
    mut_buf: Option<MutByteBuf>,
    token: Token,
    interest: Ready
}

// Sends a message and expects to receive the same exact message, one at a time
impl EchoClient {
    fn new(sock: TcpStream, tok: Token,  mut msgs: Vec<&'static str>) -> EchoClient {
        let curr = msgs.remove(0);

        EchoClient {
            sock: sock,
            msgs: msgs,
            tx: SliceBuf::wrap(curr.as_bytes()),
            rx: SliceBuf::wrap(curr.as_bytes()),
            mut_buf: Some(ByteBuf::mut_with_capacity(2048)),
            token: tok,
            interest: Ready::empty()
        }
    }

    fn readable(&mut self, event_loop: &mut EventLoop<Echo>) -> io::Result<()> {
        debug!("client socket readable");

        let mut buf = self.mut_buf.take().unwrap();

        match self.sock.try_read_buf(&mut buf) {
            Ok(None) => {
                debug!("CLIENT : spurious read wakeup");
                self.mut_buf = Some(buf);
            }
            Ok(Some(r)) => {
                debug!("CLIENT : We read {} bytes!", r);

                // prepare for reading
                let mut buf = buf.flip();

                while buf.has_remaining() {
                    let actual = buf.read_byte().unwrap();
                    let expect = self.rx.read_byte().unwrap();

                    assert!(actual == expect, "actual={}; expect={}", actual, expect);
                }

                self.mut_buf = Some(buf.flip());

                self.interest.remove(Ready::readable());

                if !self.rx.has_remaining() {
                    self.next_msg(event_loop).unwrap();
                }
            }
            Err(e) => {
                panic!("not implemented; client err={:?}", e);
            }
        };

        if !self.interest.is_empty() {
            assert!(self.interest.is_readable() || self.interest.is_writable(), "actual={:?}", self.interest);
            event_loop.reregister(&self.sock, self.token, self.interest,
                                       PollOpt::edge() | PollOpt::oneshot())?;
        }

        Ok(())
    }

    fn writable(&mut self, event_loop: &mut EventLoop<Echo>) -> io::Result<()> {
        debug!("client socket writable");

        match self.sock.try_write_buf(&mut self.tx) {
            Ok(None) => {
                debug!("client flushing buf; WOULDBLOCK");
                self.interest.insert(Ready::writable());
            }
            Ok(Some(r)) => {
                debug!("CLIENT : we wrote {} bytes!", r);
                self.interest.insert(Ready::readable());
                self.interest.remove(Ready::writable());
            }
            Err(e) => debug!("not implemented; client err={:?}", e)
        }

        if self.interest.is_readable() || self.interest.is_writable() {
            try!(event_loop.reregister(&self.sock, self.token, self.interest,
                                  PollOpt::edge() | PollOpt::oneshot()));
        }

        Ok(())
    }

    fn next_msg(&mut self, event_loop: &mut EventLoop<Echo>) -> io::Result<()> {
        if self.msgs.is_empty() {
            event_loop.shutdown();
            return Ok(());
        }

        let curr = self.msgs.remove(0);

        debug!("client prepping next message");
        self.tx = SliceBuf::wrap(curr.as_bytes());
        self.rx = SliceBuf::wrap(curr.as_bytes());

        self.interest.insert(Ready::writable());
        event_loop.reregister(&self.sock, self.token, self.interest,
                              PollOpt::edge() | PollOpt::oneshot())
    }
}

struct Echo {
    server: EchoServer,
    client: EchoClient,
}

impl Echo {
    fn new(srv: TcpListener, client: TcpStream, msgs: Vec<&'static str>) -> Echo {
        Echo {
            server: EchoServer {
                sock: srv,
                conns: Slab::with_capacity(128)
            },
            client: EchoClient::new(client, CLIENT, msgs)
        }
    }
}

impl Handler for Echo {
    type Timeout = usize;
    type Message = ();

    fn ready(&mut self, event_loop: &mut EventLoop<Echo>, token: Token,
             events: Ready) {
        debug!("ready {:?} {:?}", token, events);
        if events.is_readable() {
            match token {
                SERVER => self.server.accept(event_loop).unwrap(),
                CLIENT => self.client.readable(event_loop).unwrap(),
                i => self.server.conn_readable(event_loop, i).unwrap()
            }
        }

        if events.is_writable() {
            match token {
                SERVER => panic!("received writable for token 0"),
                CLIENT => self.client.writable(event_loop).unwrap(),
                _ => self.server.conn_writable(event_loop, token).unwrap()
            };
        }
    }
}

#[test]
pub fn test_echo_server() {
    debug!("Starting TEST_ECHO_SERVER");
    let mut event_loop = EventLoop::new().unwrap();

    let addr = localhost();
    let srv = TcpListener::bind(&addr).unwrap();

    info!("listen for connections");
    event_loop.register(&srv, SERVER, Ready::readable(),
                            PollOpt::edge() | PollOpt::oneshot()).unwrap();

    let sock = TcpStream::connect(&addr).unwrap();

    // Connect to the server
    event_loop.register(&sock, CLIENT, Ready::writable(),
                        PollOpt::edge() | PollOpt::oneshot()).unwrap();

    // Start the event loop
    event_loop.run(&mut Echo::new(srv, sock, vec!["foo", "bar"])).unwrap();
}
<commit_msg>Change test_echo_server to use Poll API. (#701)<commit_after>use {localhost, TryRead, TryWrite};
use mio::{Events, Poll, PollOpt, Ready, Token};
use mio::net::{TcpListener, TcpStream};
use bytes::{Buf, ByteBuf, MutByteBuf, SliceBuf};
use slab;
use std::io;

const SERVER: Token = Token(10_000_000);
const CLIENT: Token = Token(10_000_001);

struct EchoConn {
    sock: TcpStream,
    buf: Option<ByteBuf>,
    mut_buf: Option<MutByteBuf>,
    token: Option<Token>,
    interest: Ready
}

type Slab<T> = slab::Slab<T, Token>;

impl EchoConn {
    fn new(sock: TcpStream) -> EchoConn {
        EchoConn {
            sock: sock,
            buf: None,
            mut_buf: Some(ByteBuf::mut_with_capacity(2048)),
            token: None,
            interest: Ready::hup()
        }
    }

    fn writable(&mut self, poll: &mut Poll) -> io::Result<()> {
        let mut buf = self.buf.take().unwrap();

        match self.sock.try_write_buf(&mut buf) {
            Ok(None) => {
                debug!("client flushing buf; WOULDBLOCK");

                self.buf = Some(buf);
                self.interest.insert(Ready::writable());
            }
            Ok(Some(r)) => {
                debug!("CONN : we wrote {} bytes!", r);

                self.mut_buf = Some(buf.flip());

                self.interest.insert(Ready::readable());
                self.interest.remove(Ready::writable());
            }
            Err(e) => debug!("not implemented; client err={:?}", e),
        }

        assert!(self.interest.is_readable() || self.interest.is_writable(), "actual={:?}", self.interest);
        poll.reregister(&self.sock, self.token.unwrap(), self.interest,
                              PollOpt::edge() | PollOpt::oneshot())
    }

    fn readable(&mut self, poll: &mut Poll) -> io::Result<()> {
        let mut buf = self.mut_buf.take().unwrap();

        match self.sock.try_read_buf(&mut buf) {
            Ok(None) => {
                debug!("CONN : spurious read wakeup");
                self.mut_buf = Some(buf);
            }
            Ok(Some(r)) => {
                debug!("CONN : we read {} bytes!", r);

                // prepare to provide this to writable
                self.buf = Some(buf.flip());

                self.interest.remove(Ready::readable());
                self.interest.insert(Ready::writable());
            }
            Err(e) => {
                debug!("not implemented; client err={:?}", e);
                self.interest.remove(Ready::readable());
            }

        };

        assert!(self.interest.is_readable() || self.interest.is_writable(), "actual={:?}", self.interest);
        poll.reregister(&self.sock, self.token.unwrap(), self.interest,
                              PollOpt::edge())
    }
}

struct EchoServer {
    sock: TcpListener,
    conns: Slab<EchoConn>
}

impl EchoServer {
    fn accept(&mut self, poll: &mut Poll) -> io::Result<()> {
        debug!("server accepting socket");

        let sock = self.sock.accept().unwrap().0;
        let conn = EchoConn::new(sock,);
        let tok = self.conns.insert(conn)
            .ok().expect("could not add connection to slab");

        // Register the connection
        self.conns[tok].token = Some(tok);
        poll.register(&self.conns[tok].sock, tok, Ready::readable(),
                                PollOpt::edge() | PollOpt::oneshot())
            .ok().expect("could not register socket with event loop");

        Ok(())
    }

    fn conn_readable(&mut self, poll: &mut Poll,
                     tok: Token) -> io::Result<()> {
        debug!("server conn readable; tok={:?}", tok);
        self.conn(tok).readable(poll)
    }

    fn conn_writable(&mut self, poll: &mut Poll,
                     tok: Token) -> io::Result<()> {
        debug!("server conn writable; tok={:?}", tok);
        self.conn(tok).writable(poll)
    }

    fn conn<'a>(&'a mut self, tok: Token) -> &'a mut EchoConn {
        &mut self.conns[tok]
    }
}

struct EchoClient {
    sock: TcpStream,
    msgs: Vec<&'static str>,
    tx: SliceBuf<'static>,
    rx: SliceBuf<'static>,
    mut_buf: Option<MutByteBuf>,
    token: Token,
    interest: Ready,
    shutdown: bool,
}

// Sends a message and expects to receive the same exact message, one at a time
impl EchoClient {
    fn new(sock: TcpStream, tok: Token,  mut msgs: Vec<&'static str>) -> EchoClient {
        let curr = msgs.remove(0);

        EchoClient {
            sock: sock,
            msgs: msgs,
            tx: SliceBuf::wrap(curr.as_bytes()),
            rx: SliceBuf::wrap(curr.as_bytes()),
            mut_buf: Some(ByteBuf::mut_with_capacity(2048)),
            token: tok,
            interest: Ready::empty(),
            shutdown: false,
        }
    }

    fn readable(&mut self, poll: &mut Poll) -> io::Result<()> {
        debug!("client socket readable");

        let mut buf = self.mut_buf.take().unwrap();

        match self.sock.try_read_buf(&mut buf) {
            Ok(None) => {
                debug!("CLIENT : spurious read wakeup");
                self.mut_buf = Some(buf);
            }
            Ok(Some(r)) => {
                debug!("CLIENT : We read {} bytes!", r);

                // prepare for reading
                let mut buf = buf.flip();

                while buf.has_remaining() {
                    let actual = buf.read_byte().unwrap();
                    let expect = self.rx.read_byte().unwrap();

                    assert!(actual == expect, "actual={}; expect={}", actual, expect);
                }

                self.mut_buf = Some(buf.flip());

                self.interest.remove(Ready::readable());

                if !self.rx.has_remaining() {
                    self.next_msg(poll).unwrap();
                }
            }
            Err(e) => {
                panic!("not implemented; client err={:?}", e);
            }
        };

        if !self.interest.is_empty() {
            assert!(self.interest.is_readable() || self.interest.is_writable(), "actual={:?}", self.interest);
            poll.reregister(&self.sock, self.token, self.interest,
                                       PollOpt::edge() | PollOpt::oneshot())?;
        }

        Ok(())
    }

    fn writable(&mut self, poll: &mut Poll) -> io::Result<()> {
        debug!("client socket writable");

        match self.sock.try_write_buf(&mut self.tx) {
            Ok(None) => {
                debug!("client flushing buf; WOULDBLOCK");
                self.interest.insert(Ready::writable());
            }
            Ok(Some(r)) => {
                debug!("CLIENT : we wrote {} bytes!", r);
                self.interest.insert(Ready::readable());
                self.interest.remove(Ready::writable());
            }
            Err(e) => debug!("not implemented; client err={:?}", e)
        }

        if self.interest.is_readable() || self.interest.is_writable() {
            try!(poll.reregister(&self.sock, self.token, self.interest,
                                  PollOpt::edge() | PollOpt::oneshot()));
        }

        Ok(())
    }

    fn next_msg(&mut self, poll: &mut Poll) -> io::Result<()> {
        if self.msgs.is_empty() {
            self.shutdown = true;
            return Ok(());
        }

        let curr = self.msgs.remove(0);

        debug!("client prepping next message");
        self.tx = SliceBuf::wrap(curr.as_bytes());
        self.rx = SliceBuf::wrap(curr.as_bytes());

        self.interest.insert(Ready::writable());
        poll.reregister(&self.sock, self.token, self.interest,
                              PollOpt::edge() | PollOpt::oneshot())
    }
}

struct Echo {
    server: EchoServer,
    client: EchoClient,
}

impl Echo {
    fn new(srv: TcpListener, client: TcpStream, msgs: Vec<&'static str>) -> Echo {
        Echo {
            server: EchoServer {
                sock: srv,
                conns: Slab::with_capacity(128)
            },
            client: EchoClient::new(client, CLIENT, msgs)
        }
    }
}

#[test]
pub fn test_echo_server() {
    debug!("Starting TEST_ECHO_SERVER");
    let mut poll = Poll::new().unwrap();

    let addr = localhost();
    let srv = TcpListener::bind(&addr).unwrap();

    info!("listen for connections");
    poll.register(&srv, SERVER, Ready::readable(),
                            PollOpt::edge() | PollOpt::oneshot()).unwrap();

    let sock = TcpStream::connect(&addr).unwrap();

    // Connect to the server
    poll.register(&sock, CLIENT, Ready::writable(),
                        PollOpt::edge() | PollOpt::oneshot()).unwrap();
    // == Create storage for events
    let mut events = Events::with_capacity(1024);

    let mut handler = Echo::new(srv, sock, vec!["foo", "bar"]);

    // Start the event loop
    while !handler.client.shutdown {
        poll.poll(&mut events, None).unwrap();

        for event in &events {
            debug!("ready {:?} {:?}", event.token(), event.readiness());
            if event.readiness().is_readable() {
                match event.token() {
                    SERVER => handler.server.accept(&mut poll).unwrap(),
                    CLIENT => handler.client.readable(&mut poll).unwrap(),
                    i => handler.server.conn_readable(&mut poll, i).unwrap()
                }
            }

            if event.readiness().is_writable() {
                match event.token() {
                    SERVER => panic!("received writable for token 0"),
                    CLIENT => handler.client.writable(&mut poll).unwrap(),
                    i => handler.server.conn_writable(&mut poll, i).unwrap()
                };
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::io::stderr;
use std::path::Path;
use std::process;

const PREVIEW_LEN: usize = 50;

#[derive(Debug, PartialEq)]
struct Match {
    offset: usize,
    key: u8,
    preview: String,
}

fn main() {
    let args: Vec<String> = env::args().skip(1).collect();
    if args.len() < 2 {
        writeln!(stderr(), "usage: roxor <file> <crib>").unwrap();
        process::exit(1);
    }

    let path = Path::new(&args[0]);
    let crib = args[1].as_bytes();

    if crib.len() < 2 {
        writeln!(stderr(), "error: crib too short").unwrap();
        process::exit(1);
    }

    let mut file = match File::open(&path) {
        Err(why) => panic!("couldn't open {}: {}", path.display(), why),
        Ok(file) => file,
    };

    let mut ciphertext: Vec<u8> = Vec::new();
    file.read_to_end(&mut ciphertext).unwrap();

    if ciphertext.len() < crib.len() {
        writeln!(stderr(), "error: ciphertext too short").unwrap();
        process::exit(1);
    }

    for m in attack_cipher(&ciphertext[..], crib) {
        println!("Found text at 0x{:x} (XOR key 0x{:02x})", m.offset, m.key);
        println!("  preview: {}", m.preview);
    }
}

fn attack_cipher(ciphertext: &[u8], crib: &[u8]) -> Vec<Match> {
    let mut matches: Vec<Match> = Vec::new();
    for (i, x) in ciphertext.iter().enumerate() {
        let mut old_key = x ^ crib[0];
        let mut j = 1;
        while j < crib.len() && (i + j) < ciphertext.len() {
            let key = ciphertext[i + j] ^ crib[j];
            if key != old_key {
                break;
            }
            old_key = key;
            j += 1;
            if j == crib.len() {
                let preview: String = ciphertext[i..]
                    .iter()
                    .take(PREVIEW_LEN)
                    .map(|x| x ^ key)
                    .map(|x| if x >= 32 && x <= 126 { x as char } else { '.' })
                    .collect();
                matches.push(
                    Match {
                        offset: i,
                        key: key,
                        preview: preview,
                    },
                );
            }
        }
    }
    return matches;
}

#[cfg(test)]
mod tests {
    use super::{Match, attack_cipher};

    struct Test {
        ciphertext: &'static [u8],
        crib: &'static [u8],
        matches: Vec<Match>,
    }

    #[test]
    fn test_attack_cipher() {
        let tests = vec![
            Test {
                ciphertext: &[],
                crib: &[],
                matches: vec![],
            },
            Test {
                ciphertext: b"haystack",
                crib: b"needle",
                matches: vec![],
            },
            Test {
                ciphertext: b"needle in haystack",
                crib: b"needle",
                matches: vec![
                    Match { offset: 0, key: 0, preview: "needle in haystack".to_string() },
                ],
            },
            Test {
                ciphertext: b"a needle, another needle",
                crib: b"needle",
                matches: vec![
                    Match { offset: 2, key: 0, preview: "needle, another needle".to_string() },
                    Match { offset: 18, key: 0, preview: "needle".to_string() },
                ],
            },
            Test {
                ciphertext: b"\x23\x62\x2c\x27\x27\x26\x2e\x27\x6e\x62\x23\x2c\x2d\x36\x2a\x27\x30\x62\x2c\x27\x27\x26\x2e\x27",
                crib: b"needle",
                matches: vec![
                    Match { offset: 2, key: 0x42, preview: "needle, another needle".to_string() },
                    Match { offset: 18, key: 0x42, preview: "needle".to_string() },
                ],
            },
        ];

        for t in tests.iter() {
            assert_eq!(t.matches, attack_cipher(t.ciphertext, t.crib));
        }
    }
}
<commit_msg>rustfmt alt/roxor.rs<commit_after>use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::io::stderr;
use std::path::Path;
use std::process;

const PREVIEW_LEN: usize = 50;

#[derive(Debug, PartialEq)]
struct Match {
    offset: usize,
    key: u8,
    preview: String,
}

fn main() {
    let args: Vec<String> = env::args().skip(1).collect();
    if args.len() < 2 {
        writeln!(stderr(), "usage: roxor <file> <crib>").unwrap();
        process::exit(1);
    }

    let path = Path::new(&args[0]);
    let crib = args[1].as_bytes();

    if crib.len() < 2 {
        writeln!(stderr(), "error: crib too short").unwrap();
        process::exit(1);
    }

    let mut file = match File::open(&path) {
        Err(why) => panic!("couldn't open {}: {}", path.display(), why),
        Ok(file) => file,
    };

    let mut ciphertext: Vec<u8> = Vec::new();
    file.read_to_end(&mut ciphertext).unwrap();

    if ciphertext.len() < crib.len() {
        writeln!(stderr(), "error: ciphertext too short").unwrap();
        process::exit(1);
    }

    for m in attack_cipher(&ciphertext[..], crib) {
        println!("Found text at 0x{:x} (XOR key 0x{:02x})", m.offset, m.key);
        println!("  preview: {}", m.preview);
    }
}

fn attack_cipher(ciphertext: &[u8], crib: &[u8]) -> Vec<Match> {
    let mut matches: Vec<Match> = Vec::new();
    for (i, x) in ciphertext.iter().enumerate() {
        let mut old_key = x ^ crib[0];
        let mut j = 1;
        while j < crib.len() && (i + j) < ciphertext.len() {
            let key = ciphertext[i + j] ^ crib[j];
            if key != old_key {
                break;
            }
            old_key = key;
            j += 1;
            if j == crib.len() {
                let preview: String = ciphertext[i..]
                    .iter()
                    .take(PREVIEW_LEN)
                    .map(|x| x ^ key)
                    .map(|x| if x >= 32 && x <= 126 { x as char } else { '.' })
                    .collect();
                matches.push(Match {
                    offset: i,
                    key: key,
                    preview: preview,
                });
            }
        }
    }
    return matches;
}

#[cfg(test)]
mod tests {
    use super::{attack_cipher, Match};

    struct Test {
        ciphertext: &'static [u8],
        crib: &'static [u8],
        matches: Vec<Match>,
    }

    #[test]
    fn test_attack_cipher() {
        let tests = vec![
            Test {
                ciphertext: &[],
                crib: &[],
                matches: vec![],
            },
            Test {
                ciphertext: b"haystack",
                crib: b"needle",
                matches: vec![],
            },
            Test {
                ciphertext: b"needle in haystack",
                crib: b"needle",
                matches: vec![
                    Match { offset: 0, key: 0, preview: "needle in haystack".to_string() },
                ],
            },
            Test {
                ciphertext: b"a needle, another needle",
                crib: b"needle",
                matches: vec![
                    Match { offset: 2, key: 0, preview: "needle, another needle".to_string() },
                    Match { offset: 18, key: 0, preview: "needle".to_string() },
                ],
            },
            Test {
                ciphertext: b"\x23\x62\x2c\x27\x27\x26\x2e\x27\x6e\x62\x23\x2c\x2d\x36\x2a\x27\x30\x62\x2c\x27\x27\x26\x2e\x27",
                crib: b"needle",
                matches: vec![
                    Match { offset: 2, key: 0x42, preview: "needle, another needle".to_string() },
                    Match { offset: 18, key: 0x42, preview: "needle".to_string() },
                ],
            },
        ];

        for t in tests.iter() {
            assert_eq!(t.matches, attack_cipher(t.ciphertext, t.crib));
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>Refactor the function `many_blocks`<commit_after><|endoftext|>


<commit_before>extern crate piston;
extern crate opengl_graphics;

fn main() {
    println!("Click in the red square and drag.");
    println!("Toggle grid with G.");
    println!("Reset grid with R.");

    piston::start(
        piston::shader_version::OpenGL::_3_2,
        piston::WindowSettings {
            title: "Deform".to_string(),
            size: [300, 300],
            fullscreen: false,
            exit_on_esc: true,
            samples: 4,
        },
        || start()
    );
}

pub fn start() {
    use piston::graphics;
    use piston::graphics::ImageSize;
    use piston::drag_controller::{
        DragController,
        Drag
    };
    use opengl_graphics::Texture;

    let image = Path::new("./bin/assets/rust-logo.png");
    let image = Texture::from_path(&image).unwrap();

    let (width, height) = image.get_size();
    let width = width as f64;
    let height = height as f64;
    let mut grid = graphics::deform::DeformGrid::new(
        [0.0, 0.0, width, height],
        20, 20
    );

    let mut drag = DragController::new();
    let mut draw_grid = true;

    for e in piston::events() {
        use piston::event::{ RenderEvent, PressEvent };

        drag.event(&e, |action| {
            match action {
                Drag::Start(x, y) => {
                    match grid.hit([x, y]) {
                        None => {
                            // Did not hit deformed grid.
                            grid.add_control_point([x, y]);
                        }
                        Some(pos) => {
                            // Add point to deformed grid.
                            grid.add_control_point(pos);
                            let n = grid.ps.len();
                            grid.set_current(n - 1, [x, y]);
                        }
                    }
                    grid.update();
                    true
                }
                Drag::Move(x, y) => {
                    let n = grid.ps.len();
                    grid.set_current(n - 1, [x, y]);
                    grid.update();
                    true
                }
                Drag::End(_, _) => false,
                // Continue dragging when receiving focus.
                Drag::Interrupt => true,
            }
        });
        e.press(|button| {
            use piston::input::Button::Keyboard;
            use piston::input::keyboard::Key;

            if button == Keyboard(Key::G) {
                draw_grid = !draw_grid;
                println!("Draw grid {}", draw_grid);
            } else if button == Keyboard(Key::R) {
                grid.reset_control_points();
                grid.reset_vertices_and_texture_coords();
                grid.update();
                println!("Reset grid");
            }
        });
        e.render(|_args| {
            piston::render_2d_opengl(
                unsafe { piston::DANGER::new() },
                Some(graphics::color::WHITE),
                |c, g| {

            // Draw deformed image.
            grid.draw_image(&image, &c, g);

            if draw_grid {
                // Draw grid.
                grid.draw_vertical_lines(
                    &graphics::Line::new([0.0, 1.0, 0.0, 1.0], 0.5),
                    &c,
                    g
                );
                grid.draw_horizontal_lines(
                    &graphics::Line::new([0.0, 0.0, 1.0, 1.0], 0.5),
                    &c,
                    g
                );
            }

            // Draw rect of the original grid.
            graphics::Rectangle::border([1.0, 0.0, 0.0, 1.0], 1.5)
                .draw([0.0, 0.0, width, height], &c, g);

            // Draw control points.
            let original = graphics::Ellipse::new([1.0, 0.0, 0.0, 0.5]);
            let current = graphics::Ellipse::new([0.0, 0.0, 0.0, 0.5]);
            for i in range(0, grid.ps.len()) {
                use piston::graphics::ellipse::circle;

                // Original positions.
                let x = grid.ps[i][0];
                let y = grid.ps[i][1];
                original.draw(circle(x, y, 3.0), &c, g);

                // Current positions.
                let x = grid.qs[i][0];
                let y = grid.qs[i][1];
                current.draw(circle(x, y, 3.0), &c, g);
            };

            });
        }); // end render
    }
}

<commit_msg>Upgraded to latest piston<commit_after>extern crate piston;
extern crate opengl_graphics;

fn main() {
    println!("Click in the red square and drag.");
    println!("Toggle grid with G.");
    println!("Reset grid with R.");

    piston::start(
        piston::shader_version::OpenGL::_3_2,
        piston::WindowSettings {
            title: "Deform".to_string(),
            size: [300, 300],
            fullscreen: false,
            exit_on_esc: true,
            samples: 4,
        },
        || start()
    );
}

pub fn start() {
    use piston::graphics;
    use piston::graphics::ImageSize;
    use piston::drag_controller::{
        DragController,
        Drag
    };
    use opengl_graphics::Texture;

    let image = Path::new("./bin/assets/rust-logo.png");
    let image = Texture::from_path(&image).unwrap();

    let (width, height) = image.get_size();
    let width = width as f64;
    let height = height as f64;
    let mut grid = graphics::deform::DeformGrid::new(
        [0.0, 0.0, width, height],
        20, 20
    );

    let mut drag = DragController::new();
    let mut draw_grid = true;

    for e in piston::events() {
        use piston::event::{ RenderEvent, PressEvent };

        drag.event(&e, |action| {
            match action {
                Drag::Start(x, y) => {
                    match grid.hit([x, y]) {
                        None => {
                            // Did not hit deformed grid.
                            grid.add_control_point([x, y]);
                        }
                        Some(pos) => {
                            // Add point to deformed grid.
                            grid.add_control_point(pos);
                            let n = grid.ps.len();
                            grid.set_current(n - 1, [x, y]);
                        }
                    }
                    grid.update();
                    true
                }
                Drag::Move(x, y) => {
                    let n = grid.ps.len();
                    grid.set_current(n - 1, [x, y]);
                    grid.update();
                    true
                }
                Drag::End(_, _) => false,
                // Continue dragging when receiving focus.
                Drag::Interrupt => true,
            }
        });
        e.press(|button| {
            use piston::input::Button::Keyboard;
            use piston::input::keyboard::Key;

            if button == Keyboard(Key::G) {
                draw_grid = !draw_grid;
                println!("Draw grid {}", draw_grid);
            } else if button == Keyboard(Key::R) {
                grid.reset_control_points();
                grid.reset_vertices_and_texture_coords();
                grid.update();
                println!("Reset grid");
            }
        });
        e.render(|_args| {
            piston::render_2d_opengl(
                Some(graphics::color::WHITE),
                |c, g| {

            // Draw deformed image.
            grid.draw_image(&image, &c, g);

            if draw_grid {
                // Draw grid.
                grid.draw_vertical_lines(
                    &graphics::Line::new([0.0, 1.0, 0.0, 1.0], 0.5),
                    &c,
                    g
                );
                grid.draw_horizontal_lines(
                    &graphics::Line::new([0.0, 0.0, 1.0, 1.0], 0.5),
                    &c,
                    g
                );
            }

            // Draw rect of the original grid.
            graphics::Rectangle::border([1.0, 0.0, 0.0, 1.0], 1.5)
                .draw([0.0, 0.0, width, height], &c, g);

            // Draw control points.
            let original = graphics::Ellipse::new([1.0, 0.0, 0.0, 0.5]);
            let current = graphics::Ellipse::new([0.0, 0.0, 0.0, 0.5]);
            for i in range(0, grid.ps.len()) {
                use piston::graphics::ellipse::circle;

                // Original positions.
                let x = grid.ps[i][0];
                let y = grid.ps[i][1];
                original.draw(circle(x, y, 3.0), &c, g);

                // Current positions.
                let x = grid.qs[i][0];
                let y = grid.qs[i][1];
                current.draw(circle(x, y, 3.0), &c, g);
            };

            });
        }); // end render
    }
}

<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::io;
use std::process::Command;
use spec::{LinkArgs, LinkerFlavor, TargetOptions};

use self::Arch::*;

#[allow(non_camel_case_types)]
#[derive(Copy, Clone)]
pub enum Arch {
    Armv7,
    Armv7s,
    Arm64,
    I386,
    X86_64
}

impl Arch {
    pub fn to_string(self) -> &'static str {
        match self {
            Armv7 => "armv7",
            Armv7s => "armv7s",
            Arm64 => "arm64",
            I386 => "i386",
            X86_64 => "x86_64"
        }
    }
}

pub fn get_sdk_root(sdk_name: &str) -> Result<String, String> {
    let res = Command::new("xcrun")
                      .arg("--show-sdk-path")
                      .arg("-sdk")
                      .arg(sdk_name)
                      .output()
                      .and_then(|output| {
                          if output.status.success() {
                              Ok(String::from_utf8(output.stdout).unwrap())
                          } else {
                              let error = String::from_utf8(output.stderr);
                              let error = format!("process exit with error: {}",
                                                  error.unwrap());
                              Err(io::Error::new(io::ErrorKind::Other,
                                                 &error[..]))
                          }
                      });

    match res {
        Ok(output) => Ok(output.trim().to_string()),
        Err(e) => Err(format!("failed to get {} SDK path: {}", sdk_name, e))
    }
}

fn build_pre_link_args(arch: Arch) -> Result<LinkArgs, String> {
    let sdk_name = match arch {
        Armv7 | Armv7s | Arm64 => "iphoneos",
        I386 | X86_64 => "iphonesimulator"
    };

    let arch_name = arch.to_string();

    let sdk_root = get_sdk_root(sdk_name)?;

    let mut args = LinkArgs::new();
    args.insert(LinkerFlavor::Gcc,
                vec!["-arch".to_string(),
                     arch_name.to_string(),
                     "-Wl,-syslibroot".to_string(),
                     sdk_root]);

    Ok(args)
}

fn target_cpu(arch: Arch) -> String {
    match arch {
        Armv7 => "cortex-a8", // iOS7 is supported on iPhone 4 and higher
        Armv7s => "cortex-a9",
        Arm64 => "cyclone",
        I386 => "yonah",
        X86_64 => "core2",
    }.to_string()
}

pub fn opts(arch: Arch) -> Result<TargetOptions, String> {
    let pre_link_args = build_pre_link_args(arch)?;
    Ok(TargetOptions {
        cpu: target_cpu(arch),
        dynamic_linking: false,
        executables: true,
        pre_link_args,
        has_elf_tls: false,
        eliminate_frame_pointer: false,
        .. super::apple_base::opts()
    })
}
<commit_msg>Auto merge of #56833 - nagisa:ios-fix, r=alexcrichton<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::io;
use std::process::Command;
use spec::{LinkArgs, LinkerFlavor, TargetOptions};

use self::Arch::*;

#[allow(non_camel_case_types)]
#[derive(Copy, Clone)]
pub enum Arch {
    Armv7,
    Armv7s,
    Arm64,
    I386,
    X86_64
}

impl Arch {
    pub fn to_string(self) -> &'static str {
        match self {
            Armv7 => "armv7",
            Armv7s => "armv7s",
            Arm64 => "arm64",
            I386 => "i386",
            X86_64 => "x86_64"
        }
    }
}

pub fn get_sdk_root(sdk_name: &str) -> Result<String, String> {
    let res = Command::new("xcrun")
                      .arg("--show-sdk-path")
                      .arg("-sdk")
                      .arg(sdk_name)
                      .output()
                      .and_then(|output| {
                          if output.status.success() {
                              Ok(String::from_utf8(output.stdout).unwrap())
                          } else {
                              let error = String::from_utf8(output.stderr);
                              let error = format!("process exit with error: {}",
                                                  error.unwrap());
                              Err(io::Error::new(io::ErrorKind::Other,
                                                 &error[..]))
                          }
                      });

    match res {
        Ok(output) => Ok(output.trim().to_string()),
        Err(e) => Err(format!("failed to get {} SDK path: {}", sdk_name, e))
    }
}

fn build_pre_link_args(arch: Arch) -> Result<LinkArgs, String> {
    let sdk_name = match arch {
        Armv7 | Armv7s | Arm64 => "iphoneos",
        I386 | X86_64 => "iphonesimulator"
    };

    let arch_name = arch.to_string();

    let sdk_root = get_sdk_root(sdk_name)?;

    let mut args = LinkArgs::new();
    args.insert(LinkerFlavor::Gcc,
                vec!["-arch".to_string(),
                     arch_name.to_string(),
                     "-isysroot".to_string(),
                     sdk_root.clone(),
                     "-Wl,-syslibroot".to_string(),
                     sdk_root]);

    Ok(args)
}

fn target_cpu(arch: Arch) -> String {
    match arch {
        Armv7 => "cortex-a8", // iOS7 is supported on iPhone 4 and higher
        Armv7s => "cortex-a9",
        Arm64 => "cyclone",
        I386 => "yonah",
        X86_64 => "core2",
    }.to_string()
}

pub fn opts(arch: Arch) -> Result<TargetOptions, String> {
    let pre_link_args = build_pre_link_args(arch)?;
    Ok(TargetOptions {
        cpu: target_cpu(arch),
        dynamic_linking: false,
        executables: true,
        pre_link_args,
        has_elf_tls: false,
        eliminate_frame_pointer: false,
        .. super::apple_base::opts()
    })
}
<|endoftext|>


<commit_before>//! Renderer for `struct` literal.

use hir::{db::HirDatabase, HasAttrs, HasVisibility, Name, StructKind};
use ide_db::helpers::SnippetCap;
use itertools::Itertools;
use syntax::SmolStr;

use crate::{render::RenderContext, CompletionItem, CompletionItemKind};

pub(crate) fn render_struct_literal(
    ctx: RenderContext<'_>,
    strukt: hir::Struct,
    path: Option<hir::ModPath>,
    local_name: Option<Name>,
) -> Option<CompletionItem> {
    let _p = profile::span("render_struct_literal");

    let fields = strukt.fields(ctx.db());
    let (visible_fields, fields_omitted) = visible_fields(&ctx, &fields, strukt)?;

    if fields_omitted {
        // If some fields are private you can't make `struct` literal.
        return None;
    }

    let name = local_name.unwrap_or_else(|| strukt.name(ctx.db())).to_smol_str();

    let literal = render_literal(&ctx, path, &name, strukt.kind(ctx.db()), &visible_fields)?;

    Some(build_completion(ctx, name, literal, strukt))
}

fn build_completion(
    ctx: RenderContext<'_>,
    name: SmolStr,
    literal: String,
    def: impl HasAttrs + Copy,
) -> CompletionItem {
    let mut item = CompletionItem::new(
        CompletionItemKind::Snippet,
        ctx.source_range(),
        SmolStr::from_iter([&name, " {…}"]),
    );
    item.set_documentation(ctx.docs(def)).set_deprecated(ctx.is_deprecated(def)).detail(&literal);
    match ctx.snippet_cap() {
        Some(snippet_cap) => item.insert_snippet(snippet_cap, literal),
        None => item.insert_text(literal),
    };
    item.build()
}

fn render_literal(
    ctx: &RenderContext<'_>,
    path: Option<hir::ModPath>,
    name: &str,
    kind: StructKind,
    fields: &[hir::Field],
) -> Option<String> {
    let qualified_name = if let Some(path) = path { path.to_string() } else { name.to_string() };

    let mut literal = match kind {
        StructKind::Tuple if ctx.snippet_cap().is_some() => {
            render_tuple_as_literal(fields, &qualified_name)
        }
        StructKind::Record => {
            render_record_as_literal(ctx.db(), ctx.snippet_cap(), fields, &qualified_name)
        }
        _ => return None,
    };

    if ctx.snippet_cap().is_some() {
        literal.push_str("$0");
    }
    Some(literal)
}

fn render_record_as_literal(
    db: &dyn HirDatabase,
    snippet_cap: Option<SnippetCap>,
    fields: &[hir::Field],
    name: &str,
) -> String {
    let fields = fields.iter();
    if snippet_cap.is_some() {
        format!(
            "{name} {{ {} }}",
            fields
                .enumerate()
                .map(|(idx, field)| format!("{}: ${{{}:()}}", field.name(db), idx + 1))
                .format(", "),
            name = name
        )
    } else {
        format!(
            "{name} {{ {} }}",
            fields.map(|field| format!("{}: ()", field.name(db))).format(", "),
            name = name
        )
    }
}

fn render_tuple_as_literal(fields: &[hir::Field], name: &str) -> String {
    format!(
        "{name}({})",
        fields.iter().enumerate().map(|(idx, _)| format!("${}", idx + 1)).format(", "),
        name = name
    )
}

fn visible_fields(
    ctx: &RenderContext<'_>,
    fields: &[hir::Field],
    item: impl HasAttrs,
) -> Option<(Vec<hir::Field>, bool)> {
    let module = ctx.completion.scope.module()?;
    let n_fields = fields.len();
    let fields = fields
        .iter()
        .filter(|field| field.is_visible_from(ctx.db(), module))
        .copied()
        .collect::<Vec<_>>();

    let fields_omitted =
        n_fields - fields.len() > 0 || item.attrs(ctx.db()).by_key("non_exhaustive").exists();
    Some((fields, fields_omitted))
}
<commit_msg>Add optimization for not allocating in case of no module path for struct literal<commit_after>//! Renderer for `struct` literal.

use hir::{db::HirDatabase, HasAttrs, HasVisibility, Name, StructKind};
use ide_db::helpers::SnippetCap;
use itertools::Itertools;
use syntax::SmolStr;

use crate::{render::RenderContext, CompletionItem, CompletionItemKind};

pub(crate) fn render_struct_literal(
    ctx: RenderContext<'_>,
    strukt: hir::Struct,
    path: Option<hir::ModPath>,
    local_name: Option<Name>,
) -> Option<CompletionItem> {
    let _p = profile::span("render_struct_literal");

    let fields = strukt.fields(ctx.db());
    let (visible_fields, fields_omitted) = visible_fields(&ctx, &fields, strukt)?;

    if fields_omitted {
        // If some fields are private you can't make `struct` literal.
        return None;
    }

    let name = local_name.unwrap_or_else(|| strukt.name(ctx.db())).to_smol_str();

    let literal = render_literal(&ctx, path, &name, strukt.kind(ctx.db()), &visible_fields)?;

    Some(build_completion(ctx, name, literal, strukt))
}

fn build_completion(
    ctx: RenderContext<'_>,
    name: SmolStr,
    literal: String,
    def: impl HasAttrs + Copy,
) -> CompletionItem {
    let mut item = CompletionItem::new(
        CompletionItemKind::Snippet,
        ctx.source_range(),
        SmolStr::from_iter([&name, " {…}"]),
    );
    item.set_documentation(ctx.docs(def)).set_deprecated(ctx.is_deprecated(def)).detail(&literal);
    match ctx.snippet_cap() {
        Some(snippet_cap) => item.insert_snippet(snippet_cap, literal),
        None => item.insert_text(literal),
    };
    item.build()
}

fn render_literal(
    ctx: &RenderContext<'_>,
    path: Option<hir::ModPath>,
    name: &str,
    kind: StructKind,
    fields: &[hir::Field],
) -> Option<String> {
    let path_string;

    let qualified_name = if let Some(path) = path {
        path_string = path.to_string();
        &path_string
    } else {
        name
    };

    let mut literal = match kind {
        StructKind::Tuple if ctx.snippet_cap().is_some() => {
            render_tuple_as_literal(fields, qualified_name)
        }
        StructKind::Record => {
            render_record_as_literal(ctx.db(), ctx.snippet_cap(), fields, qualified_name)
        }
        _ => return None,
    };

    if ctx.snippet_cap().is_some() {
        literal.push_str("$0");
    }
    Some(literal)
}

fn render_record_as_literal(
    db: &dyn HirDatabase,
    snippet_cap: Option<SnippetCap>,
    fields: &[hir::Field],
    name: &str,
) -> String {
    let fields = fields.iter();
    if snippet_cap.is_some() {
        format!(
            "{name} {{ {} }}",
            fields
                .enumerate()
                .map(|(idx, field)| format!("{}: ${{{}:()}}", field.name(db), idx + 1))
                .format(", "),
            name = name
        )
    } else {
        format!(
            "{name} {{ {} }}",
            fields.map(|field| format!("{}: ()", field.name(db))).format(", "),
            name = name
        )
    }
}

fn render_tuple_as_literal(fields: &[hir::Field], name: &str) -> String {
    format!(
        "{name}({})",
        fields.iter().enumerate().map(|(idx, _)| format!("${}", idx + 1)).format(", "),
        name = name
    )
}

fn visible_fields(
    ctx: &RenderContext<'_>,
    fields: &[hir::Field],
    item: impl HasAttrs,
) -> Option<(Vec<hir::Field>, bool)> {
    let module = ctx.completion.scope.module()?;
    let n_fields = fields.len();
    let fields = fields
        .iter()
        .filter(|field| field.is_visible_from(ctx.db(), module))
        .copied()
        .collect::<Vec<_>>();

    let fields_omitted =
        n_fields - fields.len() > 0 || item.attrs(ctx.db()).by_key("non_exhaustive").exists();
    Some((fields, fields_omitted))
}
<|endoftext|>