#
cargo/util/context/
value.rs

    1//! Deserialization of a `Value<T>` type which tracks where it was deserialized
    2//! from.
    3//!
    4//! Often Cargo wants to report semantic error information or other sorts of
    5//! error information about configuration keys but it also may wish to indicate
    6//! as an error context where the key was defined as well (to help user
    7//! debugging). The `Value<T>` type here can be used to deserialize a `T` value
    8//! from configuration, but also record where it was deserialized from when it
    9//! was read.
    10
    11use crate::util::context::GlobalContext;
    12use serde::de;
    13use std::cmp::Ordering;
    14use std::fmt;
    15use std::marker;
    16use std::mem;
    17use std::path::{Path, PathBuf};
    18
    19/// A type which can be deserialized as a configuration value which records
    20/// where it was deserialized from.
    21#[derive(Debug, PartialEq, Clone)]
    22pub struct Value<T> {
    23    /// The inner value that was deserialized.
    24    pub val: T,
    25    /// The location where `val` was defined in configuration (e.g. file it was
    26    /// defined in, env var etc).
    27    pub definition: Definition,
    28}
    29
    30pub type OptValue<T> = Option<Value<T>>;
    31
    32// Deserializing `Value<T>` is pretty special, and serde doesn't have built-in
    33// support for this operation. To implement this we extend serde's "data model"
    34// a bit. We configure deserialization of `Value<T>` to basically only work with
    35// our one deserializer using configuration.
    36//
    37// We define that `Value<T>` deserialization asks the deserializer for a very
    38// special struct name and struct field names. In doing so the deserializer will
    39// recognize this and synthesize a magical value for the `definition` field when
    40// we deserialize it. This protocol is how we're able to have a channel of
    41// information flowing from the configuration deserializer into the
    42// deserialization implementation here.
    43//
    44// You'll want to also check out the implementation of `ValueDeserializer` in
    45// `de.rs`. Also note that the names below are intended to be invalid Rust
    46// identifiers to avoid how they might conflict with other valid structures.
    47// Finally the `definition` field is transmitted as a tuple of i32/string, which
    48// is effectively a tagged union of `Definition` itself.
    49
    50pub(crate) const VALUE_FIELD: &str = "$__cargo_private_value";
    51pub(crate) const DEFINITION_FIELD: &str = "$__cargo_private_definition";
    52pub(crate) const NAME: &str = "$__cargo_private_Value";
    53pub(crate) static FIELDS: [&str; 2] = [VALUE_FIELD, DEFINITION_FIELD];
    54
    55/// Location where a config value is defined.
    56#[derive(Clone, Debug, Eq)]
    57pub enum Definition {
    58    /// Defined in a `.cargo/config`, includes the path to the file.
    59    Path(PathBuf),
    60    /// Defined in an environment variable, includes the environment key.
    61    Environment(String),
    62    /// Passed in on the command line.
    63    /// A path is attached when the config value is a path to a config file.
    64    Cli(Option<PathBuf>),
    65}
    66
    67impl PartialOrd for Definition {
    68    fn partial_cmp(&self, other: &Definition) -> Option<Ordering> {
    69        Some(self.cmp(other))
    70    }
    71}
    72
    73impl Ord for Definition {
    74    fn cmp(&self, other: &Definition) -> Ordering {
    75        if mem::discriminant(self) == mem::discriminant(other) {
    76            Ordering::Equal
    77        } else if self.is_higher_priority(other) {
    78            Ordering::Greater
    79        } else {
    80            Ordering::Less
    81        }
    82    }
    83}
    84
    85impl Definition {
    86    /// Root directory where this is defined.
    87    ///
    88    /// If from a file, it is the directory above `.cargo/config`.
    89    /// CLI and env are the current working directory.
    90    pub fn root<'a>(&'a self, gctx: &'a GlobalContext) -> &'a Path {
    91        match self {
    92            Definition::Path(p) | Definition::Cli(Some(p)) => p.parent().unwrap().parent().unwrap(),
    93            Definition::Environment(_) | Definition::Cli(None) => gctx.cwd(),
    94        }
    95    }
    96
    97    /// Returns true if self is a higher priority to other.
    98    ///
    99    /// CLI is preferred over environment, which is preferred over files.
    100    pub fn is_higher_priority(&self, other: &Definition) -> bool {
    101        matches!(
    102            (self, other),
    103            (Definition::Cli(_), Definition::Environment(_))
    104                | (Definition::Cli(_), Definition::Path(_))
    105                | (Definition::Environment(_), Definition::Path(_))
    106        )
    107    }
    108}
    109
    110impl PartialEq for Definition {
    111    fn eq(&self, other: &Definition) -> bool {
    112        // configuration values are equivalent no matter where they're defined,
    113        // but they need to be defined in the same location. For example if
    114        // they're defined in the environment that's different than being
    115        // defined in a file due to path interpretations.
    116        mem::discriminant(self) == mem::discriminant(other)
    117    }
    118}
    119
    120impl fmt::Display for Definition {
    121    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    122        match self {
    123            Definition::Path(p) | Definition::Cli(Some(p)) => p.display().fmt(f),
    124            Definition::Environment(key) => write!(f, "environment variable `{}`", key),
    125            Definition::Cli(None) => write!(f, "--config cli option"),
    126        }
    127    }
    128}
    129
    130impl<'de, T> de::Deserialize<'de> for Value<T>
    131where
    132    T: de::Deserialize<'de>,
    133{
    134    fn deserialize<D>(deserializer: D) -> Result<Value<T>, D::Error>
    135    where
    136        D: de::Deserializer<'de>,
    137    {
    138        struct ValueVisitor<T> {
    139            _marker: marker::PhantomData<T>,
    140        }
    141
    142        impl<'de, T> de::Visitor<'de> for ValueVisitor<T>
    143        where
    144            T: de::Deserialize<'de>,
    145        {
    146            type Value = Value<T>;
    147
    148            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
    149                formatter.write_str("a value")
    150            }
    151
    152            fn visit_map<V>(self, mut visitor: V) -> Result<Value<T>, V::Error>
    153            where
    154                V: de::MapAccess<'de>,
    155            {
    156                let value = visitor.next_key::<ValueKey>()?;
    157                if value.is_none() {
    158                    return Err(de::Error::custom("value not found"));
    159                }
    160                let val: T = visitor.next_value()?;
    161
    162                let definition = visitor.next_key::<DefinitionKey>()?;
    163                if definition.is_none() {
    164                    return Err(de::Error::custom("definition not found"));
    165                }
    166                let definition: Definition = visitor.next_value()?;
    167                Ok(Value { val, definition })
    168            }
    169        }
    170
    171        deserializer.deserialize_struct(
    172            NAME,
    173            &FIELDS,
    174            ValueVisitor {
    175                _marker: marker::PhantomData,
    176            },
    177        )
    178    }
    179}
    180
    181struct FieldVisitor {
    182    expected: &'static str,
    183}
    184
    185impl<'de> de::Visitor<'de> for FieldVisitor {
    186    type Value = ();
    187
    188    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
    189        formatter.write_str("a valid value field")
    190    }
    191
    192    fn visit_str<E>(self, s: &str) -> Result<(), E>
    193    where
    194        E: de::Error,
    195    {
    196        if s == self.expected {
    197            Ok(())
    198        } else {
    199            Err(de::Error::custom("expected field with custom name"))
    200        }
    201    }
    202}
    203
    204struct ValueKey;
    205
    206impl<'de> de::Deserialize<'de> for ValueKey {
    207    fn deserialize<D>(deserializer: D) -> Result<ValueKey, D::Error>
    208    where
    209        D: de::Deserializer<'de>,
    210    {
    211        deserializer.deserialize_identifier(FieldVisitor {
    212            expected: VALUE_FIELD,
    213        })?;
    214        Ok(ValueKey)
    215    }
    216}
    217
    218struct DefinitionKey;
    219
    220impl<'de> de::Deserialize<'de> for DefinitionKey {
    221    fn deserialize<D>(deserializer: D) -> Result<DefinitionKey, D::Error>
    222    where
    223        D: de::Deserializer<'de>,
    224    {
    225        deserializer.deserialize_identifier(FieldVisitor {
    226            expected: DEFINITION_FIELD,
    227        })?;
    228        Ok(DefinitionKey)
    229    }
    230}
    231
    232impl<'de> de::Deserialize<'de> for Definition {
    233    fn deserialize<D>(deserializer: D) -> Result<Definition, D::Error>
    234    where
    235        D: de::Deserializer<'de>,
    236    {
    237        let (discr, value) = <(u32, String)>::deserialize(deserializer)?;
    238        match discr {
    239            0 => Ok(Definition::Path(value.into())),
    240            1 => Ok(Definition::Environment(value)),
    241            2 => {
    242                let path = (value.len() > 0).then_some(value.into());
    243                Ok(Definition::Cli(path))
    244            }
    245            _ => panic!("unexpected discriminant {discr} value {value}"),
    246        }
    247    }
    248}