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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
use std::borrow::Cow;
use crate::{
ser::wrapper::{StructActions, StructFieldAction, StructFieldActions},
Case, StaticValue,
};
/// Inspect structs and modify their contents.
///
/// See [`Hooks::on_struct`](crate::ser::Hooks::on_struct),
/// [`Hooks::on_struct_variant`](crate::ser::Hooks::on_struct_variant).
pub struct StructScope {
struct_len: usize,
struct_name: &'static str,
struct_actions: StructActions,
field_actions: StructFieldActions,
}
impl StructScope {
pub(crate) fn new(struct_len: usize, struct_name: &'static str) -> Self {
Self {
struct_len,
struct_name,
field_actions: Default::default(),
struct_actions: StructActions {
serialize_as_map: false,
},
}
}
pub(crate) fn into_actions(self) -> (StructActions, StructFieldActions) {
(self.struct_actions, self.field_actions)
}
/// Returns the original number of fields in this struct.
///
/// The returned value is not affected by any retain or skip actions.
pub fn struct_len(&self) -> usize {
self.struct_len
}
/// Returns the name of the struct.
pub fn struct_name(&self) -> &'static str {
self.struct_name
}
/// Skips a field during serialization.
///
/// Runtime equivalent to `#[serde(skip)]` or `#[serde(skip_serializing)]`.
///
/// If the field is not found in the struct, [`HooksError::FieldNotFound`](crate::ser::HooksError::FieldNotFound)
/// is produced _after_ the struct is serialized. You can process or ignore this error in
/// [`Hooks::on_scope_error`](crate::ser::Hooks::on_scope_error).
///
/// Returns `self` to allow chaining calls.
pub fn skip_field(&mut self, key: impl Into<Cow<'static, str>>) -> &mut Self {
self.field_actions.push(StructFieldAction::Skip(key.into()));
self
}
/// Retains a field.
///
/// Calling this method switches processing to a 'retain' mode, in which
/// all not retained fields are skipped. You can retain multiple fields by
/// calling this method multiple times.
///
/// There is no equivalent in serde derive, but you can see this as a 'whitelist'
/// counterpart of `#[serde(skip)]`.
///
/// If the field is not found in the struct, [`HooksError::FieldNotFound`](crate::ser::HooksError::FieldNotFound)
/// is produced _after_ the struct is serialized. You can process or ignore this error in
/// [`Hooks::on_scope_error`](crate::ser::Hooks::on_scope_error).
///
/// Returns `self` to allow chaining calls.
pub fn retain_field(&mut self, key: impl Into<Cow<'static, str>>) -> &mut Self {
self.field_actions
.push(StructFieldAction::Retain(key.into()));
self
}
/// Rename a field.
///
/// The `key` refers to the original field key in the struct, even if [`rename_all_fields_case`](Self::rename_all_fields_case)
/// is called.
///
/// If you use serde's `#[derive(Serialize)]` and `#[serde(rename=...)]` or
/// `#[serde(rename_all=...)]`, you need to specify the field key as it will be *after* serde renaming.
///
/// If the field is not found in the struct, [`HooksError::FieldNotFound`](crate::ser::HooksError::FieldNotFound)
/// is produced _after_ the struct is serialized. You can process or ignore this error in
/// [`Hooks::on_scope_error`](crate::ser::Hooks::on_scope_error).
///
/// Serde expects field names to be known at compile time, and as such, to be static. Passing in a
/// borrowed `&'static str` for the new key name here fulfills this. However, passing in
/// an owned `String` leads to special handling described in [Static strings](crate::ser#static-strings).
///
/// Returns `self` to allow chaining calls.
pub fn rename_field(
&mut self,
key: impl Into<Cow<'static, str>>,
new_key: impl Into<Cow<'static, str>>,
) -> &mut Self {
self.field_actions
.push(StructFieldAction::Rename(key.into(), new_key.into()));
self
}
/// Rename a field according to the given case convention.
///
/// The `key` refers to the original field key in the struct, even if [`rename_all_fields_case`](Self::rename_all_fields_case)
/// is called.
///
/// If you use serde's `#[derive(Serialize)]` and `#[serde(rename=...)]` or
/// `#[serde(rename_all=...)]`, you need to specify the field key as it will be *after* serde renaming.
///
/// If the field is not found in the struct, [`HooksError::FieldNotFound`](crate::ser::HooksError::FieldNotFound)
/// is produced _after_ the struct is serialized. You can process or ignore this error in
/// [`Hooks::on_scope_error`](crate::ser::Hooks::on_scope_error).
///
/// The renaming will happen at runtime, which would (most likely) lead to an allocation of a new
/// String. It is thus more optimal to pass a static string literal into [`rename_field`](Self::rename_field) instead.
/// See also [Static strings](crate::ser#static-strings) for more info on special handing of strings in serde.
///
/// Returns `self` to allow chaining calls.
pub fn rename_field_case(
&mut self,
key: impl Into<Cow<'static, str>>,
case: impl Into<Case>,
) -> &mut Self {
let key = key.into();
let new_key = Case::cow_to_case(&key, case.into());
self.field_actions
.push(StructFieldAction::Rename(key, new_key));
self
}
/// Rename all structure fields according to the given case convention.
///
/// If specified multiple times, the last case convention is used.
///
/// Calling [`rename_field`](Self::rename_field) on specific fields will override
/// this case convention.
///
/// If you use serde's `#[derive(Serialize)]` and `#[serde(rename=...)]` or
/// `#[serde(rename_all=...)]`, those renames will be applied first. See [`Case`](crate::Case) for more information
/// and caveats of case conversion.
///
/// Serde expects field names to be known at compile time, and as such, to be static.
/// Renaming field names with a case convention will produce strings in runtime,
/// which leads to special handling described in [Static strings](crate::ser#static-strings).
///
/// Returns `self` to allow chaining calls.
pub fn rename_all_fields_case(&mut self, case: impl Into<Case>) -> &mut Self {
self.field_actions
.push(StructFieldAction::RenameAllCase(case.into()));
self
}
/// Replace a value for a field.
///
/// The passed in [`StaticValue`] can represent both primitive and compound value types.
///
/// Primitive values are copied, and are later fed to the serializer instead of the original
/// struct field values.
/// For compound values, only metadata is stored, therefore it's not possible to
/// serialize the actual values from the contents of [`StaticValue`]. Passing in a
/// compound value here would result in an
/// [`HooksError::ValueNotSerializable`](crate::ser::HooksError::ValueNotSerializable) error.
/// The trick to replace a compound value is to replace it in this scope with a primitive one
/// (e.g. a unit), subscribe to `on_value` hook, and replace the value there again with the
/// compound one.
///
/// The replacement value does not necessarily need to be of the same type as the
/// original value in the struct. E.g., you can replace an integer field with a string one.
/// Some serializers might expect the types to be following a schema, and fail the serialization
/// if the replacement value is of a wrong type.
///
/// If the field is not found in the struct, [`HooksError::FieldNotFound`](crate::ser::HooksError::FieldNotFound)
/// is produced _after_ the struct is serialized. You can process or ignore this error in
/// [`Hooks::on_scope_error`](crate::ser::Hooks::on_scope_error).
///
/// Returns `self` to allow chaining calls.
pub fn replace_value(
&mut self,
key: impl Into<Cow<'static, str>>,
new_value: impl Into<StaticValue>,
) -> &mut Self {
self.field_actions.push(StructFieldAction::ReplaceValue(
key.into(),
new_value.into(),
));
self
}
/// Serialize this struct as a map.
///
/// Calling this method makes the struct to be fed to the serializer as a map
/// with string keys.
///
/// Some serializers, e.g. `serde_json`, do not distinguish between maps
/// and structs and represent them the same way in the serialized output.
/// Others however, like `ron`, do represent structs and maps differently.
/// Some might not support maps where they expect structs.
///
/// You will receive an [`on_map`](crate::ser::Hooks::on_map) hook callback
/// at the same path before this struct is serialized as a map.
///
/// If you apply modifying actions to both the struct scope here, and to map scope
/// in `on_map` hook, the actions applied to the struct scope will have precedence.
/// It generally would lead to confusing effects and is not recommended. Pick one.
///
/// Returns `self` to allow chaining calls.
pub fn serialize_as_map(&mut self) -> &mut Self {
self.struct_actions.serialize_as_map = true;
self
}
/// Flatten a field into this structure.
///
/// Runtime equivalent to `#[serde(flatten)]`.
///
/// The field value must be of struct, struct variant or map type, otherwise
/// [`CannotFlattenUnsupportedDataType`](crate::ser::HooksError::CannotFlattenUnsupportedDataType)
/// is produced. You can process or ignore this error in
/// [`Hooks::on_scope_error`](crate::ser::Hooks::on_scope_error).
///
/// This removes one level of hierarchy for the field, adding the struct fields
/// (map entries) from the field value straight into this struct.
///
/// Flattening any field causes this struct to be serialized as a map with
/// no length hint to the serializer. Some serializers do not support this.
/// See [`serialize_as_map`](Self::serialize_as_map) for more details and implications.
///
/// If the field is not found in the struct, [`HooksError::FieldNotFound`](crate::ser::HooksError::FieldNotFound)
/// is produced _after_ the struct is serialized. You can process or ignore this error in
/// [`Hooks::on_scope_error`](crate::ser::Hooks::on_scope_error).
///
/// Returns `self` to allow chaining calls.
pub fn flatten_field(&mut self, key: impl Into<Cow<'static, str>>) -> &mut Self {
self.field_actions
.push(StructFieldAction::Flatten(key.into()));
self
}
}