Skip to content

Commit 62d6233

Browse files
committed
Revert workaround and patch Deserializer
This patch changes how ConfigMapAccess iterates k/v pairs when deserializing structs. Previously we produced keys for exactly the set of fields needed for a struct, and errored out in the deserializer if we can't find anything for that field. This patch makes us produces keys from the union of two sets: 1. All fields that are both needed for the struct and can be found in the environment. 2. All fields in the config table. This change allows serde's codegen to handle both missing and unknown fields via the usual derive annotations (default or deny_unknown_fields respectively)
1 parent 08f1020 commit 62d6233

File tree

3 files changed

+36
-54
lines changed

3 files changed

+36
-54
lines changed

src/cargo/core/features.rs

Lines changed: 1 addition & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -334,68 +334,32 @@ impl Features {
334334
///
335335
/// If you have any trouble with this, please let us know!
336336
#[derive(Default, Debug, Deserialize)]
337-
#[serde(rename_all="kebab-case")]
337+
#[serde(default, rename_all = "kebab-case")]
338338
pub struct CliUnstable {
339-
#[serde(deserialize_with="default_false")]
340339
pub print_im_a_teapot: bool,
341-
#[serde(deserialize_with="default_false")]
342340
pub unstable_options: bool,
343-
#[serde(deserialize_with="default_false")]
344341
pub no_index_update: bool,
345-
#[serde(deserialize_with="default_false")]
346342
pub avoid_dev_deps: bool,
347-
#[serde(deserialize_with="default_false")]
348343
pub minimal_versions: bool,
349-
#[serde(deserialize_with="default_false")]
350344
pub package_features: bool,
351-
#[serde(deserialize_with="default_false")]
352345
pub advanced_env: bool,
353-
#[serde(deserialize_with="default_false")]
354346
pub config_include: bool,
355-
#[serde(deserialize_with="default_false")]
356347
pub dual_proc_macros: bool,
357-
#[serde(deserialize_with="default_false")]
358348
pub mtime_on_use: bool,
359-
#[serde(deserialize_with="default_false")]
360349
pub named_profiles: bool,
361-
#[serde(deserialize_with="default_false")]
362350
pub binary_dep_depinfo: bool,
363351
pub build_std: Option<Vec<String>>,
364352
pub timings: Option<Vec<String>>,
365-
#[serde(deserialize_with="default_false")]
366353
pub doctest_xcompile: bool,
367-
#[serde(deserialize_with="default_false")]
368354
pub panic_abort_tests: bool,
369-
#[serde(deserialize_with="default_false")]
370355
pub jobserver_per_rustc: bool,
371356
pub features: Option<Vec<String>>,
372-
#[serde(deserialize_with="default_false")]
373357
pub crate_versions: bool,
374-
#[serde(deserialize_with="default_false")]
375358
pub separate_nightlies: bool,
376-
#[serde(deserialize_with="default_false")]
377359
pub multitarget: bool,
378-
#[serde(deserialize_with="default_false")]
379360
pub rustdoc_map: bool,
380361
}
381362

382-
/// Treat boolean Zflags as optionals for deserializing them.
383-
/// This allows missing settings to default to disabled (effectively recreating
384-
/// the serde `default` behavior). Our Deserializer merges multiple sources
385-
/// inline, so the serde facilities for handling missing or additional fields
386-
/// don't quite fit.
387-
///
388-
/// TODO: This limitation can likely be removed by refactoring
389-
/// `de::ConfigMapAccess` to iterate the union of the config and env sets.
390-
fn default_false<'de, D, T>(deserializer: D) -> Result<T, D::Error>
391-
where
392-
D: serde::Deserializer<'de>,
393-
T: Deserialize<'de> + Default,
394-
{
395-
let option = Option::deserialize(deserializer)?;
396-
Ok(option.unwrap_or_default())
397-
}
398-
399363
impl CliUnstable {
400364
pub fn parse(&mut self, flags: &[String]) -> CargoResult<()> {
401365
if !flags.is_empty() && !nightly_features_allowed() {

src/cargo/util/config/de.rs

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::util::config::value;
44
use crate::util::config::{Config, ConfigError, ConfigKey};
55
use crate::util::config::{ConfigValue as CV, Definition, Value};
66
use serde::{de, de::IntoDeserializer};
7+
use std::collections::HashSet;
78
use std::vec;
89

910
/// Serde deserializer used to convert config values to a target type using
@@ -269,37 +270,54 @@ impl<'config> ConfigMapAccess<'config> {
269270

270271
fn new_struct(
271272
de: Deserializer<'config>,
272-
fields: &'static [&'static str],
273+
given_fields: &'static [&'static str],
273274
) -> Result<ConfigMapAccess<'config>, ConfigError> {
274-
let fields: Vec<KeyKind> = fields
275-
.iter()
276-
.map(|field| KeyKind::Normal(field.to_string()))
277-
.collect();
275+
let table = de.config.get_table(&de.key)?;
278276

279277
// Assume that if we're deserializing a struct it exhaustively lists all
280278
// possible fields on this key that we're *supposed* to use, so take
281279
// this opportunity to warn about any keys that aren't recognized as
282280
// fields and warn about them.
283-
if let Some(mut v) = de.config.get_table(&de.key)? {
284-
for (t_key, value) in v.val.drain() {
285-
if fields.iter().any(|k| match k {
286-
KeyKind::Normal(s) => s == &t_key,
287-
KeyKind::CaseSensitive(s) => s == &t_key,
288-
}) {
289-
continue;
290-
}
281+
if let Some(v) = table.as_ref() {
282+
let unused_keys = v
283+
.val
284+
.iter()
285+
.filter(|(k, _v)| !given_fields.iter().any(|gk| gk == k));
286+
for (unused_key, unused_value) in unused_keys {
291287
de.config.shell().warn(format!(
292288
"unused config key `{}.{}` in `{}`",
293289
de.key,
294-
t_key,
295-
value.definition()
290+
unused_key,
291+
unused_value.definition()
296292
))?;
297293
}
298294
}
299295

296+
let mut fields = HashSet::new();
297+
298+
// If the caller is interested in a field which we can provide from
299+
// the environment, get it from there.
300+
for field in given_fields {
301+
let mut field_key = de.key.clone();
302+
field_key.push(field);
303+
for env_key in de.config.env.keys() {
304+
if env_key.starts_with(field_key.as_env_key()) {
305+
fields.insert(KeyKind::Normal(field.to_string()));
306+
}
307+
}
308+
}
309+
310+
// Add everything from the config table we're interested in that we
311+
// haven't already provided via an environment variable
312+
if let Some(v) = table {
313+
for key in v.val.keys() {
314+
fields.insert(KeyKind::Normal(key.clone()));
315+
}
316+
}
317+
300318
Ok(ConfigMapAccess {
301319
de,
302-
fields,
320+
fields: fields.into_iter().collect(),
303321
field_index: 0,
304322
})
305323
}

src/cargo/util/toml/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ impl<'de> de::Deserialize<'de> for U32OrBool {
394394
}
395395

396396
#[derive(Deserialize, Serialize, Clone, Debug, Default, Eq, PartialEq)]
397-
#[serde(rename_all = "kebab-case")]
397+
#[serde(default, rename_all = "kebab-case")]
398398
pub struct TomlProfile {
399399
pub opt_level: Option<TomlOptLevel>,
400400
pub lto: Option<StringOrBool>,

0 commit comments

Comments
 (0)