Skip to content

Commit c0baf84

Browse files
committed
Refactor ConfigKey to its own file
Also make it a little less allocation-heavy by tweaking the API to encourage incremental building of the key and incremental destruction as we walk throughout the configuration tree.
1 parent 0f73320 commit c0baf84

File tree

5 files changed

+389
-200
lines changed

5 files changed

+389
-200
lines changed

src/cargo/util/config/de.rs

Lines changed: 130 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
//! Support for deserializing configuration via `serde`
22
3-
use crate::util::config::{Config, ConfigError, ConfigKey, ConfigKeyPart};
4-
use crate::util::config::{ConfigValue as CV, Value, Definition};
5-
use std::path::PathBuf;
3+
use crate::util::config::value;
4+
use crate::util::config::{Config, ConfigError, ConfigKey};
5+
use crate::util::config::{ConfigValue as CV, Definition, Value};
66
use serde::{de, de::IntoDeserializer};
77
use std::collections::HashSet;
8+
use std::path::PathBuf;
89
use std::vec;
910

1011
/// Serde deserializer used to convert config values to a target type using
1112
/// `Config::get`.
13+
#[derive(Clone)]
1214
pub(crate) struct Deserializer<'config> {
1315
pub(crate) config: &'config Config,
1416
pub(crate) key: ConfigKey,
@@ -21,10 +23,10 @@ macro_rules! deserialize_method {
2123
V: de::Visitor<'de>,
2224
{
2325
let v = self.config.$getter(&self.key)?.ok_or_else(||
24-
ConfigError::missing(&self.key.to_config()))?;
26+
ConfigError::missing(&self.key))?;
2527
let Value{val, definition} = v;
2628
let res: Result<V::Value, ConfigError> = visitor.$visit(val);
27-
res.map_err(|e| e.with_key_context(&self.key.to_config(), definition))
29+
res.map_err(|e| e.with_key_context(&self.key, definition))
2830
}
2931
}
3032
}
@@ -39,7 +41,7 @@ impl<'de, 'config> de::Deserializer<'de> for Deserializer<'config> {
3941
// Future note: If you ever need to deserialize a non-self describing
4042
// map type, this should implement a starts_with check (similar to how
4143
// ConfigMapAccess does).
42-
if let Some(v) = self.config.env.get(&self.key.to_env()) {
44+
if let Some(v) = self.config.env.get(self.key.as_env_key()) {
4345
let res: Result<V::Value, ConfigError> = if v == "true" || v == "false" {
4446
visitor.visit_bool(v.parse().unwrap())
4547
} else if let Ok(v) = v.parse::<i64>() {
@@ -48,38 +50,34 @@ impl<'de, 'config> de::Deserializer<'de> for Deserializer<'config> {
4850
&& v.starts_with('[')
4951
&& v.ends_with(']')
5052
{
51-
visitor.visit_seq(ConfigSeqAccess::new(self.config, &self.key)?)
53+
visitor.visit_seq(ConfigSeqAccess::new(self.clone())?)
5254
} else {
5355
visitor.visit_string(v.clone())
5456
};
5557
return res.map_err(|e| {
5658
e.with_key_context(
57-
&self.key.to_config(),
58-
Definition::Environment(self.key.to_env()),
59+
&self.key,
60+
Definition::Environment(self.key.as_env_key().to_string()),
5961
)
6062
});
6163
}
6264

63-
let o_cv = self.config.get_cv(&self.key.to_config())?;
65+
let o_cv = self.config.get_cv(self.key.as_config_key())?;
6466
if let Some(cv) = o_cv {
6567
let res: (Result<V::Value, ConfigError>, PathBuf) = match cv {
6668
CV::Integer(i, path) => (visitor.visit_i64(i), path),
6769
CV::String(s, path) => (visitor.visit_string(s), path),
68-
CV::List(_, path) => (
69-
visitor.visit_seq(ConfigSeqAccess::new(self.config, &self.key)?),
70-
path,
71-
),
70+
CV::List(_, path) => (visitor.visit_seq(ConfigSeqAccess::new(self.clone())?), path),
7271
CV::Table(_, path) => (
73-
visitor.visit_map(ConfigMapAccess::new_map(self.config, self.key.clone())?),
72+
visitor.visit_map(ConfigMapAccess::new_map(self.clone())?),
7473
path,
7574
),
7675
CV::Boolean(b, path) => (visitor.visit_bool(b), path),
7776
};
7877
let (res, path) = res;
79-
return res
80-
.map_err(|e| e.with_key_context(&self.key.to_config(), Definition::Path(path)));
78+
return res.map_err(|e| e.with_key_context(&self.key, Definition::Path(path)));
8179
}
82-
Err(ConfigError::missing(&self.key.to_config()))
80+
Err(ConfigError::missing(&self.key))
8381
}
8482

8583
deserialize_method!(deserialize_bool, visit_bool, get_bool_priv);
@@ -107,35 +105,41 @@ impl<'de, 'config> de::Deserializer<'de> for Deserializer<'config> {
107105

108106
fn deserialize_struct<V>(
109107
self,
110-
_name: &'static str,
108+
name: &'static str,
111109
fields: &'static [&'static str],
112110
visitor: V,
113111
) -> Result<V::Value, Self::Error>
114112
where
115113
V: de::Visitor<'de>,
116114
{
117-
visitor.visit_map(ConfigMapAccess::new_struct(self.config, self.key, fields)?)
115+
if name == value::NAME && fields == value::FIELDS {
116+
return visitor.visit_map(ValueDeserializer {
117+
hits: 0,
118+
deserializer: self,
119+
});
120+
}
121+
visitor.visit_map(ConfigMapAccess::new_struct(self, fields)?)
118122
}
119123

120124
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error>
121125
where
122126
V: de::Visitor<'de>,
123127
{
124-
visitor.visit_map(ConfigMapAccess::new_map(self.config, self.key)?)
128+
visitor.visit_map(ConfigMapAccess::new_map(self)?)
125129
}
126130

127131
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
128132
where
129133
V: de::Visitor<'de>,
130134
{
131-
visitor.visit_seq(ConfigSeqAccess::new(self.config, &self.key)?)
135+
visitor.visit_seq(ConfigSeqAccess::new(self)?)
132136
}
133137

134138
fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value, Self::Error>
135139
where
136140
V: de::Visitor<'de>,
137141
{
138-
visitor.visit_seq(ConfigSeqAccess::new(self.config, &self.key)?)
142+
visitor.visit_seq(ConfigSeqAccess::new(self)?)
139143
}
140144

141145
fn deserialize_tuple_struct<V>(
@@ -147,7 +151,7 @@ impl<'de, 'config> de::Deserializer<'de> for Deserializer<'config> {
147151
where
148152
V: de::Visitor<'de>,
149153
{
150-
visitor.visit_seq(ConfigSeqAccess::new(self.config, &self.key)?)
154+
visitor.visit_seq(ConfigSeqAccess::new(self)?)
151155
}
152156

153157
fn deserialize_newtype_struct<V>(
@@ -169,7 +173,7 @@ impl<'de, 'config> de::Deserializer<'de> for Deserializer<'config> {
169173
.to_string();
170174
visitor.visit_newtype_struct(path.into_deserializer())
171175
}
172-
None => Err(ConfigError::missing(&self.key.to_config())),
176+
None => Err(ConfigError::missing(&self.key)),
173177
}
174178
} else {
175179
visitor.visit_newtype_struct(self)
@@ -185,70 +189,76 @@ impl<'de, 'config> de::Deserializer<'de> for Deserializer<'config> {
185189
}
186190

187191
struct ConfigMapAccess<'config> {
188-
config: &'config Config,
189-
key: ConfigKey,
190-
set_iter: <HashSet<ConfigKeyPart> as IntoIterator>::IntoIter,
191-
next: Option<ConfigKeyPart>,
192+
de: Deserializer<'config>,
193+
set_iter: <HashSet<KeyKind> as IntoIterator>::IntoIter,
194+
next: Option<KeyKind>,
195+
}
196+
197+
#[derive(PartialEq, Eq, Hash)]
198+
enum KeyKind {
199+
Normal(String),
200+
CaseSensitive(String),
192201
}
193202

194203
impl<'config> ConfigMapAccess<'config> {
195-
fn new_map(
196-
config: &'config Config,
197-
key: ConfigKey,
198-
) -> Result<ConfigMapAccess<'config>, ConfigError> {
204+
fn new_map(de: Deserializer<'config>) -> Result<ConfigMapAccess<'config>, ConfigError> {
199205
let mut set = HashSet::new();
200-
if let Some(mut v) = config.get_table(&key.to_config())? {
206+
if let Some(mut v) = de.config.get_table(de.key.as_config_key())? {
201207
// `v: Value<HashMap<String, CV>>`
202208
for (key, _value) in v.val.drain() {
203-
set.insert(ConfigKeyPart::CasePart(key));
209+
set.insert(KeyKind::CaseSensitive(key));
204210
}
205211
}
206-
if config.cli_unstable().advanced_env {
212+
if de.config.cli_unstable().advanced_env {
207213
// `CARGO_PROFILE_DEV_OVERRIDES_`
208-
let env_pattern = format!("{}_", key.to_env());
209-
for env_key in config.env.keys() {
214+
let env_pattern = format!("{}_", de.key.as_env_key());
215+
for env_key in de.config.env.keys() {
210216
if env_key.starts_with(&env_pattern) {
211217
// `CARGO_PROFILE_DEV_OVERRIDES_bar_OPT_LEVEL = 3`
212218
let rest = &env_key[env_pattern.len()..];
213219
// `rest = bar_OPT_LEVEL`
214220
let part = rest.splitn(2, '_').next().unwrap();
215221
// `part = "bar"`
216-
set.insert(ConfigKeyPart::CasePart(part.to_string()));
222+
set.insert(KeyKind::CaseSensitive(part.to_string()));
217223
}
218224
}
219225
}
220226
Ok(ConfigMapAccess {
221-
config,
222-
key,
227+
de,
223228
set_iter: set.into_iter(),
224229
next: None,
225230
})
226231
}
227232

228233
fn new_struct(
229-
config: &'config Config,
230-
key: ConfigKey,
234+
de: Deserializer<'config>,
231235
fields: &'static [&'static str],
232236
) -> Result<ConfigMapAccess<'config>, ConfigError> {
233237
let mut set = HashSet::new();
234238
for field in fields {
235-
set.insert(ConfigKeyPart::Part(field.to_string()));
239+
set.insert(KeyKind::Normal(field.to_string()));
236240
}
237-
if let Some(mut v) = config.get_table(&key.to_config())? {
241+
242+
// Assume that if we're deserializing a struct it exhaustively lists all
243+
// possible fields on this key that we're *supposed* to use, so take
244+
// this opportunity to warn about any keys that aren't recognized as
245+
// fields and warn about them.
246+
if let Some(mut v) = de.config.get_table(de.key.as_config_key())? {
238247
for (t_key, value) in v.val.drain() {
239-
let part = ConfigKeyPart::Part(t_key);
240-
if !set.contains(&part) {
241-
config.shell().warn(format!(
242-
"unused key `{}` in config file `{}`",
243-
key.join(part).to_config(),
244-
value.definition_path().display()
245-
))?;
248+
if set.contains(&KeyKind::Normal(t_key.to_string())) {
249+
continue;
246250
}
251+
de.config.shell().warn(format!(
252+
"unused key `{}.{}` in config file `{}`",
253+
de.key.as_config_key(),
254+
t_key,
255+
value.definition_path().display()
256+
))?;
247257
}
248258
}
259+
249260
Ok(ConfigMapAccess {
250-
config,
251-
key,
261+
de,
252262
set_iter: set.into_iter(),
253263
next: None,
254264
})
@@ -264,9 +274,12 @@ impl<'de, 'config> de::MapAccess<'de> for ConfigMapAccess<'config> {
264274
{
265275
match self.set_iter.next() {
266276
Some(key) => {
267-
let de_key = key.to_config();
277+
let name = match &key {
278+
KeyKind::Normal(s) | KeyKind::CaseSensitive(s) => s.as_str(),
279+
};
280+
let result = seed.deserialize(name.into_deserializer()).map(Some);
268281
self.next = Some(key);
269-
seed.deserialize(de_key.into_deserializer()).map(Some)
282+
return result;
270283
}
271284
None => Ok(None),
272285
}
@@ -276,12 +289,16 @@ impl<'de, 'config> de::MapAccess<'de> for ConfigMapAccess<'config> {
276289
where
277290
V: de::DeserializeSeed<'de>,
278291
{
279-
let next_key = self.next.take().expect("next field missing");
280-
let next_key = self.key.join(next_key);
281-
seed.deserialize(Deserializer {
282-
config: self.config,
283-
key: next_key,
284-
})
292+
match self.next.take().expect("next field missing") {
293+
KeyKind::Normal(key) => self.de.key.push(&key),
294+
KeyKind::CaseSensitive(key) => self.de.key.push_sensitive(&key),
295+
}
296+
let result = seed.deserialize(Deserializer {
297+
config: self.de.config,
298+
key: self.de.key.clone(),
299+
});
300+
self.de.key.pop();
301+
return result;
285302
}
286303
}
287304

@@ -290,34 +307,32 @@ struct ConfigSeqAccess {
290307
}
291308

292309
impl ConfigSeqAccess {
293-
fn new(config: &Config, key: &ConfigKey) -> Result<ConfigSeqAccess, ConfigError> {
310+
fn new(de: Deserializer<'_>) -> Result<ConfigSeqAccess, ConfigError> {
294311
let mut res = Vec::new();
295-
if let Some(v) = config.get_list(&key.to_config())? {
312+
if let Some(v) = de.config.get_list(de.key.as_config_key())? {
296313
for (s, path) in v.val {
297314
res.push((s, Definition::Path(path)));
298315
}
299316
}
300317

301-
if config.cli_unstable().advanced_env {
318+
if de.config.cli_unstable().advanced_env {
302319
// Parse an environment string as a TOML array.
303-
let env_key = key.to_env();
304-
let def = Definition::Environment(env_key.clone());
305-
if let Some(v) = config.env.get(&env_key) {
320+
if let Some(v) = de.config.env.get(de.key.as_env_key()) {
321+
let def = Definition::Environment(de.key.as_env_key().to_string());
306322
if !(v.starts_with('[') && v.ends_with(']')) {
307323
return Err(ConfigError::new(
308324
format!("should have TOML list syntax, found `{}`", v),
309325
def,
310326
));
311327
}
312-
let temp_key = key.last().to_env();
313-
let toml_s = format!("{}={}", temp_key, v);
328+
let toml_s = format!("value={}", v);
314329
let toml_v: toml::Value = toml::de::from_str(&toml_s).map_err(|e| {
315330
ConfigError::new(format!("could not parse TOML list: {}", e), def.clone())
316331
})?;
317332
let values = toml_v
318333
.as_table()
319334
.unwrap()
320-
.get(&temp_key)
335+
.get("value")
321336
.unwrap()
322337
.as_array()
323338
.expect("env var was not array");
@@ -353,3 +368,47 @@ impl<'de> de::SeqAccess<'de> for ConfigSeqAccess {
353368
}
354369
}
355370
}
371+
372+
struct ValueDeserializer<'config> {
373+
hits: u32,
374+
deserializer: Deserializer<'config>,
375+
}
376+
377+
impl<'de, 'config> de::MapAccess<'de> for ValueDeserializer<'config> {
378+
type Error = ConfigError;
379+
380+
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>
381+
where
382+
K: de::DeserializeSeed<'de>,
383+
{
384+
self.hits += 1;
385+
match self.hits {
386+
1 => seed
387+
.deserialize(value::VALUE_FIELD.into_deserializer())
388+
.map(Some),
389+
2 => seed
390+
.deserialize(value::DEFINITION_FIELD.into_deserializer())
391+
.map(Some),
392+
_ => Ok(None),
393+
}
394+
}
395+
396+
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>
397+
where
398+
V: de::DeserializeSeed<'de>,
399+
{
400+
if self.hits == 1 {
401+
seed.deserialize(Deserializer {
402+
config: self.deserializer.config,
403+
key: self.deserializer.key.clone(),
404+
})
405+
} else {
406+
// let env = self.deserializer.key.to_env();
407+
// if self.deserializer.config.env.contains_key(&env) {
408+
// } else {
409+
// }
410+
// if let someself.deserializer.config.get_env(&self.deserializer.key)
411+
panic!()
412+
}
413+
}
414+
}

0 commit comments

Comments
 (0)