xmtp_mls/subscriptions/incoming/
status.rs1use std::sync::Arc;
2use xmtp_common::RetryableError;
3use xmtp_proto::types::{Cursor, Topic};
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7pub enum IncomingConnection {
8 Connecting,
9 Connected,
10 Reconnecting,
11 Failed,
12 Closed,
13}
14
15#[derive(Clone, Copy, Debug, PartialEq, Eq)]
17pub enum IncomingRegistration {
18 Pending,
19 Active,
20 Removed,
21}
22
23#[derive(Clone, Copy, Debug, PartialEq, Eq)]
25pub enum IncomingProcessing {
26 Pending,
27 Complete,
28 Blocked,
29 Cancelled,
30}
31
32#[derive(Debug, thiserror::Error)]
33pub enum IncomingError {
34 #[error("incoming receiver ended before recovery completed")]
35 ReceiverEnded,
36 #[error(transparent)]
37 Storage(#[from] xmtp_db::StorageError),
38 #[error(transparent)]
39 Store(#[from] crate::mls_store::MlsStoreError),
40 #[error(transparent)]
41 Transport(#[from] xmtp_proto::api::NetworkError),
42 #[error(transparent)]
43 Group(#[from] crate::groups::GroupError),
44 #[error(transparent)]
45 Processing(#[from] crate::groups::mls_sync::GroupMessageProcessingError),
46 #[error(transparent)]
47 Identity(#[from] crate::identity_updates::IdentityDependencyError),
48 #[error("unsupported incoming topic")]
49 UnsupportedTopic,
50}
51
52impl RetryableError for IncomingError {
53 fn is_retryable(&self) -> bool {
54 match self {
55 Self::Storage(xmtp_db::StorageError::Stream(
58 xmtp_db::stream_storage::StreamStorageError::MissingPrefix { .. },
59 ))
60 | Self::Store(crate::mls_store::MlsStoreError::Storage(
61 xmtp_db::StorageError::Stream(
62 xmtp_db::stream_storage::StreamStorageError::MissingPrefix { .. },
63 ),
64 )) => true,
65 Self::Storage(error) => error.is_retryable(),
66 Self::Store(error) => error.is_retryable(),
67 Self::Transport(error) => error.is_retryable(),
68 Self::Group(error) => error.is_retryable(),
69 Self::Processing(error) => error.is_retryable(),
70 Self::Identity(error) => error.is_retryable(),
71 Self::UnsupportedTopic => false,
72 Self::ReceiverEnded => true,
73 }
74 }
75}
76
77impl crate::worker::NeedsDbReconnect for IncomingError {
78 fn needs_db_reconnect(&self) -> bool {
79 match self {
80 Self::Storage(error) => error.db_needs_connection(),
81 Self::Store(error) => error.needs_db_reconnect(),
82 Self::Group(error) => error.needs_db_reconnect(),
83 Self::Processing(error) => error.needs_db_reconnect(),
84 Self::Identity(error) => error.needs_db_reconnect(),
85 Self::Transport(_) | Self::UnsupportedTopic | Self::ReceiverEnded => false,
86 }
87 }
88}
89
90impl IncomingError {
91 pub fn code(&self) -> &'static str {
92 match self {
93 Self::Storage(_) => "incoming_storage",
94 Self::Store(_) => "incoming_receive",
95 Self::Transport(_) => "incoming_transport",
96 Self::Group(_) => "incoming_welcome",
97 Self::Processing(error) => error.processing_code(),
98 Self::Identity(_) => "incoming_identity",
99 Self::UnsupportedTopic => "unsupported_topic",
100 Self::ReceiverEnded => "incoming_receiver_ended",
101 }
102 }
103}
104
105#[derive(Clone, Debug)]
107pub struct IncomingTopicStatus {
108 pub topic: Topic,
110 pub scope_generation: u64,
112 pub registration: IncomingRegistration,
114 pub target: Option<Cursor>,
116 pub received: Cursor,
118 pub processed: Cursor,
120 pub unresolved_welcomes: u64,
122 pub processing: IncomingProcessing,
124 pub blocked: Option<String>,
126 pub error: Option<Arc<IncomingError>>,
128}
129
130#[derive(Clone, Debug)]
132pub struct IncomingStatus {
133 pub scope_generation: u64,
135 pub connection_generation: u64,
137 pub connection: IncomingConnection,
139 pub topics: Vec<IncomingTopicStatus>,
141 pub discovery_pending: bool,
143 pub processing: IncomingProcessing,
145 pub error: Option<Arc<IncomingError>>,
147 pub previous: Option<Box<IncomingStatus>>,
149}
150
151impl IncomingStatus {
152 pub(super) fn pending(generation: u64) -> Self {
153 Self {
154 scope_generation: generation,
155 connection_generation: 0,
156 connection: IncomingConnection::Connecting,
157 topics: Vec::new(),
158 discovery_pending: true,
159 processing: IncomingProcessing::Pending,
160 error: None,
161 previous: None,
162 }
163 }
164
165 pub(super) fn cancelled(generation: u64) -> Self {
166 let mut status = Self::pending(generation);
167 status.cancel();
168 status
169 }
170
171 pub(crate) fn cancel(&mut self) {
172 self.connection = IncomingConnection::Closed;
173 self.processing = IncomingProcessing::Cancelled;
174 self.discovery_pending = false;
175 for topic in &mut self.topics {
176 topic.registration = IncomingRegistration::Removed;
177 topic.processing = IncomingProcessing::Cancelled;
178 }
179 }
180
181 pub(super) fn without_previous(&self) -> Self {
182 let mut status = self.clone();
183 status.previous = None;
184 status
185 }
186}