cargo/util/context/
de.rs

1//! Support for deserializing configuration via `serde`
2
3use crate::util::context::value;
4use crate::util::context::{ConfigError, ConfigKey, GlobalContext};
5use crate::util::context::{ConfigValue as CV, Definition, Value};
6use serde::{de, de::IntoDeserializer};
7use std::collections::HashSet;
8use std::vec;
9
10/// Serde deserializer used to convert config values to a target type using
11/// [`GlobalContext::get`].
12#[derive(Clone)]
13pub(super) struct Deserializer<'gctx> {
14    pub(super) gctx: &'gctx GlobalContext,
15    /// The current key being deserialized.
16    pub(super) key: ConfigKey,
17    /// Whether or not this key part is allowed to be an inner table. For
18    /// example, `profile.dev.build-override` needs to check if
19    /// `CARGO_PROFILE_DEV_BUILD_OVERRIDE_` prefixes exist. But
20    /// `CARGO_BUILD_TARGET` should not check for prefixes because it would
21    /// collide with `CARGO_BUILD_TARGET_DIR`. See `ConfigMapAccess` for
22    /// details.
23    pub(super) env_prefix_ok: bool,
24}
25
26macro_rules! deserialize_method {
27    ($method:ident, $visit:ident, $getter:ident) => {
28        fn $method<V>(self, visitor: V) -> Result<V::Value, Self::Error>
29        where
30            V: de::Visitor<'de>,
31        {
32            let v = self
33                .gctx
34                .$getter(&self.key)?
35                .ok_or_else(|| ConfigError::missing(&self.key))?;
36            let Value { val, definition } = v;
37            let res: Result<V::Value, ConfigError> = visitor.$visit(val);
38            res.map_err(|e| e.with_key_context(&self.key, Some(definition)))
39        }
40    };
41}
42
43impl<'de, 'gctx> de::Deserializer<'de> for Deserializer<'gctx> {
44    type Error = ConfigError;
45
46    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
47    where
48        V: de::Visitor<'de>,
49    {
50        let cv = self.gctx.get_cv_with_env(&self.key)?;
51        if let Some(cv) = cv {
52            let res: (Result<V::Value, ConfigError>, Definition) = match cv {
53                CV::Integer(i, def) => (visitor.visit_i64(i), def),
54                CV::String(s, def) => (visitor.visit_string(s), def),
55                CV::List(_, def) => (visitor.visit_seq(ConfigSeqAccess::new(self.clone())?), def),
56                CV::Table(_, def) => (
57                    visitor.visit_map(ConfigMapAccess::new_map(self.clone())?),
58                    def,
59                ),
60                CV::Boolean(b, def) => (visitor.visit_bool(b), def),
61            };
62            let (res, def) = res;
63            return res.map_err(|e| e.with_key_context(&self.key, Some(def)));
64        }
65
66        // The effect here is the same as in `deserialize_option`.
67        if self.gctx.has_key(&self.key, self.env_prefix_ok)? {
68            return visitor.visit_some(self);
69        }
70
71        Err(ConfigError::missing(&self.key))
72    }
73
74    deserialize_method!(deserialize_bool, visit_bool, get_bool);
75    deserialize_method!(deserialize_i8, visit_i64, get_integer);
76    deserialize_method!(deserialize_i16, visit_i64, get_integer);
77    deserialize_method!(deserialize_i32, visit_i64, get_integer);
78    deserialize_method!(deserialize_i64, visit_i64, get_integer);
79    deserialize_method!(deserialize_u8, visit_i64, get_integer);
80    deserialize_method!(deserialize_u16, visit_i64, get_integer);
81    deserialize_method!(deserialize_u32, visit_i64, get_integer);
82    deserialize_method!(deserialize_u64, visit_i64, get_integer);
83    deserialize_method!(deserialize_string, visit_string, get_string_priv);
84
85    fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
86    where
87        V: de::Visitor<'de>,
88    {
89        if self.gctx.has_key(&self.key, self.env_prefix_ok)? {
90            visitor.visit_some(self)
91        } else {
92            // Treat missing values as `None`.
93            visitor.visit_none()
94        }
95    }
96
97    fn deserialize_struct<V>(
98        self,
99        name: &'static str,
100        fields: &'static [&'static str],
101        visitor: V,
102    ) -> Result<V::Value, Self::Error>
103    where
104        V: de::Visitor<'de>,
105    {
106        // Match on the magical struct name/field names that are passed in to
107        // detect when we're deserializing `Value<T>`.
108        //
109        // See more comments in `value.rs` for the protocol used here.
110        if name == value::NAME && fields == value::FIELDS {
111            return visitor.visit_map(ValueDeserializer::new(self)?);
112        }
113        visitor.visit_map(ConfigMapAccess::new_struct(self, fields)?)
114    }
115
116    fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error>
117    where
118        V: de::Visitor<'de>,
119    {
120        visitor.visit_map(ConfigMapAccess::new_map(self)?)
121    }
122
123    fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
124    where
125        V: de::Visitor<'de>,
126    {
127        visitor.visit_seq(ConfigSeqAccess::new(self)?)
128    }
129
130    fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value, Self::Error>
131    where
132        V: de::Visitor<'de>,
133    {
134        visitor.visit_seq(ConfigSeqAccess::new(self)?)
135    }
136
137    fn deserialize_tuple_struct<V>(
138        self,
139        _name: &'static str,
140        _len: usize,
141        visitor: V,
142    ) -> Result<V::Value, Self::Error>
143    where
144        V: de::Visitor<'de>,
145    {
146        visitor.visit_seq(ConfigSeqAccess::new(self)?)
147    }
148
149    fn deserialize_newtype_struct<V>(
150        self,
151        name: &'static str,
152        visitor: V,
153    ) -> Result<V::Value, Self::Error>
154    where
155        V: de::Visitor<'de>,
156    {
157        if name == "StringList" {
158            let vals = self.gctx.get_list_or_string(&self.key)?;
159            let vals: Vec<String> = vals.into_iter().map(|vd| vd.0).collect();
160            visitor.visit_newtype_struct(vals.into_deserializer())
161        } else {
162            visitor.visit_newtype_struct(self)
163        }
164    }
165
166    fn deserialize_enum<V>(
167        self,
168        _name: &'static str,
169        _variants: &'static [&'static str],
170        visitor: V,
171    ) -> Result<V::Value, Self::Error>
172    where
173        V: de::Visitor<'de>,
174    {
175        let value = self
176            .gctx
177            .get_string_priv(&self.key)?
178            .ok_or_else(|| ConfigError::missing(&self.key))?;
179
180        let Value { val, definition } = value;
181        visitor
182            .visit_enum(val.into_deserializer())
183            .map_err(|e: ConfigError| e.with_key_context(&self.key, Some(definition)))
184    }
185
186    // These aren't really supported, yet.
187    serde::forward_to_deserialize_any! {
188        f32 f64 char str bytes
189        byte_buf unit unit_struct
190        identifier ignored_any
191    }
192}
193
194struct ConfigMapAccess<'gctx> {
195    de: Deserializer<'gctx>,
196    /// The fields that this map should deserialize.
197    fields: Vec<KeyKind>,
198    /// Current field being deserialized.
199    field_index: usize,
200}
201
202#[derive(Debug, PartialEq, Eq, Hash)]
203enum KeyKind {
204    Normal(String),
205    CaseSensitive(String),
206}
207
208impl<'gctx> ConfigMapAccess<'gctx> {
209    fn new_map(de: Deserializer<'gctx>) -> Result<ConfigMapAccess<'gctx>, ConfigError> {
210        let mut fields = Vec::new();
211        if let Some(mut v) = de.gctx.get_table(&de.key)? {
212            // `v: Value<HashMap<String, CV>>`
213            for (key, _value) in v.val.drain() {
214                fields.push(KeyKind::CaseSensitive(key));
215            }
216        }
217        if de.gctx.cli_unstable().advanced_env {
218            // `CARGO_PROFILE_DEV_PACKAGE_`
219            let env_prefix = format!("{}_", de.key.as_env_key());
220            for env_key in de.gctx.env_keys() {
221                // `CARGO_PROFILE_DEV_PACKAGE_bar_OPT_LEVEL = 3`
222                if let Some(rest) = env_key.strip_prefix(&env_prefix) {
223                    // `rest = bar_OPT_LEVEL`
224                    let part = rest.splitn(2, '_').next().unwrap();
225                    // `part = "bar"`
226                    fields.push(KeyKind::CaseSensitive(part.to_string()));
227                }
228            }
229        }
230        Ok(ConfigMapAccess {
231            de,
232            fields,
233            field_index: 0,
234        })
235    }
236
237    fn new_struct(
238        de: Deserializer<'gctx>,
239        given_fields: &'static [&'static str],
240    ) -> Result<ConfigMapAccess<'gctx>, ConfigError> {
241        let table = de.gctx.get_table(&de.key)?;
242
243        // Assume that if we're deserializing a struct it exhaustively lists all
244        // possible fields on this key that we're *supposed* to use, so take
245        // this opportunity to warn about any keys that aren't recognized as
246        // fields and warn about them.
247        if let Some(v) = table.as_ref() {
248            let unused_keys = v
249                .val
250                .iter()
251                .filter(|(k, _v)| !given_fields.iter().any(|gk| gk == k));
252            for (unused_key, unused_value) in unused_keys {
253                de.gctx.shell().warn(format!(
254                    "unused config key `{}.{}` in `{}`",
255                    de.key,
256                    unused_key,
257                    unused_value.definition()
258                ))?;
259            }
260        }
261
262        let mut fields = HashSet::new();
263
264        // If the caller is interested in a field which we can provide from
265        // the environment, get it from there.
266        for field in given_fields {
267            let mut field_key = de.key.clone();
268            field_key.push(field);
269            for env_key in de.gctx.env_keys() {
270                let Some(nested_field) = env_key.strip_prefix(field_key.as_env_key()) else {
271                    continue;
272                };
273                // This distinguishes fields that share the same prefix.
274                // For example, when env_key is UNSTABLE_GITOXIDE_FETCH
275                // and field_key is UNSTABLE_GIT, the field shouldn't be
276                // added because `unstable.gitoxide.fetch` doesn't
277                // belong to `unstable.git` struct.
278                if nested_field.is_empty() || nested_field.starts_with('_') {
279                    fields.insert(KeyKind::Normal(field.to_string()));
280                }
281            }
282        }
283
284        // Add everything from the config table we're interested in that we
285        // haven't already provided via an environment variable
286        if let Some(v) = table {
287            for key in v.val.keys() {
288                fields.insert(KeyKind::Normal(key.clone()));
289            }
290        }
291
292        Ok(ConfigMapAccess {
293            de,
294            fields: fields.into_iter().collect(),
295            field_index: 0,
296        })
297    }
298}
299
300impl<'de, 'gctx> de::MapAccess<'de> for ConfigMapAccess<'gctx> {
301    type Error = ConfigError;
302
303    fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>
304    where
305        K: de::DeserializeSeed<'de>,
306    {
307        if self.field_index >= self.fields.len() {
308            return Ok(None);
309        }
310        let field = match &self.fields[self.field_index] {
311            KeyKind::Normal(s) | KeyKind::CaseSensitive(s) => s.as_str(),
312        };
313        seed.deserialize(field.into_deserializer()).map(Some)
314    }
315
316    fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>
317    where
318        V: de::DeserializeSeed<'de>,
319    {
320        let field = &self.fields[self.field_index];
321        self.field_index += 1;
322        // Set this as the current key in the deserializer.
323        let field = match field {
324            KeyKind::Normal(field) => {
325                self.de.key.push(field);
326                field
327            }
328            KeyKind::CaseSensitive(field) => {
329                self.de.key.push_sensitive(field);
330                field
331            }
332        };
333        // Env vars that are a prefix of another with a dash/underscore cannot
334        // be supported by our serde implementation, so check for them here.
335        // Example:
336        //     CARGO_BUILD_TARGET
337        //     CARGO_BUILD_TARGET_DIR
338        // or
339        //     CARGO_PROFILE_DEV_DEBUG
340        //     CARGO_PROFILE_DEV_DEBUG_ASSERTIONS
341        // The `deserialize_option` method does not know the type of the field.
342        // If the type is an Option<struct> (like
343        // `profile.dev.build-override`), then it needs to check for env vars
344        // starting with CARGO_FOO_BAR_. This is a problem for keys like
345        // CARGO_BUILD_TARGET because checking for a prefix would incorrectly
346        // match CARGO_BUILD_TARGET_DIR. `deserialize_option` would have no
347        // choice but to call `visit_some()` which would then fail if
348        // CARGO_BUILD_TARGET isn't set. So we check for these prefixes and
349        // disallow them here.
350        let env_prefix = format!("{}_", field).replace('-', "_");
351        let env_prefix_ok = !self.fields.iter().any(|field| {
352            let field = match field {
353                KeyKind::Normal(s) | KeyKind::CaseSensitive(s) => s.as_str(),
354            };
355            field.replace('-', "_").starts_with(&env_prefix)
356        });
357
358        let result = seed
359            .deserialize(Deserializer {
360                gctx: self.de.gctx,
361                key: self.de.key.clone(),
362                env_prefix_ok,
363            })
364            .map_err(|e| {
365                if !e.is_missing_field() {
366                    return e;
367                }
368                e.with_key_context(
369                    &self.de.key,
370                    self.de
371                        .gctx
372                        .get_cv_with_env(&self.de.key)
373                        .ok()
374                        .and_then(|cv| cv.map(|cv| cv.get_definition().clone())),
375                )
376            });
377        self.de.key.pop();
378        result
379    }
380}
381
382struct ConfigSeqAccess {
383    list_iter: vec::IntoIter<(String, Definition)>,
384}
385
386impl ConfigSeqAccess {
387    fn new(de: Deserializer<'_>) -> Result<ConfigSeqAccess, ConfigError> {
388        let mut res = Vec::new();
389        if let Some(v) = de.gctx._get_list(&de.key)? {
390            res.extend(v.val);
391        }
392
393        de.gctx.get_env_list(&de.key, &mut res)?;
394
395        Ok(ConfigSeqAccess {
396            list_iter: res.into_iter(),
397        })
398    }
399}
400
401impl<'de> de::SeqAccess<'de> for ConfigSeqAccess {
402    type Error = ConfigError;
403
404    fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
405    where
406        T: de::DeserializeSeed<'de>,
407    {
408        match self.list_iter.next() {
409            // TODO: add `def` to error?
410            Some((value, def)) => {
411                // This might be a String or a Value<String>.
412                // ValueDeserializer will handle figuring out which one it is.
413                let maybe_value_de = ValueDeserializer::new_with_string(value, def);
414                seed.deserialize(maybe_value_de).map(Some)
415            }
416            None => Ok(None),
417        }
418    }
419}
420
421/// This is a deserializer that deserializes into a `Value<T>` for
422/// configuration.
423///
424/// This is a special deserializer because it deserializes one of its struct
425/// fields into the location that this configuration value was defined in.
426///
427/// See more comments in `value.rs` for the protocol used here.
428struct ValueDeserializer<'gctx> {
429    hits: u32,
430    definition: Definition,
431    /// The deserializer, used to actually deserialize a Value struct.
432    /// This is `None` if deserializing a string.
433    de: Option<Deserializer<'gctx>>,
434    /// A string value to deserialize.
435    ///
436    /// This is used for situations where you can't address a string via a
437    /// TOML key, such as a string inside an array. The `ConfigSeqAccess`
438    /// doesn't know if the type it should deserialize to is a `String` or
439    /// `Value<String>`, so `ValueDeserializer` needs to be able to handle
440    /// both.
441    str_value: Option<String>,
442}
443
444impl<'gctx> ValueDeserializer<'gctx> {
445    fn new(de: Deserializer<'gctx>) -> Result<ValueDeserializer<'gctx>, ConfigError> {
446        // Figure out where this key is defined.
447        let definition = {
448            let env = de.key.as_env_key();
449            let env_def = Definition::Environment(env.to_string());
450            match (de.gctx.env.contains_key(env), de.gctx.get_cv(&de.key)?) {
451                (true, Some(cv)) => {
452                    // Both, pick highest priority.
453                    if env_def.is_higher_priority(cv.definition()) {
454                        env_def
455                    } else {
456                        cv.definition().clone()
457                    }
458                }
459                (false, Some(cv)) => cv.definition().clone(),
460                // Assume it is an environment, even if the key is not set.
461                // This can happen for intermediate tables, like
462                // CARGO_FOO_BAR_* where `CARGO_FOO_BAR` is not set.
463                (_, None) => env_def,
464            }
465        };
466        Ok(ValueDeserializer {
467            hits: 0,
468            definition,
469            de: Some(de),
470            str_value: None,
471        })
472    }
473
474    fn new_with_string(s: String, definition: Definition) -> ValueDeserializer<'gctx> {
475        ValueDeserializer {
476            hits: 0,
477            definition,
478            de: None,
479            str_value: Some(s),
480        }
481    }
482}
483
484impl<'de, 'gctx> de::MapAccess<'de> for ValueDeserializer<'gctx> {
485    type Error = ConfigError;
486
487    fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>
488    where
489        K: de::DeserializeSeed<'de>,
490    {
491        self.hits += 1;
492        match self.hits {
493            1 => seed
494                .deserialize(value::VALUE_FIELD.into_deserializer())
495                .map(Some),
496            2 => seed
497                .deserialize(value::DEFINITION_FIELD.into_deserializer())
498                .map(Some),
499            _ => Ok(None),
500        }
501    }
502
503    fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>
504    where
505        V: de::DeserializeSeed<'de>,
506    {
507        // If this is the first time around we deserialize the `value` field
508        // which is the actual deserializer
509        if self.hits == 1 {
510            if let Some(de) = &self.de {
511                return seed
512                    .deserialize(de.clone())
513                    .map_err(|e| e.with_key_context(&de.key, Some(self.definition.clone())));
514            } else {
515                return seed
516                    .deserialize(self.str_value.as_ref().unwrap().clone().into_deserializer());
517            }
518        }
519
520        // ... otherwise we're deserializing the `definition` field, so we need
521        // to figure out where the field we just deserialized was defined at.
522        match &self.definition {
523            Definition::Path(path) => {
524                seed.deserialize(Tuple2Deserializer(0i32, path.to_string_lossy()))
525            }
526            Definition::Environment(env) => {
527                seed.deserialize(Tuple2Deserializer(1i32, env.as_str()))
528            }
529            Definition::Cli(path) => {
530                let s = path
531                    .as_ref()
532                    .map(|p| p.to_string_lossy())
533                    .unwrap_or_default();
534                seed.deserialize(Tuple2Deserializer(2i32, s))
535            }
536        }
537    }
538}
539
540// Deserializer is only implemented to handle deserializing a String inside a
541// sequence (like `Vec<String>` or `Vec<Value<String>>`). `Value<String>` is
542// handled by deserialize_struct, and the plain `String` is handled by all the
543// other functions here.
544impl<'de, 'gctx> de::Deserializer<'de> for ValueDeserializer<'gctx> {
545    type Error = ConfigError;
546
547    fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error>
548    where
549        V: de::Visitor<'de>,
550    {
551        visitor.visit_str(&self.str_value.expect("string expected"))
552    }
553
554    fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Self::Error>
555    where
556        V: de::Visitor<'de>,
557    {
558        visitor.visit_string(self.str_value.expect("string expected"))
559    }
560
561    fn deserialize_struct<V>(
562        self,
563        name: &'static str,
564        fields: &'static [&'static str],
565        visitor: V,
566    ) -> Result<V::Value, Self::Error>
567    where
568        V: de::Visitor<'de>,
569    {
570        // Match on the magical struct name/field names that are passed in to
571        // detect when we're deserializing `Value<T>`.
572        //
573        // See more comments in `value.rs` for the protocol used here.
574        if name == value::NAME && fields == value::FIELDS {
575            return visitor.visit_map(self);
576        }
577        unimplemented!("only strings and Value can be deserialized from a sequence");
578    }
579
580    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
581    where
582        V: de::Visitor<'de>,
583    {
584        visitor.visit_string(self.str_value.expect("string expected"))
585    }
586
587    fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
588    where
589        V: de::Visitor<'de>,
590    {
591        visitor.visit_unit()
592    }
593
594    serde::forward_to_deserialize_any! {
595        i8 i16 i32 i64
596        u8 u16 u32 u64
597        option
598        newtype_struct seq tuple tuple_struct map enum bool
599        f32 f64 char bytes
600        byte_buf unit unit_struct
601        identifier
602    }
603}
604
605/// A deserializer which takes two values and deserializes into a tuple of those
606/// two values. This is similar to types like `StrDeserializer` in upstream
607/// serde itself.
608struct Tuple2Deserializer<T, U>(T, U);
609
610impl<'de, T, U> de::Deserializer<'de> for Tuple2Deserializer<T, U>
611where
612    T: IntoDeserializer<'de, ConfigError>,
613    U: IntoDeserializer<'de, ConfigError>,
614{
615    type Error = ConfigError;
616
617    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, ConfigError>
618    where
619        V: de::Visitor<'de>,
620    {
621        struct SeqVisitor<T, U> {
622            first: Option<T>,
623            second: Option<U>,
624        }
625        impl<'de, T, U> de::SeqAccess<'de> for SeqVisitor<T, U>
626        where
627            T: IntoDeserializer<'de, ConfigError>,
628            U: IntoDeserializer<'de, ConfigError>,
629        {
630            type Error = ConfigError;
631            fn next_element_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>
632            where
633                K: de::DeserializeSeed<'de>,
634            {
635                if let Some(first) = self.first.take() {
636                    return seed.deserialize(first.into_deserializer()).map(Some);
637                }
638                if let Some(second) = self.second.take() {
639                    return seed.deserialize(second.into_deserializer()).map(Some);
640                }
641                Ok(None)
642            }
643        }
644
645        visitor.visit_seq(SeqVisitor {
646            first: Some(self.0),
647            second: Some(self.1),
648        })
649    }
650
651    serde::forward_to_deserialize_any! {
652        bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq
653        bytes byte_buf map struct option unit newtype_struct
654        ignored_any unit_struct tuple_struct tuple enum identifier
655    }
656}