Skip to main content

hyperstack_sdk/
client.rs

1use crate::config::{ConnectionConfig, HyperStackConfig};
2use crate::connection::{ConnectionManager, ConnectionState};
3use crate::entity::Stack;
4use crate::error::HyperStackError;
5use crate::frame::Frame;
6use crate::store::{SharedStore, StoreConfig};
7use crate::view::Views;
8use std::marker::PhantomData;
9use std::time::Duration;
10use tokio::sync::mpsc;
11
12/// HyperStack client with typed views access.
13///
14/// ```ignore
15/// use hyperstack_sdk::prelude::*;
16/// use hyperstack_stacks::ore::OreStack;
17///
18/// let hs = HyperStack::<OreStack>::connect().await?;
19/// let rounds = hs.views.latest().get().await;
20/// ```
21pub struct HyperStack<S: Stack> {
22    connection: ConnectionManager,
23    store: SharedStore,
24    #[allow(dead_code)]
25    config: HyperStackConfig,
26    pub views: S::Views,
27    _stack: PhantomData<S>,
28}
29
30impl<S: Stack> HyperStack<S> {
31    /// Connect to the stack's default URL.
32    pub async fn connect() -> Result<Self, HyperStackError> {
33        Self::builder().connect().await
34    }
35
36    /// Connect with custom URL.
37    pub async fn connect_url(url: &str) -> Result<Self, HyperStackError> {
38        Self::builder().url(url).connect().await
39    }
40
41    /// Create a builder for custom configuration.
42    pub fn builder() -> HyperStackBuilder<S> {
43        HyperStackBuilder::new()
44    }
45
46    pub async fn connection_state(&self) -> ConnectionState {
47        self.connection.state().await
48    }
49
50    pub async fn disconnect(&self) {
51        self.connection.disconnect().await;
52    }
53
54    pub fn store(&self) -> &SharedStore {
55        &self.store
56    }
57}
58
59/// Builder for HyperStack with custom configuration.
60pub struct HyperStackBuilder<S: Stack> {
61    url: String,
62    config: HyperStackConfig,
63    _stack: PhantomData<S>,
64}
65
66impl<S: Stack> HyperStackBuilder<S> {
67    fn new() -> Self {
68        Self {
69            url: S::url().to_string(),
70            config: HyperStackConfig::default(),
71            _stack: PhantomData,
72        }
73    }
74
75    pub fn url(mut self, url: &str) -> Self {
76        self.url = url.to_string();
77        self
78    }
79
80    pub fn auto_reconnect(mut self, enabled: bool) -> Self {
81        self.config.auto_reconnect = enabled;
82        self
83    }
84
85    pub fn reconnect_intervals(mut self, intervals: Vec<Duration>) -> Self {
86        self.config.reconnect_intervals = intervals;
87        self
88    }
89
90    pub fn max_reconnect_attempts(mut self, max: u32) -> Self {
91        self.config.max_reconnect_attempts = max;
92        self
93    }
94
95    pub fn ping_interval(mut self, interval: Duration) -> Self {
96        self.config.ping_interval = interval;
97        self
98    }
99
100    pub fn initial_data_timeout(mut self, timeout: Duration) -> Self {
101        self.config.initial_data_timeout = timeout;
102        self
103    }
104
105    pub fn max_entries_per_view(mut self, max: usize) -> Self {
106        self.config.max_entries_per_view = Some(max);
107        self
108    }
109
110    pub fn unlimited_entries(mut self) -> Self {
111        self.config.max_entries_per_view = None;
112        self
113    }
114
115    pub async fn connect(self) -> Result<HyperStack<S>, HyperStackError> {
116        let store_config = StoreConfig {
117            max_entries_per_view: self.config.max_entries_per_view,
118        };
119        let store = SharedStore::with_config(store_config);
120        let store_clone = store.clone();
121
122        let (frame_tx, mut frame_rx) = mpsc::channel::<Frame>(1000);
123
124        let connection_config: ConnectionConfig = self.config.clone().into();
125        let connection = ConnectionManager::new(self.url, connection_config, frame_tx).await;
126
127        tokio::spawn(async move {
128            while let Some(frame) = frame_rx.recv().await {
129                store_clone.apply_frame(frame).await;
130            }
131        });
132
133        let view_builder = crate::view::ViewBuilder::new(
134            connection.clone(),
135            store.clone(),
136            self.config.initial_data_timeout,
137        );
138        let views = S::Views::from_builder(view_builder);
139
140        Ok(HyperStack {
141            connection,
142            store,
143            config: self.config,
144            views,
145            _stack: PhantomData,
146        })
147    }
148}