1use polyoxide_core::{
2 HttpClient, HttpClientBuilder, RateLimiter, RetryConfig, DEFAULT_POOL_SIZE, DEFAULT_TIMEOUT_MS,
3};
4
5use crate::{
6 api::{
7 builders::BuildersApi,
8 health::Health,
9 holders::Holders,
10 live_volume::LiveVolumeApi,
11 open_interest::OpenInterestApi,
12 trades::Trades,
13 users::{UserApi, UserTraded},
14 },
15 error::DataApiError,
16};
17
18const DEFAULT_BASE_URL: &str = "https://data-api.polymarket.com";
19
20#[derive(Clone)]
22pub struct DataApi {
23 pub(crate) http_client: HttpClient,
24}
25
26impl DataApi {
27 pub fn new() -> Result<Self, DataApiError> {
29 Self::builder().build()
30 }
31
32 pub fn builder() -> DataApiBuilder {
34 DataApiBuilder::new()
35 }
36
37 pub fn health(&self) -> Health {
39 Health {
40 http_client: self.http_client.clone(),
41 }
42 }
43
44 pub fn user(&self, user_address: impl Into<String>) -> UserApi {
46 UserApi {
47 http_client: self.http_client.clone(),
48 user_address: user_address.into(),
49 }
50 }
51
52 pub fn positions(&self, user_address: impl Into<String>) -> UserApi {
54 self.user(user_address)
55 }
56
57 pub fn traded(&self, user_address: impl Into<String>) -> Traded {
59 Traded {
60 user_api: self.user(user_address),
61 }
62 }
63
64 pub fn trades(&self) -> Trades {
66 Trades {
67 http_client: self.http_client.clone(),
68 }
69 }
70
71 pub fn holders(&self) -> Holders {
73 Holders {
74 http_client: self.http_client.clone(),
75 }
76 }
77
78 pub fn open_interest(&self) -> OpenInterestApi {
80 OpenInterestApi {
81 http_client: self.http_client.clone(),
82 }
83 }
84
85 pub fn live_volume(&self) -> LiveVolumeApi {
87 LiveVolumeApi {
88 http_client: self.http_client.clone(),
89 }
90 }
91
92 pub fn builders(&self) -> BuildersApi {
94 BuildersApi {
95 http_client: self.http_client.clone(),
96 }
97 }
98}
99
100pub struct DataApiBuilder {
102 base_url: String,
103 timeout_ms: u64,
104 pool_size: usize,
105 retry_config: Option<RetryConfig>,
106}
107
108impl DataApiBuilder {
109 fn new() -> Self {
110 Self {
111 base_url: DEFAULT_BASE_URL.to_string(),
112 timeout_ms: DEFAULT_TIMEOUT_MS,
113 pool_size: DEFAULT_POOL_SIZE,
114 retry_config: None,
115 }
116 }
117
118 pub fn base_url(mut self, url: impl Into<String>) -> Self {
120 self.base_url = url.into();
121 self
122 }
123
124 pub fn timeout_ms(mut self, timeout: u64) -> Self {
126 self.timeout_ms = timeout;
127 self
128 }
129
130 pub fn pool_size(mut self, size: usize) -> Self {
132 self.pool_size = size;
133 self
134 }
135
136 pub fn with_retry_config(mut self, config: RetryConfig) -> Self {
138 self.retry_config = Some(config);
139 self
140 }
141
142 pub fn build(self) -> Result<DataApi, DataApiError> {
144 let mut builder = HttpClientBuilder::new(&self.base_url)
145 .timeout_ms(self.timeout_ms)
146 .pool_size(self.pool_size)
147 .with_rate_limiter(RateLimiter::data_default());
148 if let Some(config) = self.retry_config {
149 builder = builder.with_retry_config(config);
150 }
151 let http_client = builder.build()?;
152
153 Ok(DataApi { http_client })
154 }
155}
156
157impl Default for DataApiBuilder {
158 fn default() -> Self {
159 Self::new()
160 }
161}
162
163pub struct Traded {
165 user_api: UserApi,
166}
167
168impl Traded {
169 pub async fn get(self) -> std::result::Result<UserTraded, DataApiError> {
171 self.user_api.traded().await
172 }
173}
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178
179 #[test]
180 fn test_builder_default() {
181 let builder = DataApiBuilder::default();
182 assert_eq!(builder.base_url, DEFAULT_BASE_URL);
183 }
184
185 #[test]
186 fn test_builder_custom_retry_config() {
187 let config = RetryConfig {
188 max_retries: 5,
189 initial_backoff_ms: 1000,
190 max_backoff_ms: 30_000,
191 };
192 let builder = DataApiBuilder::new().with_retry_config(config);
193 let config = builder.retry_config.unwrap();
194 assert_eq!(config.max_retries, 5);
195 assert_eq!(config.initial_backoff_ms, 1000);
196 }
197
198 #[test]
199 fn test_builder_build_success() {
200 let data = DataApi::builder().build();
201 assert!(data.is_ok());
202 }
203}