use reqwest::{Client, RequestBuilder};
use serde::{Serialize, de::DeserializeOwned};
use std::sync::LazyLock;
use std::time::{SystemTime, UNIX_EPOCH};
use crate::error::SearchError;
pub use reqwest::{Method, Response, StatusCode};
pub const USER_AGENT: &str = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36";
pub const COOKIE: &str = "os=pc;osver=Microsoft-Windows-10-Professional-build-16299.125-64bit;appver=2.0.3.131777;channel=netease;__remember_me=true";
static HTTP_CLIENT: LazyLock<Client> = LazyLock::new(|| {
Client::builder()
.user_agent(USER_AGENT)
.build()
.expect("Failed to create HTTP client")
});
pub fn unix_seconds() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0)
}
pub fn unix_millis() -> u128 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_millis())
.unwrap_or(0)
}
pub async fn send(
method: Method,
url: &str,
headers: &[(&str, &str)],
) -> Result<Response, SearchError> {
send_request(method, url, headers, |request| request).await
}
pub async fn send_json(
url: &str,
body: &impl Serialize,
headers: &[(&str, &str)],
) -> Result<Response, SearchError> {
send_request(Method::POST, url, headers, |request| request.json(body)).await
}
pub async fn send_form(
url: &str,
form: &[(&str, &str)],
headers: &[(&str, &str)],
) -> Result<Response, SearchError> {
send_request(Method::POST, url, headers, |request| request.form(form)).await
}
pub async fn json<T: DeserializeOwned>(response: Response) -> Result<T, SearchError> {
ensure_success(&response)?;
response.json::<T>().await.map_err(SearchError::Http)
}
pub async fn text(response: Response) -> Result<String, SearchError> {
ensure_success(&response)?;
response.text().await.map_err(SearchError::Http)
}
async fn send_request(
method: Method,
url: &str,
headers: &[(&str, &str)],
body: impl FnOnce(RequestBuilder) -> RequestBuilder,
) -> Result<Response, SearchError> {
let mut request = HTTP_CLIENT.request(method, url);
for (key, value) in headers {
request = request.header(*key, *value);
}
body(request).send().await.map_err(SearchError::Http)
}
fn ensure_success(response: &Response) -> Result<(), SearchError> {
let status = response.status();
if status.is_success() {
Ok(())
} else {
Err(SearchError::Status(status.as_u16()))
}
}