1use crate::util::data_structures::HashMap;
14use std::borrow::Cow;
15use std::ffi::OsStr;
16
17use cargo_credential::Secret;
18use serde::Deserialize;
19use serde::Serialize;
20use serde_untagged::UntaggedEnumVisitor;
21
22use std::path::Path;
23
24use crate::CargoResult;
25
26use super::OptValue;
27use super::PathAndArgs;
28use super::StringList;
29use super::Value;
30use super::path::ConfigRelativePath;
31
32#[derive(Debug, Default, Deserialize, PartialEq)]
46#[serde(rename_all = "kebab-case")]
47pub struct CargoHttpConfig {
48 pub proxy: Option<String>,
49 pub low_speed_limit: Option<u32>,
50 pub timeout: Option<u64>,
51 pub cainfo: Option<ConfigRelativePath>,
52 pub proxy_cainfo: Option<ConfigRelativePath>,
53 pub check_revoke: Option<bool>,
54 pub user_agent: Option<String>,
55 pub debug: Option<bool>,
56 pub multiplexing: Option<bool>,
57 pub ssl_version: Option<SslVersionConfig>,
58}
59
60#[derive(Debug, Default, Deserialize, PartialEq)]
69#[serde(rename_all = "kebab-case")]
70pub struct CargoFutureIncompatConfig {
71 frequency: Option<CargoFutureIncompatFrequencyConfig>,
72}
73
74#[derive(Debug, Default, Deserialize, PartialEq)]
75#[serde(rename_all = "kebab-case")]
76pub enum CargoFutureIncompatFrequencyConfig {
77 #[default]
78 Always,
79 Never,
80}
81
82impl CargoFutureIncompatConfig {
83 pub fn should_display_message(&self) -> bool {
84 use CargoFutureIncompatFrequencyConfig::*;
85
86 let frequency = self.frequency.as_ref().unwrap_or(&Always);
87 match frequency {
88 Always => true,
89 Never => false,
90 }
91 }
92}
93
94#[derive(Clone, Debug, PartialEq)]
108pub enum SslVersionConfig {
109 Single(String),
110 Range(SslVersionConfigRange),
111}
112
113impl<'de> Deserialize<'de> for SslVersionConfig {
114 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
115 where
116 D: serde::Deserializer<'de>,
117 {
118 UntaggedEnumVisitor::new()
119 .string(|single| Ok(SslVersionConfig::Single(single.to_owned())))
120 .map(|map| map.deserialize().map(SslVersionConfig::Range))
121 .deserialize(deserializer)
122 }
123}
124
125#[derive(Clone, Debug, Deserialize, PartialEq)]
126#[serde(rename_all = "kebab-case")]
127pub struct SslVersionConfigRange {
128 pub min: Option<String>,
129 pub max: Option<String>,
130}
131
132#[derive(Debug, Deserialize)]
143#[serde(rename_all = "kebab-case")]
144pub struct CargoNetConfig {
145 pub retry: Option<u32>,
146 pub offline: Option<bool>,
147 pub git_fetch_with_cli: Option<bool>,
148 pub ssh: Option<CargoSshConfig>,
149}
150
151#[derive(Debug, Deserialize)]
152#[serde(rename_all = "kebab-case")]
153pub struct CargoSshConfig {
154 pub known_hosts: Option<Vec<Value<String>>>,
155}
156
157#[derive(Debug, Clone)]
170pub enum JobsConfig {
171 Integer(i32),
172 String(String),
173}
174
175impl<'de> Deserialize<'de> for JobsConfig {
176 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
177 where
178 D: serde::Deserializer<'de>,
179 {
180 UntaggedEnumVisitor::new()
181 .i32(|int| Ok(JobsConfig::Integer(int)))
182 .string(|string| Ok(JobsConfig::String(string.to_owned())))
183 .deserialize(deserializer)
184 }
185}
186
187#[derive(Debug, Deserialize)]
200#[serde(rename_all = "kebab-case")]
201pub struct CargoBuildConfig {
202 pub pipelining: Option<bool>,
204 pub dep_info_basedir: Option<ConfigRelativePath>,
205 pub target_dir: Option<ConfigRelativePath>,
206 pub build_dir: Option<ConfigRelativePath>,
207 pub incremental: Option<bool>,
208 pub target: Option<BuildTargetConfig>,
209 pub jobs: Option<JobsConfig>,
210 pub rustflags: Option<StringList>,
211 pub rustdocflags: Option<StringList>,
212 pub rustc_wrapper: Option<ConfigRelativePath>,
213 pub rustc_workspace_wrapper: Option<ConfigRelativePath>,
214 pub rustc: Option<ConfigRelativePath>,
215 pub rustdoc: Option<ConfigRelativePath>,
216 pub artifact_dir: Option<ConfigRelativePath>,
217 pub warnings: Option<WarningHandling>,
218 pub sbom: Option<bool>,
220 pub analysis: Option<CargoBuildAnalysis>,
222 pub fingerprint: Option<FingerprintMethod>,
224}
225
226#[derive(Debug, Deserialize, Default)]
228#[serde(rename_all = "kebab-case")]
229pub struct CargoBuildAnalysis {
230 pub enabled: bool,
231}
232
233#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Default)]
235#[serde(rename_all = "kebab-case")]
236pub enum WarningHandling {
237 #[default]
238 Warn,
240 Allow,
242 Deny,
244}
245
246#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize, Default)]
247#[serde(rename_all = "kebab-case")]
248pub enum FingerprintMethod {
249 #[default]
250 Mtime,
251 Content,
252}
253
254impl FingerprintMethod {
255 pub fn as_str(&self) -> &'static str {
256 match self {
257 Self::Mtime => "mtime",
258 Self::Content => "content",
259 }
260 }
261}
262
263impl std::fmt::Display for FingerprintMethod {
264 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
265 self.as_str().fmt(f)
266 }
267}
268
269#[derive(Debug, Deserialize)]
279#[serde(transparent)]
280pub struct BuildTargetConfig {
281 inner: Value<BuildTargetConfigInner>,
282}
283
284#[derive(Debug)]
285enum BuildTargetConfigInner {
286 One(String),
287 Many(Vec<String>),
288}
289
290impl<'de> Deserialize<'de> for BuildTargetConfigInner {
291 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
292 where
293 D: serde::Deserializer<'de>,
294 {
295 UntaggedEnumVisitor::new()
296 .string(|one| Ok(BuildTargetConfigInner::One(one.to_owned())))
297 .seq(|many| many.deserialize().map(BuildTargetConfigInner::Many))
298 .deserialize(deserializer)
299 }
300}
301
302impl BuildTargetConfig {
303 pub fn values(&self, cwd: &Path) -> CargoResult<Vec<String>> {
305 let map = |s: &String| {
306 if s.ends_with(".json") {
307 self.inner
310 .definition
311 .root(cwd)
312 .join(s)
313 .to_str()
314 .expect("must be utf-8 in toml")
315 .to_string()
316 } else {
317 s.to_string()
319 }
320 };
321 let values = match &self.inner.val {
322 BuildTargetConfigInner::One(s) => vec![map(s)],
323 BuildTargetConfigInner::Many(v) => v.iter().map(map).collect(),
324 };
325 Ok(values)
326 }
327}
328
329#[derive(Debug, Deserialize)]
341#[serde(rename_all = "kebab-case")]
342pub struct CargoResolverConfig {
343 pub incompatible_rust_versions: Option<IncompatibleRustVersions>,
344 pub incompatible_publish_age: Option<IncompatiblePublishAge>,
345 pub feature_unification: Option<FeatureUnification>,
346 pub lockfile_path: Option<ConfigRelativePath>,
347}
348
349#[derive(Debug, Deserialize, PartialEq, Eq)]
350#[serde(rename_all = "kebab-case")]
351pub enum IncompatibleRustVersions {
352 Allow,
353 Fallback,
354}
355
356#[derive(Debug, Deserialize, PartialEq, Eq)]
357#[serde(rename_all = "kebab-case")]
358pub enum IncompatiblePublishAge {
359 Allow,
360 Deny,
361}
362
363#[derive(Copy, Clone, Debug, Deserialize)]
364#[serde(rename_all = "kebab-case")]
365pub enum FeatureUnification {
366 Package,
367 Selected,
368 Workspace,
369}
370
371#[derive(Debug, Deserialize, Default)]
383#[serde(rename_all = "kebab-case")]
384pub struct TermConfig {
385 pub verbose: Option<bool>,
386 pub quiet: Option<bool>,
387 pub color: Option<String>,
388 pub hyperlinks: Option<bool>,
389 pub unicode: Option<bool>,
390 pub progress: Option<ProgressConfig>,
391}
392
393#[derive(Debug, Default)]
408pub struct ProgressConfig {
409 pub when: ProgressWhen,
410 pub width: Option<usize>,
411 pub term_integration: Option<bool>,
413}
414
415#[derive(Debug, Default, Deserialize)]
416#[serde(rename_all = "kebab-case")]
417pub enum ProgressWhen {
418 #[default]
419 Auto,
420 Never,
421 Always,
422}
423
424impl<'de> Deserialize<'de> for ProgressConfig {
427 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
428 where
429 D: serde::Deserializer<'de>,
430 {
431 #[derive(Deserialize)]
432 #[serde(rename_all = "kebab-case")]
433 struct ProgressConfigInner {
434 #[serde(default)]
435 when: ProgressWhen,
436 width: Option<usize>,
437 term_integration: Option<bool>,
438 }
439
440 let pc = ProgressConfigInner::deserialize(deserializer)?;
441 if let ProgressConfigInner {
442 when: ProgressWhen::Always,
443 width: None,
444 ..
445 } = pc
446 {
447 return Err(serde::de::Error::custom(
448 "\"always\" progress requires a `width` key",
449 ));
450 }
451 Ok(ProgressConfig {
452 when: pc.when,
453 width: pc.width,
454 term_integration: pc.term_integration,
455 })
456 }
457}
458
459#[derive(Debug)]
460enum EnvConfigValueInner {
461 Simple(String),
462 WithOptions {
463 value: ConfigRelativePath,
464 force: bool,
465 relative: bool,
466 },
467}
468
469impl<'de> Deserialize<'de> for EnvConfigValueInner {
470 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
471 where
472 D: serde::Deserializer<'de>,
473 {
474 #[derive(Deserialize)]
475 struct WithOptions {
476 value: ConfigRelativePath,
477 #[serde(default)]
478 force: bool,
479 #[serde(default)]
480 relative: bool,
481 }
482
483 UntaggedEnumVisitor::new()
484 .string(|simple| Ok(EnvConfigValueInner::Simple(simple.to_owned())))
485 .map(|map| {
486 let with_options: WithOptions = map.deserialize()?;
487 Ok(EnvConfigValueInner::WithOptions {
488 value: with_options.value,
489 force: with_options.force,
490 relative: with_options.relative,
491 })
492 })
493 .deserialize(deserializer)
494 }
495}
496
497#[derive(Debug, Deserialize)]
512#[serde(transparent)]
513pub struct EnvConfigValue {
514 inner: EnvConfigValueInner,
515}
516
517impl EnvConfigValue {
518 pub fn is_force(&self) -> bool {
520 match self.inner {
521 EnvConfigValueInner::Simple(_) => false,
522 EnvConfigValueInner::WithOptions { force, .. } => force,
523 }
524 }
525
526 pub fn resolve<'a>(&'a self, cwd: &Path) -> Cow<'a, OsStr> {
531 match self.inner {
532 EnvConfigValueInner::Simple(ref s) => Cow::Borrowed(OsStr::new(s.as_str())),
533 EnvConfigValueInner::WithOptions {
534 ref value,
535 relative,
536 ..
537 } => {
538 if relative {
539 let p = value.value().definition.root(cwd).join(value.raw_value());
540 Cow::Owned(p.into_os_string())
541 } else {
542 Cow::Borrowed(OsStr::new(value.raw_value()))
543 }
544 }
545 }
546 }
547}
548
549pub type EnvConfig = HashMap<String, EnvConfigValue>;
550
551#[derive(Deserialize, Clone, Debug)]
555#[serde(rename_all = "kebab-case")]
556pub struct RegistryConfig {
557 pub index: Option<String>,
558 pub token: OptValue<Secret<String>>,
559 pub credential_provider: Option<PathAndArgs>,
560 pub secret_key: OptValue<Secret<String>>,
561 pub secret_key_subject: Option<String>,
562 pub min_publish_age: Option<String>,
564 #[serde(rename = "protocol")]
565 _protocol: Option<String>,
566}
567
568#[derive(Deserialize)]
573#[serde(rename_all = "kebab-case")]
574pub struct GlobalRegistryConfig {
575 pub index: Option<String>,
576 pub token: OptValue<Secret<String>>,
577 pub credential_provider: Option<PathAndArgs>,
578 pub secret_key: OptValue<Secret<String>>,
579 pub secret_key_subject: Option<String>,
580 pub global_min_publish_age: Option<String>,
582 #[serde(rename = "default")]
583 _default: Option<String>,
584 #[serde(rename = "global-credential-providers")]
585 _global_credential_providers: Option<Vec<String>>,
586}
587
588impl GlobalRegistryConfig {
589 pub fn to_registry_config(self) -> RegistryConfig {
590 RegistryConfig {
591 index: self.index,
592 token: self.token,
593 credential_provider: self.credential_provider,
594 secret_key: self.secret_key,
595 secret_key_subject: self.secret_key_subject,
596 min_publish_age: None,
598 _protocol: None,
599 }
600 }
601}