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 api_key(self, api_key: impl Into<String>) -> Self {
160 self.publishable_key(api_key)
161 }
162
163 pub fn token_endpoint(mut self, token_endpoint: impl Into<String>) -> Self {
164 let auth = self
165 .config
166 .auth
167 .take()
168 .unwrap_or_default()
169 .with_token_endpoint(token_endpoint);
170 self.config.auth = Some(auth);
171 self
172 }
173
174 pub fn token_endpoint_header(
175 mut self,
176 key: impl Into<String>,
177 value: impl Into<String>,
178 ) -> Self {
179 let auth = self
180 .config
181 .auth
182 .take()
183 .unwrap_or_default()
184 .with_token_endpoint_header(key, value);
185 self.config.auth = Some(auth);
186 self
187 }
188
189 pub fn token_transport(mut self, transport: TokenTransport) -> Self {
190 let auth = self
191 .config
192 .auth
193 .take()
194 .unwrap_or_default()
195 .with_token_transport(transport);
196 self.config.auth = Some(auth);
197 self
198 }
199
200 pub fn get_token<F, Fut>(mut self, provider: F) -> Self
201 where
202 F: Fn() -> Fut + Send + Sync + 'static,
203 Fut: Future<Output = Result<AuthToken, HyperStackError>> + Send + 'static,
204 {
205 let auth = self
206 .config
207 .auth
208 .take()
209 .unwrap_or_default()
210 .with_token_provider(provider);
211 self.config.auth = Some(auth);
212 self
213 }
214
215 pub async fn connect(self) -> Result<HyperStack<S>, HyperStackError> {
216 let HyperStackBuilder {
217 url,
218 config,
219 _stack: _,
220 } = self;
221
222 let store_config = StoreConfig {
223 max_entries_per_view: config.max_entries_per_view,
224 };
225 let store = SharedStore::with_config(store_config);
226 let store_clone = store.clone();
227
228 let (frame_tx, mut frame_rx) = mpsc::channel::<Frame>(1000);
229
230 let connection_config: ConnectionConfig = config.clone().into();
231 let connection = ConnectionManager::new(url, connection_config, frame_tx).await?;
232
233 tokio::spawn(async move {
234 while let Some(frame) = frame_rx.recv().await {
235 store_clone.apply_frame(frame).await;
236 }
237 });
238
239 let view_builder = crate::view::ViewBuilder::new(
240 connection.clone(),
241 store.clone(),
242 config.initial_data_timeout,
243 );
244 let views = S::Views::from_builder(view_builder);
245
246 Ok(HyperStack {
247 connection,
248 store,
249 config,
250 views,
251 _stack: PhantomData,
252 })
253 }
254}