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