xmtp_proto/impls/
update_dedupe.rs1use crate::xmtp::mls::message_contents::GroupUpdated;
2use rustc_hash::FxHasher;
3use std::hash::{Hash, Hasher};
4
5macro_rules! hash_arm {
6 ($self:ident, $update:ident, $field:ident) => {
7 if !$update.$field.is_empty() {
8 let mut hasher = FxHasher::default();
9 $update.$field.hash(&mut hasher);
10 $self.$field = Some(hasher.finish());
11 }
12 };
13}
14
15macro_rules! match_arm {
16 ($self:ident, $other:ident, $field:ident) => {
17 match $self.$field.is_some() {
18 true => $self.$field == $other.$field,
19 false => true,
20 }
21 };
22}
23
24#[derive(Default)]
25pub struct GroupUpdateDeduper {
26 added_inboxes: Option<u64>,
27 removed_inboxes: Option<u64>,
28 metadata_field_changes: Option<u64>,
29 left_inboxes: Option<u64>,
30 added_admin_inboxes: Option<u64>,
31 removed_admin_inboxes: Option<u64>,
32 added_super_admin_inboxes: Option<u64>,
33 removed_super_admin_inboxes: Option<u64>,
34}
35
36impl GroupUpdateDeduper {
37 pub fn consume(&mut self, update: &GroupUpdated) {
38 hash_arm!(self, update, added_inboxes);
39 hash_arm!(self, update, removed_inboxes);
40 hash_arm!(self, update, metadata_field_changes);
41 hash_arm!(self, update, left_inboxes);
42 hash_arm!(self, update, added_admin_inboxes);
43 hash_arm!(self, update, removed_admin_inboxes);
44 hash_arm!(self, update, added_super_admin_inboxes);
45 hash_arm!(self, update, removed_super_admin_inboxes);
46 }
47
48 pub fn is_dupe(&self, update: &GroupUpdated) -> bool {
49 let mut hash = Self::default();
50 hash.consume(update);
51
52 match_arm!(hash, self, added_inboxes)
53 && match_arm!(hash, self, removed_inboxes)
54 && match_arm!(hash, self, metadata_field_changes)
55 && match_arm!(hash, self, left_inboxes)
56 && match_arm!(hash, self, added_admin_inboxes)
57 && match_arm!(hash, self, removed_admin_inboxes)
58 && match_arm!(hash, self, added_super_admin_inboxes)
59 && match_arm!(hash, self, removed_super_admin_inboxes)
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use crate::{
66 impls::update_dedupe::GroupUpdateDeduper,
67 xmtp::mls::message_contents::{
68 GroupUpdated,
69 group_updated::{Inbox, MetadataFieldChange},
70 },
71 };
72
73 #[xmtp_common::test(unwrap_try = true)]
74 async fn test_dedupe() {
75 let add_update = GroupUpdated {
76 added_inboxes: vec![Inbox {
77 inbox_id: "123".to_string(),
78 }],
79 ..Default::default()
80 };
81
82 let mut deduper = GroupUpdateDeduper::default();
83 deduper.consume(&add_update);
84
85 assert!(deduper.is_dupe(&add_update));
87
88 let mut another_add_update = add_update.clone();
90 another_add_update.added_inboxes = vec![Inbox {
91 inbox_id: "234".to_string(),
92 }];
93 assert!(!deduper.is_dupe(&another_add_update));
94
95 deduper.consume(&another_add_update);
97 assert!(deduper.is_dupe(&another_add_update));
99
100 let remove_update = GroupUpdated {
102 removed_inboxes: vec![Inbox {
103 inbox_id: "123".to_string(),
104 }],
105 ..Default::default()
106 };
107 assert!(!deduper.is_dupe(&remove_update));
109
110 assert!(deduper.is_dupe(&another_add_update));
112 assert!(!deduper.is_dupe(&add_update));
115
116 let multi_update = GroupUpdated {
118 removed_inboxes: vec![Inbox {
119 inbox_id: "234".to_string(),
120 }],
121 metadata_field_changes: vec![MetadataFieldChange {
122 field_name: "disappearing_msgs".to_string(),
123 ..Default::default()
124 }],
125 ..Default::default()
126 };
127
128 assert!(!deduper.is_dupe(&multi_update));
132
133 deduper.consume(&multi_update);
135 assert!(deduper.is_dupe(&multi_update));
136 }
137}