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.
1011use 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};
1819/// 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.
24pub val: T,
25/// The location where `val` was defined in configuration (e.g. file it was
26 /// defined in, env var etc).
27pub definition: Definition,
28}
2930pub type OptValue<T> = Option<Value<T>>;
3132// 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.
4950pub(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];
5455/// 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.
59Path(PathBuf),
60/// Defined in an environment variable, includes the environment key.
61Environment(String),
62/// Passed in on the command line.
63 /// A path is attached when the config value is a path to a config file.
64Cli(Option<PathBuf>),
65}
6667impl PartialOrd for Definition {
68fn partial_cmp(&self, other: &Definition) -> Option<Ordering> {
69Some(self.cmp(other))
70 }
71}
7273impl Ord for Definition {
74fn cmp(&self, other: &Definition) -> Ordering {
75if 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}
8485impl 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.
90pub fn root<'a>(&'a self, gctx: &'a GlobalContext) -> &'a Path {
91match self {
92 Definition::Path(p) | Definition::Cli(Some(p)) => p.parent().unwrap().parent().unwrap(),
93 Definition::Environment(_) | Definition::Cli(None) => gctx.cwd(),
94 }
95 }
9697/// Returns true if self is a higher priority to other.
98 ///
99 /// CLI is preferred over environment, which is preferred over files.
100pub fn is_higher_priority(&self, other: &Definition) -> bool {
101matches!(
102 (self, other),
103 (Definition::Cli(_), Definition::Environment(_))
104 | (Definition::Cli(_), Definition::Path(_))
105 | (Definition::Environment(_), Definition::Path(_))
106 )
107 }
108}
109110impl PartialEq for Definition {
111fn 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.
116mem::discriminant(self) == mem::discriminant(other)
117 }
118}
119120impl fmt::Display for Definition {
121fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122match 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}
129130impl<'de, T> de::Deserialize<'de> for Value<T>
131where
132T: de::Deserialize<'de>,
133{
134fn deserialize<D>(deserializer: D) -> Result<Value<T>, D::Error>
135where
136D: de::Deserializer<'de>,
137 {
138struct ValueVisitor<T> {
139 _marker: marker::PhantomData<T>,
140 }
141142impl<'de, T> de::Visitor<'de> for ValueVisitor<T>
143where
144T: de::Deserialize<'de>,
145 {
146type Value = Value<T>;
147148fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
149 formatter.write_str("a value")
150 }
151152fn visit_map<V>(self, mut visitor: V) -> Result<Value<T>, V::Error>
153where
154V: de::MapAccess<'de>,
155 {
156let value = visitor.next_key::<ValueKey>()?;
157if value.is_none() {
158return Err(de::Error::custom("value not found"));
159 }
160let val: T = visitor.next_value()?;
161162let definition = visitor.next_key::<DefinitionKey>()?;
163if definition.is_none() {
164return Err(de::Error::custom("definition not found"));
165 }
166let definition: Definition = visitor.next_value()?;
167Ok(Value { val, definition })
168 }
169 }
170171 deserializer.deserialize_struct(
172 NAME,
173&FIELDS,
174 ValueVisitor {
175 _marker: marker::PhantomData,
176 },
177 )
178 }
179}
180181struct FieldVisitor {
182 expected: &'static str,
183}
184185impl<'de> de::Visitor<'de> for FieldVisitor {
186type Value = ();
187188fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
189 formatter.write_str("a valid value field")
190 }
191192fn visit_str<E>(self, s: &str) -> Result<(), E>
193where
194E: de::Error,
195 {
196if s == self.expected {
197Ok(())
198 } else {
199Err(de::Error::custom("expected field with custom name"))
200 }
201 }
202}
203204struct ValueKey;
205206impl<'de> de::Deserialize<'de> for ValueKey {
207fn deserialize<D>(deserializer: D) -> Result<ValueKey, D::Error>
208where
209D: de::Deserializer<'de>,
210 {
211 deserializer.deserialize_identifier(FieldVisitor {
212 expected: VALUE_FIELD,
213 })?;
214Ok(ValueKey)
215 }
216}
217218struct DefinitionKey;
219220impl<'de> de::Deserialize<'de> for DefinitionKey {
221fn deserialize<D>(deserializer: D) -> Result<DefinitionKey, D::Error>
222where
223D: de::Deserializer<'de>,
224 {
225 deserializer.deserialize_identifier(FieldVisitor {
226 expected: DEFINITION_FIELD,
227 })?;
228Ok(DefinitionKey)
229 }
230}
231232impl<'de> de::Deserialize<'de> for Definition {
233fn deserialize<D>(deserializer: D) -> Result<Definition, D::Error>
234where
235D: de::Deserializer<'de>,
236 {
237let (discr, value) = <(u32, String)>::deserialize(deserializer)?;
238match discr {
2390 => Ok(Definition::Path(value.into())),
2401 => Ok(Definition::Environment(value)),
2412 => {
242let path = (value.len() > 0).then_some(value.into());
243Ok(Definition::Cli(path))
244 }
245_ => panic!("unexpected discriminant {discr} value {value}"),
246 }
247 }
248}