<commit_before>//! The distrib module provides methods for executing the rendering in a
//! distributed environment across multiple nodes. The worker module provides
//! the Worker which does the actual job of rendering a subsection of the image.
//! The master module provides the Master which instructs the Workers and collects
//! their results.
//!
//! **Note:** At this time I do nothing for distributed fault handling.

use serde_json::{self, Value};

pub use self::worker::Worker;
pub use self::master::Master;

pub mod worker;
pub mod master;

#[derive(Debug, Clone)]
struct Instructions {
    pub scene: String,
    pub master_port: u16,
    pub frames: Option<(usize, usize)>,
    pub block_start: usize,
    pub block_count: usize,
}

impl Instructions {
    pub fn new(scene: &String, master_port: u16, frames: Option<(usize, usize)>,
               block_start: usize, block_count: usize) -> Instructions {
        Instructions { scene: scene.clone(), master_port: master_port, frames: frames,
                       block_start: block_start, block_count: block_count }
    }
    // TODO: This method is also temporary while we wait for custom derive
    pub fn from_json(data: String) -> Instructions {
        let json: Value = serde_json::from_str(&data[..]).expect("Invalid Instructions JSON string");
        println!("instructions = {:?}", json);
        let scene = json.find("scene").unwrap().as_string().unwrap();
        let master_port = json.find("master_port").unwrap().as_u64().unwrap() as u16;
        let block_start = json.find("block_start").unwrap().as_u64().unwrap() as usize;
        let block_count = json.find("block_count").unwrap().as_u64().unwrap() as usize;
        let frame_range = json.find("frame_range").unwrap().as_array().unwrap();
        let frames =
            if !frame_range.is_empty() {
                Some((frame_range[0].as_u64().unwrap() as usize, frame_range[0].as_u64().unwrap() as usize))
            } else {
                None
            };
        Instructions { scene: scene.to_string(), master_port: master_port, frames: frames,
                       block_start: block_start, block_count: block_count }
    }
    // TODO: This to_json method is temporary while we wait for custom derive
    // to stabilize
    pub fn to_json(&self) -> String {
        let frame_string = match self.frames {
            Some((start, end)) => format!("\"frame_range\": [{}, {}]", start, end),
            None => format!("\"frame_range\": []"),
        };
        // Note: We swap \ for / in file paths since JSON expects unix-style paths, a \xxx is
        // interpreted as an escape sequence
        let json = format!("{{
            \"scene\": \"{}\",
            \"master_port\": {},
            \"block_start\": {},
            \"block_count\": {},
            {}
        }}", self.scene.replace("\\", "/"), self.master_port, self.block_start, self.block_count, frame_string);
        println!("Built JSON {}", json);
        json
    }
}

<commit_msg>We no longer send the master port since it's not needed<commit_after>//! The distrib module provides methods for executing the rendering in a
//! distributed environment across multiple nodes. The worker module provides
//! the Worker which does the actual job of rendering a subsection of the image.
//! The master module provides the Master which instructs the Workers and collects
//! their results.
//!
//! **Note:** At this time I do nothing for distributed fault handling.

use serde_json::{self, Value};

pub use self::worker::Worker;
pub use self::master::Master;

pub mod worker;
pub mod master;

#[derive(Debug, Clone)]
struct Instructions {
    pub scene: String,
    pub frames: Option<(usize, usize)>,
    pub block_start: usize,
    pub block_count: usize,
}

impl Instructions {
    pub fn new(scene: &String, frames: Option<(usize, usize)>, block_start: usize,
               block_count: usize) -> Instructions {
        Instructions { scene: scene.clone(), frames: frames,
                       block_start: block_start, block_count: block_count }
    }
    // TODO: This method is also temporary while we wait for custom derive
    // Or could we just use bincode?
    pub fn from_json(data: String) -> Instructions {
        let json: Value = serde_json::from_str(&data[..]).expect("Invalid Instructions JSON string");
        println!("instructions = {:?}", json);
        let scene = json.find("scene").unwrap().as_string().unwrap();
        let block_start = json.find("block_start").unwrap().as_u64().unwrap() as usize;
        let block_count = json.find("block_count").unwrap().as_u64().unwrap() as usize;
        let frame_range = json.find("frame_range").unwrap().as_array().unwrap();
        let frames =
            if !frame_range.is_empty() {
                Some((frame_range[0].as_u64().unwrap() as usize, frame_range[0].as_u64().unwrap() as usize))
            } else {
                None
            };
        Instructions { scene: scene.to_string(), frames: frames,
                       block_start: block_start, block_count: block_count }
    }
    // TODO: This to_json method is temporary while we wait for custom derive
    // to stabilize
    pub fn to_json(&self) -> String {
        let frame_string = match self.frames {
            Some((start, end)) => format!("\"frame_range\": [{}, {}]", start, end),
            None => format!("\"frame_range\": []"),
        };
        // Note: We swap \ for / in file paths since JSON expects unix-style paths, a \xxx is
        // interpreted as an escape sequence
        let json = format!("{{
            \"scene\": \"{}\",
            \"block_start\": {},
            \"block_count\": {},
            {}
        }}", self.scene.replace("\\", "/"), self.block_start, self.block_count, frame_string);
        println!("Built JSON {}", json);
        json
    }
}

<|endoftext|>


<commit_before>//! `cargo upgrade`
#![warn(
    missing_docs,
    missing_debug_implementations,
    missing_copy_implementations,
    trivial_casts,
    trivial_numeric_casts,
    unsafe_code,
    unstable_features,
    unused_import_braces,
    unused_qualifications
)]

#[macro_use]
extern crate error_chain;

use crate::errors::*;
use cargo_edit::{
    find, get_latest_dependency, registry_url, update_registry_index, CrateName, Dependency,
    LocalManifest,
};
use failure::Fail;
use std::collections::{HashMap, HashSet};
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process;
use structopt::StructOpt;
use termcolor::{BufferWriter, Color, ColorChoice, ColorSpec, WriteColor};
use url::Url;

mod errors {
    error_chain! {
        links {
            CargoEditLib(::cargo_edit::Error, ::cargo_edit::ErrorKind);
        }
        foreign_links {
            CargoMetadata(::failure::Compat<::cargo_metadata::Error>);
        }
    }
}

#[derive(Debug, StructOpt)]
#[structopt(bin_name = "cargo")]
enum Command {
    /// Upgrade dependencies as specified in the local manifest file (i.e. Cargo.toml).
    #[structopt(name = "upgrade", author = "")]
    #[structopt(
        after_help = "This command differs from `cargo update`, which updates the dependency versions recorded in the
local lock file (Cargo.lock).

If `<dependency>`(s) are provided, only the specified dependencies will be upgraded. The version to
upgrade to for each can be specified with e.g. `docopt@0.8.0` or `serde@>=0.9,<2.0`.

Dev, build, and all target dependencies will also be upgraded. Only dependencies from crates.io are
supported. Git/path dependencies will be ignored.

All packages in the workspace will be upgraded if the `--all` flag is supplied. The `--all` flag may
be supplied in the presence of a virtual manifest.

If the '--to-lockfile' flag is supplied, all dependencies will be upgraded to the currently locked
version as recorded in the Cargo.lock file. This flag requires that the Cargo.lock file is
up-to-date. If the lock file is missing, or it needs to be updated, cargo-upgrade will exit with an
error. If the '--to-lockfile' flag is supplied then the network won't be accessed."
    )]
    Upgrade(Args),
}

#[derive(Debug, StructOpt)]
struct Args {
    /// Crates to be upgraded.
    dependency: Vec<String>,

    /// Path to the manifest to upgrade
    #[structopt(long = "manifest-path", value_name = "path")]
    manifest_path: Option<PathBuf>,

    /// Upgrade all packages in the workspace.
    #[structopt(long = "all")]
    all: bool,

    /// Include prerelease versions when fetching from crates.io (e.g. 0.6.0-alpha').
    #[structopt(long = "allow-prerelease")]
    allow_prerelease: bool,

    /// Print changes to be made without making them.
    #[structopt(long = "dry-run")]
    dry_run: bool,

    /// Run without accessing the network
    #[structopt(long = "offline")]
    pub offline: bool,

    /// Upgrade all packages to the version in the lockfile.
    #[structopt(long = "to-lockfile", conflicts_with = "dependency")]
    pub to_lockfile: bool,
}

/// A collection of manifests.
struct Manifests(Vec<(LocalManifest, cargo_metadata::Package)>);

/// Helper function to check whether a `cargo_metadata::Dependency` is a version dependency.
fn is_version_dep(dependency: &cargo_metadata::Dependency) -> bool {
    match dependency.source {
        // This is the criterion cargo uses (in `SourceId::from_url`) to decide whether a
        // dependency has the 'registry' kind.
        Some(ref s) => s.splitn(2, '+').next() == Some("registry"),
        _ => false,
    }
}

fn dry_run_message() -> Result<()> {
    let bufwtr = BufferWriter::stdout(ColorChoice::Always);
    let mut buffer = bufwtr.buffer();
    buffer
        .set_color(ColorSpec::new().set_fg(Some(Color::Cyan)).set_bold(true))
        .chain_err(|| "Failed to set output colour")?;
    write!(&mut buffer, "Starting dry run. ").chain_err(|| "Failed to write dry run message")?;
    buffer
        .set_color(&ColorSpec::new())
        .chain_err(|| "Failed to clear output colour")?;
    writeln!(&mut buffer, "Changes will not be saved.")
        .chain_err(|| "Failed to write dry run message")?;
    bufwtr
        .print(&buffer)
        .chain_err(|| "Failed to print dry run message")
}

impl Manifests {
    /// Get all manifests in the workspace.
    fn get_all(manifest_path: &Option<PathBuf>) -> Result<Self> {
        let mut cmd = cargo_metadata::MetadataCommand::new();
        cmd.no_deps();
        if let Some(path) = manifest_path {
            cmd.manifest_path(path);
        }
        let result = cmd.exec().map_err(|e| {
            Error::from(e.compat()).chain_err(|| "Failed to get workspace metadata")
        })?;
        result
            .packages
            .into_iter()
            .map(|package| {
                Ok((
                    LocalManifest::try_new(Path::new(&package.manifest_path))?,
                    package,
                ))
            })
            .collect::<Result<Vec<_>>>()
            .map(Manifests)
    }

    /// Get the manifest specified by the manifest path. Try to make an educated guess if no path is
    /// provided.
    fn get_local_one(manifest_path: &Option<PathBuf>) -> Result<Self> {
        let resolved_manifest_path: String = find(&manifest_path)?.to_string_lossy().into();

        let manifest = LocalManifest::find(&manifest_path)?;

        let mut cmd = cargo_metadata::MetadataCommand::new();
        cmd.no_deps();
        if let Some(path) = manifest_path {
            cmd.manifest_path(path);
        }
        let result = cmd
            .exec()
            .map_err(|e| Error::from(e.compat()).chain_err(|| "Invalid manifest"))?;
        let packages = result.packages;
        let package = packages
            .iter()
            .find(|p| p.manifest_path.to_string_lossy() == resolved_manifest_path)
            // If we have successfully got metadata, but our manifest path does not correspond to a
            // package, we must have been called against a virtual manifest.
            .chain_err(|| {
                "Found virtual manifest, but this command requires running against an \
                 actual package in this workspace. Try adding `--all`."
            })?;

        Ok(Manifests(vec![(manifest, package.to_owned())]))
    }

    /// Get the the combined set of dependencies to upgrade. If the user has specified
    /// per-dependency desired versions, extract those here.
    fn get_dependencies(&self, only_update: Vec<String>) -> Result<DesiredUpgrades> {
        // Map the names of user-specified dependencies to the (optionally) requested version.
        let selected_dependencies = only_update
            .into_iter()
            .map(|name| {
                if let Some(dependency) = CrateName::new(&name.clone()).parse_as_version()? {
                    Ok((
                        dependency.name.clone(),
                        dependency.version().map(String::from),
                    ))
                } else {
                    Ok((name, None))
                }
            })
            .collect::<Result<HashMap<_, _>>>()?;

        Ok(DesiredUpgrades(
            self.0
                .iter()
                .flat_map(|&(_, ref package)| package.dependencies.clone())
                .filter(is_version_dep)
                .filter_map(|dependency| {
                    if selected_dependencies.is_empty() {
                        // User hasn't asked for any specific dependencies to be upgraded,
                        // so upgrade all the dependencies.
                        let mut dep = Dependency::new(&dependency.name);
                        if let Some(rename) = dependency.rename {
                            dep = dep.set_rename(&rename);
                        }
                        let is_prerelease = dependency.req.to_string().contains("-");
                        Some((dep, UpgradeMetadata {
                            registry: dependency.registry,
                            version: None,
                            is_prerelease
                        }))
                    } else {
                        // User has asked for specific dependencies. Check if this dependency
                        // was specified, populating the registry from the lockfile metadata.
                        match selected_dependencies.get(&dependency.name) {
                            Some(version) => Some((
                                Dependency::new(&dependency.name),
                                UpgradeMetadata {
                                    registry: dependency.registry,
                                    version: version.clone(),
                                    is_prerelease: false,
                                }
                            )),
                            None => None,
                        }
                    }
                })
                .collect(),
        ))
    }

    /// Upgrade the manifests on disk following the previously-determined upgrade schema.
    fn upgrade(self, upgraded_deps: &ActualUpgrades, dry_run: bool) -> Result<()> {
        if dry_run {
            dry_run_message()?;
        }

        for (mut manifest, package) in self.0 {
            println!("{}:", package.name);

            for (dep, version) in &upgraded_deps.0 {
                let mut new_dep = Dependency::new(&dep.name).set_version(version);
                if let Some(rename) = dep.rename() {
                    new_dep = new_dep.set_rename(&rename);
                }
                manifest.upgrade(&new_dep, dry_run)?;
            }
        }

        Ok(())
    }

    /// Update dependencies in Cargo.toml file(s) to match the corresponding
    /// version in Cargo.lock.
    fn sync_to_lockfile(self, dry_run: bool) -> Result<()> {
        // Get locked dependencies. For workspaces with multiple Cargo.toml
        // files, there is only a single lockfile, so it suffices to get
        // metadata for any one of Cargo.toml files.
        let (manifest, _package) =
            self.0.iter().next().ok_or_else(|| {
                ErrorKind::CargoEditLib(::cargo_edit::ErrorKind::InvalidCargoConfig)
            })?;
        let mut cmd = cargo_metadata::MetadataCommand::new();
        cmd.manifest_path(manifest.path.clone());
        cmd.other_options(vec!["--locked".to_string()]);

        let result = cmd
            .exec()
            .map_err(|e| Error::from(e.compat()).chain_err(|| "Invalid manifest"))?;

        let locked = result
            .packages
            .into_iter()
            .filter(|p| p.source.is_some()) // Source is none for local packages
            .collect::<Vec<_>>();

        if dry_run {
            dry_run_message()?;
        }

        for (mut manifest, package) in self.0 {
            println!("{}:", package.name);

            // Upgrade the manifests one at a time, as multiple manifests may
            // request the same dependency at differing versions.
            for (name, version) in package
                .dependencies
                .clone()
                .into_iter()
                .filter(is_version_dep)
                .filter_map(|d| {
                    for p in &locked {
                        // The requested dependency may be present in the lock file with different versions,
                        // but only one will be semver-compatible with the requested version.
                        if d.name == p.name && d.req.matches(&p.version) {
                            return Some((d.name, p.version.to_string()));
                        }
                    }
                    None
                })
            {
                manifest.upgrade(&Dependency::new(&name).set_version(&version), dry_run)?;
            }
        }
        Ok(())
    }
}

// Some metadata about the dependency
// we're trying to upgrade.
struct UpgradeMetadata {
    registry: Option<String>,
    // `Some` if the user has specified an explicit
    // version to upgrade to.
    version: Option<String>,
    is_prerelease: bool,
}

/// The set of dependencies to be upgraded, alongside the registries returned from cargo metadata, and
/// the desired versions, if specified by the user.
struct DesiredUpgrades(HashMap<Dependency, UpgradeMetadata>);

/// The complete specification of the upgrades that will be performed. Map of the dependency names
/// to the new versions.
struct ActualUpgrades(HashMap<Dependency, String>);

impl DesiredUpgrades {
    /// Transform the dependencies into their upgraded forms. If a version is specified, all
    /// dependencies will get that version.
    fn get_upgraded(self, allow_prerelease: bool, manifest_path: &Path) -> Result<ActualUpgrades> {
        self.0
            .into_iter()
            .map(|(dep, UpgradeMetadata{ registry, version, is_prerelease })| {
                if let Some(v) = version {
                    Ok((dep, v))
                } else {
                    let registry_url = match registry {
                        Some(x) => Some(Url::parse(&x).map_err(|_| {
                            ErrorKind::CargoEditLib(::cargo_edit::ErrorKind::InvalidCargoConfig)
                        })?),
                        None => None,
                    };
                    let allow_prerelease = allow_prerelease || is_prerelease;
                    get_latest_dependency(&dep.name, allow_prerelease, manifest_path, &registry_url)
                        .map(|new_dep| {
                            (
                                dep,
                                new_dep
                                    .version()
                                    .expect("Invalid dependency type")
                                    .to_string(),
                            )
                        })
                        .chain_err(|| "Failed to get new version")
                }
            })
            .collect::<Result<_>>()
            .map(ActualUpgrades)
    }
}

/// Main processing function. Allows us to return a `Result` so that `main` can print pretty error
/// messages.
fn process(args: Args) -> Result<()> {
    let Args {
        dependency,
        manifest_path,
        all,
        allow_prerelease,
        dry_run,
        to_lockfile,
        ..
    } = args;

    if !args.offline && !to_lockfile {
        let url = registry_url(&find(&manifest_path)?, None)?;
        update_registry_index(&url)?;
    }

    let manifests = if all {
        Manifests::get_all(&manifest_path)
    } else {
        Manifests::get_local_one(&manifest_path)
    }?;

    if to_lockfile {
        manifests.sync_to_lockfile(dry_run)
    } else {
        let existing_dependencies = manifests.get_dependencies(dependency)?;

        // Update indices for any alternative registries, unless
        // we're offline.
        if !args.offline {
            for registry_url in existing_dependencies
                .0
                .values()
                .filter_map(|UpgradeMetadata { registry, .. }| registry.as_ref())
                .collect::<HashSet<_>>()
            {
                update_registry_index(&Url::parse(registry_url).map_err(|_| {
                    ErrorKind::CargoEditLib(::cargo_edit::ErrorKind::InvalidCargoConfig)
                })?)?;
            }
        }

        let upgraded_dependencies =
            existing_dependencies.get_upgraded(allow_prerelease, &find(&manifest_path)?)?;

        manifests.upgrade(&upgraded_dependencies, dry_run)
    }
}

fn main() {
    let args: Command = Command::from_args();
    let Command::Upgrade(args) = args;

    if let Err(err) = process(args) {
        eprintln!("Command failed due to unhandled error: {}\n", err);

        for e in err.iter().skip(1) {
            eprintln!("Caused by: {}", e);
        }

        if let Some(backtrace) = err.backtrace() {
            eprintln!("Backtrace: {:?}", backtrace);
        }

        process::exit(1);
    }
}
<commit_msg>cargo fmt<commit_after>//! `cargo upgrade`
#![warn(
    missing_docs,
    missing_debug_implementations,
    missing_copy_implementations,
    trivial_casts,
    trivial_numeric_casts,
    unsafe_code,
    unstable_features,
    unused_import_braces,
    unused_qualifications
)]

#[macro_use]
extern crate error_chain;

use crate::errors::*;
use cargo_edit::{
    find, get_latest_dependency, registry_url, update_registry_index, CrateName, Dependency,
    LocalManifest,
};
use failure::Fail;
use std::collections::{HashMap, HashSet};
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process;
use structopt::StructOpt;
use termcolor::{BufferWriter, Color, ColorChoice, ColorSpec, WriteColor};
use url::Url;

mod errors {
    error_chain! {
        links {
            CargoEditLib(::cargo_edit::Error, ::cargo_edit::ErrorKind);
        }
        foreign_links {
            CargoMetadata(::failure::Compat<::cargo_metadata::Error>);
        }
    }
}

#[derive(Debug, StructOpt)]
#[structopt(bin_name = "cargo")]
enum Command {
    /// Upgrade dependencies as specified in the local manifest file (i.e. Cargo.toml).
    #[structopt(name = "upgrade", author = "")]
    #[structopt(
        after_help = "This command differs from `cargo update`, which updates the dependency versions recorded in the
local lock file (Cargo.lock).

If `<dependency>`(s) are provided, only the specified dependencies will be upgraded. The version to
upgrade to for each can be specified with e.g. `docopt@0.8.0` or `serde@>=0.9,<2.0`.

Dev, build, and all target dependencies will also be upgraded. Only dependencies from crates.io are
supported. Git/path dependencies will be ignored.

All packages in the workspace will be upgraded if the `--all` flag is supplied. The `--all` flag may
be supplied in the presence of a virtual manifest.

If the '--to-lockfile' flag is supplied, all dependencies will be upgraded to the currently locked
version as recorded in the Cargo.lock file. This flag requires that the Cargo.lock file is
up-to-date. If the lock file is missing, or it needs to be updated, cargo-upgrade will exit with an
error. If the '--to-lockfile' flag is supplied then the network won't be accessed."
    )]
    Upgrade(Args),
}

#[derive(Debug, StructOpt)]
struct Args {
    /// Crates to be upgraded.
    dependency: Vec<String>,

    /// Path to the manifest to upgrade
    #[structopt(long = "manifest-path", value_name = "path")]
    manifest_path: Option<PathBuf>,

    /// Upgrade all packages in the workspace.
    #[structopt(long = "all")]
    all: bool,

    /// Include prerelease versions when fetching from crates.io (e.g. 0.6.0-alpha').
    #[structopt(long = "allow-prerelease")]
    allow_prerelease: bool,

    /// Print changes to be made without making them.
    #[structopt(long = "dry-run")]
    dry_run: bool,

    /// Run without accessing the network
    #[structopt(long = "offline")]
    pub offline: bool,

    /// Upgrade all packages to the version in the lockfile.
    #[structopt(long = "to-lockfile", conflicts_with = "dependency")]
    pub to_lockfile: bool,
}

/// A collection of manifests.
struct Manifests(Vec<(LocalManifest, cargo_metadata::Package)>);

/// Helper function to check whether a `cargo_metadata::Dependency` is a version dependency.
fn is_version_dep(dependency: &cargo_metadata::Dependency) -> bool {
    match dependency.source {
        // This is the criterion cargo uses (in `SourceId::from_url`) to decide whether a
        // dependency has the 'registry' kind.
        Some(ref s) => s.splitn(2, '+').next() == Some("registry"),
        _ => false,
    }
}

fn dry_run_message() -> Result<()> {
    let bufwtr = BufferWriter::stdout(ColorChoice::Always);
    let mut buffer = bufwtr.buffer();
    buffer
        .set_color(ColorSpec::new().set_fg(Some(Color::Cyan)).set_bold(true))
        .chain_err(|| "Failed to set output colour")?;
    write!(&mut buffer, "Starting dry run. ").chain_err(|| "Failed to write dry run message")?;
    buffer
        .set_color(&ColorSpec::new())
        .chain_err(|| "Failed to clear output colour")?;
    writeln!(&mut buffer, "Changes will not be saved.")
        .chain_err(|| "Failed to write dry run message")?;
    bufwtr
        .print(&buffer)
        .chain_err(|| "Failed to print dry run message")
}

impl Manifests {
    /// Get all manifests in the workspace.
    fn get_all(manifest_path: &Option<PathBuf>) -> Result<Self> {
        let mut cmd = cargo_metadata::MetadataCommand::new();
        cmd.no_deps();
        if let Some(path) = manifest_path {
            cmd.manifest_path(path);
        }
        let result = cmd.exec().map_err(|e| {
            Error::from(e.compat()).chain_err(|| "Failed to get workspace metadata")
        })?;
        result
            .packages
            .into_iter()
            .map(|package| {
                Ok((
                    LocalManifest::try_new(Path::new(&package.manifest_path))?,
                    package,
                ))
            })
            .collect::<Result<Vec<_>>>()
            .map(Manifests)
    }

    /// Get the manifest specified by the manifest path. Try to make an educated guess if no path is
    /// provided.
    fn get_local_one(manifest_path: &Option<PathBuf>) -> Result<Self> {
        let resolved_manifest_path: String = find(&manifest_path)?.to_string_lossy().into();

        let manifest = LocalManifest::find(&manifest_path)?;

        let mut cmd = cargo_metadata::MetadataCommand::new();
        cmd.no_deps();
        if let Some(path) = manifest_path {
            cmd.manifest_path(path);
        }
        let result = cmd
            .exec()
            .map_err(|e| Error::from(e.compat()).chain_err(|| "Invalid manifest"))?;
        let packages = result.packages;
        let package = packages
            .iter()
            .find(|p| p.manifest_path.to_string_lossy() == resolved_manifest_path)
            // If we have successfully got metadata, but our manifest path does not correspond to a
            // package, we must have been called against a virtual manifest.
            .chain_err(|| {
                "Found virtual manifest, but this command requires running against an \
                 actual package in this workspace. Try adding `--all`."
            })?;

        Ok(Manifests(vec![(manifest, package.to_owned())]))
    }

    /// Get the the combined set of dependencies to upgrade. If the user has specified
    /// per-dependency desired versions, extract those here.
    fn get_dependencies(&self, only_update: Vec<String>) -> Result<DesiredUpgrades> {
        // Map the names of user-specified dependencies to the (optionally) requested version.
        let selected_dependencies = only_update
            .into_iter()
            .map(|name| {
                if let Some(dependency) = CrateName::new(&name.clone()).parse_as_version()? {
                    Ok((
                        dependency.name.clone(),
                        dependency.version().map(String::from),
                    ))
                } else {
                    Ok((name, None))
                }
            })
            .collect::<Result<HashMap<_, _>>>()?;

        Ok(DesiredUpgrades(
            self.0
                .iter()
                .flat_map(|&(_, ref package)| package.dependencies.clone())
                .filter(is_version_dep)
                .filter_map(|dependency| {
                    if selected_dependencies.is_empty() {
                        // User hasn't asked for any specific dependencies to be upgraded,
                        // so upgrade all the dependencies.
                        let mut dep = Dependency::new(&dependency.name);
                        if let Some(rename) = dependency.rename {
                            dep = dep.set_rename(&rename);
                        }
                        let is_prerelease = dependency.req.to_string().contains("-");
                        Some((
                            dep,
                            UpgradeMetadata {
                                registry: dependency.registry,
                                version: None,
                                is_prerelease,
                            },
                        ))
                    } else {
                        // User has asked for specific dependencies. Check if this dependency
                        // was specified, populating the registry from the lockfile metadata.
                        match selected_dependencies.get(&dependency.name) {
                            Some(version) => Some((
                                Dependency::new(&dependency.name),
                                UpgradeMetadata {
                                    registry: dependency.registry,
                                    version: version.clone(),
                                    is_prerelease: false,
                                },
                            )),
                            None => None,
                        }
                    }
                })
                .collect(),
        ))
    }

    /// Upgrade the manifests on disk following the previously-determined upgrade schema.
    fn upgrade(self, upgraded_deps: &ActualUpgrades, dry_run: bool) -> Result<()> {
        if dry_run {
            dry_run_message()?;
        }

        for (mut manifest, package) in self.0 {
            println!("{}:", package.name);

            for (dep, version) in &upgraded_deps.0 {
                let mut new_dep = Dependency::new(&dep.name).set_version(version);
                if let Some(rename) = dep.rename() {
                    new_dep = new_dep.set_rename(&rename);
                }
                manifest.upgrade(&new_dep, dry_run)?;
            }
        }

        Ok(())
    }

    /// Update dependencies in Cargo.toml file(s) to match the corresponding
    /// version in Cargo.lock.
    fn sync_to_lockfile(self, dry_run: bool) -> Result<()> {
        // Get locked dependencies. For workspaces with multiple Cargo.toml
        // files, there is only a single lockfile, so it suffices to get
        // metadata for any one of Cargo.toml files.
        let (manifest, _package) =
            self.0.iter().next().ok_or_else(|| {
                ErrorKind::CargoEditLib(::cargo_edit::ErrorKind::InvalidCargoConfig)
            })?;
        let mut cmd = cargo_metadata::MetadataCommand::new();
        cmd.manifest_path(manifest.path.clone());
        cmd.other_options(vec!["--locked".to_string()]);

        let result = cmd
            .exec()
            .map_err(|e| Error::from(e.compat()).chain_err(|| "Invalid manifest"))?;

        let locked = result
            .packages
            .into_iter()
            .filter(|p| p.source.is_some()) // Source is none for local packages
            .collect::<Vec<_>>();

        if dry_run {
            dry_run_message()?;
        }

        for (mut manifest, package) in self.0 {
            println!("{}:", package.name);

            // Upgrade the manifests one at a time, as multiple manifests may
            // request the same dependency at differing versions.
            for (name, version) in package
                .dependencies
                .clone()
                .into_iter()
                .filter(is_version_dep)
                .filter_map(|d| {
                    for p in &locked {
                        // The requested dependency may be present in the lock file with different versions,
                        // but only one will be semver-compatible with the requested version.
                        if d.name == p.name && d.req.matches(&p.version) {
                            return Some((d.name, p.version.to_string()));
                        }
                    }
                    None
                })
            {
                manifest.upgrade(&Dependency::new(&name).set_version(&version), dry_run)?;
            }
        }
        Ok(())
    }
}

// Some metadata about the dependency
// we're trying to upgrade.
struct UpgradeMetadata {
    registry: Option<String>,
    // `Some` if the user has specified an explicit
    // version to upgrade to.
    version: Option<String>,
    is_prerelease: bool,
}

/// The set of dependencies to be upgraded, alongside the registries returned from cargo metadata, and
/// the desired versions, if specified by the user.
struct DesiredUpgrades(HashMap<Dependency, UpgradeMetadata>);

/// The complete specification of the upgrades that will be performed. Map of the dependency names
/// to the new versions.
struct ActualUpgrades(HashMap<Dependency, String>);

impl DesiredUpgrades {
    /// Transform the dependencies into their upgraded forms. If a version is specified, all
    /// dependencies will get that version.
    fn get_upgraded(self, allow_prerelease: bool, manifest_path: &Path) -> Result<ActualUpgrades> {
        self.0
            .into_iter()
            .map(
                |(
                    dep,
                    UpgradeMetadata {
                        registry,
                        version,
                        is_prerelease,
                    },
                )| {
                    if let Some(v) = version {
                        Ok((dep, v))
                    } else {
                        let registry_url = match registry {
                            Some(x) => Some(Url::parse(&x).map_err(|_| {
                                ErrorKind::CargoEditLib(::cargo_edit::ErrorKind::InvalidCargoConfig)
                            })?),
                            None => None,
                        };
                        let allow_prerelease = allow_prerelease || is_prerelease;
                        get_latest_dependency(
                            &dep.name,
                            allow_prerelease,
                            manifest_path,
                            &registry_url,
                        )
                        .map(|new_dep| {
                            (
                                dep,
                                new_dep
                                    .version()
                                    .expect("Invalid dependency type")
                                    .to_string(),
                            )
                        })
                        .chain_err(|| "Failed to get new version")
                    }
                },
            )
            .collect::<Result<_>>()
            .map(ActualUpgrades)
    }
}

/// Main processing function. Allows us to return a `Result` so that `main` can print pretty error
/// messages.
fn process(args: Args) -> Result<()> {
    let Args {
        dependency,
        manifest_path,
        all,
        allow_prerelease,
        dry_run,
        to_lockfile,
        ..
    } = args;

    if !args.offline && !to_lockfile {
        let url = registry_url(&find(&manifest_path)?, None)?;
        update_registry_index(&url)?;
    }

    let manifests = if all {
        Manifests::get_all(&manifest_path)
    } else {
        Manifests::get_local_one(&manifest_path)
    }?;

    if to_lockfile {
        manifests.sync_to_lockfile(dry_run)
    } else {
        let existing_dependencies = manifests.get_dependencies(dependency)?;

        // Update indices for any alternative registries, unless
        // we're offline.
        if !args.offline {
            for registry_url in existing_dependencies
                .0
                .values()
                .filter_map(|UpgradeMetadata { registry, .. }| registry.as_ref())
                .collect::<HashSet<_>>()
            {
                update_registry_index(&Url::parse(registry_url).map_err(|_| {
                    ErrorKind::CargoEditLib(::cargo_edit::ErrorKind::InvalidCargoConfig)
                })?)?;
            }
        }

        let upgraded_dependencies =
            existing_dependencies.get_upgraded(allow_prerelease, &find(&manifest_path)?)?;

        manifests.upgrade(&upgraded_dependencies, dry_run)
    }
}

fn main() {
    let args: Command = Command::from_args();
    let Command::Upgrade(args) = args;

    if let Err(err) = process(args) {
        eprintln!("Command failed due to unhandled error: {}\n", err);

        for e in err.iter().skip(1) {
            eprintln!("Caused by: {}", e);
        }

        if let Some(backtrace) = err.backtrace() {
            eprintln!("Backtrace: {:?}", backtrace);
        }

        process::exit(1);
    }
}
<|endoftext|>


<commit_before>use auto_enums::auto_enum;
use glob::{glob_with, MatchOptions};
use ion_shell::{
    expansion::{unescape, Expander},
    Shell,
};
use liner::{BasicCompleter, Completer, CursorPosition, Event, EventKind};
use std::{env, iter, path::PathBuf, str};

pub struct IonCompleter<'a, 'b> {
    shell:             &'b Shell<'a>,
    history_completer: Option<BasicCompleter>,
}

/// Escapes filenames from the completer so that special characters will be properly escaped.
///
/// NOTE: Perhaps we should submit a PR to Liner to add a &'static [u8] field to
/// `FilenameCompleter` so that we don't have to perform the escaping ourselves?
fn escape(input: &str) -> String {
    let mut output = Vec::with_capacity(input.len());
    for character in input.bytes() {
        match character {
            b'(' | b')' | b'[' | b']' | b'&' | b'$' | b'@' | b'{' | b'}' | b'<' | b'>' | b';'
            | b'"' | b'\'' | b'#' | b'^' | b'*' | b' ' => output.push(b'\\'),
            _ => (),
        }
        output.push(character);
    }
    unsafe { String::from_utf8_unchecked(output) }
}

impl<'a, 'b> IonCompleter<'a, 'b> {
    pub fn new(shell: &'b Shell<'a>) -> Self { IonCompleter { shell, history_completer: None } }
}

impl<'a, 'b> Completer for IonCompleter<'a, 'b> {
    fn completions(&mut self, start: &str) -> Vec<String> {
        let mut completions = IonFileCompleter::new(None, &self.shell).completions(start);

        if let Some(ref mut history) = &mut self.history_completer {
            let vars = self.shell.variables();

            completions.extend(history.completions(start));
            // Initialize a new completer from the definitions collected.
            // Creates a list of definitions from the shell environment that
            // will be used
            // in the creation of a custom completer.
            completions.extend(
                self.shell
                    .builtins()
                    .keys()
                    // Add built-in commands to the completer's definitions.
                    .map(ToString::to_string)
                    // Add the aliases to the completer's definitions.
                    .chain(vars.aliases().map(|(key, _)| key.to_string()))
                    // Add the list of available functions to the completer's
                    // definitions.
                    .chain(vars.functions().map(|(key, _)| key.to_string()))
                    // Add the list of available variables to the completer's
                    // definitions. TODO: We should make
                    // it free to do String->SmallString
                    //       and mostly free to go back (free if allocated)
                    .chain(vars.string_vars().map(|(s, _)| ["$", &s].concat()))
                    .filter(|s| s.starts_with(start)),
            );
            // Creates completers containing definitions from all directories
            // listed
            // in the environment's **$PATH** variable.
            let file_completers: Vec<_> = if let Some(paths) = env::var_os("PATH") {
                env::split_paths(&paths)
                    .map(|s| IonFileCompleter::new(Some(s), &self.shell))
                    .collect()
            } else {
                vec![IonFileCompleter::new(Some("/bin/".into()), &self.shell)]
            };
            // Merge the collected definitions with the file path definitions.
            completions.extend(MultiCompleter::new(file_completers).completions(start));
        }
        completions
    }

    fn on_event<W: std::io::Write>(&mut self, event: Event<'_, '_, W>) {
        if let EventKind::BeforeComplete = event.kind {
            let (words, pos) = event.editor.get_words_and_cursor_position();

            let filename = match pos {
                CursorPosition::InWord(index) => index > 0,
                CursorPosition::InSpace(Some(_), _) => true,
                CursorPosition::InSpace(None, _) => false,
                CursorPosition::OnWordLeftEdge(index) => index >= 1,
                CursorPosition::OnWordRightEdge(index) => words
                    .into_iter()
                    .nth(index)
                    .map(|(start, end)| event.editor.current_buffer().range(start, end))
                    .and_then(|filename| {
                        Some(complete_as_file(&env::current_dir().ok()?, &filename, index))
                    })
                    .filter(|&x| x)
                    .is_some(),
            };

            // Add the history list to the completer's definitions.
            // Map each underlying `liner::Buffer` into a `String`.
            self.history_completer = if filename {
                Some(BasicCompleter::new(
                    event
                        .editor
                        .context()
                        .history
                        .buffers
                        .iter()
                        .map(ToString::to_string)
                        .collect(),
                ))
            } else {
                None
            };
        }
    }
}

/// Performs escaping to an inner `FilenameCompleter` to enable a handful of special cases
/// needed by the shell, such as expanding '~' to a home directory, or adding a backslash
/// when a special character is contained within an expanded filename.
pub struct IonFileCompleter<'a, 'b> {
    shell: &'b Shell<'a>,
    /// The directory the expansion takes place in
    path: PathBuf,
}

impl<'a, 'b> IonFileCompleter<'a, 'b> {
    pub fn new(path: Option<PathBuf>, shell: &'b Shell<'a>) -> Self {
        let path = path.unwrap_or_default();
        IonFileCompleter { shell, path }
    }
}

impl<'a, 'b> Completer for IonFileCompleter<'a, 'b> {
    /// When the tab key is pressed, **Liner** will use this method to perform completions of
    /// filenames. As our `IonFileCompleter` is a wrapper around **Liner**'s
    /// `FilenameCompleter`,
    /// the purpose of our custom `Completer` is to expand possible `~` characters in the
    /// `start`
    /// value that we receive from the prompt, grab completions from the inner
    /// `FilenameCompleter`,
    /// and then escape the resulting filenames, as well as remove the expanded form of the `~`
    /// character and re-add the `~` character in it's place.
    fn completions(&mut self, start: &str) -> Vec<String> {
        // Dereferencing the raw pointers here should be entirely safe, theoretically,
        // because no changes will occur to either of the underlying references in the
        // duration between creation of the completers and execution of their
        // completions.
        let expanded = match self.shell.tilde(start) {
            Ok(expanded) => expanded,
            Err(why) => {
                eprintln!("ion: {}", why);
                return vec![start.into()];
            }
        };
        // Now we obtain completions for the `expanded` form of the `start` value.
        let completions = filename_completion(&expanded, &self.path);
        if expanded == start {
            return completions.collect();
        }
        // We can do that by obtaining the index position where the tilde character
        // ends. We don't search with `~` because we also want to
        // handle other tilde variants.
        let t_index = start.find('/').unwrap_or(1);
        // `tilde` is the tilde pattern, and `search` is the pattern that follows.
        let (tilde, search) = start.split_at(t_index);

        if search.len() < 2 {
            // If the length of the search pattern is less than 2, the search pattern is
            // empty, and thus the completions actually contain files and directories in
            // the home directory.

            // The tilde pattern will actually be our `start` command in itself,
            // and the completed form will be all of the characters beyond the length of
            // the expanded form of the tilde pattern.
            completions.map(|completion| [start, &completion[expanded.len()..]].concat()).collect()
        // To save processing time, we should get obtain the index position where our
        // search pattern begins, and re-use that index to slice the completions so
        // that we may re-add the tilde character with the completion that follows.
        } else if let Some(e_index) = expanded.rfind(search) {
            // And then we will need to take those completions and remove the expanded form
            // of the tilde pattern and replace it with that pattern yet again.
            completions
                .map(|completion| escape(&[tilde, &completion[e_index..]].concat()))
                .collect()
        } else {
            Vec::new()
        }
    }
}

#[auto_enum]
fn filename_completion<'a>(start: &'a str, path: &'a PathBuf) -> impl Iterator<Item = String> + 'a {
    let unescaped_start = unescape(start);

    let mut split_start = unescaped_start.split('/');
    let mut string = String::with_capacity(128);

    // When 'start' is an absolute path, "/..." gets split to ["", "..."]
    // So we skip the first element and add "/" to the start of the string
    if unescaped_start.starts_with('/') {
        split_start.next();
        string.push('/');
    } else {
        string.push_str(&path.to_string_lossy());
    }

    for element in split_start {
        string.push_str(element);
        if element != "." && element != ".." {
            string.push('*');
        }
        string.push('/');
    }

    string.pop(); // pop out the last '/' character
    if string.ends_with('.') {
        string.push('*')
    }

    let globs = glob_with(
        &string,
        MatchOptions {
            case_sensitive:              true,
            require_literal_separator:   true,
            require_literal_leading_dot: false,
        },
    )
    .ok()
    .map(|completions| {
        completions.filter_map(Result::ok).filter_map(move |file| {
            let out = file.to_str()?;
            let mut joined = String::with_capacity(out.len() + 3); // worst case senario
            if unescaped_start.starts_with("./") {
                joined.push_str("./");
            }
            joined.push_str(out);
            if file.is_dir() {
                joined.push('/');
            }
            Some(escape(&joined))
        })
    });

    #[auto_enum(Iterator)]
    match globs {
        Some(iter) => iter,
        None => iter::once(escape(start)),
    }
}

/// A completer that combines suggestions from multiple completers.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct MultiCompleter<A>(Vec<A>);

impl<A> MultiCompleter<A> {
    pub fn new(completions: Vec<A>) -> Self { MultiCompleter(completions) }
}

impl<A> Completer for MultiCompleter<A>
where
    A: Completer,
{
    fn completions(&mut self, start: &str) -> Vec<String> {
        self.0.iter_mut().flat_map(|comp| comp.completions(start)).collect()
    }
}

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

    #[test]
    fn filename_completion() {
        let shell = Shell::default();
        let mut completer = IonFileCompleter::new(None, &shell);
        assert_eq!(completer.completions("testing"), vec!["testing/"]);
        assert_eq!(completer.completions("testing/file"), vec!["testing/file_with_text"]);
        assert_eq!(completer.completions("~"), vec!["~/"]);
        assert_eq!(completer.completions("tes/fil"), vec!["testing/file_with_text"]);
    }
}

/// Infer if the given filename is actually a partial filename
fn complete_as_file(current_dir: &PathBuf, filename: &str, index: usize) -> bool {
    let filename = filename.trim();
    let mut file = current_dir.clone();
    file.push(&filename);
    // If the user explicitly requests a file through this syntax then complete as
    // a file
    filename.starts_with('.') ||
    // If the file starts with a dollar sign, it's a variable, not a file
    (!filename.starts_with('$') &&
    // Once we are beyond the first string, assume its a file
    (index > 0 ||
    // If we are referencing a file that exists then just complete to that file
    file.exists() ||
    // If we have a partial file inside an existing directory, e.g. /foo/b when
    // /foo/bar exists, then treat it as file as long as `foo` isn't the
    // current directory, otherwise this would apply to any string `foo`
    file.parent().filter(|parent| parent.exists() && parent != current_dir).is_some()))
    // By default assume its not a file
}
<commit_msg>Use saner default completion<commit_after>use auto_enums::auto_enum;
use glob::{glob_with, MatchOptions};
use ion_shell::{
    expansion::{unescape, Expander},
    Shell,
};
use liner::{Completer, CursorPosition, Event, EventKind};
use std::{env, iter, path::PathBuf, str};

pub struct IonCompleter<'a, 'b> {
    shell:      &'b Shell<'a>,
    completion: CompletionType,
}

/// Escapes filenames from the completer so that special characters will be properly escaped.
///
/// NOTE: Perhaps we should submit a PR to Liner to add a &'static [u8] field to
/// `FilenameCompleter` so that we don't have to perform the escaping ourselves?
fn escape(input: &str) -> String {
    let mut output = Vec::with_capacity(input.len());
    for character in input.bytes() {
        match character {
            b'(' | b')' | b'[' | b']' | b'&' | b'$' | b'@' | b'{' | b'}' | b'<' | b'>' | b';'
            | b'"' | b'\'' | b'#' | b'^' | b'*' | b' ' => output.push(b'\\'),
            _ => (),
        }
        output.push(character);
    }
    unsafe { String::from_utf8_unchecked(output) }
}

enum CompletionType {
    Nothing,
    Command,
    VariableAndFiles,
}

impl<'a, 'b> IonCompleter<'a, 'b> {
    pub fn new(shell: &'b Shell<'a>) -> Self {
        IonCompleter { shell, completion: CompletionType::Nothing }
    }
}

impl<'a, 'b> Completer for IonCompleter<'a, 'b> {
    fn completions(&mut self, start: &str) -> Vec<String> {
        let mut completions = IonFileCompleter::new(None, &self.shell).completions(start);
        let vars = self.shell.variables();

        match self.completion {
            CompletionType::VariableAndFiles => {
                // Initialize a new completer from the definitions collected.
                // Creates a list of definitions from the shell environment that
                // will be used
                // in the creation of a custom completer.
                if start.is_empty() {
                    completions.extend(vars.string_vars().map(|(s, _)| format!("${}", s)));
                    completions.extend(vars.arrays().map(|(s, _)| format!("@{}", s)));
                } else if start.starts_with('$') {
                    completions.extend(
                        // Add the list of available variables to the completer's
                        // definitions. TODO: We should make
                        // it free to do String->SmallString
                        //       and mostly free to go back (free if allocated)
                        vars.string_vars()
                            .filter(|(s, _)| s.starts_with(&start[1..]))
                            .map(|(s, _)| format!("${}", &s)),
                    );
                } else if start.starts_with('@') {
                    completions.extend(
                        vars.arrays()
                            .filter(|(s, _)| s.starts_with(&start[1..]))
                            .map(|(s, _)| format!("@{}", &s)),
                    );
                }
            }
            CompletionType::Command => {
                // Initialize a new completer from the definitions collected.
                // Creates a list of definitions from the shell environment that
                // will be used
                // in the creation of a custom completer.
                completions.extend(
                    self.shell
                        .builtins()
                        .keys()
                        // Add built-in commands to the completer's definitions.
                        .map(ToString::to_string)
                        // Add the aliases to the completer's definitions.
                        .chain(vars.aliases().map(|(key, _)| key.to_string()))
                        // Add the list of available functions to the completer's
                        // definitions.
                        .chain(vars.functions().map(|(key, _)| key.to_string()))
                        .filter(|s| s.starts_with(start)),
                );
                // Creates completers containing definitions from all directories
                // listed
                // in the environment's **$PATH** variable.
                let file_completers: Vec<_> = if let Some(paths) = env::var_os("PATH") {
                    env::split_paths(&paths)
                        .map(|s| IonFileCompleter::new(Some(s), &self.shell))
                        .collect()
                } else {
                    vec![IonFileCompleter::new(Some("/bin/".into()), &self.shell)]
                };
                // Merge the collected definitions with the file path definitions.
                completions.extend(MultiCompleter::new(file_completers).completions(start));
            }
            CompletionType::Nothing => (),
        }
        completions
    }

    fn on_event<W: std::io::Write>(&mut self, event: Event<'_, '_, W>) {
        if let EventKind::BeforeComplete = event.kind {
            let (words, pos) = event.editor.get_words_and_cursor_position();

            self.completion = match (words.len(), pos) {
                (0, _) => CompletionType::Nothing,
                (1, CursorPosition::InSpace(..)) => CompletionType::VariableAndFiles,
                (1, _) => CompletionType::Command,
                _ => CompletionType::VariableAndFiles,
            };
        }
    }
}

/// Performs escaping to an inner `FilenameCompleter` to enable a handful of special cases
/// needed by the shell, such as expanding '~' to a home directory, or adding a backslash
/// when a special character is contained within an expanded filename.
pub struct IonFileCompleter<'a, 'b> {
    shell: &'b Shell<'a>,
    /// The directory the expansion takes place in
    path: PathBuf,
}

impl<'a, 'b> IonFileCompleter<'a, 'b> {
    pub fn new(path: Option<PathBuf>, shell: &'b Shell<'a>) -> Self {
        let path = path.unwrap_or_default();
        IonFileCompleter { shell, path }
    }
}

impl<'a, 'b> Completer for IonFileCompleter<'a, 'b> {
    /// When the tab key is pressed, **Liner** will use this method to perform completions of
    /// filenames. As our `IonFileCompleter` is a wrapper around **Liner**'s
    /// `FilenameCompleter`,
    /// the purpose of our custom `Completer` is to expand possible `~` characters in the
    /// `start`
    /// value that we receive from the prompt, grab completions from the inner
    /// `FilenameCompleter`,
    /// and then escape the resulting filenames, as well as remove the expanded form of the `~`
    /// character and re-add the `~` character in it's place.
    fn completions(&mut self, start: &str) -> Vec<String> {
        // Dereferencing the raw pointers here should be entirely safe, theoretically,
        // because no changes will occur to either of the underlying references in the
        // duration between creation of the completers and execution of their
        // completions.
        let expanded = match self.shell.tilde(start) {
            Ok(expanded) => expanded,
            Err(why) => {
                eprintln!("ion: {}", why);
                return vec![start.into()];
            }
        };
        // Now we obtain completions for the `expanded` form of the `start` value.
        let completions = filename_completion(&expanded, &self.path);
        if expanded == start {
            return completions.collect();
        }
        // We can do that by obtaining the index position where the tilde character
        // ends. We don't search with `~` because we also want to
        // handle other tilde variants.
        let t_index = start.find('/').unwrap_or(1);
        // `tilde` is the tilde pattern, and `search` is the pattern that follows.
        let (tilde, search) = start.split_at(t_index);

        if search.len() < 2 {
            // If the length of the search pattern is less than 2, the search pattern is
            // empty, and thus the completions actually contain files and directories in
            // the home directory.

            // The tilde pattern will actually be our `start` command in itself,
            // and the completed form will be all of the characters beyond the length of
            // the expanded form of the tilde pattern.
            completions.map(|completion| [start, &completion[expanded.len()..]].concat()).collect()
        // To save processing time, we should get obtain the index position where our
        // search pattern begins, and re-use that index to slice the completions so
        // that we may re-add the tilde character with the completion that follows.
        } else if let Some(e_index) = expanded.rfind(search) {
            // And then we will need to take those completions and remove the expanded form
            // of the tilde pattern and replace it with that pattern yet again.
            completions
                .map(|completion| escape(&[tilde, &completion[e_index..]].concat()))
                .collect()
        } else {
            Vec::new()
        }
    }
}

#[auto_enum]
fn filename_completion<'a>(start: &'a str, path: &'a PathBuf) -> impl Iterator<Item = String> + 'a {
    let unescaped_start = unescape(start);

    let mut split_start = unescaped_start.split('/');
    let mut string = String::with_capacity(128);

    // When 'start' is an absolute path, "/..." gets split to ["", "..."]
    // So we skip the first element and add "/" to the start of the string
    if unescaped_start.starts_with('/') {
        split_start.next();
        string.push('/');
    } else {
        string.push_str(&path.to_string_lossy());
    }

    for element in split_start {
        string.push_str(element);
        if element != "." && element != ".." {
            string.push('*');
        }
        string.push('/');
    }

    string.pop(); // pop out the last '/' character
    if string.ends_with('.') {
        string.push('*')
    }

    let globs = glob_with(
        &string,
        MatchOptions {
            case_sensitive:              true,
            require_literal_separator:   true,
            require_literal_leading_dot: false,
        },
    )
    .ok()
    .map(|completions| {
        completions.filter_map(Result::ok).filter_map(move |file| {
            let out = file.to_str()?;
            let mut joined = String::with_capacity(out.len() + 3); // worst case senario
            if unescaped_start.starts_with("./") {
                joined.push_str("./");
            }
            joined.push_str(out);
            if file.is_dir() {
                joined.push('/');
            }
            Some(escape(&joined))
        })
    });

    #[auto_enum(Iterator)]
    match globs {
        Some(iter) => iter,
        None => iter::once(escape(start)),
    }
}

/// A completer that combines suggestions from multiple completers.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct MultiCompleter<A>(Vec<A>);

impl<A> MultiCompleter<A> {
    pub fn new(completions: Vec<A>) -> Self { MultiCompleter(completions) }
}

impl<A> Completer for MultiCompleter<A>
where
    A: Completer,
{
    fn completions(&mut self, start: &str) -> Vec<String> {
        self.0.iter_mut().flat_map(|comp| comp.completions(start)).collect()
    }
}

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

    #[test]
    fn filename_completion() {
        let shell = Shell::default();
        let mut completer = IonFileCompleter::new(None, &shell);
        assert_eq!(completer.completions("testing"), vec!["testing/"]);
        assert_eq!(completer.completions("testing/file"), vec!["testing/file_with_text"]);
        assert_eq!(completer.completions("~"), vec!["~/"]);
        assert_eq!(completer.completions("tes/fil"), vec!["testing/file_with_text"]);
    }
}
<|endoftext|>


<commit_before>//! Runs rustfmt on the repository.

use crate::Build;
use build_helper::{output, t};
use ignore::WalkBuilder;
use std::path::Path;
use std::process::Command;

fn rustfmt(src: &Path, rustfmt: &Path, path: &Path, check: bool) {
    let mut cmd = Command::new(&rustfmt);
    // avoid the submodule config paths from coming into play,
    // we only allow a single global config for the workspace for now
    cmd.arg("--config-path").arg(&src.canonicalize().unwrap());
    cmd.arg("--edition").arg("2018");
    cmd.arg("--unstable-features");
    cmd.arg("--skip-children");
    if check {
        cmd.arg("--check");
    }
    cmd.arg(&path);
    let cmd_debug = format!("{:?}", cmd);
    let status = cmd.status().expect("executing rustfmt");
    if !status.success() {
        eprintln!(
            "Running `{}` failed.\nIf you're running `tidy`, \
            try again with `--bless` flag. Or, you just want to format \
            code, run `./x.py fmt` instead.",
            cmd_debug,
        );
        std::process::exit(1);
    }
}

#[derive(serde::Deserialize)]
struct RustfmtConfig {
    ignore: Vec<String>,
}

pub fn format(build: &Build, check: bool) {
    let mut builder = ignore::types::TypesBuilder::new();
    builder.add_defaults();
    builder.select("rust");
    let matcher = builder.build().unwrap();
    let rustfmt_config = build.src.join("rustfmt.toml");
    if !rustfmt_config.exists() {
        eprintln!("Not running formatting checks; rustfmt.toml does not exist.");
        eprintln!("This may happen in distributed tarballs.");
        return;
    }
    let rustfmt_config = t!(std::fs::read_to_string(&rustfmt_config));
    let rustfmt_config: RustfmtConfig = t!(toml::from_str(&rustfmt_config));
    let mut ignore_fmt = ignore::overrides::OverrideBuilder::new(&build.src);
    for ignore in rustfmt_config.ignore {
        ignore_fmt.add(&format!("!{}", ignore)).expect(&ignore);
    }
    let untracked_paths_output = output(
        Command::new("git").arg("status").arg("--porcelain").arg("--untracked-files=normal"),
    );
    let untracked_paths = untracked_paths_output
        .lines()
        .filter(|entry| entry.starts_with("??"))
        .map(|entry| entry.split(" ").nth(1).expect("every git status entry should list a path"));
    for untracked_path in untracked_paths {
        eprintln!("skip untracked path {} during rustfmt invocations", untracked_path);
        ignore_fmt.add(&format!("!{}", untracked_path)).expect(&untracked_path);
    }
    let ignore_fmt = ignore_fmt.build().unwrap();

    let rustfmt_path = build.config.initial_rustfmt.as_ref().unwrap_or_else(|| {
        eprintln!("./x.py fmt is not supported on this channel");
        std::process::exit(1);
    });
    let src = build.src.clone();
    let walker = WalkBuilder::new(&build.src).types(matcher).overrides(ignore_fmt).build_parallel();
    walker.run(|| {
        let src = src.clone();
        let rustfmt_path = rustfmt_path.clone();
        Box::new(move |entry| {
            let entry = t!(entry);
            if entry.file_type().map_or(false, |t| t.is_file()) {
                rustfmt(&src, &rustfmt_path, &entry.path(), check);
            }
            ignore::WalkState::Continue
        })
    });
}
<commit_msg>Rollup merge of #70106 - ehuss:fix-tidy-fmt-twice, r=Mark-Simulacrum<commit_after>//! Runs rustfmt on the repository.

use crate::Build;
use build_helper::{output, t};
use ignore::WalkBuilder;
use std::path::Path;
use std::process::Command;

fn rustfmt(src: &Path, rustfmt: &Path, path: &Path, check: bool) {
    let mut cmd = Command::new(&rustfmt);
    // avoid the submodule config paths from coming into play,
    // we only allow a single global config for the workspace for now
    cmd.arg("--config-path").arg(&src.canonicalize().unwrap());
    cmd.arg("--edition").arg("2018");
    cmd.arg("--unstable-features");
    cmd.arg("--skip-children");
    if check {
        cmd.arg("--check");
    }
    cmd.arg(&path);
    let cmd_debug = format!("{:?}", cmd);
    let status = cmd.status().expect("executing rustfmt");
    if !status.success() {
        eprintln!(
            "Running `{}` failed.\nIf you're running `tidy`, \
            try again with `--bless` flag. Or, you just want to format \
            code, run `./x.py fmt` instead.",
            cmd_debug,
        );
        std::process::exit(1);
    }
}

#[derive(serde::Deserialize)]
struct RustfmtConfig {
    ignore: Vec<String>,
}

pub fn format(build: &Build, check: bool) {
    if build.config.dry_run {
        return;
    }
    let mut builder = ignore::types::TypesBuilder::new();
    builder.add_defaults();
    builder.select("rust");
    let matcher = builder.build().unwrap();
    let rustfmt_config = build.src.join("rustfmt.toml");
    if !rustfmt_config.exists() {
        eprintln!("Not running formatting checks; rustfmt.toml does not exist.");
        eprintln!("This may happen in distributed tarballs.");
        return;
    }
    let rustfmt_config = t!(std::fs::read_to_string(&rustfmt_config));
    let rustfmt_config: RustfmtConfig = t!(toml::from_str(&rustfmt_config));
    let mut ignore_fmt = ignore::overrides::OverrideBuilder::new(&build.src);
    for ignore in rustfmt_config.ignore {
        ignore_fmt.add(&format!("!{}", ignore)).expect(&ignore);
    }
    let untracked_paths_output = output(
        Command::new("git").arg("status").arg("--porcelain").arg("--untracked-files=normal"),
    );
    let untracked_paths = untracked_paths_output
        .lines()
        .filter(|entry| entry.starts_with("??"))
        .map(|entry| entry.split(" ").nth(1).expect("every git status entry should list a path"));
    for untracked_path in untracked_paths {
        eprintln!("skip untracked path {} during rustfmt invocations", untracked_path);
        ignore_fmt.add(&format!("!{}", untracked_path)).expect(&untracked_path);
    }
    let ignore_fmt = ignore_fmt.build().unwrap();

    let rustfmt_path = build.config.initial_rustfmt.as_ref().unwrap_or_else(|| {
        eprintln!("./x.py fmt is not supported on this channel");
        std::process::exit(1);
    });
    let src = build.src.clone();
    let walker = WalkBuilder::new(&build.src).types(matcher).overrides(ignore_fmt).build_parallel();
    walker.run(|| {
        let src = src.clone();
        let rustfmt_path = rustfmt_path.clone();
        Box::new(move |entry| {
            let entry = t!(entry);
            if entry.file_type().map_or(false, |t| t.is_file()) {
                rustfmt(&src, &rustfmt_path, &entry.path(), check);
            }
            ignore::WalkState::Continue
        })
    });
}
<|endoftext|>


<commit_before>// Copyright 2013-2014 The gl-rs developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! # gl_generator
//!
//! `gl_generator` is an OpenGL bindings generator. It defines a function
//!  named `generate_bindings` which can be used to generate all constants
//!  and functions of a given OpenGL version.
//!
//! ## Example
//!
//! In `build.rs`:
//!
//! ~~~no_run
//! extern crate gl_generator;
//! extern crate khronos_api;
//!
//! use std::os;
//! use std::old_io::File;
//!
//! fn main() {
//!     let dest = Path::new(os::getenv("OUT_DIR").unwrap());
//!
//!     let mut file = File::create(&dest.join("gl_bindings.rs")).unwrap();
//!
//!     gl_generator::generate_bindings(gl_generator::GlobalGenerator,
//!                                     gl_generator::registry::Ns::Gl,
//!                                     khronos_api::GL_XML, vec![], "4.5", "core"
//!                                     &mut file).unwrap();
//! }
//! ~~~
//!
//! In your project:
//!
//! ~~~ignore
//! include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs"));
//! ~~~
//!
//! ## Arguments
//!
//! Each field can be specified at most once, or not at all. If the field is not
//! specified, then a default value will be used.
//!
//! - `api`: The API to generate. Can be either `"gl"`, `"gles1"`, `"gles2"`,
//!   `"wgl"`, `"glx"`, `"egl"`. Defaults to `"gl"`.
//! - `profile`: Can be either `"core"` or `"compatibility"`. Defaults to
//!   `"core"`. `"core"` will only include all functions supported by the
//!   requested version it self, while `"compatibility"` will include all the
//!   functions from previous versions as well.
//! - `version`: The requested API version. This is usually in the form
//!   `"major.minor"`. Defaults to `"1.0"`
//! - `generator`: The type of loader to generate. Can be either `"static"`,
//!   `"global"`, or `"struct"`. Defaults to `"static"`.
//! - `extensions`: Extra extensions to include in the bindings. These are
//!   specified as a list of strings. Defaults to `[]`.
//!
//! ## About EGL
//!
//! When you generate bindings for EGL, the following platform-specific types must be declared
//!  *at the same level where you include the bindings*:
//!
//! - `khronos_utime_nanoseconds_t`
//! - `khronos_uint64_t`
//! - `khronos_ssize_t`
//! - `EGLNativeDisplayType`
//! - `EGLNativePixmapType`
//! - `EGLNativeWindowType`
//! - `EGLint`
//! - `NativeDisplayType`
//! - `NativePixmapType`
//! - `NativeWindowType`
//!

#![crate_name = "gl_generator"]
#![crate_type = "dylib"]

#![feature(advanced_slice_patterns)]
#![feature(plugin)]
#![feature(plugin_registrar)]
#![feature(quote)]

#[plugin]
#[macro_use]
extern crate log;

extern crate khronos_api;

use generators::Generator;
use registry::{Registry, Filter, Ns};

use std::old_io::IoResult;

pub use generators::global_gen::GlobalGenerator;
pub use generators::static_gen::StaticGenerator;
pub use generators::static_struct_gen::StaticStructGenerator;
pub use generators::struct_gen::StructGenerator;

pub mod generators;

#[allow(dead_code)]
pub mod registry;

/// Public function that generates Rust source code.
pub fn generate_bindings<G, W>(generator: G, ns: registry::Ns, source: &[u8],
                               extensions: Vec<String>, version: &str, profile: &str,
                               dest: &mut W) -> IoResult<()> where G: Generator, W: Writer
{
    // Get generator field values, using default values if they have not been
    // specified
    let filter = Some(Filter {
        api: ns.to_string(),
        extensions: extensions,
        version: version.to_string(),
        profile: profile.to_string(),
    });

    // Generate the registry of all bindings
    let registry = {
        use std::old_io::BufReader;
        let reader = BufReader::new(source);
        Registry::from_xml(reader, ns, filter)
    };

    generator.write(&registry, ns, dest)
}
<commit_msg>Fix plugin syntax.<commit_after>// Copyright 2013-2014 The gl-rs developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! # gl_generator
//!
//! `gl_generator` is an OpenGL bindings generator. It defines a function
//!  named `generate_bindings` which can be used to generate all constants
//!  and functions of a given OpenGL version.
//!
//! ## Example
//!
//! In `build.rs`:
//!
//! ~~~no_run
//! extern crate gl_generator;
//! extern crate khronos_api;
//!
//! use std::os;
//! use std::old_io::File;
//!
//! fn main() {
//!     let dest = Path::new(os::getenv("OUT_DIR").unwrap());
//!
//!     let mut file = File::create(&dest.join("gl_bindings.rs")).unwrap();
//!
//!     gl_generator::generate_bindings(gl_generator::GlobalGenerator,
//!                                     gl_generator::registry::Ns::Gl,
//!                                     khronos_api::GL_XML, vec![], "4.5", "core"
//!                                     &mut file).unwrap();
//! }
//! ~~~
//!
//! In your project:
//!
//! ~~~ignore
//! include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs"));
//! ~~~
//!
//! ## Arguments
//!
//! Each field can be specified at most once, or not at all. If the field is not
//! specified, then a default value will be used.
//!
//! - `api`: The API to generate. Can be either `"gl"`, `"gles1"`, `"gles2"`,
//!   `"wgl"`, `"glx"`, `"egl"`. Defaults to `"gl"`.
//! - `profile`: Can be either `"core"` or `"compatibility"`. Defaults to
//!   `"core"`. `"core"` will only include all functions supported by the
//!   requested version it self, while `"compatibility"` will include all the
//!   functions from previous versions as well.
//! - `version`: The requested API version. This is usually in the form
//!   `"major.minor"`. Defaults to `"1.0"`
//! - `generator`: The type of loader to generate. Can be either `"static"`,
//!   `"global"`, or `"struct"`. Defaults to `"static"`.
//! - `extensions`: Extra extensions to include in the bindings. These are
//!   specified as a list of strings. Defaults to `[]`.
//!
//! ## About EGL
//!
//! When you generate bindings for EGL, the following platform-specific types must be declared
//!  *at the same level where you include the bindings*:
//!
//! - `khronos_utime_nanoseconds_t`
//! - `khronos_uint64_t`
//! - `khronos_ssize_t`
//! - `EGLNativeDisplayType`
//! - `EGLNativePixmapType`
//! - `EGLNativeWindowType`
//! - `EGLint`
//! - `NativeDisplayType`
//! - `NativePixmapType`
//! - `NativeWindowType`
//!

#![crate_name = "gl_generator"]
#![crate_type = "dylib"]

#![feature(advanced_slice_patterns)]
#![feature(plugin)]
#![feature(plugin_registrar)]
#![feature(quote)]
#![plugin(log)]

#[macro_use]
extern crate log;

extern crate khronos_api;

use generators::Generator;
use registry::{Registry, Filter, Ns};

use std::old_io::IoResult;

pub use generators::global_gen::GlobalGenerator;
pub use generators::static_gen::StaticGenerator;
pub use generators::static_struct_gen::StaticStructGenerator;
pub use generators::struct_gen::StructGenerator;

pub mod generators;

#[allow(dead_code)]
pub mod registry;

/// Public function that generates Rust source code.
pub fn generate_bindings<G, W>(generator: G, ns: registry::Ns, source: &[u8],
                               extensions: Vec<String>, version: &str, profile: &str,
                               dest: &mut W) -> IoResult<()> where G: Generator, W: Writer
{
    // Get generator field values, using default values if they have not been
    // specified
    let filter = Some(Filter {
        api: ns.to_string(),
        extensions: extensions,
        version: version.to_string(),
        profile: profile.to_string(),
    });

    // Generate the registry of all bindings
    let registry = {
        use std::old_io::BufReader;
        let reader = BufReader::new(source);
        Registry::from_xml(reader, ns, filter)
    };

    generator.write(&registry, ns, dest)
}
<|endoftext|>