1use serde::{Deserialize, Deserializer, Serialize, Serializer};
9
10use std::{fmt, hash, ops::Deref};
11
12#[derive(Debug, Clone, PartialEq, Eq, Hash)]
75pub struct Forgettable<T>(Option<T>);
76
77impl<T> Default for Forgettable<T> {
78 fn default() -> Self {
80 Forgettable(None)
81 }
82}
83
84impl<T> From<T> for Forgettable<T> {
85 fn from(value: T) -> Self {
86 Forgettable(Some(value))
87 }
88}
89
90impl<T> Forgettable<T> {
91 pub fn new(value: T) -> Self {
93 Forgettable(Some(value))
94 }
95
96 pub fn forgotten() -> Self {
98 Forgettable(None)
99 }
100
101 pub fn value(&self) -> Option<ForgettableRef<'_, T>> {
106 self.0.as_ref().map(ForgettableRef)
107 }
108
109 pub fn is_set(&self) -> bool {
111 self.0.is_some()
112 }
113
114 pub fn is_forgotten(&self) -> bool {
116 self.0.is_none()
117 }
118}
119
120impl<T: Serialize> Forgettable<T> {
121 #[doc(hidden)]
124 pub fn __extract_payload_value(&self) -> Option<serde_json::Value> {
125 self.0
126 .as_ref()
127 .map(|v| serde_json::to_value(v).expect("Failed to serialize forgettable field"))
128 }
129}
130
131impl<T: Serialize> Serialize for Forgettable<T> {
132 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
133 serializer.serialize_none()
134 }
135}
136
137impl<'de, T: Deserialize<'de>> Deserialize<'de> for Forgettable<T> {
138 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
139 let value = Option::<T>::deserialize(deserializer)?;
140 match value {
141 Some(v) => Ok(Forgettable(Some(v))),
142 None => Ok(Forgettable(None)),
143 }
144 }
145}
146
147#[cfg(feature = "json-schema")]
148impl<T: schemars::JsonSchema> schemars::JsonSchema for Forgettable<T> {
149 fn inline_schema() -> bool {
150 Option::<T>::inline_schema()
151 }
152
153 fn schema_name() -> std::borrow::Cow<'static, str> {
154 Option::<T>::schema_name()
155 }
156
157 fn schema_id() -> std::borrow::Cow<'static, str> {
158 Option::<T>::schema_id()
159 }
160
161 fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
162 Option::<T>::json_schema(generator)
163 }
164}
165
166pub struct ForgettableRef<'a, T>(&'a T);
172
173impl<T: fmt::Debug> fmt::Debug for ForgettableRef<'_, T> {
174 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
175 self.0.fmt(f)
176 }
177}
178
179impl<T: fmt::Display> fmt::Display for ForgettableRef<'_, T> {
180 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181 self.0.fmt(f)
182 }
183}
184
185impl<T> Deref for ForgettableRef<'_, T> {
186 type Target = T;
187
188 fn deref(&self) -> &T {
189 self.0
190 }
191}
192
193impl<T: PartialEq> PartialEq<T> for ForgettableRef<'_, T> {
194 fn eq(&self, other: &T) -> bool {
195 self.0 == other
196 }
197}
198
199impl<T: PartialEq> PartialEq for ForgettableRef<'_, T> {
200 fn eq(&self, other: &Self) -> bool {
201 self.0 == other.0
202 }
203}
204
205impl<T: Eq> Eq for ForgettableRef<'_, T> {}
206
207impl<T: hash::Hash> hash::Hash for ForgettableRef<'_, T> {
208 fn hash<H: hash::Hasher>(&self, state: &mut H) {
209 self.0.hash(state);
210 }
211}
212
213#[doc(hidden)]
218pub fn inject_forgettable_payload(event_json: &mut serde_json::Value, payload: serde_json::Value) {
219 if let (Some(event_obj), serde_json::Value::Object(payload_obj)) =
220 (event_json.as_object_mut(), payload)
221 {
222 for (key, value) in payload_obj {
223 event_obj.insert(key, value);
224 }
225 }
226}
227
228#[cfg(test)]
229mod tests {
230 use super::*;
231 use proptest::prelude::*;
232
233 fn json_value() -> impl Strategy<Value = serde_json::Value> {
236 let scalar = prop_oneof![
237 Just(serde_json::Value::Null),
238 any::<bool>().prop_map(serde_json::Value::Bool),
239 any::<i64>().prop_map(serde_json::Value::from),
240 any::<f64>().prop_map(serde_json::Value::from),
241 ".{0,15}".prop_map(serde_json::Value::String),
242 ]
243 .boxed();
244 let nested = prop_oneof![
245 proptest::collection::vec(scalar.clone(), 0..4).prop_map(serde_json::Value::Array),
246 proptest::collection::vec((".{0,6}", scalar.clone()), 0..4).prop_map(|pairs| {
247 let mut m = serde_json::Map::new();
248 for (k, v) in pairs {
249 m.insert(k, v);
250 }
251 serde_json::Value::Object(m)
252 },),
253 ];
254 prop_oneof![scalar, nested]
255 }
256
257 #[test]
258 fn serialize_set_emits_null() {
259 let value: Forgettable<String> = Forgettable::new("Alice".to_string());
260 let json = serde_json::to_value(&value).unwrap();
261 assert_eq!(json, serde_json::json!(null));
262 }
263
264 #[test]
265 fn serialize_forgotten_emits_null() {
266 let value: Forgettable<String> = Forgettable::forgotten();
267 let json = serde_json::to_value(&value).unwrap();
268 assert_eq!(json, serde_json::json!(null));
269 }
270
271 #[test]
272 fn deserialize_value() {
273 let json = serde_json::json!("Alice");
274 let value: Forgettable<String> = serde_json::from_value(json).unwrap();
275 assert_eq!(value, Forgettable::new("Alice".to_string()));
276 }
277
278 #[test]
279 fn deserialize_null() {
280 let json = serde_json::json!(null);
281 let value: Forgettable<String> = serde_json::from_value(json).unwrap();
282 assert_eq!(value, Forgettable::forgotten());
283 }
284
285 #[test]
286 fn serialize_struct_with_forgettable_emits_null() {
287 #[derive(Serialize, Deserialize, Debug, PartialEq)]
288 struct Event {
289 #[serde(rename = "type")]
290 kind: String,
291 name: Forgettable<String>,
292 email: String,
293 }
294
295 let event = Event {
296 kind: "initialized".to_string(),
297 name: Forgettable::new("Alice".to_string()),
298 email: "alice@test.com".to_string(),
299 };
300 let json = serde_json::to_value(&event).unwrap();
301 assert_eq!(json["name"], serde_json::json!(null));
303 assert_eq!(json["email"], serde_json::json!("alice@test.com"));
304
305 let deserialized: Event = serde_json::from_value(json).unwrap();
307 assert_eq!(deserialized.name, Forgettable::forgotten());
308
309 let event_forgotten = Event {
311 kind: "initialized".to_string(),
312 name: Forgettable::forgotten(),
313 email: "alice@test.com".to_string(),
314 };
315 let json = serde_json::to_value(&event_forgotten).unwrap();
316 assert_eq!(json["name"], serde_json::json!(null));
317
318 let deserialized: Event = serde_json::from_value(json).unwrap();
319 assert_eq!(deserialized, event_forgotten);
320 }
321
322 #[test]
323 fn inject_payload() {
324 let mut json = serde_json::json!({
325 "type": "initialized",
326 "id": "uuid",
327 "name": null,
328 "email": "alice@test.com"
329 });
330
331 let payload = serde_json::json!({"name": "Alice"});
332 inject_forgettable_payload(&mut json, payload);
333
334 assert_eq!(json["name"], serde_json::json!("Alice"));
335 assert_eq!(json["email"], serde_json::json!("alice@test.com"));
336 }
337
338 #[test]
339 fn value_helpers() {
340 let set: Forgettable<String> = Forgettable::new("test".to_string());
341 assert!(set.is_set());
342 assert!(!set.is_forgotten());
343 assert_eq!(&*set.value().unwrap(), "test");
344
345 let forgotten: Forgettable<String> = Forgettable::forgotten();
346 assert!(!forgotten.is_set());
347 assert!(forgotten.is_forgotten());
348 assert!(forgotten.value().is_none());
349 }
350
351 #[test]
352 fn extract_payload_value() {
353 let set: Forgettable<String> = Forgettable::new("Alice".to_string());
354 assert_eq!(
355 set.__extract_payload_value(),
356 Some(serde_json::json!("Alice"))
357 );
358
359 let forgotten: Forgettable<String> = Forgettable::forgotten();
360 assert_eq!(forgotten.__extract_payload_value(), None);
361 }
362
363 #[test]
364 fn forgettable_ref_deref() {
365 let f = Forgettable::new("hello".to_string());
366 let r = f.value().unwrap();
367 assert_eq!(r.len(), 5);
369 assert_eq!(&*r, "hello");
370 }
371
372 #[test]
373 fn forgettable_ref_display() {
374 let f = Forgettable::new("Alice".to_string());
375 let r = f.value().unwrap();
376 assert_eq!(format!("{r}"), "Alice");
377 }
378
379 #[test]
380 fn forgettable_ref_partial_eq() {
381 let f = Forgettable::new("Alice".to_string());
382 let r = f.value().unwrap();
383 assert_eq!(r, "Alice".to_string());
384 }
385
386 #[test]
387 fn default_is_forgotten() {
388 let f: Forgettable<String> = Default::default();
389 assert!(f.is_forgotten());
390 assert!(f.value().is_none());
391 }
392
393 #[test]
394 fn from_value() {
395 let f: Forgettable<String> = "Alice".to_string().into();
396 assert!(f.is_set());
397 assert_eq!(&*f.value().unwrap(), "Alice");
398 }
399
400 proptest! {
401 #[test]
405 fn inject_never_panics_and_merges_only_objects(
406 event_in in json_value(),
407 payload in json_value(),
408 ) {
409 let mut event = event_in.clone();
410 inject_forgettable_payload(&mut event, payload.clone());
411
412 if event_in.is_object() && payload.is_object() {
413 let payload_obj = payload.as_object().unwrap();
414 for (k, v) in payload_obj {
416 prop_assert_eq!(event.get(k), Some(v));
417 }
418 for (k, v) in event_in.as_object().unwrap() {
420 if !payload_obj.contains_key(k) {
421 prop_assert_eq!(event.get(k), Some(v));
422 }
423 }
424 } else {
425 prop_assert_eq!(event, event_in);
426 }
427 }
428
429 #[test]
433 fn forgettable_always_serializes_to_null(opt in any::<Option<String>>()) {
434 let v = serde_json::to_value(&opt).expect("serialize option");
435 let f: Forgettable<String> =
436 serde_json::from_value(v.clone()).expect("deserialize forgettable");
437 prop_assert_eq!(f.is_set(), opt.is_some());
438 prop_assert_eq!(f.is_forgotten(), opt.is_none());
439 prop_assert_eq!(serde_json::to_value(&f).unwrap(), serde_json::Value::Null);
440 let from_null: Forgettable<String> =
442 serde_json::from_value(serde_json::Value::Null).unwrap();
443 prop_assert!(from_null.is_forgotten());
444 }
445
446 #[test]
448 fn forgettable_rejects_non_string_value(n in any::<i64>()) {
449 let res: Result<Forgettable<String>, _> =
450 serde_json::from_value(serde_json::Value::from(n));
451 prop_assert!(res.is_err());
452 }
453 }
454}