Skip to content

Commit 0f73320

Browse files
committed
Extra serde config support to a separate file
1 parent 022814d commit 0f73320

File tree

2 files changed

+361
-352
lines changed

2 files changed

+361
-352
lines changed

src/cargo/util/config/de.rs

Lines changed: 355 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,355 @@
1+
//! Support for deserializing configuration via `serde`
2+
3+
use crate::util::config::{Config, ConfigError, ConfigKey, ConfigKeyPart};
4+
use crate::util::config::{ConfigValue as CV, Value, Definition};
5+
use std::path::PathBuf;
6+
use serde::{de, de::IntoDeserializer};
7+
use std::collections::HashSet;
8+
use std::vec;
9+
10+
/// Serde deserializer used to convert config values to a target type using
11+
/// `Config::get`.
12+
pub(crate) struct Deserializer<'config> {
13+
pub(crate) config: &'config Config,
14+
pub(crate) key: ConfigKey,
15+
}
16+
17+
macro_rules! deserialize_method {
18+
($method:ident, $visit:ident, $getter:ident) => {
19+
fn $method<V>(self, visitor: V) -> Result<V::Value, Self::Error>
20+
where
21+
V: de::Visitor<'de>,
22+
{
23+
let v = self.config.$getter(&self.key)?.ok_or_else(||
24+
ConfigError::missing(&self.key.to_config()))?;
25+
let Value{val, definition} = v;
26+
let res: Result<V::Value, ConfigError> = visitor.$visit(val);
27+
res.map_err(|e| e.with_key_context(&self.key.to_config(), definition))
28+
}
29+
}
30+
}
31+
32+
impl<'de, 'config> de::Deserializer<'de> for Deserializer<'config> {
33+
type Error = ConfigError;
34+
35+
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
36+
where
37+
V: de::Visitor<'de>,
38+
{
39+
// Future note: If you ever need to deserialize a non-self describing
40+
// map type, this should implement a starts_with check (similar to how
41+
// ConfigMapAccess does).
42+
if let Some(v) = self.config.env.get(&self.key.to_env()) {
43+
let res: Result<V::Value, ConfigError> = if v == "true" || v == "false" {
44+
visitor.visit_bool(v.parse().unwrap())
45+
} else if let Ok(v) = v.parse::<i64>() {
46+
visitor.visit_i64(v)
47+
} else if self.config.cli_unstable().advanced_env
48+
&& v.starts_with('[')
49+
&& v.ends_with(']')
50+
{
51+
visitor.visit_seq(ConfigSeqAccess::new(self.config, &self.key)?)
52+
} else {
53+
visitor.visit_string(v.clone())
54+
};
55+
return res.map_err(|e| {
56+
e.with_key_context(
57+
&self.key.to_config(),
58+
Definition::Environment(self.key.to_env()),
59+
)
60+
});
61+
}
62+
63+
let o_cv = self.config.get_cv(&self.key.to_config())?;
64+
if let Some(cv) = o_cv {
65+
let res: (Result<V::Value, ConfigError>, PathBuf) = match cv {
66+
CV::Integer(i, path) => (visitor.visit_i64(i), path),
67+
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+
),
72+
CV::Table(_, path) => (
73+
visitor.visit_map(ConfigMapAccess::new_map(self.config, self.key.clone())?),
74+
path,
75+
),
76+
CV::Boolean(b, path) => (visitor.visit_bool(b), path),
77+
};
78+
let (res, path) = res;
79+
return res
80+
.map_err(|e| e.with_key_context(&self.key.to_config(), Definition::Path(path)));
81+
}
82+
Err(ConfigError::missing(&self.key.to_config()))
83+
}
84+
85+
deserialize_method!(deserialize_bool, visit_bool, get_bool_priv);
86+
deserialize_method!(deserialize_i8, visit_i64, get_integer);
87+
deserialize_method!(deserialize_i16, visit_i64, get_integer);
88+
deserialize_method!(deserialize_i32, visit_i64, get_integer);
89+
deserialize_method!(deserialize_i64, visit_i64, get_integer);
90+
deserialize_method!(deserialize_u8, visit_i64, get_integer);
91+
deserialize_method!(deserialize_u16, visit_i64, get_integer);
92+
deserialize_method!(deserialize_u32, visit_i64, get_integer);
93+
deserialize_method!(deserialize_u64, visit_i64, get_integer);
94+
deserialize_method!(deserialize_string, visit_string, get_string_priv);
95+
96+
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
97+
where
98+
V: de::Visitor<'de>,
99+
{
100+
if self.config.has_key(&self.key) {
101+
visitor.visit_some(self)
102+
} else {
103+
// Treat missing values as `None`.
104+
visitor.visit_none()
105+
}
106+
}
107+
108+
fn deserialize_struct<V>(
109+
self,
110+
_name: &'static str,
111+
fields: &'static [&'static str],
112+
visitor: V,
113+
) -> Result<V::Value, Self::Error>
114+
where
115+
V: de::Visitor<'de>,
116+
{
117+
visitor.visit_map(ConfigMapAccess::new_struct(self.config, self.key, fields)?)
118+
}
119+
120+
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error>
121+
where
122+
V: de::Visitor<'de>,
123+
{
124+
visitor.visit_map(ConfigMapAccess::new_map(self.config, self.key)?)
125+
}
126+
127+
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
128+
where
129+
V: de::Visitor<'de>,
130+
{
131+
visitor.visit_seq(ConfigSeqAccess::new(self.config, &self.key)?)
132+
}
133+
134+
fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value, Self::Error>
135+
where
136+
V: de::Visitor<'de>,
137+
{
138+
visitor.visit_seq(ConfigSeqAccess::new(self.config, &self.key)?)
139+
}
140+
141+
fn deserialize_tuple_struct<V>(
142+
self,
143+
_name: &'static str,
144+
_len: usize,
145+
visitor: V,
146+
) -> Result<V::Value, Self::Error>
147+
where
148+
V: de::Visitor<'de>,
149+
{
150+
visitor.visit_seq(ConfigSeqAccess::new(self.config, &self.key)?)
151+
}
152+
153+
fn deserialize_newtype_struct<V>(
154+
self,
155+
name: &'static str,
156+
visitor: V,
157+
) -> Result<V::Value, Self::Error>
158+
where
159+
V: de::Visitor<'de>,
160+
{
161+
if name == "ConfigRelativePath" {
162+
match self.config.get_string_priv(&self.key)? {
163+
Some(v) => {
164+
let path = v
165+
.definition
166+
.root(self.config)
167+
.join(v.val)
168+
.display()
169+
.to_string();
170+
visitor.visit_newtype_struct(path.into_deserializer())
171+
}
172+
None => Err(ConfigError::missing(&self.key.to_config())),
173+
}
174+
} else {
175+
visitor.visit_newtype_struct(self)
176+
}
177+
}
178+
179+
// These aren't really supported, yet.
180+
serde::forward_to_deserialize_any! {
181+
f32 f64 char str bytes
182+
byte_buf unit unit_struct
183+
enum identifier ignored_any
184+
}
185+
}
186+
187+
struct ConfigMapAccess<'config> {
188+
config: &'config Config,
189+
key: ConfigKey,
190+
set_iter: <HashSet<ConfigKeyPart> as IntoIterator>::IntoIter,
191+
next: Option<ConfigKeyPart>,
192+
}
193+
194+
impl<'config> ConfigMapAccess<'config> {
195+
fn new_map(
196+
config: &'config Config,
197+
key: ConfigKey,
198+
) -> Result<ConfigMapAccess<'config>, ConfigError> {
199+
let mut set = HashSet::new();
200+
if let Some(mut v) = config.get_table(&key.to_config())? {
201+
// `v: Value<HashMap<String, CV>>`
202+
for (key, _value) in v.val.drain() {
203+
set.insert(ConfigKeyPart::CasePart(key));
204+
}
205+
}
206+
if config.cli_unstable().advanced_env {
207+
// `CARGO_PROFILE_DEV_OVERRIDES_`
208+
let env_pattern = format!("{}_", key.to_env());
209+
for env_key in config.env.keys() {
210+
if env_key.starts_with(&env_pattern) {
211+
// `CARGO_PROFILE_DEV_OVERRIDES_bar_OPT_LEVEL = 3`
212+
let rest = &env_key[env_pattern.len()..];
213+
// `rest = bar_OPT_LEVEL`
214+
let part = rest.splitn(2, '_').next().unwrap();
215+
// `part = "bar"`
216+
set.insert(ConfigKeyPart::CasePart(part.to_string()));
217+
}
218+
}
219+
}
220+
Ok(ConfigMapAccess {
221+
config,
222+
key,
223+
set_iter: set.into_iter(),
224+
next: None,
225+
})
226+
}
227+
228+
fn new_struct(
229+
config: &'config Config,
230+
key: ConfigKey,
231+
fields: &'static [&'static str],
232+
) -> Result<ConfigMapAccess<'config>, ConfigError> {
233+
let mut set = HashSet::new();
234+
for field in fields {
235+
set.insert(ConfigKeyPart::Part(field.to_string()));
236+
}
237+
if let Some(mut v) = config.get_table(&key.to_config())? {
238+
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+
))?;
246+
}
247+
}
248+
}
249+
Ok(ConfigMapAccess {
250+
config,
251+
key,
252+
set_iter: set.into_iter(),
253+
next: None,
254+
})
255+
}
256+
}
257+
258+
impl<'de, 'config> de::MapAccess<'de> for ConfigMapAccess<'config> {
259+
type Error = ConfigError;
260+
261+
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>
262+
where
263+
K: de::DeserializeSeed<'de>,
264+
{
265+
match self.set_iter.next() {
266+
Some(key) => {
267+
let de_key = key.to_config();
268+
self.next = Some(key);
269+
seed.deserialize(de_key.into_deserializer()).map(Some)
270+
}
271+
None => Ok(None),
272+
}
273+
}
274+
275+
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>
276+
where
277+
V: de::DeserializeSeed<'de>,
278+
{
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+
})
285+
}
286+
}
287+
288+
struct ConfigSeqAccess {
289+
list_iter: vec::IntoIter<(String, Definition)>,
290+
}
291+
292+
impl ConfigSeqAccess {
293+
fn new(config: &Config, key: &ConfigKey) -> Result<ConfigSeqAccess, ConfigError> {
294+
let mut res = Vec::new();
295+
if let Some(v) = config.get_list(&key.to_config())? {
296+
for (s, path) in v.val {
297+
res.push((s, Definition::Path(path)));
298+
}
299+
}
300+
301+
if config.cli_unstable().advanced_env {
302+
// 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) {
306+
if !(v.starts_with('[') && v.ends_with(']')) {
307+
return Err(ConfigError::new(
308+
format!("should have TOML list syntax, found `{}`", v),
309+
def,
310+
));
311+
}
312+
let temp_key = key.last().to_env();
313+
let toml_s = format!("{}={}", temp_key, v);
314+
let toml_v: toml::Value = toml::de::from_str(&toml_s).map_err(|e| {
315+
ConfigError::new(format!("could not parse TOML list: {}", e), def.clone())
316+
})?;
317+
let values = toml_v
318+
.as_table()
319+
.unwrap()
320+
.get(&temp_key)
321+
.unwrap()
322+
.as_array()
323+
.expect("env var was not array");
324+
for value in values {
325+
// TODO: support other types.
326+
let s = value.as_str().ok_or_else(|| {
327+
ConfigError::new(
328+
format!("expected string, found {}", value.type_str()),
329+
def.clone(),
330+
)
331+
})?;
332+
res.push((s.to_string(), def.clone()));
333+
}
334+
}
335+
}
336+
Ok(ConfigSeqAccess {
337+
list_iter: res.into_iter(),
338+
})
339+
}
340+
}
341+
342+
impl<'de> de::SeqAccess<'de> for ConfigSeqAccess {
343+
type Error = ConfigError;
344+
345+
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
346+
where
347+
T: de::DeserializeSeed<'de>,
348+
{
349+
match self.list_iter.next() {
350+
// TODO: add `def` to error?
351+
Some((value, _def)) => seed.deserialize(value.into_deserializer()).map(Some),
352+
None => Ok(None),
353+
}
354+
}
355+
}

0 commit comments

Comments
 (0)