#[cfg(any(test, target_family = "wasm"))]
pub mod fetch;
#[cfg(not(target_family = "wasm"))]
pub use ::reqwest;
#[cfg(target_family = "wasm")]
pub use fetch as reqwest;
use self::reqwest::header::HeaderMap;
use crate::errors::{http_error_in, HttpError};
use crate::tr;
use anyhow::{anyhow, Result};
use serde_json::Value;
use std::time::Duration;
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Network {
pub proxy: String,
pub no_proxy: String,
pub insecure_tls: bool,
}
impl Network {
pub fn from_env() -> Self {
let var = |k: &str| std::env::var(k).unwrap_or_default();
Self {
proxy: var("ESCOUADE_PROXY").trim().to_string(),
no_proxy: var("ESCOUADE_NO_PROXY"),
insecure_tls: var("ESCOUADE_INSECURE_TLS").trim() == "1",
}
}
pub fn client(&self) -> Result<reqwest::Client> {
let mut b = reqwest::Client::builder()
.timeout(Duration::from_secs(15))
.danger_accept_invalid_certs(self.insecure_tls);
if !self.proxy.is_empty() {
let proxy = reqwest::Proxy::all(&self.proxy)?
.no_proxy(reqwest::NoProxy::from_string(&self.no_proxy));
b = b.proxy(proxy);
}
Ok(b.build()?)
}
}
pub async fn send_named(name: &str, rb: reqwest::RequestBuilder) -> Result<(Value, HeaderMap)> {
let resp = rb.send().await.map_err(|e| {
let e = anyhow::Error::from(e.without_url());
anyhow!(tr!(
"{name} injoignable : {e:#}",
"{name} can’t be reached: {e:#}"
))
})?;
let status = resp.status().as_u16();
let headers = resp.headers().clone();
let text = resp.text().await.unwrap_or_default();
if !(200..300).contains(&status) {
return Err(HttpError {
status,
message: http_error_in(crate::i18n::ui(), name, status, &text),
}
.into());
}
if text.trim().is_empty() {
return Ok((Value::Null, headers));
}
let v = serde_json::from_str(&text).map_err(|_| {
anyhow!(tr!(
"{name} : réponse illisible",
"{name}: unreadable answer"
))
})?;
Ok((v, headers))
}
pub async fn call(name: &str, rb: reqwest::RequestBuilder) -> Result<Value> {
Ok(send_named(name, rb).await?.0)
}
pub async fn call_page(name: &str, rb: reqwest::RequestBuilder) -> Result<(Value, bool)> {
let (v, headers) = send_named(name, rb).await?;
let next = headers
.get_all(reqwest::header::LINK)
.iter()
.filter_map(|h| h.to_str().ok())
.flat_map(|h| h.split(','))
.any(|link| link.contains("rel=\"next\""));
Ok((v, next))
}
pub fn encode(s: &str) -> String {
let mut out = String::new();
for b in s.bytes() {
if b.is_ascii_alphanumeric() || b"-_.~".contains(&b) {
out.push(b as char);
} else {
out.push_str(&format!("%{b:02X}"));
}
}
out
}
pub fn host_of(url: &str) -> String {
let rest = url.split_once("://").map_or(url, |(_, r)| r);
rest.split('/').next().unwrap_or("").to_ascii_lowercase()
}