Skip to content

Commit 021f167

Browse files
authored
docs(gctx): explain Value deserialization step-by-step (#16105)
### What does this PR try to resolve? Addressing <https://github.com/rust-lang/cargo/pull/16094/files#r2427236777> r? 0xPoe
2 parents 6b40446 + d7408c4 commit 021f167

File tree

1 file changed

+25
-15
lines changed

1 file changed

+25
-15
lines changed

src/cargo/util/context/value.rs

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,36 @@
99
//! from configuration, but also record where it was deserialized from when it
1010
//! was read.
1111
//!
12-
//! ## How `Value<T>` deserialization works
13-
//!
1412
//! Deserializing `Value<T>` is pretty special, and serde doesn't have built-in
1513
//! support for this operation. To implement this we extend serde's "data model"
1614
//! a bit. We configure deserialization of `Value<T>` to basically only work with
1715
//! our one deserializer using configuration.
1816
//!
19-
//! We define that `Value<T>` deserialization asks the deserializer for a very
20-
//! special [struct name](NAME) and [struct field names](FIELDS). In doing so,
21-
//! the deserializer will recognize this and synthesize a magical value for the
22-
//! `definition` field when we deserialize it. This protocol is how we're able
23-
//! to have a channel of information flowing from the configuration deserializer
24-
//! into the deserialization implementation here.
17+
//! ## How `Value<T>` deserialization works
18+
//!
19+
//! `Value<T>` uses a custom protocol to inject source location information
20+
//! into serde's deserialization process:
21+
//!
22+
//! **Magic identifiers**: `Value<T>::deserialize` requests a struct with special
23+
//! [name](NAME) and [field names](FIELDS) that use invalid Rust syntax to avoid
24+
//! conflicts. This signals to Cargo's deserializer that location tracking is needed.
25+
//!
26+
//! **Custom deserializer response**: When Cargo's deserializer sees these magic
27+
//! identifiers, it switches to `ValueDeserializer` (from the [`de`] module)
28+
//! instead of normal struct deserialization.
29+
//!
30+
//! **Two-field protocol**: `ValueDeserializer` presents exactly two fields
31+
//! through map visiting:
32+
//! * The actual value (deserialized normally)
33+
//! * The definition context (encoded as a `(u32, String)` tuple acting as a
34+
//! tagged union of [`Definition`] variants)
2535
//!
26-
//! You'll want to also check out the implementation of `ValueDeserializer` in
27-
//! the [`de`] module. Also note that the names below are intended to be invalid
28-
//! Rust identifiers to avoid conflicts with other valid structures.
36+
//! This allows `Value<T>` to capture both the deserialized data and where it
37+
//! came from.
2938
//!
30-
//! Finally the `definition` field is transmitted as a tuple of i32/string,
31-
//! which is effectively a tagged union of [`Definition`] itself. You should
32-
//! update both places here and in the impl of [`serde::de::MapAccess`] for
33-
//! `ValueDeserializer` when adding or modifying enum variants of [`Definition`].
39+
//! **Note**: When modifying [`Definition`] variants, be sure to update both
40+
//! the `Definition::deserialize` implementation here and the
41+
//! `MapAccess::next_value_seed` implementation in `ValueDeserializer`.
3442
//!
3543
//! [`de`]: crate::util::context::de
3644
@@ -55,6 +63,8 @@ pub struct Value<T> {
5563

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

66+
// The names below are intended to be invalid Rust identifiers
67+
// to avoid conflicts with other valid structures.
5868
pub(crate) const VALUE_FIELD: &str = "$__cargo_private_value";
5969
pub(crate) const DEFINITION_FIELD: &str = "$__cargo_private_definition";
6070
pub(crate) const NAME: &str = "$__cargo_private_Value";

0 commit comments

Comments
 (0)