1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
//! Types shared across all resources: [`ApiResponse`], [`RateLimitInfo`], [`RequestOptions`].
//!
//! [`RequestOptions`] allows per-call overrides of client defaults. Pass `None` to use client
//! defaults. For cancellation, wrap the call in `tokio::select!` or `tokio::time::timeout` — the
//! returned future is cancellable by being dropped.
use std::time::Duration;
use reqwest::header::HeaderMap;
use reqwest::StatusCode;
use crate::error::parse_numeric_header;
/// Response metadata + the parsed body. Returned from resource methods' `*_with_metadata` variants.
#[derive(Debug, Clone)]
pub struct ApiResponse<T> {
/// The parsed response body.
pub data: T,
/// HTTP status code.
pub status: StatusCode,
/// Raw response headers.
pub headers: HeaderMap,
/// Rate limit information, if any `X-RateLimit-*` or `Retry-After` headers were present.
pub rate_limit: Option<RateLimitInfo>,
}
/// Rate limit information parsed from `X-RateLimit-*` and `Retry-After` response headers.
#[derive(Debug, Clone, Copy, Default)]
pub struct RateLimitInfo {
/// Maximum number of requests allowed in the current window.
pub limit: u64,
/// Requests remaining in the current window.
pub remaining: u64,
/// Seconds to wait before retrying, or `None` if not rate-limited.
pub retry_after: Option<u64>,
}
impl RateLimitInfo {
/// Parse rate limit headers. Returns `None` if none of the relevant headers are present.
pub(crate) fn from_headers(headers: &HeaderMap) -> Option<Self> {
let limit = parse_numeric_header(headers, "x-ratelimit-limit");
let remaining = parse_numeric_header(headers, "x-ratelimit-remaining");
let retry_after = parse_numeric_header(headers, "retry-after");
if limit.is_none() && remaining.is_none() && retry_after.is_none() {
return None;
}
Some(Self {
limit: limit.unwrap_or(0),
remaining: remaining.unwrap_or(0),
retry_after,
})
}
}
/// Per-call overrides for a single request. Every resource method accepts `Option<RequestOptions>`.
///
/// `X-Tenant-Id` is never overridable per-call — changing tenants requires a new client.
///
/// Cancellation: wrap the call in `tokio::select!` or `tokio::time::timeout`; the returned future
/// is cancellable by being dropped.
#[derive(Debug, Clone, Default)]
pub struct RequestOptions {
/// Override `X-Namespace-Id` for this request.
pub namespace_id: Option<String>,
/// Override `X-User-Id` for this request (admin impersonation).
pub user_id: Option<String>,
/// Per-call timeout. Overrides [`ClientConfig::timeout`](crate::ClientConfig::timeout).
pub timeout: Option<Duration>,
/// Per-call retry count. `Some(0)` disables retries for this request.
pub retries: Option<u32>,
}
impl RequestOptions {
/// A fresh, empty set of options.
pub fn new() -> Self {
Self::default()
}
/// Override the namespace ID.
#[must_use]
pub fn with_namespace_id(mut self, namespace_id: impl Into<String>) -> Self {
self.namespace_id = Some(namespace_id.into());
self
}
/// Override the user ID.
#[must_use]
pub fn with_user_id(mut self, user_id: impl Into<String>) -> Self {
self.user_id = Some(user_id.into());
self
}
/// Override the request timeout.
#[must_use]
pub fn with_timeout(mut self, timeout: Duration) -> Self {
self.timeout = Some(timeout);
self
}
/// Override the retry count for this request.
#[must_use]
pub fn with_retries(mut self, retries: u32) -> Self {
self.retries = Some(retries);
self
}
}