1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
//! De-serialize and re-serialize a type while preserving ignored fields.
//!
//! Sometimes you may wish to preserve the ignored fields of something you are deserializing.
//! If you are in controll of the type, you could make use of the `#[serde(flatten)]` attribute:
//! ```
//! # fn main() -> Result<(), Box<dyn std::error::Error>>{
//! # use assert2::assert;
//! #
//! #[derive(serde::Deserialize, serde::Serialize)]
//! struct Thing {
//! name: String,
//!
//! #[serde(flatten)]
//! extra_fields: serde_yaml::Mapping,
//! }
//! #
//! # let thing: Thing = serde_yaml::from_str("
//! # name: Turbo Encabulator
//! # base_plate:
//! # prefabulated: true
//! # material: aluminite
//! # casing: malleable logarithmic
//! # ")?;
//! #
//! # assert!(thing.name == "Turbo Encabulator");
//! # assert!(thing.extra_fields["base_plate"]["prefabulated"] == true);
//! # assert!(thing.extra_fields["base_plate"]["material"] == "aluminite");
//! # assert!(thing.extra_fields["casing"] == "malleable logarithmic");
//! # Ok(())
//! # }
//! ```
//!
//! This crate can help you if you are *not* in control of the type.
//! You can wrap the type in [`PreserveIgnoredFields`]:
//!
//! ```
//! # fn main() -> Result<(), Box<dyn std::error::Error>>{
//! # use assert2::assert;
//! use serde_ignored_fields::PreserveIgnoredFields;
//!
//! #[derive(serde::Deserialize, serde::Serialize)]
//! struct Thing {
//! name: String,
//! }
//!
//! let thing: PreserveIgnoredFields<Thing, serde_yaml::Mapping> = serde_yaml::from_str("
//! name: Turbo Encabulator
//! base_plate:
//! prefabulated: true
//! material: aluminite
//! casing: malleable logarithmic
//! ")?;
//!
//! assert!(thing.value.name == "Turbo Encabulator");
//! assert!(thing.ignored_fields["base_plate"]["prefabulated"] == true);
//! assert!(thing.ignored_fields["base_plate"]["material"] == "aluminite");
//! assert!(thing.ignored_fields["casing"] == "malleable logarithmic");
//! # Ok(())
//! # }
//! ```
//!
//! If you enable the `schemars` feature, [`PreserveIgnoredFields<T, U>`] implements the [`schemars::JsonSchema`] trait.
//! It forwards directly to the [`schemars::JsonSchema`] implementation of `T`.
//!
//! # Limitations
//! Because `serde` does not provide first class support for capturing ignored fields, there are some limitations.
//!
//! ## Self-describing format
//! First, [`PreserveIgnoredFields`] only works with a self-describing format such as JSON, YAML or TOML.
//! This should not come as a surprise, and will not be a real limitation in practise
//! (how can you have ignored fields if the data format doesn't tell you what the fields are?).
//!
//! In [`serde`] terms: the [`serde::Deserializer`] must support [`serde::Deserializer::deserialize_any()`].
//!
//! ## Serialize/Deserialize implementations
//! Secondly, the type `T` being (de)serialized must be represented as a key/value map,
//! and it must call [`serde::Deserializer::deserialize_ignored_any()`] to deserialize ignored fields.
//! It must not produce an error when encountering an unknown field (so the type must not use `#[serde(deny_unknown_fields)]`).
//!
//! In particular, this means that it will not work for *externally* tagged enums, *internally* tagged enums and *untagged* enums.
//! Externally tagged enums are not always serialized as key/value maps (the serialization format controls their layout).
//! Internally and untagged enums have to look at fields before knowing which of the fields are actually going to be ignored.
//! This crate *does* work with adjectently tagged enums.
//!
//! It also means that it will not work for types that first deserialize into something like [`serde_json::Value`] before processing the value further.
//! When deserialized, the [`serde_json::Value`] uses all fields.
//! The next processing step may discard them again, but there is no way for [`PreserveIgnoredFields`] to know about this.
//!
//! In summary:
//! Using [`PreserveIgnoredFields`] with structs that use the standard serde derive macros from [`serde`] will work, as long as you did not use `#[serde(deny_unknown_fields)]`.
//! Using it with enums that use the standard derive macros will only work if they are *adjectently tagged* (they have a serde `tag = "..."` *and* `content = "..."` attribute).
/// Wrapper to preserve ignored fields.
///
/// The wrapped type is stored in the [value][Self::value] field.
/// Ignored fields are stored in the [`ignored_fields`][Self::ignored_fields] field.
///
/// The `IgnoredFields` type has to implement [`DeserializeIgnoredFields`] for this type to implement [`serde::Deserialize`],
/// and it has to implement [`SerializeIgnoredFields`] for this type to implement [`serde::Serialize`].
///
/// Be sure the read the [main library documentation](crate) about the limitations.
///
/// If you enable the `schemars` feature, this type implements the [`schemars::JsonSchema`] trait.
/// The implementation forwards directly to the implementation of `T`.
/// Trait for types that can collect ignored fields during deserialization.
/// Trait for types that can be used to re-serialize ignored fields.