1use crate::util::data_structures::HashMap;
14use std::borrow::Cow;
15use std::ffi::OsStr;
16
17use cargo_credential::Secret;
18use serde::Deserialize;
19use serde_untagged::UntaggedEnumVisitor;
20
21use std::path::Path;
22
23use crate::CargoResult;
24
25use super::OptValue;
26use super::PathAndArgs;
27use super::StringList;
28use super::Value;
29use super::path::ConfigRelativePath;
30
31#[derive(Debug, Default, Deserialize, PartialEq)]
45#[serde(rename_all = "kebab-case")]
46pub struct CargoHttpConfig {
47 pub proxy: Option<String>,
48 pub low_speed_limit: Option<u32>,
49 pub timeout: Option<u64>,
50 pub cainfo: Option<ConfigRelativePath>,
51 pub proxy_cainfo: Option<ConfigRelativePath>,
52 pub check_revoke: Option<bool>,
53 pub user_agent: Option<String>,
54 pub debug: Option<bool>,
55 pub multiplexing: Option<bool>,
56 pub ssl_version: Option<SslVersionConfig>,
57}
58
59#[derive(Debug, Default, Deserialize, PartialEq)]
68#[serde(rename_all = "kebab-case")]
69pub struct CargoFutureIncompatConfig {
70 frequency: Option<CargoFutureIncompatFrequencyConfig>,
71}
72
73#[derive(Debug, Default, Deserialize, PartialEq)]
74#[serde(rename_all = "kebab-case")]
75pub enum CargoFutureIncompatFrequencyConfig {
76 #[default]
77 Always,
78 Never,
79}
80
81impl CargoFutureIncompatConfig {
82 pub fn should_display_message(&self) -> bool {
83 use CargoFutureIncompatFrequencyConfig::*;
84
85 let frequency = self.frequency.as_ref().unwrap_or(&Always);
86 match frequency {
87 Always => true,
88 Never => false,
89 }
90 }
91}
92
93#[derive(Clone, Debug, PartialEq)]
107pub enum SslVersionConfig {
108 Single(String),
109 Range(SslVersionConfigRange),
110}
111
112impl<'de> Deserialize<'de> for SslVersionConfig {
113 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
114 where
115 D: serde::Deserializer<'de>,
116 {
117 UntaggedEnumVisitor::new()
118 .string(|single| Ok(SslVersionConfig::Single(single.to_owned())))
119 .map(|map| map.deserialize().map(SslVersionConfig::Range))
120 .deserialize(deserializer)
121 }
122}
123
124#[derive(Clone, Debug, Deserialize, PartialEq)]
125#[serde(rename_all = "kebab-case")]
126pub struct SslVersionConfigRange {
127 pub min: Option<String>,
128 pub max: Option<String>,
129}
130
131#[derive(Debug, Deserialize)]
142#[serde(rename_all = "kebab-case")]
143pub struct CargoNetConfig {
144 pub retry: Option<u32>,
145 pub offline: Option<bool>,
146 pub git_fetch_with_cli: Option<bool>,
147 pub ssh: Option<CargoSshConfig>,
148}
149
150#[derive(Debug, Deserialize)]
151#[serde(rename_all = "kebab-case")]
152pub struct CargoSshConfig {
153 pub known_hosts: Option<Vec<Value<String>>>,
154}
155
156#[derive(Debug, Clone)]
169pub enum JobsConfig {
170 Integer(i32),
171 String(String),
172}
173
174impl<'de> Deserialize<'de> for JobsConfig {
175 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
176 where
177 D: serde::Deserializer<'de>,
178 {
179 UntaggedEnumVisitor::new()
180 .i32(|int| Ok(JobsConfig::Integer(int)))
181 .string(|string| Ok(JobsConfig::String(string.to_owned())))
182 .deserialize(deserializer)
183 }
184}
185
186#[derive(Debug, Deserialize)]
199#[serde(rename_all = "kebab-case")]
200pub struct CargoBuildConfig {
201 pub pipelining: Option<bool>,
203 pub dep_info_basedir: Option<ConfigRelativePath>,
204 pub target_dir: Option<ConfigRelativePath>,
205 pub build_dir: Option<ConfigRelativePath>,
206 pub incremental: Option<bool>,
207 pub target: Option<BuildTargetConfig>,
208 pub jobs: Option<JobsConfig>,
209 pub rustflags: Option<StringList>,
210 pub rustdocflags: Option<StringList>,
211 pub rustc_wrapper: Option<ConfigRelativePath>,
212 pub rustc_workspace_wrapper: Option<ConfigRelativePath>,
213 pub rustc: Option<ConfigRelativePath>,
214 pub rustdoc: Option<ConfigRelativePath>,
215 pub artifact_dir: Option<ConfigRelativePath>,
216 pub warnings: Option<WarningHandling>,
217 pub sbom: Option<bool>,
219 pub analysis: Option<CargoBuildAnalysis>,
221}
222
223#[derive(Debug, Deserialize, Default)]
225#[serde(rename_all = "kebab-case")]
226pub struct CargoBuildAnalysis {
227 pub enabled: bool,
228}
229
230#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Default)]
232#[serde(rename_all = "kebab-case")]
233pub enum WarningHandling {
234 #[default]
235 Warn,
237 Allow,
239 Deny,
241}
242
243#[derive(Debug, Deserialize)]
253#[serde(transparent)]
254pub struct BuildTargetConfig {
255 inner: Value<BuildTargetConfigInner>,
256}
257
258#[derive(Debug)]
259enum BuildTargetConfigInner {
260 One(String),
261 Many(Vec<String>),
262}
263
264impl<'de> Deserialize<'de> for BuildTargetConfigInner {
265 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
266 where
267 D: serde::Deserializer<'de>,
268 {
269 UntaggedEnumVisitor::new()
270 .string(|one| Ok(BuildTargetConfigInner::One(one.to_owned())))
271 .seq(|many| many.deserialize().map(BuildTargetConfigInner::Many))
272 .deserialize(deserializer)
273 }
274}
275
276impl BuildTargetConfig {
277 pub fn values(&self, cwd: &Path) -> CargoResult<Vec<String>> {
279 let map = |s: &String| {
280 if s.ends_with(".json") {
281 self.inner
284 .definition
285 .root(cwd)
286 .join(s)
287 .to_str()
288 .expect("must be utf-8 in toml")
289 .to_string()
290 } else {
291 s.to_string()
293 }
294 };
295 let values = match &self.inner.val {
296 BuildTargetConfigInner::One(s) => vec![map(s)],
297 BuildTargetConfigInner::Many(v) => v.iter().map(map).collect(),
298 };
299 Ok(values)
300 }
301}
302
303#[derive(Debug, Deserialize)]
315#[serde(rename_all = "kebab-case")]
316pub struct CargoResolverConfig {
317 pub incompatible_rust_versions: Option<IncompatibleRustVersions>,
318 pub incompatible_publish_age: Option<IncompatiblePublishAge>,
319 pub feature_unification: Option<FeatureUnification>,
320 pub lockfile_path: Option<ConfigRelativePath>,
321}
322
323#[derive(Debug, Deserialize, PartialEq, Eq)]
324#[serde(rename_all = "kebab-case")]
325pub enum IncompatibleRustVersions {
326 Allow,
327 Fallback,
328}
329
330#[derive(Debug, Deserialize, PartialEq, Eq)]
331#[serde(rename_all = "kebab-case")]
332pub enum IncompatiblePublishAge {
333 Allow,
334 Deny,
335}
336
337#[derive(Copy, Clone, Debug, Deserialize)]
338#[serde(rename_all = "kebab-case")]
339pub enum FeatureUnification {
340 Package,
341 Selected,
342 Workspace,
343}
344
345#[derive(Debug, Deserialize, Default)]
357#[serde(rename_all = "kebab-case")]
358pub struct TermConfig {
359 pub verbose: Option<bool>,
360 pub quiet: Option<bool>,
361 pub color: Option<String>,
362 pub hyperlinks: Option<bool>,
363 pub unicode: Option<bool>,
364 pub progress: Option<ProgressConfig>,
365}
366
367#[derive(Debug, Default)]
382pub struct ProgressConfig {
383 pub when: ProgressWhen,
384 pub width: Option<usize>,
385 pub term_integration: Option<bool>,
387}
388
389#[derive(Debug, Default, Deserialize)]
390#[serde(rename_all = "kebab-case")]
391pub enum ProgressWhen {
392 #[default]
393 Auto,
394 Never,
395 Always,
396}
397
398impl<'de> Deserialize<'de> for ProgressConfig {
401 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
402 where
403 D: serde::Deserializer<'de>,
404 {
405 #[derive(Deserialize)]
406 #[serde(rename_all = "kebab-case")]
407 struct ProgressConfigInner {
408 #[serde(default)]
409 when: ProgressWhen,
410 width: Option<usize>,
411 term_integration: Option<bool>,
412 }
413
414 let pc = ProgressConfigInner::deserialize(deserializer)?;
415 if let ProgressConfigInner {
416 when: ProgressWhen::Always,
417 width: None,
418 ..
419 } = pc
420 {
421 return Err(serde::de::Error::custom(
422 "\"always\" progress requires a `width` key",
423 ));
424 }
425 Ok(ProgressConfig {
426 when: pc.when,
427 width: pc.width,
428 term_integration: pc.term_integration,
429 })
430 }
431}
432
433#[derive(Debug)]
434enum EnvConfigValueInner {
435 Simple(String),
436 WithOptions {
437 value: ConfigRelativePath,
438 force: bool,
439 relative: bool,
440 },
441}
442
443impl<'de> Deserialize<'de> for EnvConfigValueInner {
444 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
445 where
446 D: serde::Deserializer<'de>,
447 {
448 #[derive(Deserialize)]
449 struct WithOptions {
450 value: ConfigRelativePath,
451 #[serde(default)]
452 force: bool,
453 #[serde(default)]
454 relative: bool,
455 }
456
457 UntaggedEnumVisitor::new()
458 .string(|simple| Ok(EnvConfigValueInner::Simple(simple.to_owned())))
459 .map(|map| {
460 let with_options: WithOptions = map.deserialize()?;
461 Ok(EnvConfigValueInner::WithOptions {
462 value: with_options.value,
463 force: with_options.force,
464 relative: with_options.relative,
465 })
466 })
467 .deserialize(deserializer)
468 }
469}
470
471#[derive(Debug, Deserialize)]
486#[serde(transparent)]
487pub struct EnvConfigValue {
488 inner: EnvConfigValueInner,
489}
490
491impl EnvConfigValue {
492 pub fn is_force(&self) -> bool {
494 match self.inner {
495 EnvConfigValueInner::Simple(_) => false,
496 EnvConfigValueInner::WithOptions { force, .. } => force,
497 }
498 }
499
500 pub fn resolve<'a>(&'a self, cwd: &Path) -> Cow<'a, OsStr> {
505 match self.inner {
506 EnvConfigValueInner::Simple(ref s) => Cow::Borrowed(OsStr::new(s.as_str())),
507 EnvConfigValueInner::WithOptions {
508 ref value,
509 relative,
510 ..
511 } => {
512 if relative {
513 let p = value.value().definition.root(cwd).join(value.raw_value());
514 Cow::Owned(p.into_os_string())
515 } else {
516 Cow::Borrowed(OsStr::new(value.raw_value()))
517 }
518 }
519 }
520 }
521}
522
523pub type EnvConfig = HashMap<String, EnvConfigValue>;
524
525#[derive(Deserialize, Clone, Debug)]
529#[serde(rename_all = "kebab-case")]
530pub struct RegistryConfig {
531 pub index: Option<String>,
532 pub token: OptValue<Secret<String>>,
533 pub credential_provider: Option<PathAndArgs>,
534 pub secret_key: OptValue<Secret<String>>,
535 pub secret_key_subject: Option<String>,
536 pub min_publish_age: Option<String>,
538 #[serde(rename = "protocol")]
539 _protocol: Option<String>,
540}
541
542#[derive(Deserialize)]
547#[serde(rename_all = "kebab-case")]
548pub struct GlobalRegistryConfig {
549 pub index: Option<String>,
550 pub token: OptValue<Secret<String>>,
551 pub credential_provider: Option<PathAndArgs>,
552 pub secret_key: OptValue<Secret<String>>,
553 pub secret_key_subject: Option<String>,
554 pub min_publish_age: Option<String>,
556 pub global_min_publish_age: Option<String>,
558 #[serde(rename = "default")]
559 _default: Option<String>,
560 #[serde(rename = "global-credential-providers")]
561 _global_credential_providers: Option<Vec<String>>,
562}
563
564impl GlobalRegistryConfig {
565 pub fn to_registry_config(self) -> RegistryConfig {
566 RegistryConfig {
567 index: self.index,
568 token: self.token,
569 credential_provider: self.credential_provider,
570 secret_key: self.secret_key,
571 secret_key_subject: self.secret_key_subject,
572 min_publish_age: self.min_publish_age,
573 _protocol: None,
574 }
575 }
576}