1use std::borrow::Cow;
14use std::collections::HashMap;
15use std::ffi::OsStr;
16
17use serde::Deserialize;
18use serde_untagged::UntaggedEnumVisitor;
19
20use std::path::Path;
21
22use crate::CargoResult;
23
24use super::StringList;
25use super::Value;
26use super::path::ConfigRelativePath;
27
28#[derive(Debug, Default, Deserialize, PartialEq)]
42#[serde(rename_all = "kebab-case")]
43pub struct CargoHttpConfig {
44 pub proxy: Option<String>,
45 pub low_speed_limit: Option<u32>,
46 pub timeout: Option<u64>,
47 pub cainfo: Option<ConfigRelativePath>,
48 pub proxy_cainfo: Option<ConfigRelativePath>,
49 pub check_revoke: Option<bool>,
50 pub user_agent: Option<String>,
51 pub debug: Option<bool>,
52 pub multiplexing: Option<bool>,
53 pub ssl_version: Option<SslVersionConfig>,
54}
55
56#[derive(Debug, Default, Deserialize, PartialEq)]
65#[serde(rename_all = "kebab-case")]
66pub struct CargoFutureIncompatConfig {
67 frequency: Option<CargoFutureIncompatFrequencyConfig>,
68}
69
70#[derive(Debug, Default, Deserialize, PartialEq)]
71#[serde(rename_all = "kebab-case")]
72pub enum CargoFutureIncompatFrequencyConfig {
73 #[default]
74 Always,
75 Never,
76}
77
78impl CargoFutureIncompatConfig {
79 pub fn should_display_message(&self) -> bool {
80 use CargoFutureIncompatFrequencyConfig::*;
81
82 let frequency = self.frequency.as_ref().unwrap_or(&Always);
83 match frequency {
84 Always => true,
85 Never => false,
86 }
87 }
88}
89
90#[derive(Clone, Debug, PartialEq)]
104pub enum SslVersionConfig {
105 Single(String),
106 Range(SslVersionConfigRange),
107}
108
109impl<'de> Deserialize<'de> for SslVersionConfig {
110 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
111 where
112 D: serde::Deserializer<'de>,
113 {
114 UntaggedEnumVisitor::new()
115 .string(|single| Ok(SslVersionConfig::Single(single.to_owned())))
116 .map(|map| map.deserialize().map(SslVersionConfig::Range))
117 .deserialize(deserializer)
118 }
119}
120
121#[derive(Clone, Debug, Deserialize, PartialEq)]
122#[serde(rename_all = "kebab-case")]
123pub struct SslVersionConfigRange {
124 pub min: Option<String>,
125 pub max: Option<String>,
126}
127
128#[derive(Debug, Deserialize)]
139#[serde(rename_all = "kebab-case")]
140pub struct CargoNetConfig {
141 pub retry: Option<u32>,
142 pub offline: Option<bool>,
143 pub git_fetch_with_cli: Option<bool>,
144 pub ssh: Option<CargoSshConfig>,
145}
146
147#[derive(Debug, Deserialize)]
148#[serde(rename_all = "kebab-case")]
149pub struct CargoSshConfig {
150 pub known_hosts: Option<Vec<Value<String>>>,
151}
152
153#[derive(Debug, Clone)]
166pub enum JobsConfig {
167 Integer(i32),
168 String(String),
169}
170
171impl<'de> Deserialize<'de> for JobsConfig {
172 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
173 where
174 D: serde::Deserializer<'de>,
175 {
176 UntaggedEnumVisitor::new()
177 .i32(|int| Ok(JobsConfig::Integer(int)))
178 .string(|string| Ok(JobsConfig::String(string.to_owned())))
179 .deserialize(deserializer)
180 }
181}
182
183#[derive(Debug, Deserialize)]
196#[serde(rename_all = "kebab-case")]
197pub struct CargoBuildConfig {
198 pub pipelining: Option<bool>,
200 pub dep_info_basedir: Option<ConfigRelativePath>,
201 pub target_dir: Option<ConfigRelativePath>,
202 pub build_dir: Option<ConfigRelativePath>,
203 pub incremental: Option<bool>,
204 pub target: Option<BuildTargetConfig>,
205 pub jobs: Option<JobsConfig>,
206 pub rustflags: Option<StringList>,
207 pub rustdocflags: Option<StringList>,
208 pub rustc_wrapper: Option<ConfigRelativePath>,
209 pub rustc_workspace_wrapper: Option<ConfigRelativePath>,
210 pub rustc: Option<ConfigRelativePath>,
211 pub rustdoc: Option<ConfigRelativePath>,
212 pub artifact_dir: Option<ConfigRelativePath>,
213 pub warnings: Option<WarningHandling>,
214 pub sbom: Option<bool>,
216 pub analysis: Option<CargoBuildAnalysis>,
218}
219
220#[derive(Debug, Deserialize, Default)]
222#[serde(rename_all = "kebab-case")]
223pub struct CargoBuildAnalysis {
224 pub enabled: bool,
225}
226
227#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Default)]
229#[serde(rename_all = "kebab-case")]
230pub enum WarningHandling {
231 #[default]
232 Warn,
234 Allow,
236 Deny,
238}
239
240#[derive(Debug, Deserialize)]
250#[serde(transparent)]
251pub struct BuildTargetConfig {
252 inner: Value<BuildTargetConfigInner>,
253}
254
255#[derive(Debug)]
256enum BuildTargetConfigInner {
257 One(String),
258 Many(Vec<String>),
259}
260
261impl<'de> Deserialize<'de> for BuildTargetConfigInner {
262 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
263 where
264 D: serde::Deserializer<'de>,
265 {
266 UntaggedEnumVisitor::new()
267 .string(|one| Ok(BuildTargetConfigInner::One(one.to_owned())))
268 .seq(|many| many.deserialize().map(BuildTargetConfigInner::Many))
269 .deserialize(deserializer)
270 }
271}
272
273impl BuildTargetConfig {
274 pub fn values(&self, cwd: &Path) -> CargoResult<Vec<String>> {
276 let map = |s: &String| {
277 if s.ends_with(".json") {
278 self.inner
281 .definition
282 .root(cwd)
283 .join(s)
284 .to_str()
285 .expect("must be utf-8 in toml")
286 .to_string()
287 } else {
288 s.to_string()
290 }
291 };
292 let values = match &self.inner.val {
293 BuildTargetConfigInner::One(s) => vec![map(s)],
294 BuildTargetConfigInner::Many(v) => v.iter().map(map).collect(),
295 };
296 Ok(values)
297 }
298}
299
300#[derive(Debug, Deserialize)]
312#[serde(rename_all = "kebab-case")]
313pub struct CargoResolverConfig {
314 pub incompatible_rust_versions: Option<IncompatibleRustVersions>,
315 pub incompatible_publish_age: Option<IncompatiblePublishAge>,
316 pub feature_unification: Option<FeatureUnification>,
317 pub lockfile_path: Option<ConfigRelativePath>,
318}
319
320#[derive(Debug, Deserialize, PartialEq, Eq)]
321#[serde(rename_all = "kebab-case")]
322pub enum IncompatibleRustVersions {
323 Allow,
324 Fallback,
325}
326
327#[derive(Debug, Deserialize, PartialEq, Eq)]
328#[serde(rename_all = "kebab-case")]
329pub enum IncompatiblePublishAge {
330 Allow,
331 Deny,
332}
333
334#[derive(Copy, Clone, Debug, Deserialize)]
335#[serde(rename_all = "kebab-case")]
336pub enum FeatureUnification {
337 Package,
338 Selected,
339 Workspace,
340}
341
342#[derive(Debug, Deserialize, Default)]
354#[serde(rename_all = "kebab-case")]
355pub struct TermConfig {
356 pub verbose: Option<bool>,
357 pub quiet: Option<bool>,
358 pub color: Option<String>,
359 pub hyperlinks: Option<bool>,
360 pub unicode: Option<bool>,
361 pub progress: Option<ProgressConfig>,
362}
363
364#[derive(Debug, Default)]
379pub struct ProgressConfig {
380 pub when: ProgressWhen,
381 pub width: Option<usize>,
382 pub term_integration: Option<bool>,
384}
385
386#[derive(Debug, Default, Deserialize)]
387#[serde(rename_all = "kebab-case")]
388pub enum ProgressWhen {
389 #[default]
390 Auto,
391 Never,
392 Always,
393}
394
395impl<'de> Deserialize<'de> for ProgressConfig {
398 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
399 where
400 D: serde::Deserializer<'de>,
401 {
402 #[derive(Deserialize)]
403 #[serde(rename_all = "kebab-case")]
404 struct ProgressConfigInner {
405 #[serde(default)]
406 when: ProgressWhen,
407 width: Option<usize>,
408 term_integration: Option<bool>,
409 }
410
411 let pc = ProgressConfigInner::deserialize(deserializer)?;
412 if let ProgressConfigInner {
413 when: ProgressWhen::Always,
414 width: None,
415 ..
416 } = pc
417 {
418 return Err(serde::de::Error::custom(
419 "\"always\" progress requires a `width` key",
420 ));
421 }
422 Ok(ProgressConfig {
423 when: pc.when,
424 width: pc.width,
425 term_integration: pc.term_integration,
426 })
427 }
428}
429
430#[derive(Debug)]
431enum EnvConfigValueInner {
432 Simple(String),
433 WithOptions {
434 value: ConfigRelativePath,
435 force: bool,
436 relative: bool,
437 },
438}
439
440impl<'de> Deserialize<'de> for EnvConfigValueInner {
441 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
442 where
443 D: serde::Deserializer<'de>,
444 {
445 #[derive(Deserialize)]
446 struct WithOptions {
447 value: ConfigRelativePath,
448 #[serde(default)]
449 force: bool,
450 #[serde(default)]
451 relative: bool,
452 }
453
454 UntaggedEnumVisitor::new()
455 .string(|simple| Ok(EnvConfigValueInner::Simple(simple.to_owned())))
456 .map(|map| {
457 let with_options: WithOptions = map.deserialize()?;
458 Ok(EnvConfigValueInner::WithOptions {
459 value: with_options.value,
460 force: with_options.force,
461 relative: with_options.relative,
462 })
463 })
464 .deserialize(deserializer)
465 }
466}
467
468#[derive(Debug, Deserialize)]
483#[serde(transparent)]
484pub struct EnvConfigValue {
485 inner: EnvConfigValueInner,
486}
487
488impl EnvConfigValue {
489 pub fn is_force(&self) -> bool {
491 match self.inner {
492 EnvConfigValueInner::Simple(_) => false,
493 EnvConfigValueInner::WithOptions { force, .. } => force,
494 }
495 }
496
497 pub fn resolve<'a>(&'a self, cwd: &Path) -> Cow<'a, OsStr> {
502 match self.inner {
503 EnvConfigValueInner::Simple(ref s) => Cow::Borrowed(OsStr::new(s.as_str())),
504 EnvConfigValueInner::WithOptions {
505 ref value,
506 relative,
507 ..
508 } => {
509 if relative {
510 let p = value.value().definition.root(cwd).join(value.raw_value());
511 Cow::Owned(p.into_os_string())
512 } else {
513 Cow::Borrowed(OsStr::new(value.raw_value()))
514 }
515 }
516 }
517 }
518}
519
520pub type EnvConfig = HashMap<String, EnvConfigValue>;