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)]
312#[serde(rename_all = "kebab-case")]
313pub struct CargoResolverConfig {
314 pub incompatible_rust_versions: Option<IncompatibleRustVersions>,
315 pub feature_unification: Option<FeatureUnification>,
316}
317
318#[derive(Debug, Deserialize, PartialEq, Eq)]
319#[serde(rename_all = "kebab-case")]
320pub enum IncompatibleRustVersions {
321 Allow,
322 Fallback,
323}
324
325#[derive(Copy, Clone, Debug, Deserialize)]
326#[serde(rename_all = "kebab-case")]
327pub enum FeatureUnification {
328 Package,
329 Selected,
330 Workspace,
331}
332
333#[derive(Debug, Deserialize, Default)]
345#[serde(rename_all = "kebab-case")]
346pub struct TermConfig {
347 pub verbose: Option<bool>,
348 pub quiet: Option<bool>,
349 pub color: Option<String>,
350 pub hyperlinks: Option<bool>,
351 pub unicode: Option<bool>,
352 pub progress: Option<ProgressConfig>,
353}
354
355#[derive(Debug, Default)]
370pub struct ProgressConfig {
371 pub when: ProgressWhen,
372 pub width: Option<usize>,
373 pub term_integration: Option<bool>,
375}
376
377#[derive(Debug, Default, Deserialize)]
378#[serde(rename_all = "kebab-case")]
379pub enum ProgressWhen {
380 #[default]
381 Auto,
382 Never,
383 Always,
384}
385
386impl<'de> Deserialize<'de> for ProgressConfig {
389 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
390 where
391 D: serde::Deserializer<'de>,
392 {
393 #[derive(Deserialize)]
394 #[serde(rename_all = "kebab-case")]
395 struct ProgressConfigInner {
396 #[serde(default)]
397 when: ProgressWhen,
398 width: Option<usize>,
399 term_integration: Option<bool>,
400 }
401
402 let pc = ProgressConfigInner::deserialize(deserializer)?;
403 if let ProgressConfigInner {
404 when: ProgressWhen::Always,
405 width: None,
406 ..
407 } = pc
408 {
409 return Err(serde::de::Error::custom(
410 "\"always\" progress requires a `width` key",
411 ));
412 }
413 Ok(ProgressConfig {
414 when: pc.when,
415 width: pc.width,
416 term_integration: pc.term_integration,
417 })
418 }
419}
420
421#[derive(Debug)]
422enum EnvConfigValueInner {
423 Simple(String),
424 WithOptions {
425 value: String,
426 force: bool,
427 relative: bool,
428 },
429}
430
431impl<'de> Deserialize<'de> for EnvConfigValueInner {
432 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
433 where
434 D: serde::Deserializer<'de>,
435 {
436 #[derive(Deserialize)]
437 struct WithOptions {
438 value: String,
439 #[serde(default)]
440 force: bool,
441 #[serde(default)]
442 relative: bool,
443 }
444
445 UntaggedEnumVisitor::new()
446 .string(|simple| Ok(EnvConfigValueInner::Simple(simple.to_owned())))
447 .map(|map| {
448 let with_options: WithOptions = map.deserialize()?;
449 Ok(EnvConfigValueInner::WithOptions {
450 value: with_options.value,
451 force: with_options.force,
452 relative: with_options.relative,
453 })
454 })
455 .deserialize(deserializer)
456 }
457}
458
459#[derive(Debug, Deserialize)]
474#[serde(transparent)]
475pub struct EnvConfigValue {
476 inner: Value<EnvConfigValueInner>,
477}
478
479impl EnvConfigValue {
480 pub fn is_force(&self) -> bool {
482 match self.inner.val {
483 EnvConfigValueInner::Simple(_) => false,
484 EnvConfigValueInner::WithOptions { force, .. } => force,
485 }
486 }
487
488 pub fn resolve<'a>(&'a self, cwd: &Path) -> Cow<'a, OsStr> {
493 match self.inner.val {
494 EnvConfigValueInner::Simple(ref s) => Cow::Borrowed(OsStr::new(s.as_str())),
495 EnvConfigValueInner::WithOptions {
496 ref value,
497 relative,
498 ..
499 } => {
500 if relative {
501 let p = self.inner.definition.root(cwd).join(&value);
502 Cow::Owned(p.into_os_string())
503 } else {
504 Cow::Borrowed(OsStr::new(value.as_str()))
505 }
506 }
507 }
508 }
509}
510
511pub type EnvConfig = HashMap<String, EnvConfigValue>;