Whether the child has put a fact away, kept apart from the parents' permanent forgotten.
The two are different acts with different reaches. Forgetting is the parents': the fact is deleted and its identity is tombstoned so no device brings it back. Hiding is the child's: the fact stays (the parents still see it, under "removed by her"), Whiskers stops using it, and a parent can restore it. A hidden fact is therefore not a second list but a state of the fact.
The state is a last-writer-wins register, so merging two copies in any order, any number of times,
gives the same answer (the invariant of MemorySnapshot). It carries its own time, at_ms, and the
newer write wins; on an exact tie the fact stays hidden, so the answer does not depend on which copy
was merged first and a tie never lets Whiskers use what she put away.
The state is a typed enum, not two booleans: "hidden and shown" and "hidden with no time" cannot be written down.
16use serde::{Deserialize, Serialize};
Whether Whiskers may use a fact, and the moment that was decided.
Whiskers uses it. at_ms is when a parent last restored it (0 for a fact never hidden).
22 Shown { at_ms: u64 },
She put it away. Whiskers does not use it; the parents can restore it.
True for the state every new fact has, which is left out of the stored and wire forms so a fact nobody has touched is byte for byte what it was before this state existed.
Her putting the fact away at now_ms. The write is made later than the one it follows even if
this device's clock is behind, so a causally later act always wins the merge.
A parent's restore at now_ms, later than whatever it follows (see hidden_after).
The register's merge: the later write, and on a tie the hidden one. Commutative, associative and idempotent.
67 fn order_key(&self) -> (u64, bool) { 68 (self.at_ms(), self.is_hidden()) 69 } 70} 71 72#[cfg(test)] 73mod tests { 74 use super::*; 75 76 fn all() -> Vec<Visibility> { 77 let mut v = Vec::new(); 78 for t in [0, 1, 2, 5] { 79 v.push(Visibility::Shown { at_ms: t }); 80 v.push(Visibility::HiddenByChild { at_ms: t }); 81 } 82 v 83 } 84 85 #[test] 86 fn the_merge_is_commutative_associative_and_idempotent() { 87 for a in all() { 88 assert_eq!(a.merged(a), a); 89 for b in all() { 90 assert_eq!(a.merged(b), b.merged(a), "{a:?} {b:?}"); 91 for c in all() { 92 assert_eq!(a.merged(b).merged(c), a.merged(b.merged(c)), "{a:?} {b:?} {c:?}"); 93 } 94 } 95 } 96 } 97 98 #[test] 99 fn a_tie_stays_hidden_in_either_order() { 100 let (s, h) = (Visibility::Shown { at_ms: 7 }, Visibility::HiddenByChild { at_ms: 7 }); 101 assert_eq!(s.merged(h), h); 102 assert_eq!(h.merged(s), h); 103 } 104 105 #[test] 106 fn a_restore_beats_the_hide_it_follows_even_from_a_slow_clock() { 107 let hidden = Visibility::default().hidden_after(1_000); 108 let restored = hidden.restored_after(10); // the parents' clock is behind 109 assert!(!restored.is_hidden()); 110 assert_eq!(hidden.merged(restored), restored); 111 assert_eq!(restored.merged(hidden), restored); 112 // and her hiding it again beats that restore 113 let again = restored.hidden_after(0); 114 assert_eq!(restored.merged(again), again); 115 } 116 117 #[test] 118 fn the_default_is_left_out_of_the_stored_form() { 119 assert!(Visibility::default().is_default()); 120 assert!(!Visibility::default().hidden_after(3).is_default()); 121 assert!(!Visibility::default().hidden_after(3).restored_after(9).is_default()); 122 } 123}