viewpoint_core/api/options/mod.rs
1//! API context options for configuring HTTP clients.
2
3use std::collections::HashMap;
4use std::time::Duration;
5
6/// Options for creating an API request context.
7///
8/// # Example
9///
10/// ```no_run
11/// use viewpoint_core::api::APIContextOptions;
12/// use std::time::Duration;
13///
14/// let options = APIContextOptions::new()
15/// .base_url("https://api.example.com")
16/// .timeout(Duration::from_secs(30))
17/// .extra_http_headers([
18/// ("Authorization".to_string(), "Bearer token".to_string()),
19/// ]);
20/// ```
21#[derive(Debug, Clone, Default)]
22pub struct APIContextOptions {
23 /// Base URL for all requests. Relative URLs will be resolved against this.
24 pub(crate) base_url: Option<String>,
25 /// Extra HTTP headers to include in all requests.
26 pub(crate) extra_http_headers: HashMap<String, String>,
27 /// HTTP credentials for Basic/Digest authentication.
28 pub(crate) http_credentials: Option<HttpCredentials>,
29 /// Whether to ignore HTTPS certificate errors.
30 pub(crate) ignore_https_errors: bool,
31 /// Proxy configuration.
32 pub(crate) proxy: Option<ProxyConfig>,
33 /// Default timeout for requests.
34 pub(crate) timeout: Option<Duration>,
35 /// User agent string.
36 pub(crate) user_agent: Option<String>,
37}
38
39/// HTTP authentication credentials.
40#[derive(Debug, Clone)]
41pub struct HttpCredentials {
42 /// Username for authentication.
43 pub username: String,
44 /// Password for authentication.
45 pub password: String,
46 /// Optional origin to send credentials only to specific domain.
47 pub origin: Option<String>,
48 /// Whether to send credentials preemptively.
49 pub send: Option<CredentialSend>,
50}
51
52impl HttpCredentials {
53 /// Create new HTTP credentials.
54 pub fn new(username: impl Into<String>, password: impl Into<String>) -> Self {
55 Self {
56 username: username.into(),
57 password: password.into(),
58 origin: None,
59 send: None,
60 }
61 }
62
63 /// Set the origin to send credentials only to specific domain.
64 #[must_use]
65 pub fn origin(mut self, origin: impl Into<String>) -> Self {
66 self.origin = Some(origin.into());
67 self
68 }
69
70 /// Set when to send credentials.
71 #[must_use]
72 pub fn send(mut self, send: CredentialSend) -> Self {
73 self.send = Some(send);
74 self
75 }
76}
77
78/// When to send credentials.
79#[derive(Debug, Clone, Copy, PartialEq, Eq)]
80pub enum CredentialSend {
81 /// Send credentials only when the server requests them (401 response).
82 Unauthorized,
83 /// Always send credentials with every request (preemptive).
84 Always,
85}
86
87/// Proxy configuration.
88#[derive(Debug, Clone)]
89pub struct ProxyConfig {
90 /// Proxy server URL.
91 pub server: String,
92 /// Optional username for proxy authentication.
93 pub username: Option<String>,
94 /// Optional password for proxy authentication.
95 pub password: Option<String>,
96 /// Bypass proxy for these domains (comma-separated).
97 pub bypass: Option<String>,
98}
99
100impl ProxyConfig {
101 /// Create a new proxy configuration.
102 pub fn new(server: impl Into<String>) -> Self {
103 Self {
104 server: server.into(),
105 username: None,
106 password: None,
107 bypass: None,
108 }
109 }
110
111 /// Set proxy authentication credentials.
112 #[must_use]
113 pub fn credentials(mut self, username: impl Into<String>, password: impl Into<String>) -> Self {
114 self.username = Some(username.into());
115 self.password = Some(password.into());
116 self
117 }
118
119 /// Set domains to bypass the proxy.
120 #[must_use]
121 pub fn bypass(mut self, bypass: impl Into<String>) -> Self {
122 self.bypass = Some(bypass.into());
123 self
124 }
125}
126
127impl APIContextOptions {
128 /// Create a new options builder with default settings.
129 pub fn new() -> Self {
130 Self::default()
131 }
132
133 /// Set the base URL for all requests.
134 ///
135 /// Relative URLs passed to request methods will be resolved against this base URL.
136 ///
137 /// # Example
138 ///
139 /// ```no_run
140 /// use viewpoint_core::api::APIContextOptions;
141 ///
142 /// let options = APIContextOptions::new()
143 /// .base_url("https://api.example.com/v1");
144 /// // Now api.get("/users") will request https://api.example.com/v1/users
145 /// ```
146 #[must_use]
147 pub fn base_url(mut self, url: impl Into<String>) -> Self {
148 self.base_url = Some(url.into());
149 self
150 }
151
152 /// Set extra HTTP headers to include in all requests.
153 ///
154 /// # Example
155 ///
156 /// ```no_run
157 /// use viewpoint_core::api::APIContextOptions;
158 ///
159 /// let options = APIContextOptions::new()
160 /// .extra_http_headers([
161 /// ("Authorization".to_string(), "Bearer token".to_string()),
162 /// ("X-API-Key".to_string(), "secret".to_string()),
163 /// ]);
164 /// ```
165 #[must_use]
166 pub fn extra_http_headers(
167 mut self,
168 headers: impl IntoIterator<Item = (String, String)>,
169 ) -> Self {
170 self.extra_http_headers = headers.into_iter().collect();
171 self
172 }
173
174 /// Add a single extra HTTP header.
175 #[must_use]
176 pub fn header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
177 self.extra_http_headers.insert(name.into(), value.into());
178 self
179 }
180
181 /// Set HTTP credentials for Basic authentication.
182 ///
183 /// # Example
184 ///
185 /// ```no_run
186 /// use viewpoint_core::api::{APIContextOptions, HttpCredentials};
187 ///
188 /// let options = APIContextOptions::new()
189 /// .http_credentials(HttpCredentials::new("user", "pass"));
190 /// ```
191 #[must_use]
192 pub fn http_credentials(mut self, credentials: HttpCredentials) -> Self {
193 self.http_credentials = Some(credentials);
194 self
195 }
196
197 /// Set whether to ignore HTTPS certificate errors.
198 ///
199 /// **Warning**: This should only be used for testing. Never use this in production
200 /// as it makes the connection vulnerable to man-in-the-middle attacks.
201 #[must_use]
202 pub fn ignore_https_errors(mut self, ignore: bool) -> Self {
203 self.ignore_https_errors = ignore;
204 self
205 }
206
207 /// Set proxy configuration.
208 ///
209 /// # Example
210 ///
211 /// ```no_run
212 /// use viewpoint_core::api::{APIContextOptions, ProxyConfig};
213 ///
214 /// let options = APIContextOptions::new()
215 /// .proxy(ProxyConfig::new("http://proxy.example.com:8080")
216 /// .credentials("user", "pass"));
217 /// ```
218 #[must_use]
219 pub fn proxy(mut self, proxy: ProxyConfig) -> Self {
220 self.proxy = Some(proxy);
221 self
222 }
223
224 /// Set the default timeout for all requests.
225 ///
226 /// This can be overridden on a per-request basis.
227 #[must_use]
228 pub fn timeout(mut self, timeout: Duration) -> Self {
229 self.timeout = Some(timeout);
230 self
231 }
232
233 /// Set the user agent string.
234 #[must_use]
235 pub fn user_agent(mut self, user_agent: impl Into<String>) -> Self {
236 self.user_agent = Some(user_agent.into());
237 self
238 }
239}
240
241#[cfg(test)]
242mod tests;