Skip to main content

polyoxide_gamma/
client.rs

1use polyoxide_core::{
2    HttpClient, HttpClientBuilder, RateLimiter, RetryConfig, DEFAULT_POOL_SIZE, DEFAULT_TIMEOUT_MS,
3};
4
5use crate::{
6    api::{
7        comments::Comments, events::Events, health::Health, markets::Markets, series::Series,
8        sports::Sports, tags::Tags, user::User,
9    },
10    error::GammaError,
11};
12
13const DEFAULT_BASE_URL: &str = "https://gamma-api.polymarket.com";
14
15/// Main Gamma API client
16#[derive(Clone)]
17pub struct Gamma {
18    pub(crate) http_client: HttpClient,
19}
20
21impl Gamma {
22    /// Create a new Gamma client with default configuration
23    pub fn new() -> Result<Self, GammaError> {
24        Self::builder().build()
25    }
26
27    /// Create a builder for configuring the client
28    pub fn builder() -> GammaBuilder {
29        GammaBuilder::new()
30    }
31
32    /// Get markets namespace
33    pub fn markets(&self) -> Markets {
34        Markets {
35            http_client: self.http_client.clone(),
36        }
37    }
38
39    /// Get events namespace
40    pub fn events(&self) -> Events {
41        Events {
42            http_client: self.http_client.clone(),
43        }
44    }
45
46    /// Get series namespace
47    pub fn series(&self) -> Series {
48        Series {
49            http_client: self.http_client.clone(),
50        }
51    }
52
53    /// Get tags namespace
54    pub fn tags(&self) -> Tags {
55        Tags {
56            http_client: self.http_client.clone(),
57        }
58    }
59
60    /// Get sports namespace
61    pub fn sports(&self) -> Sports {
62        Sports {
63            http_client: self.http_client.clone(),
64        }
65    }
66
67    /// Get comments namespace
68    pub fn comments(&self) -> Comments {
69        Comments {
70            http_client: self.http_client.clone(),
71        }
72    }
73
74    /// Get user namespace
75    pub fn user(&self) -> User {
76        User {
77            http_client: self.http_client.clone(),
78        }
79    }
80
81    /// Get health namespace
82    pub fn health(&self) -> Health {
83        Health {
84            http_client: self.http_client.clone(),
85        }
86    }
87}
88
89/// Builder for configuring Gamma client
90pub struct GammaBuilder {
91    base_url: String,
92    timeout_ms: u64,
93    pool_size: usize,
94    retry_config: Option<RetryConfig>,
95}
96
97impl GammaBuilder {
98    fn new() -> Self {
99        Self {
100            base_url: DEFAULT_BASE_URL.to_string(),
101            timeout_ms: DEFAULT_TIMEOUT_MS,
102            pool_size: DEFAULT_POOL_SIZE,
103            retry_config: None,
104        }
105    }
106
107    /// Set base URL for the API
108    pub fn base_url(mut self, url: impl Into<String>) -> Self {
109        self.base_url = url.into();
110        self
111    }
112
113    /// Set request timeout in milliseconds
114    pub fn timeout_ms(mut self, timeout: u64) -> Self {
115        self.timeout_ms = timeout;
116        self
117    }
118
119    /// Set connection pool size
120    pub fn pool_size(mut self, size: usize) -> Self {
121        self.pool_size = size;
122        self
123    }
124
125    /// Set retry configuration for 429 responses
126    pub fn with_retry_config(mut self, config: RetryConfig) -> Self {
127        self.retry_config = Some(config);
128        self
129    }
130
131    /// Build the Gamma client
132    pub fn build(self) -> Result<Gamma, GammaError> {
133        let mut builder = HttpClientBuilder::new(&self.base_url)
134            .timeout_ms(self.timeout_ms)
135            .pool_size(self.pool_size)
136            .with_rate_limiter(RateLimiter::gamma_default());
137        if let Some(config) = self.retry_config {
138            builder = builder.with_retry_config(config);
139        }
140        let http_client = builder.build()?;
141
142        Ok(Gamma { http_client })
143    }
144}
145
146impl Default for GammaBuilder {
147    fn default() -> Self {
148        Self::new()
149    }
150}
151
152#[cfg(test)]
153mod tests {
154    use super::*;
155
156    #[test]
157    fn test_builder_default() {
158        let builder = GammaBuilder::default();
159        assert_eq!(builder.base_url, DEFAULT_BASE_URL);
160        assert_eq!(builder.timeout_ms, DEFAULT_TIMEOUT_MS);
161        assert_eq!(builder.pool_size, DEFAULT_POOL_SIZE);
162    }
163
164    #[test]
165    fn test_builder_custom_url() {
166        let builder = GammaBuilder::new().base_url("https://custom.api.com");
167        assert_eq!(builder.base_url, "https://custom.api.com");
168    }
169
170    #[test]
171    fn test_builder_custom_timeout() {
172        let builder = GammaBuilder::new().timeout_ms(60_000);
173        assert_eq!(builder.timeout_ms, 60_000);
174    }
175
176    #[test]
177    fn test_builder_custom_pool_size() {
178        let builder = GammaBuilder::new().pool_size(20);
179        assert_eq!(builder.pool_size, 20);
180    }
181
182    #[test]
183    fn test_builder_custom_retry_config() {
184        let config = RetryConfig {
185            max_retries: 5,
186            initial_backoff_ms: 1000,
187            max_backoff_ms: 30_000,
188        };
189        let builder = GammaBuilder::new().with_retry_config(config);
190        let config = builder.retry_config.unwrap();
191        assert_eq!(config.max_retries, 5);
192        assert_eq!(config.initial_backoff_ms, 1000);
193    }
194
195    #[test]
196    fn test_builder_build_success() {
197        let gamma = Gamma::builder().build();
198        assert!(gamma.is_ok());
199    }
200
201    #[test]
202    fn test_builder_invalid_url() {
203        let result = Gamma::builder().base_url("://bad").build();
204        assert!(result.is_err());
205    }
206
207    #[test]
208    fn test_new_creates_client() {
209        let gamma = Gamma::new();
210        assert!(gamma.is_ok());
211    }
212
213    #[test]
214    fn test_client_namespaces_accessible() {
215        let gamma = Gamma::new().unwrap();
216        let _markets = gamma.markets();
217        let _events = gamma.events();
218        let _series = gamma.series();
219        let _tags = gamma.tags();
220        let _sports = gamma.sports();
221        let _comments = gamma.comments();
222        let _user = gamma.user();
223        let _health = gamma.health();
224    }
225}