use crate::util::data_structures::HashMap;
use std::borrow::Cow;
use std::ffi::OsStr;
use cargo_credential::Secret;
use serde::Deserialize;
use serde_untagged::UntaggedEnumVisitor;
use std::path::Path;
use crate::CargoResult;
use super::OptValue;
use super::PathAndArgs;
use super::StringList;
use super::Value;
use super::path::ConfigRelativePath;
#[derive(Debug, Default, Deserialize, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub struct CargoHttpConfig {
pub proxy: Option<String>,
pub low_speed_limit: Option<u32>,
pub timeout: Option<u64>,
pub cainfo: Option<ConfigRelativePath>,
pub proxy_cainfo: Option<ConfigRelativePath>,
pub check_revoke: Option<bool>,
pub user_agent: Option<String>,
pub debug: Option<bool>,
pub multiplexing: Option<bool>,
pub ssl_version: Option<SslVersionConfig>,
}
#[derive(Debug, Default, Deserialize, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub struct CargoFutureIncompatConfig {
frequency: Option<CargoFutureIncompatFrequencyConfig>,
}
#[derive(Debug, Default, Deserialize, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub enum CargoFutureIncompatFrequencyConfig {
#[default]
Always,
Never,
}
impl CargoFutureIncompatConfig {
pub fn should_display_message(&self) -> bool {
use CargoFutureIncompatFrequencyConfig::*;
let frequency = self.frequency.as_ref().unwrap_or(&Always);
match frequency {
Always => true,
Never => false,
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum SslVersionConfig {
Single(String),
Range(SslVersionConfigRange),
}
impl<'de> Deserialize<'de> for SslVersionConfig {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
UntaggedEnumVisitor::new()
.string(|single| Ok(SslVersionConfig::Single(single.to_owned())))
.map(|map| map.deserialize().map(SslVersionConfig::Range))
.deserialize(deserializer)
}
}
#[derive(Clone, Debug, Deserialize, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub struct SslVersionConfigRange {
pub min: Option<String>,
pub max: Option<String>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct CargoNetConfig {
pub retry: Option<u32>,
pub offline: Option<bool>,
pub git_fetch_with_cli: Option<bool>,
pub ssh: Option<CargoSshConfig>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct CargoSshConfig {
pub known_hosts: Option<Vec<Value<String>>>,
}
#[derive(Debug, Clone)]
pub enum JobsConfig {
Integer(i32),
String(String),
}
impl<'de> Deserialize<'de> for JobsConfig {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
UntaggedEnumVisitor::new()
.i32(|int| Ok(JobsConfig::Integer(int)))
.string(|string| Ok(JobsConfig::String(string.to_owned())))
.deserialize(deserializer)
}
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct CargoBuildConfig {
pub pipelining: Option<bool>,
pub dep_info_basedir: Option<ConfigRelativePath>,
pub target_dir: Option<ConfigRelativePath>,
pub build_dir: Option<ConfigRelativePath>,
pub incremental: Option<bool>,
pub target: Option<BuildTargetConfig>,
pub jobs: Option<JobsConfig>,
pub rustflags: Option<StringList>,
pub rustdocflags: Option<StringList>,
pub rustc_wrapper: Option<ConfigRelativePath>,
pub rustc_workspace_wrapper: Option<ConfigRelativePath>,
pub rustc: Option<ConfigRelativePath>,
pub rustdoc: Option<ConfigRelativePath>,
pub artifact_dir: Option<ConfigRelativePath>,
pub warnings: Option<WarningHandling>,
pub sbom: Option<bool>,
pub analysis: Option<CargoBuildAnalysis>,
}
#[derive(Debug, Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
pub struct CargoBuildAnalysis {
pub enabled: bool,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
pub enum WarningHandling {
#[default]
Warn,
Allow,
Deny,
}
#[derive(Debug, Deserialize)]
#[serde(transparent)]
pub struct BuildTargetConfig {
inner: Value<BuildTargetConfigInner>,
}
#[derive(Debug)]
enum BuildTargetConfigInner {
One(String),
Many(Vec<String>),
}
impl<'de> Deserialize<'de> for BuildTargetConfigInner {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
UntaggedEnumVisitor::new()
.string(|one| Ok(BuildTargetConfigInner::One(one.to_owned())))
.seq(|many| many.deserialize().map(BuildTargetConfigInner::Many))
.deserialize(deserializer)
}
}
impl BuildTargetConfig {
pub fn values(&self, cwd: &Path) -> CargoResult<Vec<String>> {
let map = |s: &String| {
if s.ends_with(".json") {
self.inner
.definition
.root(cwd)
.join(s)
.to_str()
.expect("must be utf-8 in toml")
.to_string()
} else {
s.to_string()
}
};
let values = match &self.inner.val {
BuildTargetConfigInner::One(s) => vec![map(s)],
BuildTargetConfigInner::Many(v) => v.iter().map(map).collect(),
};
Ok(values)
}
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct CargoResolverConfig {
pub incompatible_rust_versions: Option<IncompatibleRustVersions>,
pub incompatible_publish_age: Option<IncompatiblePublishAge>,
pub feature_unification: Option<FeatureUnification>,
pub lockfile_path: Option<ConfigRelativePath>,
}
#[derive(Debug, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub enum IncompatibleRustVersions {
Allow,
Fallback,
}
#[derive(Debug, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub enum IncompatiblePublishAge {
Allow,
Deny,
}
#[derive(Copy, Clone, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum FeatureUnification {
Package,
Selected,
Workspace,
}
#[derive(Debug, Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
pub struct TermConfig {
pub verbose: Option<bool>,
pub quiet: Option<bool>,
pub color: Option<String>,
pub hyperlinks: Option<bool>,
pub unicode: Option<bool>,
pub progress: Option<ProgressConfig>,
}
#[derive(Debug, Default)]
pub struct ProgressConfig {
pub when: ProgressWhen,
pub width: Option<usize>,
pub term_integration: Option<bool>,
}
#[derive(Debug, Default, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum ProgressWhen {
#[default]
Auto,
Never,
Always,
}
impl<'de> Deserialize<'de> for ProgressConfig {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(rename_all = "kebab-case")]
struct ProgressConfigInner {
#[serde(default)]
when: ProgressWhen,
width: Option<usize>,
term_integration: Option<bool>,
}
let pc = ProgressConfigInner::deserialize(deserializer)?;
if let ProgressConfigInner {
when: ProgressWhen::Always,
width: None,
..
} = pc
{
return Err(serde::de::Error::custom(
"\"always\" progress requires a `width` key",
));
}
Ok(ProgressConfig {
when: pc.when,
width: pc.width,
term_integration: pc.term_integration,
})
}
}
#[derive(Debug)]
enum EnvConfigValueInner {
Simple(String),
WithOptions {
value: ConfigRelativePath,
force: bool,
relative: bool,
},
}
impl<'de> Deserialize<'de> for EnvConfigValueInner {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(Deserialize)]
struct WithOptions {
value: ConfigRelativePath,
#[serde(default)]
force: bool,
#[serde(default)]
relative: bool,
}
UntaggedEnumVisitor::new()
.string(|simple| Ok(EnvConfigValueInner::Simple(simple.to_owned())))
.map(|map| {
let with_options: WithOptions = map.deserialize()?;
Ok(EnvConfigValueInner::WithOptions {
value: with_options.value,
force: with_options.force,
relative: with_options.relative,
})
})
.deserialize(deserializer)
}
}
#[derive(Debug, Deserialize)]
#[serde(transparent)]
pub struct EnvConfigValue {
inner: EnvConfigValueInner,
}
impl EnvConfigValue {
pub fn is_force(&self) -> bool {
match self.inner {
EnvConfigValueInner::Simple(_) => false,
EnvConfigValueInner::WithOptions { force, .. } => force,
}
}
pub fn resolve<'a>(&'a self, cwd: &Path) -> Cow<'a, OsStr> {
match self.inner {
EnvConfigValueInner::Simple(ref s) => Cow::Borrowed(OsStr::new(s.as_str())),
EnvConfigValueInner::WithOptions {
ref value,
relative,
..
} => {
if relative {
let p = value.value().definition.root(cwd).join(value.raw_value());
Cow::Owned(p.into_os_string())
} else {
Cow::Borrowed(OsStr::new(value.raw_value()))
}
}
}
}
}
pub type EnvConfig = HashMap<String, EnvConfigValue>;
#[derive(Deserialize, Clone, Debug)]
#[serde(rename_all = "kebab-case")]
pub struct RegistryConfig {
pub index: Option<String>,
pub token: OptValue<Secret<String>>,
pub credential_provider: Option<PathAndArgs>,
pub secret_key: OptValue<Secret<String>>,
pub secret_key_subject: Option<String>,
pub min_publish_age: Option<String>,
#[serde(rename = "protocol")]
_protocol: Option<String>,
}
#[derive(Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct GlobalRegistryConfig {
pub index: Option<String>,
pub token: OptValue<Secret<String>>,
pub credential_provider: Option<PathAndArgs>,
pub secret_key: OptValue<Secret<String>>,
pub secret_key_subject: Option<String>,
pub min_publish_age: Option<String>,
pub global_min_publish_age: Option<String>,
#[serde(rename = "default")]
_default: Option<String>,
#[serde(rename = "global-credential-providers")]
_global_credential_providers: Option<Vec<String>>,
}
impl GlobalRegistryConfig {
pub fn to_registry_config(self) -> RegistryConfig {
RegistryConfig {
index: self.index,
token: self.token,
credential_provider: self.credential_provider,
secret_key: self.secret_key,
secret_key_subject: self.secret_key_subject,
min_publish_age: self.min_publish_age,
_protocol: None,
}
}
}