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 out_dir: Option<ConfigRelativePath>,
214 pub artifact_dir: Option<ConfigRelativePath>,
215 pub warnings: Option<WarningHandling>,
216 pub sbom: Option<bool>,
218 pub analysis: Option<CargoBuildAnalysis>,
220}
221
222#[derive(Debug, Deserialize, Default)]
224#[serde(rename_all = "kebab-case")]
225pub struct CargoBuildAnalysis {
226 pub enabled: bool,
227}
228
229#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Default)]
231#[serde(rename_all = "kebab-case")]
232pub enum WarningHandling {
233 #[default]
234 Warn,
236 Allow,
238 Deny,
240}
241
242#[derive(Debug, Deserialize)]
252#[serde(transparent)]
253pub struct BuildTargetConfig {
254 inner: Value<BuildTargetConfigInner>,
255}
256
257#[derive(Debug)]
258enum BuildTargetConfigInner {
259 One(String),
260 Many(Vec<String>),
261}
262
263impl<'de> Deserialize<'de> for BuildTargetConfigInner {
264 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
265 where
266 D: serde::Deserializer<'de>,
267 {
268 UntaggedEnumVisitor::new()
269 .string(|one| Ok(BuildTargetConfigInner::One(one.to_owned())))
270 .seq(|many| many.deserialize().map(BuildTargetConfigInner::Many))
271 .deserialize(deserializer)
272 }
273}
274
275impl BuildTargetConfig {
276 pub fn values(&self, cwd: &Path) -> CargoResult<Vec<String>> {
278 let map = |s: &String| {
279 if s.ends_with(".json") {
280 self.inner
283 .definition
284 .root(cwd)
285 .join(s)
286 .to_str()
287 .expect("must be utf-8 in toml")
288 .to_string()
289 } else {
290 s.to_string()
292 }
293 };
294 let values = match &self.inner.val {
295 BuildTargetConfigInner::One(s) => vec![map(s)],
296 BuildTargetConfigInner::Many(v) => v.iter().map(map).collect(),
297 };
298 Ok(values)
299 }
300}
301
302#[derive(Debug, Deserialize)]
313#[serde(rename_all = "kebab-case")]
314pub struct CargoResolverConfig {
315 pub incompatible_rust_versions: Option<IncompatibleRustVersions>,
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(Copy, Clone, Debug, Deserialize)]
328#[serde(rename_all = "kebab-case")]
329pub enum FeatureUnification {
330 Package,
331 Selected,
332 Workspace,
333}
334
335#[derive(Debug, Deserialize, Default)]
347#[serde(rename_all = "kebab-case")]
348pub struct TermConfig {
349 pub verbose: Option<bool>,
350 pub quiet: Option<bool>,
351 pub color: Option<String>,
352 pub hyperlinks: Option<bool>,
353 pub unicode: Option<bool>,
354 pub progress: Option<ProgressConfig>,
355}
356
357#[derive(Debug, Default)]
372pub struct ProgressConfig {
373 pub when: ProgressWhen,
374 pub width: Option<usize>,
375 pub term_integration: Option<bool>,
377}
378
379#[derive(Debug, Default, Deserialize)]
380#[serde(rename_all = "kebab-case")]
381pub enum ProgressWhen {
382 #[default]
383 Auto,
384 Never,
385 Always,
386}
387
388impl<'de> Deserialize<'de> for ProgressConfig {
391 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
392 where
393 D: serde::Deserializer<'de>,
394 {
395 #[derive(Deserialize)]
396 #[serde(rename_all = "kebab-case")]
397 struct ProgressConfigInner {
398 #[serde(default)]
399 when: ProgressWhen,
400 width: Option<usize>,
401 term_integration: Option<bool>,
402 }
403
404 let pc = ProgressConfigInner::deserialize(deserializer)?;
405 if let ProgressConfigInner {
406 when: ProgressWhen::Always,
407 width: None,
408 ..
409 } = pc
410 {
411 return Err(serde::de::Error::custom(
412 "\"always\" progress requires a `width` key",
413 ));
414 }
415 Ok(ProgressConfig {
416 when: pc.when,
417 width: pc.width,
418 term_integration: pc.term_integration,
419 })
420 }
421}
422
423#[derive(Debug)]
424enum EnvConfigValueInner {
425 Simple(String),
426 WithOptions {
427 value: String,
428 force: bool,
429 relative: bool,
430 },
431}
432
433impl<'de> Deserialize<'de> for EnvConfigValueInner {
434 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
435 where
436 D: serde::Deserializer<'de>,
437 {
438 #[derive(Deserialize)]
439 struct WithOptions {
440 value: String,
441 #[serde(default)]
442 force: bool,
443 #[serde(default)]
444 relative: bool,
445 }
446
447 UntaggedEnumVisitor::new()
448 .string(|simple| Ok(EnvConfigValueInner::Simple(simple.to_owned())))
449 .map(|map| {
450 let with_options: WithOptions = map.deserialize()?;
451 Ok(EnvConfigValueInner::WithOptions {
452 value: with_options.value,
453 force: with_options.force,
454 relative: with_options.relative,
455 })
456 })
457 .deserialize(deserializer)
458 }
459}
460
461#[derive(Debug, Deserialize)]
476#[serde(transparent)]
477pub struct EnvConfigValue {
478 inner: Value<EnvConfigValueInner>,
479}
480
481impl EnvConfigValue {
482 pub fn is_force(&self) -> bool {
484 match self.inner.val {
485 EnvConfigValueInner::Simple(_) => false,
486 EnvConfigValueInner::WithOptions { force, .. } => force,
487 }
488 }
489
490 pub fn resolve<'a>(&'a self, cwd: &Path) -> Cow<'a, OsStr> {
495 match self.inner.val {
496 EnvConfigValueInner::Simple(ref s) => Cow::Borrowed(OsStr::new(s.as_str())),
497 EnvConfigValueInner::WithOptions {
498 ref value,
499 relative,
500 ..
501 } => {
502 if relative {
503 let p = self.inner.definition.root(cwd).join(&value);
504 Cow::Owned(p.into_os_string())
505 } else {
506 Cow::Borrowed(OsStr::new(value.as_str()))
507 }
508 }
509 }
510 }
511}
512
513pub type EnvConfig = HashMap<String, EnvConfigValue>;