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 fn rate_limit_delay(&self) -> Option<xmtp_common::time::Duration> {
77 match self {
78 Self::Store(error) => error.rate_limit_delay(),
79 Self::Transport(error) => error.rate_limit_delay(),
80 Self::Group(error) => error.rate_limit_delay(),
81 Self::Processing(error) => error.rate_limit_delay(),
82 Self::Identity(error) => error.rate_limit_delay(),
83 _ => None,
84 }
85 }
86}
87
88impl crate::worker::NeedsDbReconnect for IncomingError {
89 #[deny(clippy::wildcard_enum_match_arm)]
90 fn needs_db_reconnect(&self) -> bool {
91 match self {
92 Self::Storage(error) => error.db_needs_connection(),
93 Self::Store(error) => error.needs_db_reconnect(),
94 Self::Group(error) => error.needs_db_reconnect(),
95 Self::Processing(error) => error.needs_db_reconnect(),
96 Self::Identity(error) => error.needs_db_reconnect(),
97 Self::Transport(_) | Self::UnsupportedTopic | Self::ReceiverEnded => false,
98 }
99 }
100}
101
102impl IncomingError {
103 pub fn code(&self) -> &'static str {
104 match self {
105 Self::Storage(_) => "incoming_storage",
106 Self::Store(_) => "incoming_receive",
107 Self::Transport(_) => "incoming_transport",
108 Self::Group(_) => "incoming_welcome",
109 Self::Processing(error) => error.processing_code(),
110 Self::Identity(_) => "incoming_identity",
111 Self::UnsupportedTopic => "unsupported_topic",
112 Self::ReceiverEnded => "incoming_receiver_ended",
113 }
114 }
115}
116
117#[derive(Clone, Debug)]
119pub struct IncomingTopicStatus {
120 pub topic: Topic,
122 pub scope_generation: u64,
124 pub registration: IncomingRegistration,
126 pub target: Option<Cursor>,
128 pub received: Cursor,
130 pub processed: Cursor,
132 pub unresolved_welcomes: u64,
134 pub processing: IncomingProcessing,
136 pub blocked: Option<String>,
138 pub error: Option<Arc<IncomingError>>,
140}
141
142#[derive(Clone, Debug)]
144pub struct IncomingStatus {
145 pub scope_generation: u64,
147 pub connection_generation: u64,
149 pub connection: IncomingConnection,
151 pub topics: Vec<IncomingTopicStatus>,
153 pub discovery_pending: bool,
155 pub processing: IncomingProcessing,
157 pub error: Option<Arc<IncomingError>>,
159 pub previous: Option<Box<IncomingStatus>>,
161}
162
163impl IncomingStatus {
164 pub(super) fn pending(generation: u64) -> Self {
165 Self {
166 scope_generation: generation,
167 connection_generation: 0,
168 connection: IncomingConnection::Connecting,
169 topics: Vec::new(),
170 discovery_pending: true,
171 processing: IncomingProcessing::Pending,
172 error: None,
173 previous: None,
174 }
175 }
176
177 pub(super) fn cancelled(generation: u64) -> Self {
178 let mut status = Self::pending(generation);
179 status.cancel();
180 status
181 }
182
183 pub(super) fn without_previous(&self) -> Self {
184 let mut status = self.clone();
185 status.previous = None;
186 status
187 }
188}