<commit_before>use std::collections::{BTreeMap, HashSet};
use std::fs::File;
use std::io::{self, BufRead};
use std::iter::repeat;
use std::path::PathBuf;
use std::str;
use std::task::Poll;
use std::time::Duration;
use std::{cmp, env};

use anyhow::{bail, format_err, Context as _};
use cargo_util::paths;
use crates_io::{self, NewCrate, NewCrateDependency, Registry};
use curl::easy::{Easy, InfoType, SslOpt, SslVersion};
use log::{log, Level};
use percent_encoding::{percent_encode, NON_ALPHANUMERIC};
use termcolor::Color::Green;
use termcolor::ColorSpec;

use crate::core::dependency::DepKind;
use crate::core::dependency::Dependency;
use crate::core::manifest::ManifestMetadata;
use crate::core::resolver::CliFeatures;
use crate::core::source::Source;
use crate::core::QueryKind;
use crate::core::{Package, SourceId, Workspace};
use crate::ops;
use crate::ops::Packages;
use crate::sources::{RegistrySource, SourceConfigMap, CRATES_IO_DOMAIN, CRATES_IO_REGISTRY};
use crate::util::config::{self, Config, SslVersionConfig, SslVersionConfigRange};
use crate::util::errors::CargoResult;
use crate::util::important_paths::find_root_manifest_for_wd;
use crate::util::{truncate_with_ellipsis, IntoUrl};
use crate::{drop_print, drop_println, version};

mod auth;

/// Registry settings loaded from config files.
///
/// This is loaded based on the `--registry` flag and the config settings.
#[derive(Debug)]
pub enum RegistryConfig {
    None,
    /// The authentication token.
    Token(String),
    /// Process used for fetching a token.
    Process((PathBuf, Vec<String>)),
}

impl RegistryConfig {
    /// Returns `true` if the credential is [`None`].
    ///
    /// [`None`]: Self::None
    pub fn is_none(&self) -> bool {
        matches!(self, Self::None)
    }
    /// Returns `true` if the credential is [`Token`].
    ///
    /// [`Token`]: Self::Token
    pub fn is_token(&self) -> bool {
        matches!(self, Self::Token(..))
    }
    pub fn as_token(&self) -> Option<&str> {
        if let Self::Token(v) = self {
            Some(&*v)
        } else {
            None
        }
    }
    pub fn as_process(&self) -> Option<&(PathBuf, Vec<String>)> {
        if let Self::Process(v) = self {
            Some(v)
        } else {
            None
        }
    }
}

pub struct PublishOpts<'cfg> {
    pub config: &'cfg Config,
    pub token: Option<String>,
    pub index: Option<String>,
    pub verify: bool,
    pub allow_dirty: bool,
    pub jobs: Option<i32>,
    pub keep_going: bool,
    pub to_publish: ops::Packages,
    pub targets: Vec<String>,
    pub dry_run: bool,
    pub registry: Option<String>,
    pub cli_features: CliFeatures,
}

pub fn publish(ws: &Workspace<'_>, opts: &PublishOpts<'_>) -> CargoResult<()> {
    let specs = opts.to_publish.to_package_id_specs(ws)?;
    if specs.len() > 1 {
        bail!("the `-p` argument must be specified to select a single package to publish")
    }
    if Packages::Default == opts.to_publish && ws.is_virtual() {
        bail!("the `-p` argument must be specified in the root of a virtual workspace")
    }
    let member_ids = ws.members().map(|p| p.package_id());
    // Check that the spec matches exactly one member.
    specs[0].query(member_ids)?;
    let mut pkgs = ws.members_with_features(&specs, &opts.cli_features)?;
    // In `members_with_features_old`, it will add "current" package (determined by the cwd)
    // So we need filter
    pkgs = pkgs
        .into_iter()
        .filter(|(m, _)| specs.iter().any(|spec| spec.matches(m.package_id())))
        .collect();
    // Double check. It is safe theoretically, unless logic has updated.
    assert_eq!(pkgs.len(), 1);

    let (pkg, cli_features) = pkgs.pop().unwrap();

    let mut publish_registry = opts.registry.clone();
    if let Some(ref allowed_registries) = *pkg.publish() {
        if publish_registry.is_none() && allowed_registries.len() == 1 {
            // If there is only one allowed registry, push to that one directly,
            // even though there is no registry specified in the command.
            let default_registry = &allowed_registries[0];
            if default_registry != CRATES_IO_REGISTRY {
                // Don't change the registry for crates.io and don't warn the user.
                // crates.io will be defaulted even without this.
                opts.config.shell().note(&format!(
                    "Found `{}` as only allowed registry. Publishing to it automatically.",
                    default_registry
                ))?;
                publish_registry = Some(default_registry.clone());
            }
        }

        let reg_name = publish_registry
            .clone()
            .unwrap_or_else(|| CRATES_IO_REGISTRY.to_string());
        if !allowed_registries.contains(&reg_name) {
            bail!(
                "`{}` cannot be published.\n\
                 The registry `{}` is not listed in the `publish` value in Cargo.toml.",
                pkg.name(),
                reg_name
            );
        }
    }

    let (mut registry, _reg_cfg, reg_ids) = registry(
        opts.config,
        opts.token.clone(),
        opts.index.as_deref(),
        publish_registry.as_deref(),
        true,
        !opts.dry_run,
    )?;
    verify_dependencies(pkg, &registry, reg_ids.original)?;

    // Prepare a tarball, with a non-suppressible warning if metadata
    // is missing since this is being put online.
    let tarball = ops::package_one(
        ws,<commit_msg>Improve error message if `publish` is false or empty list<commit_after>use std::collections::{BTreeMap, HashSet};
use std::fs::File;
use std::io::{self, BufRead};
use std::iter::repeat;
use std::path::PathBuf;
use std::str;
use std::task::Poll;
use std::time::Duration;
use std::{cmp, env};

use anyhow::{bail, format_err, Context as _};
use cargo_util::paths;
use crates_io::{self, NewCrate, NewCrateDependency, Registry};
use curl::easy::{Easy, InfoType, SslOpt, SslVersion};
use log::{log, Level};
use percent_encoding::{percent_encode, NON_ALPHANUMERIC};
use termcolor::Color::Green;
use termcolor::ColorSpec;

use crate::core::dependency::DepKind;
use crate::core::dependency::Dependency;
use crate::core::manifest::ManifestMetadata;
use crate::core::resolver::CliFeatures;
use crate::core::source::Source;
use crate::core::QueryKind;
use crate::core::{Package, SourceId, Workspace};
use crate::ops;
use crate::ops::Packages;
use crate::sources::{RegistrySource, SourceConfigMap, CRATES_IO_DOMAIN, CRATES_IO_REGISTRY};
use crate::util::config::{self, Config, SslVersionConfig, SslVersionConfigRange};
use crate::util::errors::CargoResult;
use crate::util::important_paths::find_root_manifest_for_wd;
use crate::util::{truncate_with_ellipsis, IntoUrl};
use crate::{drop_print, drop_println, version};

mod auth;

/// Registry settings loaded from config files.
///
/// This is loaded based on the `--registry` flag and the config settings.
#[derive(Debug)]
pub enum RegistryConfig {
    None,
    /// The authentication token.
    Token(String),
    /// Process used for fetching a token.
    Process((PathBuf, Vec<String>)),
}

impl RegistryConfig {
    /// Returns `true` if the credential is [`None`].
    ///
    /// [`None`]: Self::None
    pub fn is_none(&self) -> bool {
        matches!(self, Self::None)
    }
    /// Returns `true` if the credential is [`Token`].
    ///
    /// [`Token`]: Self::Token
    pub fn is_token(&self) -> bool {
        matches!(self, Self::Token(..))
    }
    pub fn as_token(&self) -> Option<&str> {
        if let Self::Token(v) = self {
            Some(&*v)
        } else {
            None
        }
    }
    pub fn as_process(&self) -> Option<&(PathBuf, Vec<String>)> {
        if let Self::Process(v) = self {
            Some(v)
        } else {
            None
        }
    }
}

pub struct PublishOpts<'cfg> {
    pub config: &'cfg Config,
    pub token: Option<String>,
    pub index: Option<String>,
    pub verify: bool,
    pub allow_dirty: bool,
    pub jobs: Option<i32>,
    pub keep_going: bool,
    pub to_publish: ops::Packages,
    pub targets: Vec<String>,
    pub dry_run: bool,
    pub registry: Option<String>,
    pub cli_features: CliFeatures,
}

pub fn publish(ws: &Workspace<'_>, opts: &PublishOpts<'_>) -> CargoResult<()> {
    let specs = opts.to_publish.to_package_id_specs(ws)?;
    if specs.len() > 1 {
        bail!("the `-p` argument must be specified to select a single package to publish")
    }
    if Packages::Default == opts.to_publish && ws.is_virtual() {
        bail!("the `-p` argument must be specified in the root of a virtual workspace")
    }
    let member_ids = ws.members().map(|p| p.package_id());
    // Check that the spec matches exactly one member.
    specs[0].query(member_ids)?;
    let mut pkgs = ws.members_with_features(&specs, &opts.cli_features)?;
    // In `members_with_features_old`, it will add "current" package (determined by the cwd)
    // So we need filter
    pkgs = pkgs
        .into_iter()
        .filter(|(m, _)| specs.iter().any(|spec| spec.matches(m.package_id())))
        .collect();
    // Double check. It is safe theoretically, unless logic has updated.
    assert_eq!(pkgs.len(), 1);

    let (pkg, cli_features) = pkgs.pop().unwrap();

    let mut publish_registry = opts.registry.clone();
    if let Some(ref allowed_registries) = *pkg.publish() {
        if publish_registry.is_none() && allowed_registries.len() == 1 {
            // If there is only one allowed registry, push to that one directly,
            // even though there is no registry specified in the command.
            let default_registry = &allowed_registries[0];
            if default_registry != CRATES_IO_REGISTRY {
                // Don't change the registry for crates.io and don't warn the user.
                // crates.io will be defaulted even without this.
                opts.config.shell().note(&format!(
                    "Found `{}` as only allowed registry. Publishing to it automatically.",
                    default_registry
                ))?;
                publish_registry = Some(default_registry.clone());
            }
        }

        let reg_name = publish_registry
            .clone()
            .unwrap_or_else(|| CRATES_IO_REGISTRY.to_string());
        if allowed_registries.is_empty() {
            bail!(
                "`{}` cannot be published.\n\
                 `publish` is set to `false` or an empty list in Cargo.toml and prevents publishing.",
                pkg.name(),
            );
        } else if !allowed_registries.contains(&reg_name) {
            bail!(
                "`{}` cannot be published.\n\
                 The registry `{}` is not listed in the `publish` value in Cargo.toml.",
                pkg.name(),
                reg_name
            );
        }
    }

    let (mut registry, _reg_cfg, reg_ids) = registry(
        opts.config,
        opts.token.clone(),
        opts.index.as_deref(),
        publish_registry.as_deref(),
        true,
        !opts.dry_run,
    )?;
    verify_dependencies(pkg, &registry, reg_ids.original)?;

    // Prepare a tarball, with a non-suppressible warning if metadata
    // is missing since this is being put online.
    let tarball = ops::package_one(
        ws,
{'fraction_non_alphanumeric': 0.1371089249313126, 'fraction_numerical': 0.0012408047505096163, 'mean_word_length': 2.746347941567065, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 41, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2728481', 'n_tokens_mistral': 3493, 'n_tokens_neox': 3430, 'n_words': 939}