use core::time::Duration;
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Instant;
#[cfg(any(feature = "sync", feature = "async"))]
use ureq::Body;
#[cfg(any(feature = "sync", feature = "async"))]
use ureq::RequestBuilder;
#[cfg(any(feature = "sync", feature = "async"))]
use ureq::http::Response;
use ureq::http::Uri;
use crate::HTTPVerb;
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub mod async_funcs;
#[cfg(feature = "sync")]
#[cfg_attr(docsrs, doc(cfg(feature = "sync")))]
pub mod sync_funcs;
#[derive(Debug, Clone, bon::Builder)]
pub struct ApiRequest<P> {
#[builder(into)]
uri: Uri,
verb: HTTPVerb,
#[builder(default)]
headers: HashMap<String, String>,
body: Option<serde_json::Value>,
#[builder(into)]
#[cfg_attr(not(any(feature = "sync", feature = "async")), allow(dead_code))]
parser: Arc<P>,
#[builder(skip = 10 * 1024 * 1024)]
max_body_size: u64,
#[builder(skip)]
pub tries: u32,
#[builder(skip = Instant::now())]
pub retry_after: Instant,
}
impl<T> ApiRequest<T> {
pub fn uri(&self) -> &Uri {
&self.uri
}
pub fn verb(&self) -> HTTPVerb {
self.verb
}
pub fn headers(&self) -> &HashMap<String, String> {
&self.headers
}
pub fn body(&self) -> &Option<serde_json::Value> {
&self.body
}
pub fn max_body_size(&self) -> u64 {
self.max_body_size
}
pub fn reset(&mut self) {
self.tries = 0
}
pub fn increment_retry(&mut self, retry_after: Option<Instant>) {
self.tries += 1;
if let Some(retry_after) = retry_after {
self.retry_after = retry_after;
return;
}
let secs_to_wait = self.tries * (self.tries as f32 / 0.5).round() as u32;
self.retry_after = Instant::now() + Duration::from_secs(secs_to_wait as u64)
}
#[cfg(any(feature = "sync", feature = "async"))]
pub(super) fn config_req<Bod>(&self, req: RequestBuilder<Bod>) -> RequestBuilder<Bod> {
let mut req = req.config().http_status_as_error(false).build();
for (name, value) in &self.headers {
req = req.header(name, value)
}
req
}
pub fn set_parser<U>(self, parser: U) -> ApiRequest<U> {
ApiRequest {
body: self.body,
headers: self.headers,
max_body_size: self.max_body_size,
parser: Arc::new(parser),
retry_after: self.retry_after,
tries: self.tries,
uri: self.uri,
verb: self.verb,
}
}
pub fn headers_mut(&mut self) -> &mut HashMap<String, String> {
&mut self.headers
}
}
#[cfg(any(feature = "sync", feature = "async"))]
fn get_temporary_error_timeout(response: &Response<Body>) -> Option<Instant> {
let headers = response.headers();
let retry_after = headers.get("retry-after")?;
let Ok(retry_after) = retry_after.to_str() else {
return None;
};
let Ok(retry_after) = retry_after.parse::<u64>() else {
return None;
};
Some(Instant::now() + Duration::from_secs(retry_after + 1))
}