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(transparent)]
35 Storage(#[from] xmtp_db::StorageError),
36 #[error(transparent)]
37 Store(#[from] crate::mls_store::MlsStoreError),
38 #[error(transparent)]
39 Transport(#[from] xmtp_proto::api::NetworkError),
40 #[error(transparent)]
41 Group(#[from] crate::groups::GroupError),
42 #[error(transparent)]
43 Processing(#[from] crate::groups::mls_sync::GroupMessageProcessingError),
44 #[error(transparent)]
45 Identity(#[from] crate::identity_updates::IdentityDependencyError),
46 #[error("unsupported incoming topic")]
47 UnsupportedTopic,
48}
49
50impl RetryableError for IncomingError {
51 fn is_retryable(&self) -> bool {
52 match self {
53 Self::Storage(xmtp_db::StorageError::Stream(
56 xmtp_db::stream_storage::StreamStorageError::MissingPrefix { .. },
57 ))
58 | Self::Store(crate::mls_store::MlsStoreError::Storage(
59 xmtp_db::StorageError::Stream(
60 xmtp_db::stream_storage::StreamStorageError::MissingPrefix { .. },
61 ),
62 )) => true,
63 Self::Storage(error) => error.is_retryable(),
64 Self::Store(error) => error.is_retryable(),
65 Self::Transport(error) => error.is_retryable(),
66 Self::Group(error) => error.is_retryable(),
67 Self::Processing(error) => error.is_retryable(),
68 Self::Identity(error) => error.is_retryable(),
69 Self::UnsupportedTopic => false,
70 }
71 }
72}
73
74impl crate::worker::NeedsDbReconnect for IncomingError {
75 fn needs_db_reconnect(&self) -> bool {
76 match self {
77 Self::Storage(error) => error.db_needs_connection(),
78 Self::Store(error) => error.needs_db_reconnect(),
79 Self::Group(error) => error.needs_db_reconnect(),
80 Self::Processing(error) => error.needs_db_reconnect(),
81 Self::Identity(error) => error.needs_db_reconnect(),
82 Self::Transport(_) | Self::UnsupportedTopic => false,
83 }
84 }
85}
86
87impl IncomingError {
88 pub fn code(&self) -> &'static str {
89 match self {
90 Self::Storage(_) => "incoming_storage",
91 Self::Store(_) => "incoming_receive",
92 Self::Transport(_) => "incoming_transport",
93 Self::Group(_) => "incoming_welcome",
94 Self::Processing(error) => error.processing_code(),
95 Self::Identity(_) => "incoming_identity",
96 Self::UnsupportedTopic => "unsupported_topic",
97 }
98 }
99}
100
101#[derive(Clone, Debug)]
103pub struct IncomingTopicStatus {
104 pub topic: Topic,
106 pub scope_generation: u64,
108 pub registration: IncomingRegistration,
110 pub target: Option<Cursor>,
112 pub received: Cursor,
114 pub processed: Cursor,
116 pub unresolved_welcomes: u64,
118 pub processing: IncomingProcessing,
120 pub blocked: Option<String>,
122 pub error: Option<Arc<IncomingError>>,
124}
125
126#[derive(Clone, Debug)]
128pub struct IncomingStatus {
129 pub scope_generation: u64,
131 pub connection_generation: u64,
133 pub connection: IncomingConnection,
135 pub topics: Vec<IncomingTopicStatus>,
137 pub discovery_pending: bool,
139 pub processing: IncomingProcessing,
141 pub error: Option<Arc<IncomingError>>,
143 pub previous: Option<Box<IncomingStatus>>,
145}
146
147impl IncomingStatus {
148 pub(super) fn pending(generation: u64) -> Self {
149 Self {
150 scope_generation: generation,
151 connection_generation: 0,
152 connection: IncomingConnection::Connecting,
153 topics: Vec::new(),
154 discovery_pending: true,
155 processing: IncomingProcessing::Pending,
156 error: None,
157 previous: None,
158 }
159 }
160
161 pub(super) fn cancelled(generation: u64) -> Self {
162 let mut status = Self::pending(generation);
163 status.cancel();
164 status
165 }
166
167 pub(crate) fn cancel(&mut self) {
168 self.connection = IncomingConnection::Closed;
169 self.processing = IncomingProcessing::Cancelled;
170 self.discovery_pending = false;
171 for topic in &mut self.topics {
172 topic.registration = IncomingRegistration::Removed;
173 topic.processing = IncomingProcessing::Cancelled;
174 }
175 }
176
177 pub(super) fn without_previous(&self) -> Self {
178 let mut status = self.clone();
179 status.previous = None;
180 status
181 }
182}