<commit_before>use std::io::prelude::*;
use std::fs::{self, File};
use std::path::{self, Path, PathBuf};

use tar::Archive;
use flate2::{GzBuilder, Compression};
use flate2::read::GzDecoder;

use core::{SourceId, Package, PackageId};
use sources::PathSource;
use util::{self, CargoResult, human, internal, ChainError, Config};
use ops;

struct Bomb { path: Option<PathBuf> }

impl Drop for Bomb {
    fn drop(&mut self) {
        match self.path.as_ref() {
            Some(path) => { let _ = fs::remove_file(path); }
            None => {}
        }
    }
}

pub fn package(manifest_path: &Path,
               config: &Config,
               verify: bool,
               list: bool,
               metadata: bool) -> CargoResult<Option<PathBuf>> {
    let mut src = try!(PathSource::for_path(manifest_path.parent().unwrap(),
                                            config));
    let pkg = try!(src.root_package());

    if metadata {
        try!(check_metadata(&pkg, config));
    }

    if list {
        let root = pkg.root();
        let mut list: Vec<_> = try!(src.list_files(&pkg)).iter().map(|file| {
            util::without_prefix(&file, &root).unwrap().to_path_buf()
        }).collect();
        list.sort();
        for file in list.iter() {
            println!("{}", file.display());
        }
        return Ok(None)
    }

    let filename = format!("package/{}-{}.crate", pkg.name(), pkg.version());
    let target_dir = config.target_dir(&pkg);
    let dst = target_dir.join(&filename);
    if fs::metadata(&dst).is_ok() { return Ok(Some(dst)) }

    let mut bomb = Bomb { path: Some(dst.clone()) };

    try!(config.shell().status("Packaging", pkg.package_id().to_string()));
    try!(tar(&pkg, &src, config, &dst).chain_error(|| {
        human("failed to prepare local package for uploading")
    }));
    if verify {
        try!(run_verify(config, &pkg, &dst).chain_error(|| {
            human("failed to verify package tarball")
        }))
    }
    Ok(Some(bomb.path.take().unwrap()))
}

// check that the package has some piece of metadata that a human can
// use to tell what the package is about.
#[allow(deprecated)] // connect => join in 1.3
fn check_metadata(pkg: &Package, config: &Config) -> CargoResult<()> {
    let md = pkg.manifest().metadata();

    let mut missing = vec![];

    macro_rules! lacking {
        ($( $($field: ident)||* ),*) => {{
            $(
                if $(md.$field.as_ref().map_or(true, |s| s.is_empty()))&&* {
                    $(missing.push(stringify!($field).replace("_", "-"));)*
                }
            )*
        }}
    }
    lacking!(description, license || license_file, documentation || homepage || repository);

    if !missing.is_empty() {
        let mut things = missing[..missing.len() - 1].connect(", ");
        // things will be empty if and only if length == 1 (i.e. the only case
        // to have no `or`).
        if !things.is_empty() {
            things.push_str(" or ");
        }
        things.push_str(&missing.last().unwrap());

        try!(config.shell().warn(
            &format!("warning: manifest has no {things}. \
                    See http://doc.crates.io/manifest.html#package-metadata for more info.",
                    things = things)))
    }
    Ok(())
}

fn tar(pkg: &Package, src: &PathSource, config: &Config,
       dst: &Path) -> CargoResult<()> {

    if fs::metadata(&dst).is_ok() {
        return Err(human(format!("destination already exists: {}",
                                 dst.display())))
    }<commit_msg>Add a warning when packaging crates with wildcard dependencies<commit_after>use std::io::prelude::*;
use std::fs::{self, File};
use std::path::{self, Path, PathBuf};

use semver::VersionReq;
use tar::Archive;
use flate2::{GzBuilder, Compression};
use flate2::read::GzDecoder;

use core::{SourceId, Package, PackageId};
use core::dependency::Kind;
use sources::PathSource;
use util::{self, CargoResult, human, internal, ChainError, Config};
use ops;

struct Bomb { path: Option<PathBuf> }

impl Drop for Bomb {
    fn drop(&mut self) {
        match self.path.as_ref() {
            Some(path) => { let _ = fs::remove_file(path); }
            None => {}
        }
    }
}

pub fn package(manifest_path: &Path,
               config: &Config,
               verify: bool,
               list: bool,
               metadata: bool) -> CargoResult<Option<PathBuf>> {
    let mut src = try!(PathSource::for_path(manifest_path.parent().unwrap(),
                                            config));
    let pkg = try!(src.root_package());

    if metadata {
        try!(check_metadata(&pkg, config));
    }

    try!(check_dependencies(&pkg, config));

    if list {
        let root = pkg.root();
        let mut list: Vec<_> = try!(src.list_files(&pkg)).iter().map(|file| {
            util::without_prefix(&file, &root).unwrap().to_path_buf()
        }).collect();
        list.sort();
        for file in list.iter() {
            println!("{}", file.display());
        }
        return Ok(None)
    }

    let filename = format!("package/{}-{}.crate", pkg.name(), pkg.version());
    let target_dir = config.target_dir(&pkg);
    let dst = target_dir.join(&filename);
    if fs::metadata(&dst).is_ok() { return Ok(Some(dst)) }

    let mut bomb = Bomb { path: Some(dst.clone()) };

    try!(config.shell().status("Packaging", pkg.package_id().to_string()));
    try!(tar(&pkg, &src, config, &dst).chain_error(|| {
        human("failed to prepare local package for uploading")
    }));
    if verify {
        try!(run_verify(config, &pkg, &dst).chain_error(|| {
            human("failed to verify package tarball")
        }))
    }
    Ok(Some(bomb.path.take().unwrap()))
}

// check that the package has some piece of metadata that a human can
// use to tell what the package is about.
#[allow(deprecated)] // connect => join in 1.3
fn check_metadata(pkg: &Package, config: &Config) -> CargoResult<()> {
    let md = pkg.manifest().metadata();

    let mut missing = vec![];

    macro_rules! lacking {
        ($( $($field: ident)||* ),*) => {{
            $(
                if $(md.$field.as_ref().map_or(true, |s| s.is_empty()))&&* {
                    $(missing.push(stringify!($field).replace("_", "-"));)*
                }
            )*
        }}
    }
    lacking!(description, license || license_file, documentation || homepage || repository);

    if !missing.is_empty() {
        let mut things = missing[..missing.len() - 1].connect(", ");
        // things will be empty if and only if length == 1 (i.e. the only case
        // to have no `or`).
        if !things.is_empty() {
            things.push_str(" or ");
        }
        things.push_str(&missing.last().unwrap());

        try!(config.shell().warn(
            &format!("warning: manifest has no {things}. \
                    See http://doc.crates.io/manifest.html#package-metadata for more info.",
                    things = things)))
    }
    Ok(())
}

// Warn about wildcard deps which will soon be prohibited on crates.io
#[allow(deprecated)] // connect => join in 1.3
fn check_dependencies(pkg: &Package, config: &Config) -> CargoResult<()> {
    let wildcard = VersionReq::parse("*").unwrap();

    let mut wildcard_deps = vec![];
    for dep in pkg.dependencies() {
        if dep.kind() != Kind::Development && dep.version_req() == &wildcard {
            wildcard_deps.push(dep.name());
        }
    }

    if !wildcard_deps.is_empty() {
        let deps = wildcard_deps.connect(", ");
        try!(config.shell().warn(
            "warning: some dependencies have wildcard (\"*\") version constraints. \
             On December 11th, 2015, crates.io will begin rejecting packages with \
             wildcard dependency constraints. See \
             http://doc.crates.io/crates-io.html#using-crates.io-based-crates \
             for information on version constraints."));
        try!(config.shell().warn(
            &format!("dependencies for these crates have wildcard constraints: {}", deps)));
    }
    Ok(())
}

fn tar(pkg: &Package, src: &PathSource, config: &Config,
       dst: &Path) -> CargoResult<()> {

    if fs::metadata(&dst).is_ok() {
        return Err(human(format!("destination already exists: {}",
                                 dst.display())))
    }
{'fraction_non_alphanumeric': 0.16898536819381146, 'fraction_numerical': 0.0023986567522187576, 'mean_word_length': 2.3409455128205128, 'pattern_counts': {'":': 0, '<': 16, '<?xml version=': 0, '>': 32, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 7, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5084658', 'n_tokens_mistral': 2527, 'n_tokens_neox': 2472, 'n_words': 606}