Skip to content

Commit

Permalink
Add comments of Value<T>'s deserialization protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
alexcrichton committed Oct 8, 2019
1 parent 2387e1a commit e0667f5
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 0 deletions.
14 changes: 14 additions & 0 deletions src/cargo/util/config/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ impl<'de, 'config> de::Deserializer<'de> for Deserializer<'config> {
where
V: de::Visitor<'de>,
{
// Match on the magical struct name/field names that are passed in to
// detect when we're deserializing `Value<T>`.
//
// See more comments in `value.rs` for the protocol used here.
if name == value::NAME && fields == value::FIELDS {
return visitor.visit_map(ValueDeserializer { hits: 0, de: self });
}
Expand Down Expand Up @@ -340,6 +344,13 @@ impl<'de> de::SeqAccess<'de> for ConfigSeqAccess {
}
}

/// This is a deserializer that deserializes into a `Value<T>` for
/// configuration.
///
/// This is a special deserializer because it deserializes one of its struct
/// fields into the location that this configuration value was defined in.
///
/// See more comments in `value.rs` for the protocol used here.
struct ValueDeserializer<'config> {
hits: u32,
de: Deserializer<'config>,
Expand Down Expand Up @@ -396,6 +407,9 @@ impl<'de, 'config> de::MapAccess<'de> for ValueDeserializer<'config> {
}
}

/// A deserializer which takes two values and deserializes into a tuple of those
/// two values. This is similar to types like `StrDeserializer` in upstream
/// serde itself.
struct Tuple2Deserializer<T, U>(T, U);

impl<'de, T, U> de::Deserializer<'de> for Tuple2Deserializer<T, U>
Expand Down
33 changes: 33 additions & 0 deletions src/cargo/util/config/value.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,51 @@
//! Deserialization of a `Value<T>` type which tracks where it was deserialized
//! from.
//!
//! Often Cargo wants to report semantic error information or other sorts of
//! error information about configuration keys but it also may wish to indicate
//! as an error context where the key was defined as well (to help user
//! debugging). The `Value<T>` type here can be used to deserialize a `T` value
//! from configuration, but also record where it was deserialized from when it
//! was read.

use crate::util::config::Config;
use serde::de;
use std::fmt;
use std::marker;
use std::mem;
use std::path::{Path, PathBuf};

/// A type which can be deserialized as a configuration value which records
/// where it was deserialized from.
#[derive(Debug, PartialEq, Clone)]
pub struct Value<T> {
/// The inner value that was deserialized.
pub val: T,
/// The location where `val` was defined in configuration (e.g. file it was
/// defined in, env var etc).
pub definition: Definition,
}

pub type OptValue<T> = Option<Value<T>>;

// Deserializing `Value<T>` is pretty special, and serde doesn't have built-in
// support for this operation. To implement this we extend serde's "data model"
// a bit. We configure deserialization of `Value<T>` to basically only work with
// our one deserializer using configuration.
//
// We define that `Value<T>` deserialization asks the deserializer for a very
// special struct name and struct field names. In doing so the deserializer will
// recognize this and synthesize a magical value for the `definition` field when
// we deserialize it. This protocol is how we're able to have a channel of
// information flowing from the configuration deserializer into the
// deserialization implementation here.
//
// You'll want to also check out the implementation of `ValueDeserializer` in
// `de.rs`. Also note that the names below are intended to be invalid Rust
// identifiers to avoid how they might conflict with other valid structures.
// Finally the `definition` field is transmitted as a tuple of i32/string, which
// is effectively a tagged union of `Definition` itself.

pub(crate) const VALUE_FIELD: &str = "$__cargo_private_value";
pub(crate) const DEFINITION_FIELD: &str = "$__cargo_private_definition";
pub(crate) const NAME: &str = "$__cargo_private_Value";
Expand Down

0 comments on commit e0667f5

Please sign in to comment.