1use alloc::borrow::ToOwned as _;
7
8use crate::{
9 de::{
10 BuildConfig, CargoNewConfig, Config, CredentialProvider, DocConfig, Flags,
11 FutureIncompatReportConfig, GlobalCredentialProviders, HttpConfig, NetConfig, PathAndArgs,
12 RegistriesConfigValue, RegistryConfig, StringList, StringOrArray, TermConfig, TermProgress,
13 split_space_separated,
14 },
15 error::{Context as _, Error, Result},
16 resolve::ResolveContext,
17 value::{Definition, Value},
18};
19
20pub(crate) trait ApplyEnv {
21 fn apply_env(&mut self, cx: &ResolveContext) -> Result<()>;
23}
24
25impl Config {
26 #[doc(hidden)] pub fn apply_env(&mut self, cx: &ResolveContext) -> Result<()> {
35 for (k, v) in &cx.env {
36 let definition = || Some(Definition::Environment(k.clone().into()));
37 let error_env_not_unicode = || Error::env_not_unicode(k, v.clone());
38 let error_env_not_unicode_redacted = || Error::env_not_unicode_redacted(k);
39
40 if let Some(k) = k.strip_prefix("CARGO_ALIAS_") {
42 self.alias.insert(
43 k.to_owned(),
44 StringList::from_string(
45 v.to_str().ok_or_else(error_env_not_unicode)?,
46 definition().as_ref(),
47 ),
48 );
49 }
50 else if let Some(k) = k.strip_prefix("CARGO_CREDENTIAL_ALIAS_") {
52 self.credential_alias.insert(
53 k.to_owned(),
54 PathAndArgs::from_string(
55 v.to_str().ok_or_else(error_env_not_unicode)?,
56 definition().as_ref(),
57 )
58 .context("invalid length 0, expected at least one element")?,
59 );
60 }
61 else if let Some(k) = k.strip_prefix("CARGO_REGISTRIES_") {
63 if let Some(k) = k.strip_suffix("_INDEX") {
64 let v = v.to_str().ok_or_else(error_env_not_unicode)?;
65 let index = Some(Value { val: v.to_owned(), definition: definition() });
66 if let Some(registries_config_value) = self.registries.get_mut(k) {
67 registries_config_value.index = index;
68 } else {
69 self.registries.insert(k.to_owned(), RegistriesConfigValue {
70 index,
71 token: None,
72 credential_provider: None,
73 protocol: None,
74 });
75 }
76 } else if let Some(k) = k.strip_suffix("_TOKEN") {
77 let v = v.to_str().ok_or_else(error_env_not_unicode_redacted)?;
78 let token = Some(Value { val: v.to_owned(), definition: definition() });
79 if let Some(registries_config_value) = self.registries.get_mut(k) {
80 registries_config_value.token = token;
81 } else {
82 self.registries.insert(k.to_owned(), RegistriesConfigValue {
83 index: None,
84 token,
85 credential_provider: None,
86 protocol: None,
87 });
88 }
89 } else if let Some(k) = k.strip_suffix("_CREDENTIAL_PROVIDER") {
90 let credential_provider = Some(
91 CredentialProvider::from_string(k, definition().as_ref())
92 .context("invalid length 0, expected at least one element")?,
93 );
94 if let Some(registries_config_value) = self.registries.get_mut(k) {
95 registries_config_value.credential_provider = credential_provider;
96 } else {
97 self.registries.insert(k.to_owned(), RegistriesConfigValue {
98 index: None,
99 token: None,
100 credential_provider,
101 protocol: None,
102 });
103 }
104 } else if k == "CRATES_IO_PROTOCOL" {
105 let k = "crates-io";
106 let v = v.to_str().ok_or_else(error_env_not_unicode)?;
107 let protocol =
108 Some(Value { val: v.to_owned(), definition: definition() }.parse()?);
109 if let Some(registries_config_value) = self.registries.get_mut(k) {
110 registries_config_value.protocol = protocol;
111 } else {
112 self.registries.insert(k.to_owned(), RegistriesConfigValue {
113 index: None,
114 token: None,
115 credential_provider: None,
116 protocol,
117 });
118 }
119 }
120 }
121 }
122
123 self.build.apply_env(cx)?;
126 self.doc.apply_env(cx)?;
127 self.future_incompat_report.apply_env(cx)?;
128 self.http.apply_env(cx)?;
129 self.net.apply_env(cx)?;
130 self.registry.apply_env(cx)?;
131 self.term.apply_env(cx)?;
132 Ok(())
133 }
134}
135
136impl ApplyEnv for BuildConfig {
137 fn apply_env(&mut self, cx: &ResolveContext) -> Result<()> {
138 if let Some(jobs) = cx.env_parse("CARGO_BUILD_JOBS")? {
140 self.jobs = Some(jobs);
141 }
142
143 if let Some(rustc) = cx.env("RUSTC")? {
150 self.rustc = Some(rustc);
151 } else if let Some(rustc) = cx.env("CARGO_BUILD_RUSTC")? {
152 self.rustc = Some(rustc);
153 }
154 if let Some(rustc_wrapper) = cx.env("RUSTC_WRAPPER")? {
160 if rustc_wrapper.val.is_empty() {
161 self.rustc_wrapper = None;
162 } else {
163 self.rustc_wrapper = Some(rustc_wrapper);
164 }
165 } else if let Some(rustc_wrapper) = cx.env("CARGO_BUILD_RUSTC_WRAPPER")? {
166 if rustc_wrapper.val.is_empty() {
167 self.rustc_wrapper = None;
168 } else {
169 self.rustc_wrapper = Some(rustc_wrapper);
170 }
171 }
172 if let Some(rustc_workspace_wrapper) = cx.env("RUSTC_WORKSPACE_WRAPPER")? {
178 if rustc_workspace_wrapper.val.is_empty() {
179 self.rustc_workspace_wrapper = None;
180 } else {
181 self.rustc_workspace_wrapper = Some(rustc_workspace_wrapper);
182 }
183 } else if let Some(rustc_workspace_wrapper) =
184 cx.env("CARGO_BUILD_RUSTC_WORKSPACE_WRAPPER")?
185 {
186 if rustc_workspace_wrapper.val.is_empty() {
187 self.rustc_workspace_wrapper = None;
188 } else {
189 self.rustc_workspace_wrapper = Some(rustc_workspace_wrapper);
190 }
191 }
192 if let Some(rustdoc) = cx.env("RUSTDOC")? {
196 self.rustdoc = Some(rustdoc);
197 } else if let Some(rustdoc) = cx.env("CARGO_BUILD_RUSTDOC")? {
198 self.rustdoc = Some(rustdoc);
199 }
200
201 if let Some(target) = cx.env("CARGO_BUILD_TARGET")? {
203 self.target = Some(StringOrArray::String(target));
204 }
205
206 if let Some(target_dir) = cx.env("CARGO_TARGET_DIR")? {
212 self.target_dir = Some(target_dir);
213 } else if let Some(target_dir) = cx.env("CARGO_BUILD_TARGET_DIR")? {
214 self.target_dir = Some(target_dir);
215 }
216
217 if let Some(build_dir) = cx.env("CARGO_BUILD_BUILD_DIR")? {
219 self.build_dir = Some(build_dir);
220 }
221
222 self.override_target_rustflags = false;
229 if let Some(rustflags) = cx.env("CARGO_ENCODED_RUSTFLAGS")? {
230 self.rustflags = Some(Flags::from_encoded(&rustflags));
231 self.override_target_rustflags = true;
232 } else if let Some(rustflags) = cx.env("RUSTFLAGS")? {
233 self.rustflags =
234 Some(Flags::from_space_separated(&rustflags.val, rustflags.definition.as_ref()));
235 self.override_target_rustflags = true;
236 } else if let Some(rustflags) = cx.env("CARGO_BUILD_RUSTFLAGS")? {
237 self.rustflags =
238 Some(Flags::from_space_separated(&rustflags.val, rustflags.definition.as_ref()));
239 }
240 self.override_target_rustdocflags = false;
247 if let Some(rustdocflags) = cx.env("CARGO_ENCODED_RUSTDOCFLAGS")? {
248 self.rustdocflags = Some(Flags::from_encoded(&rustdocflags));
249 self.override_target_rustdocflags = true;
250 } else if let Some(rustdocflags) = cx.env("RUSTDOCFLAGS")? {
251 self.rustdocflags = Some(Flags::from_space_separated(
252 &rustdocflags.val,
253 rustdocflags.definition.as_ref(),
254 ));
255 self.override_target_rustdocflags = true;
256 } else if let Some(rustdocflags) = cx.env("CARGO_BUILD_RUSTDOCFLAGS")? {
257 self.rustdocflags = Some(Flags::from_space_separated(
258 &rustdocflags.val,
259 rustdocflags.definition.as_ref(),
260 ));
261 }
262
263 if let Some(incremental) = cx.env("CARGO_INCREMENTAL")? {
269 self.incremental =
271 Some(Value { val: incremental.val == "1", definition: incremental.definition });
272 } else if let Some(incremental) = cx.env_parse("CARGO_BUILD_INCREMENTAL")? {
273 self.incremental = Some(incremental);
274 }
275
276 if let Some(dep_info_basedir) = cx.env("CARGO_BUILD_DEP_INFO_BASEDIR")? {
278 self.dep_info_basedir = Some(dep_info_basedir);
279 }
280
281 Ok(())
282 }
283}
284
285impl ApplyEnv for DocConfig {
286 fn apply_env(&mut self, cx: &ResolveContext) -> Result<()> {
287 if self.browser.is_none() {
290 if let Some(browser) = cx.env("BROWSER")? {
291 self.browser = Some(
292 PathAndArgs::from_string(&browser.val, browser.definition.as_ref())
293 .context("invalid length 0, expected at least one element")?,
294 );
295 }
296 }
297 Ok(())
298 }
299}
300
301impl ApplyEnv for FutureIncompatReportConfig {
302 fn apply_env(&mut self, cx: &ResolveContext) -> Result<()> {
303 if let Some(frequency) = cx.env_parse("CARGO_FUTURE_INCOMPAT_REPORT_FREQUENCY")? {
305 self.frequency = Some(frequency);
306 }
307 Ok(())
308 }
309}
310
311impl ApplyEnv for CargoNewConfig {
312 fn apply_env(&mut self, cx: &ResolveContext) -> Result<()> {
313 if let Some(vcs) = cx.env_parse("CARGO_CARGO_NEW_VCS")? {
315 self.vcs = Some(vcs);
316 }
317 Ok(())
318 }
319}
320
321impl ApplyEnv for HttpConfig {
322 fn apply_env(&mut self, cx: &ResolveContext) -> Result<()> {
323 if let Some(debug) = cx.env_parse("CARGO_HTTP_DEBUG")? {
325 self.debug = Some(debug);
326 }
327 if let Some(proxy) = cx.env("CARGO_HTTP_PROXY")? {
331 self.proxy = Some(proxy);
332 }
333 if let Some(timeout) = cx.env_parse("CARGO_HTTP_TIMEOUT")? {
337 self.timeout = Some(timeout);
338 }
339 if let Some(cainfo) = cx.env("CARGO_HTTP_CAINFO")? {
341 self.cainfo = Some(cainfo);
342 }
343 if let Some(check_revoke) = cx.env_parse("CARGO_HTTP_CHECK_REVOKE")? {
345 self.check_revoke = Some(check_revoke);
346 }
347 if let Some(low_speed_limit) = cx.env_parse("CARGO_HTTP_LOW_SPEED_LIMIT")? {
349 self.low_speed_limit = Some(low_speed_limit);
350 }
351 if let Some(multiplexing) = cx.env_parse("CARGO_HTTP_MULTIPLEXING")? {
353 self.multiplexing = Some(multiplexing);
354 }
355 if let Some(user_agent) = cx.env("CARGO_HTTP_USER_AGENT")? {
357 self.user_agent = Some(user_agent);
358 }
359 Ok(())
360 }
361}
362
363impl ApplyEnv for NetConfig {
364 fn apply_env(&mut self, cx: &ResolveContext) -> Result<()> {
365 if let Some(retry) = cx.env_parse("CARGO_NET_RETRY")? {
367 self.retry = Some(retry);
368 }
369 if let Some(git_fetch_with_cli) = cx.env_parse("CARGO_NET_GIT_FETCH_WITH_CLI")? {
371 self.git_fetch_with_cli = Some(git_fetch_with_cli);
372 }
373 if let Some(offline) = cx.env_parse("CARGO_NET_OFFLINE")? {
375 self.offline = Some(offline);
376 }
377 Ok(())
378 }
379}
380
381impl ApplyEnv for RegistryConfig {
382 fn apply_env(&mut self, cx: &ResolveContext) -> Result<()> {
383 if let Some(default) = cx.env("CARGO_REGISTRY_DEFAULT")? {
385 self.default = Some(default);
386 }
387 if let Some(credential_provider) = cx.env("CARGO_REGISTRY_CREDENTIAL_PROVIDER")? {
389 self.credential_provider = Some(CredentialProvider::from_string(
390 &credential_provider.val,
391 credential_provider.definition.as_ref(),
392 )?);
393 }
394 if let Some(token) = cx.env_redacted("CARGO_REGISTRY_TOKEN")? {
396 self.token = Some(token);
397 }
398 if let Some(global_credential_providers) =
400 cx.env("CARGO_REGISTRY_GLOBAL_CREDENTIAL_PROVIDERS")?
401 {
402 self.global_credential_providers = GlobalCredentialProviders::from_list(
403 split_space_separated(&global_credential_providers.val),
404 global_credential_providers.definition.as_ref(),
405 )?;
406 }
407 Ok(())
408 }
409}
410
411impl ApplyEnv for TermConfig {
412 fn apply_env(&mut self, cx: &ResolveContext) -> Result<()> {
413 if let Some(quiet) = cx.env_parse("CARGO_TERM_QUIET")? {
415 self.quiet = Some(quiet);
416 }
417 if let Some(verbose) = cx.env_parse("CARGO_TERM_VERBOSE")? {
419 self.verbose = Some(verbose);
420 }
421 if let Some(color) = cx.env_parse("CARGO_TERM_COLOR")? {
423 self.color = Some(color);
424 }
425 self.progress.apply_env(cx)?;
426 Ok(())
427 }
428}
429
430impl ApplyEnv for TermProgress {
431 fn apply_env(&mut self, cx: &ResolveContext) -> Result<()> {
432 if let Some(when) = cx.env_parse("CARGO_TERM_PROGRESS_WHEN")? {
434 self.when = Some(when);
435 }
436 if let Some(width) = cx.env_parse("CARGO_TERM_PROGRESS_WIDTH")? {
438 self.width = Some(width);
439 }
440 Ok(())
441 }
442}
443
444#[cfg(test)]
445mod tests {
446 use alloc::borrow::ToOwned as _;
447
448 use super::ApplyEnv as _;
449 use crate::{ResolveOptions, value::Value};
450
451 #[test]
452 fn empty_string_wrapper_envs() {
453 let env_list = [("RUSTC_WRAPPER", ""), ("RUSTC_WORKSPACE_WRAPPER", "")];
454 let mut config = crate::de::BuildConfig::default();
455 let cx =
456 &ResolveOptions::default().env(env_list).into_context(std::env::current_dir().unwrap());
457 config.rustc_wrapper = Some(Value { val: "rustc_wrapper".to_owned(), definition: None });
458 config.rustc_workspace_wrapper =
459 Some(Value { val: "rustc_workspace_wrapper".to_owned(), definition: None });
460 config.apply_env(cx).unwrap();
461 assert!(config.rustc_wrapper.is_none());
462 assert!(config.rustc_workspace_wrapper.is_none());
463 }
464}