xmtp_proto/types/
topic_cursor.rs1use std::{
2 collections::{HashMap, hash_map::Entry},
3 ops::{Deref, DerefMut},
4};
5
6use crate::types::{GlobalCursor, InstallationId, Topic};
7
8#[derive(Default, Debug, PartialEq, Clone)]
10pub struct TopicCursor {
11 inner: HashMap<Topic, GlobalCursor>,
12}
13
14pub type TopicEntry<'a> = Entry<'a, Topic, GlobalCursor>;
15
16impl TopicCursor {
17 pub fn get_or_default(&mut self, topic: &Topic) -> &GlobalCursor {
19 self.inner.entry(topic.clone()).or_default()
20 }
21
22 pub fn get_group(&self, group_id: impl AsRef<[u8]>) -> GlobalCursor {
25 self.inner
26 .get(&Topic::new_group_message(group_id))
27 .cloned()
28 .unwrap_or_default()
29 }
30
31 pub fn contains_group(&self, group_id: impl AsRef<[u8]>) -> bool {
33 self.inner.contains_key(&Topic::new_group_message(group_id))
34 }
35
36 pub fn into_topics(self) -> Vec<Topic> {
38 self.inner.into_keys().collect()
39 }
40
41 pub fn topics(&self) -> Vec<Topic> {
44 self.inner.keys().cloned().collect()
45 }
46
47 pub fn group_entry(&mut self, group_id: impl AsRef<[u8]>) -> TopicEntry<'_> {
49 self.inner.entry(Topic::new_group_message(group_id))
50 }
51
52 pub fn identity_entry(&mut self, inbox_id: impl AsRef<[u8]>) -> TopicEntry<'_> {
54 self.inner.entry(Topic::new_identity_update(inbox_id))
55 }
56
57 pub fn welcome_entry(&mut self, installation_id: InstallationId) -> TopicEntry<'_> {
59 self.inner
60 .entry(Topic::new_welcome_message(installation_id))
61 }
62
63 pub fn key_package_entry(&mut self, installation_id: InstallationId) -> TopicEntry<'_> {
65 self.inner.entry(Topic::new_key_package(installation_id))
66 }
67
68 pub fn groups(&self) -> impl Iterator<Item = (&[u8], &GlobalCursor)> {
71 self.inner
72 .iter()
73 .filter_map(|(t, g)| Some((t.group_message_v1()?.identifier(), g)))
74 }
75}
76
77impl From<HashMap<Topic, GlobalCursor>> for TopicCursor {
78 fn from(inner: HashMap<Topic, GlobalCursor>) -> Self {
79 TopicCursor { inner }
80 }
81}
82
83impl FromIterator<(Topic, GlobalCursor)> for TopicCursor {
84 fn from_iter<T: IntoIterator<Item = (Topic, GlobalCursor)>>(iter: T) -> Self {
85 TopicCursor {
86 inner: HashMap::from_iter(iter),
87 }
88 }
89}
90
91impl IntoIterator for TopicCursor {
92 type Item = (Topic, GlobalCursor);
93 type IntoIter = <HashMap<Topic, GlobalCursor> as IntoIterator>::IntoIter;
94
95 fn into_iter(self) -> Self::IntoIter {
96 self.inner.into_iter()
97 }
98}
99
100impl<'a> IntoIterator for &'a TopicCursor {
101 type Item = (&'a Topic, &'a GlobalCursor);
102 type IntoIter = std::collections::hash_map::Iter<'a, Topic, GlobalCursor>;
103 fn into_iter(self) -> Self::IntoIter {
104 self.inner.iter()
105 }
106}
107
108impl<'a> IntoIterator for &'a mut TopicCursor {
109 type Item = (&'a Topic, &'a mut GlobalCursor);
110 type IntoIter = std::collections::hash_map::IterMut<'a, Topic, GlobalCursor>;
111
112 fn into_iter(self) -> Self::IntoIter {
113 self.inner.iter_mut()
114 }
115}
116
117impl Deref for TopicCursor {
118 type Target = HashMap<Topic, GlobalCursor>;
119 fn deref(&self) -> &Self::Target {
120 &self.inner
121 }
122}
123
124impl DerefMut for TopicCursor {
125 fn deref_mut(&mut self) -> &mut Self::Target {
126 &mut self.inner
127 }
128}
129
130impl TopicCursor {
131 pub fn add(&mut self, topic: Topic, cursor: GlobalCursor) {
132 self.inner.insert(topic, cursor);
133 }
134}
135
136impl std::fmt::Display for TopicCursor {
137 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138 for (topic, has_seen) in self.inner.iter() {
139 writeln!(f, "{} -> {}", topic, has_seen)?;
140 }
141 Ok(())
142 }
143}