1use crate::auth::{AuthConfig, AuthToken, TokenTransport};
2use crate::config::{ConnectionConfig, HyperStackConfig};
3use crate::connection::{ConnectionManager, ConnectionState};
4use crate::entity::Stack;
5use crate::error::{HyperStackError, SocketIssue};
6use crate::frame::Frame;
7use crate::store::{SharedStore, StoreConfig};
8use crate::view::Views;
9use std::future::Future;
10use std::marker::PhantomData;
11use std::sync::Arc;
12use std::time::Duration;
13use tokio::sync::{broadcast, mpsc};
14
15pub struct HyperStack<S: Stack> {
25 connection: ConnectionManager,
26 store: SharedStore,
27 #[allow(dead_code)]
28 config: HyperStackConfig,
29 pub views: S::Views,
30 _stack: PhantomData<S>,
31}
32
33impl<S: Stack> HyperStack<S> {
34 pub async fn connect() -> Result<Self, HyperStackError> {
36 Self::builder().connect().await
37 }
38
39 pub async fn connect_url(url: &str) -> Result<Self, HyperStackError> {
41 Self::builder().url(url).connect().await
42 }
43
44 pub fn builder() -> HyperStackBuilder<S> {
46 HyperStackBuilder::new()
47 }
48
49 pub async fn connection_state(&self) -> ConnectionState {
50 self.connection.state().await
51 }
52
53 pub async fn last_error(&self) -> Option<Arc<HyperStackError>> {
54 self.connection.last_error().await
55 }
56
57 pub async fn last_socket_issue(&self) -> Option<SocketIssue> {
58 self.connection.last_socket_issue().await
59 }
60
61 pub fn subscribe_socket_issues(&self) -> broadcast::Receiver<SocketIssue> {
62 self.connection.subscribe_socket_issues()
63 }
64
65 pub async fn disconnect(&self) {
66 self.connection.disconnect().await;
67 }
68
69 pub fn store(&self) -> &SharedStore {
70 &self.store
71 }
72}
73
74pub struct HyperStackBuilder<S: Stack> {
76 url: String,
77 config: HyperStackConfig,
78 _stack: PhantomData<S>,
79}
80
81impl<S: Stack> HyperStackBuilder<S> {
82 fn new() -> Self {
83 Self {
84 url: S::url().to_string(),
85 config: HyperStackConfig::default(),
86 _stack: PhantomData,
87 }
88 }
89
90 pub fn url(mut self, url: &str) -> Self {
91 self.url = url.to_string();
92 self
93 }
94
95 pub fn auto_reconnect(mut self, enabled: bool) -> Self {
96 self.config.auto_reconnect = enabled;
97 self
98 }
99
100 pub fn reconnect_intervals(mut self, intervals: Vec<Duration>) -> Self {
101 self.config.reconnect_intervals = intervals;
102 self
103 }
104
105 pub fn max_reconnect_attempts(mut self, max: u32) -> Self {
106 self.config.max_reconnect_attempts = max;
107 self
108 }
109
110 pub fn ping_interval(mut self, interval: Duration) -> Self {
111 self.config.ping_interval = interval;
112 self
113 }
114
115 pub fn initial_data_timeout(mut self, timeout: Duration) -> Self {
116 self.config.initial_data_timeout = timeout;
117 self
118 }
119
120 pub fn max_entries_per_view(mut self, max: usize) -> Self {
121 self.config.max_entries_per_view = Some(max);
122 self
123 }
124
125 pub fn unlimited_entries(mut self) -> Self {
126 self.config.max_entries_per_view = None;
127 self
128 }
129
130 pub fn auth(mut self, auth: AuthConfig) -> Self {
131 self.config.auth = Some(auth);
132 self
133 }
134
135 pub fn auth_token(mut self, token: impl Into<String>) -> Self {
136 let auth = self
137 .config
138 .auth
139 .take()
140 .unwrap_or_default()
141 .with_token(token);
142 self.config.auth = Some(auth);
143 self
144 }
145
146 pub fn publishable_key(mut self, publishable_key: impl Into<String>) -> Self {
147 let auth = self
148 .config
149 .auth
150 .take()
151 .unwrap_or_default()
152 .with_publishable_key(publishable_key);
153 self.config.auth = Some(auth);
154 self
155 }
156
157 pub fn token_endpoint(mut self, token_endpoint: impl Into<String>) -> Self {
158 let auth = self
159 .config
160 .auth
161 .take()
162 .unwrap_or_default()
163 .with_token_endpoint(token_endpoint);
164 self.config.auth = Some(auth);
165 self
166 }
167
168 pub fn token_endpoint_header(
169 mut self,
170 key: impl Into<String>,
171 value: impl Into<String>,
172 ) -> Self {
173 let auth = self
174 .config
175 .auth
176 .take()
177 .unwrap_or_default()
178 .with_token_endpoint_header(key, value);
179 self.config.auth = Some(auth);
180 self
181 }
182
183 pub fn token_transport(mut self, transport: TokenTransport) -> Self {
184 let auth = self
185 .config
186 .auth
187 .take()
188 .unwrap_or_default()
189 .with_token_transport(transport);
190 self.config.auth = Some(auth);
191 self
192 }
193
194 pub fn get_token<F, Fut>(mut self, provider: F) -> Self
195 where
196 F: Fn() -> Fut + Send + Sync + 'static,
197 Fut: Future<Output = Result<AuthToken, HyperStackError>> + Send + 'static,
198 {
199 let auth = self
200 .config
201 .auth
202 .take()
203 .unwrap_or_default()
204 .with_token_provider(provider);
205 self.config.auth = Some(auth);
206 self
207 }
208
209 pub async fn connect(self) -> Result<HyperStack<S>, HyperStackError> {
210 let HyperStackBuilder {
211 url,
212 config,
213 _stack: _,
214 } = self;
215
216 let store_config = StoreConfig {
217 max_entries_per_view: config.max_entries_per_view,
218 };
219 let store = SharedStore::with_config(store_config);
220 let store_clone = store.clone();
221
222 let (frame_tx, mut frame_rx) = mpsc::channel::<Frame>(1000);
223
224 let connection_config: ConnectionConfig = config.clone().into();
225 let connection = ConnectionManager::new(url, connection_config, frame_tx).await?;
226
227 tokio::spawn(async move {
228 while let Some(frame) = frame_rx.recv().await {
229 store_clone.apply_frame(frame).await;
230 }
231 });
232
233 let view_builder = crate::view::ViewBuilder::new(
234 connection.clone(),
235 store.clone(),
236 config.initial_data_timeout,
237 );
238 let views = S::Views::from_builder(view_builder);
239
240 Ok(HyperStack {
241 connection,
242 store,
243 config,
244 views,
245 _stack: PhantomData,
246 })
247 }
248}