1use std::time::Duration;
2
3#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
5#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
6#[serde(rename_all = "lowercase")]
7pub enum MarketStatusFilter {
8 Active,
9 Closed,
10 Resolved,
11 All,
12}
13
14impl std::fmt::Display for MarketStatusFilter {
15 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
16 match self {
17 MarketStatusFilter::Active => write!(f, "active"),
18 MarketStatusFilter::Closed => write!(f, "closed"),
19 MarketStatusFilter::Resolved => write!(f, "resolved"),
20 MarketStatusFilter::All => write!(f, "all"),
21 }
22 }
23}
24
25impl std::str::FromStr for MarketStatusFilter {
26 type Err = String;
27
28 fn from_str(s: &str) -> Result<Self, Self::Err> {
29 match s.to_lowercase().as_str() {
30 "active" | "open" => Ok(MarketStatusFilter::Active),
31 "closed" | "inactive" | "paused" => Ok(MarketStatusFilter::Closed),
32 "resolved" | "settled" | "determined" | "finalized" => Ok(MarketStatusFilter::Resolved),
33 "all" => Ok(MarketStatusFilter::All),
34 _ => Err(format!("Unknown market status filter: {}", s)),
35 }
36 }
37}
38
39impl From<crate::models::MarketStatus> for MarketStatusFilter {
40 fn from(s: crate::models::MarketStatus) -> Self {
41 match s {
42 crate::models::MarketStatus::Active => MarketStatusFilter::Active,
43 crate::models::MarketStatus::Closed => MarketStatusFilter::Closed,
44 crate::models::MarketStatus::Resolved => MarketStatusFilter::Resolved,
45 }
46 }
47}
48
49#[derive(Debug, Clone)]
50pub struct ExchangeConfig {
51 pub timeout: Duration,
52 pub rate_limit_per_second: u32,
53 pub max_retries: u32,
54 pub retry_delay: Duration,
55 pub verbose: bool,
56}
57
58impl Default for ExchangeConfig {
59 fn default() -> Self {
60 Self {
61 timeout: Duration::from_secs(30),
62 rate_limit_per_second: 10,
63 max_retries: 3,
64 retry_delay: Duration::from_secs(1),
65 verbose: false,
66 }
67 }
68}
69
70impl ExchangeConfig {
71 pub fn new() -> Self {
72 Self::default()
73 }
74
75 pub fn with_timeout(mut self, timeout: Duration) -> Self {
76 self.timeout = timeout;
77 self
78 }
79
80 pub fn with_rate_limit(mut self, requests_per_second: u32) -> Self {
81 self.rate_limit_per_second = requests_per_second;
82 self
83 }
84
85 pub fn with_retries(mut self, max_retries: u32, delay: Duration) -> Self {
86 self.max_retries = max_retries;
87 self.retry_delay = delay;
88 self
89 }
90
91 pub fn with_verbose(mut self, verbose: bool) -> Self {
92 self.verbose = verbose;
93 self
94 }
95}
96
97#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
99#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
100pub struct FetchMarketsParams {
101 pub limit: Option<usize>,
103 #[serde(default)]
105 pub cursor: Option<String>,
106 #[serde(default)]
108 pub status: Option<MarketStatusFilter>,
109 #[serde(default)]
111 pub market_tickers: Vec<String>,
112 #[serde(default)]
118 pub series_ticker: Option<String>,
119 #[serde(default)]
121 pub event_ticker: Option<String>,
122}
123
124#[derive(Debug, Clone)]
158pub struct PolymarketCredentials {
159 pub private_key: Option<String>,
160 pub funder: Option<String>,
161 pub api_key: Option<String>,
162 pub api_secret: Option<String>,
163 pub api_passphrase: Option<String>,
164 pub signature_type: String,
165}
166
167impl PolymarketCredentials {
168 pub fn from_fields(
174 private_key: Option<String>,
175 funder: Option<String>,
176 api_key: Option<String>,
177 api_secret: Option<String>,
178 api_passphrase: Option<String>,
179 signature_type: Option<String>,
180 ) -> Self {
181 let resolved_signature_type = signature_type.unwrap_or_else(|| {
183 if funder.is_some() {
184 "GnosisSafe".to_string()
185 } else {
186 "EOA".to_string()
187 }
188 });
189
190 Self {
191 private_key,
192 funder,
193 api_key,
194 api_secret,
195 api_passphrase,
196 signature_type: resolved_signature_type,
197 }
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use super::*;
204 use crate::models::MarketStatus;
205
206 #[test]
207 fn market_status_filter_from_str() {
208 assert_eq!(
209 "active".parse::<MarketStatusFilter>().unwrap(),
210 MarketStatusFilter::Active
211 );
212 assert_eq!(
213 "open".parse::<MarketStatusFilter>().unwrap(),
214 MarketStatusFilter::Active
215 );
216 assert_eq!(
217 "closed".parse::<MarketStatusFilter>().unwrap(),
218 MarketStatusFilter::Closed
219 );
220 assert_eq!(
221 "resolved".parse::<MarketStatusFilter>().unwrap(),
222 MarketStatusFilter::Resolved
223 );
224 assert_eq!(
225 "settled".parse::<MarketStatusFilter>().unwrap(),
226 MarketStatusFilter::Resolved
227 );
228 assert_eq!(
229 "all".parse::<MarketStatusFilter>().unwrap(),
230 MarketStatusFilter::All
231 );
232 assert_eq!(
233 "ALL".parse::<MarketStatusFilter>().unwrap(),
234 MarketStatusFilter::All
235 );
236 assert!("invalid".parse::<MarketStatusFilter>().is_err());
237 }
238
239 #[test]
240 fn market_status_filter_display() {
241 assert_eq!(MarketStatusFilter::Active.to_string(), "active");
242 assert_eq!(MarketStatusFilter::Closed.to_string(), "closed");
243 assert_eq!(MarketStatusFilter::Resolved.to_string(), "resolved");
244 assert_eq!(MarketStatusFilter::All.to_string(), "all");
245 }
246
247 #[test]
248 fn market_status_filter_from_market_status() {
249 assert_eq!(
250 MarketStatusFilter::from(MarketStatus::Active),
251 MarketStatusFilter::Active
252 );
253 assert_eq!(
254 MarketStatusFilter::from(MarketStatus::Closed),
255 MarketStatusFilter::Closed
256 );
257 assert_eq!(
258 MarketStatusFilter::from(MarketStatus::Resolved),
259 MarketStatusFilter::Resolved
260 );
261 }
262
263 #[test]
264 fn market_status_filter_serde_roundtrip() {
265 let filter = MarketStatusFilter::All;
266 let json = serde_json::to_string(&filter).unwrap();
267 assert_eq!(json, "\"all\"");
268 let parsed: MarketStatusFilter = serde_json::from_str(&json).unwrap();
269 assert_eq!(parsed, MarketStatusFilter::All);
270 }
271
272 #[test]
273 fn fetch_markets_params_default_status_is_none() {
274 let params = FetchMarketsParams::default();
275 assert!(params.status.is_none());
276 }
277
278 #[test]
279 fn fetch_markets_params_serde_with_all_status() {
280 let params = FetchMarketsParams {
281 status: Some(MarketStatusFilter::All),
282 ..Default::default()
283 };
284 let json = serde_json::to_value(¶ms).unwrap();
285 assert_eq!(json["status"], "all");
286
287 let parsed: FetchMarketsParams = serde_json::from_value(json).unwrap();
288 assert_eq!(parsed.status, Some(MarketStatusFilter::All));
289 }
290}
291
292#[derive(Debug, Clone)]
293pub struct KalshiCredentials {
294 pub api_key_id: String,
295 pub private_key: String,
296}