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#[derive(Debug, Clone, PartialEq, Eq, Default)]
245pub struct ForgettableRemnants {
246 pub payload_rows: usize,
248 pub live_index_columns: Vec<&'static str>,
250 pub event_fields: Vec<(String, String)>,
253}
254
255impl ForgettableRemnants {
256 pub fn is_empty(&self) -> bool {
258 self.payload_rows == 0 && self.live_index_columns.is_empty() && self.event_fields.is_empty()
259 }
260}
261
262impl fmt::Display for ForgettableRemnants {
263 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
264 write!(
265 f,
266 "forgettable data still present at storage level: {} payload row(s), non-NULL index columns {:?}, non-null event fields {:?}",
267 self.payload_rows, self.live_index_columns, self.event_fields
268 )
269 }
270}
271
272#[cfg(test)]
273mod tests {
274 use super::*;
275 use proptest::prelude::*;
276
277 fn json_value() -> impl Strategy<Value = serde_json::Value> {
280 let scalar = prop_oneof![
281 Just(serde_json::Value::Null),
282 any::<bool>().prop_map(serde_json::Value::Bool),
283 any::<i64>().prop_map(serde_json::Value::from),
284 any::<f64>().prop_map(serde_json::Value::from),
285 ".{0,15}".prop_map(serde_json::Value::String),
286 ]
287 .boxed();
288 let nested = prop_oneof![
289 proptest::collection::vec(scalar.clone(), 0..4).prop_map(serde_json::Value::Array),
290 proptest::collection::vec((".{0,6}", scalar.clone()), 0..4).prop_map(|pairs| {
291 let mut m = serde_json::Map::new();
292 for (k, v) in pairs {
293 m.insert(k, v);
294 }
295 serde_json::Value::Object(m)
296 },),
297 ];
298 prop_oneof![scalar, nested]
299 }
300
301 #[test]
302 fn serialize_set_emits_null() {
303 let value: Forgettable<String> = Forgettable::new("Alice".to_string());
304 let json = serde_json::to_value(&value).unwrap();
305 assert_eq!(json, serde_json::json!(null));
306 }
307
308 #[test]
309 fn serialize_forgotten_emits_null() {
310 let value: Forgettable<String> = Forgettable::forgotten();
311 let json = serde_json::to_value(&value).unwrap();
312 assert_eq!(json, serde_json::json!(null));
313 }
314
315 #[test]
316 fn deserialize_value() {
317 let json = serde_json::json!("Alice");
318 let value: Forgettable<String> = serde_json::from_value(json).unwrap();
319 assert_eq!(value, Forgettable::new("Alice".to_string()));
320 }
321
322 #[test]
323 fn deserialize_null() {
324 let json = serde_json::json!(null);
325 let value: Forgettable<String> = serde_json::from_value(json).unwrap();
326 assert_eq!(value, Forgettable::forgotten());
327 }
328
329 #[test]
330 fn serialize_struct_with_forgettable_emits_null() {
331 #[derive(Serialize, Deserialize, Debug, PartialEq)]
332 struct Event {
333 #[serde(rename = "type")]
334 kind: String,
335 name: Forgettable<String>,
336 email: String,
337 }
338
339 let event = Event {
340 kind: "initialized".to_string(),
341 name: Forgettable::new("Alice".to_string()),
342 email: "alice@test.com".to_string(),
343 };
344 let json = serde_json::to_value(&event).unwrap();
345 assert_eq!(json["name"], serde_json::json!(null));
347 assert_eq!(json["email"], serde_json::json!("alice@test.com"));
348
349 let deserialized: Event = serde_json::from_value(json).unwrap();
351 assert_eq!(deserialized.name, Forgettable::forgotten());
352
353 let event_forgotten = Event {
355 kind: "initialized".to_string(),
356 name: Forgettable::forgotten(),
357 email: "alice@test.com".to_string(),
358 };
359 let json = serde_json::to_value(&event_forgotten).unwrap();
360 assert_eq!(json["name"], serde_json::json!(null));
361
362 let deserialized: Event = serde_json::from_value(json).unwrap();
363 assert_eq!(deserialized, event_forgotten);
364 }
365
366 #[test]
367 fn inject_payload() {
368 let mut json = serde_json::json!({
369 "type": "initialized",
370 "id": "uuid",
371 "name": null,
372 "email": "alice@test.com"
373 });
374
375 let payload = serde_json::json!({"name": "Alice"});
376 inject_forgettable_payload(&mut json, payload);
377
378 assert_eq!(json["name"], serde_json::json!("Alice"));
379 assert_eq!(json["email"], serde_json::json!("alice@test.com"));
380 }
381
382 #[test]
383 fn value_helpers() {
384 let set: Forgettable<String> = Forgettable::new("test".to_string());
385 assert!(set.is_set());
386 assert!(!set.is_forgotten());
387 assert_eq!(&*set.value().unwrap(), "test");
388
389 let forgotten: Forgettable<String> = Forgettable::forgotten();
390 assert!(!forgotten.is_set());
391 assert!(forgotten.is_forgotten());
392 assert!(forgotten.value().is_none());
393 }
394
395 #[test]
396 fn extract_payload_value() {
397 let set: Forgettable<String> = Forgettable::new("Alice".to_string());
398 assert_eq!(
399 set.__extract_payload_value(),
400 Some(serde_json::json!("Alice"))
401 );
402
403 let forgotten: Forgettable<String> = Forgettable::forgotten();
404 assert_eq!(forgotten.__extract_payload_value(), None);
405 }
406
407 #[test]
408 fn forgettable_ref_deref() {
409 let f = Forgettable::new("hello".to_string());
410 let r = f.value().unwrap();
411 assert_eq!(r.len(), 5);
413 assert_eq!(&*r, "hello");
414 }
415
416 #[test]
417 fn forgettable_ref_display() {
418 let f = Forgettable::new("Alice".to_string());
419 let r = f.value().unwrap();
420 assert_eq!(format!("{r}"), "Alice");
421 }
422
423 #[test]
424 fn forgettable_ref_partial_eq() {
425 let f = Forgettable::new("Alice".to_string());
426 let r = f.value().unwrap();
427 assert_eq!(r, "Alice".to_string());
428 }
429
430 #[test]
431 fn default_is_forgotten() {
432 let f: Forgettable<String> = Default::default();
433 assert!(f.is_forgotten());
434 assert!(f.value().is_none());
435 }
436
437 #[test]
438 fn from_value() {
439 let f: Forgettable<String> = "Alice".to_string().into();
440 assert!(f.is_set());
441 assert_eq!(&*f.value().unwrap(), "Alice");
442 }
443
444 proptest! {
445 #[test]
449 fn inject_never_panics_and_merges_only_objects(
450 event_in in json_value(),
451 payload in json_value(),
452 ) {
453 let mut event = event_in.clone();
454 inject_forgettable_payload(&mut event, payload.clone());
455
456 if event_in.is_object() && payload.is_object() {
457 let payload_obj = payload.as_object().unwrap();
458 for (k, v) in payload_obj {
460 prop_assert_eq!(event.get(k), Some(v));
461 }
462 for (k, v) in event_in.as_object().unwrap() {
464 if !payload_obj.contains_key(k) {
465 prop_assert_eq!(event.get(k), Some(v));
466 }
467 }
468 } else {
469 prop_assert_eq!(event, event_in);
470 }
471 }
472
473 #[test]
477 fn forgettable_always_serializes_to_null(opt in any::<Option<String>>()) {
478 let v = serde_json::to_value(&opt).expect("serialize option");
479 let f: Forgettable<String> =
480 serde_json::from_value(v.clone()).expect("deserialize forgettable");
481 prop_assert_eq!(f.is_set(), opt.is_some());
482 prop_assert_eq!(f.is_forgotten(), opt.is_none());
483 prop_assert_eq!(serde_json::to_value(&f).unwrap(), serde_json::Value::Null);
484 let from_null: Forgettable<String> =
486 serde_json::from_value(serde_json::Value::Null).unwrap();
487 prop_assert!(from_null.is_forgotten());
488 }
489
490 #[test]
492 fn forgettable_rejects_non_string_value(n in any::<i64>()) {
493 let res: Result<Forgettable<String>, _> =
494 serde_json::from_value(serde_json::Value::from(n));
495 prop_assert!(res.is_err());
496 }
497 }
498}