koios_sdk/client/
builder.rs

1// src/client/builder.rs
2
3use crate::{
4    client::{AuthConfig, Client, Config},
5    error::Result,
6    network::Network,
7};
8use chrono::{DateTime, Utc};
9use governor::Quota;
10use std::time::Duration;
11
12/// Builder for creating a configured Client instance
13#[derive(Debug, Default)]
14pub struct ClientBuilder {
15    config: Config,
16}
17
18impl ClientBuilder {
19    /// Create a new client builder with default configuration
20    pub fn new() -> Self {
21        Self {
22            config: Config::default(),
23        }
24    }
25
26    /// Set the network to use (mainnet, preprod, preview, or guild)
27    pub fn network(mut self, network: Network) -> Self {
28        self.config.network = network;
29        self.config.custom_url = None; // Clear any custom URL when setting network
30        self
31    }
32
33    /// Set a custom base URL (overrides network setting)
34    pub fn base_url<S: Into<String>>(mut self, url: S) -> Self {
35        self.config.custom_url = Some(url.into());
36        self
37    }
38
39    /// Set the authentication token without expiry
40    pub fn auth_token<S: Into<String>>(mut self, token: S) -> Self {
41        self.config.auth = Some(AuthConfig::new(token.into()));
42        self
43    }
44
45    /// Set the authentication token with expiry
46    pub fn auth_token_with_expiry<S: Into<String>>(
47        mut self,
48        token: S,
49        expiry: DateTime<Utc>,
50    ) -> Self {
51        self.config.auth = Some(AuthConfig::with_expiry(token.into(), expiry));
52        self
53    }
54
55    /// Set authentication configuration directly
56    pub fn auth(mut self, auth: AuthConfig) -> Self {
57        self.config.auth = Some(auth);
58        self
59    }
60
61    /// Set the request timeout
62    pub fn timeout(mut self, timeout: Duration) -> Self {
63        self.config.timeout = timeout;
64        self
65    }
66
67    /// Set the rate limit quota
68    pub fn rate_limit(mut self, quota: Quota) -> Self {
69        self.config.rate_limit = quota;
70        self
71    }
72
73    /// Build the client with the current configuration
74    pub fn build(self) -> Result<Client> {
75        Client::with_config(self.config)
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82    use std::time::Duration;
83
84    #[test]
85    fn test_builder_default() {
86        let client = ClientBuilder::new().build().unwrap();
87        assert_eq!(client.base_url(), crate::DEFAULT_API_URL);
88    }
89
90    #[test]
91    fn test_builder_with_auth() {
92        let client = ClientBuilder::new()
93            .auth_token("test-token")
94            .build()
95            .unwrap();
96
97        // Use tokio runtime for async auth check
98        let rt = tokio::runtime::Runtime::new().unwrap();
99        assert!(rt.block_on(client.has_valid_auth()));
100        assert_eq!(client.auth_token(), Some("test-token".to_string()));
101    }
102
103    #[test]
104    fn test_builder_with_auth_expiry() {
105        let expiry = Utc::now() + chrono::Duration::hours(1);
106        let client = ClientBuilder::new()
107            .auth_token_with_expiry("test-token", expiry)
108            .build()
109            .unwrap();
110
111        let rt = tokio::runtime::Runtime::new().unwrap();
112        assert!(rt.block_on(client.has_valid_auth()));
113        assert_eq!(client.auth_token(), Some("test-token".to_string()));
114    }
115
116    #[test]
117    fn test_builder_with_expired_auth() {
118        let expiry = Utc::now() - chrono::Duration::hours(1);
119        let client = ClientBuilder::new()
120            .auth_token_with_expiry("test-token", expiry)
121            .build()
122            .unwrap();
123
124        let rt = tokio::runtime::Runtime::new().unwrap();
125        assert!(!rt.block_on(client.has_valid_auth()));
126        assert_eq!(client.auth_token(), None);
127    }
128
129    #[test]
130    fn test_builder_with_custom_timeout() {
131        let timeout = Duration::from_secs(60);
132        let client = ClientBuilder::new().timeout(timeout).build().unwrap();
133
134        // Note: We can't directly test the timeout value as it's private
135        // but we can verify the client builds successfully
136        assert_eq!(client.base_url(), crate::DEFAULT_API_URL);
137    }
138
139    #[test]
140    fn test_builder_with_rate_limit() {
141        let quota = Quota::per_second(50u32.try_into().unwrap());
142        let client = ClientBuilder::new().rate_limit(quota).build().unwrap();
143
144        assert_eq!(client.base_url(), crate::DEFAULT_API_URL);
145    }
146
147    #[test]
148    fn test_builder_with_all_options() {
149        let expiry = Utc::now() + chrono::Duration::hours(1);
150        let client = ClientBuilder::new()
151            .base_url("https://custom.api.com")
152            .auth_token_with_expiry("test-token", expiry)
153            .timeout(Duration::from_secs(60))
154            .rate_limit(Quota::per_second(50u32.try_into().unwrap()))
155            .build()
156            .unwrap();
157
158        assert_eq!(client.base_url(), "https://custom.api.com");
159        assert_eq!(client.auth_token(), Some("test-token".to_string()));
160    }
161
162    #[test]
163    fn test_builder_custom_auth_config() {
164        let auth = AuthConfig::new("test-token".to_string());
165        let client = ClientBuilder::new().auth(auth).build().unwrap();
166
167        assert_eq!(client.auth_token(), Some("test-token".to_string()));
168    }
169
170    #[test]
171    fn test_builder_chaining() {
172        let client = ClientBuilder::new()
173            .base_url("https://custom.api.com")
174            .auth_token("test-token")
175            .timeout(Duration::from_secs(60))
176            .build()
177            .unwrap();
178
179        assert_eq!(client.base_url(), "https://custom.api.com");
180        assert_eq!(client.auth_token(), Some("test-token".to_string()));
181    }
182}