1use reqwest::{Client, RequestBuilder, Response};
2use serde::de::DeserializeOwned;
3use std::time::Duration;
4
5use crate::error::{Error, Result};
6
7pub const USER_AGENT: &str = "Mozilla/5.0 (Linux; Android 15; Pixel 9) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Mobile Safari/537.36";
8pub const DEFAULT_HTTP_TIMEOUT: Duration = Duration::from_secs(15);
9
10pub fn with_user_agent(request: RequestBuilder) -> RequestBuilder {
11 request.header("User-Agent", USER_AGENT)
12}
13
14pub fn with_form_headers(request: RequestBuilder) -> RequestBuilder {
15 with_user_agent(request).header("Content-Type", "application/x-www-form-urlencoded")
16}
17
18pub async fn send(request: RequestBuilder) -> Result<Response> {
19 let response = request.send().await.map_err(Error::Transport)?;
20 ensure_success(response)
21}
22
23pub async fn send_json<T>(request: RequestBuilder) -> Result<T>
24where
25 T: DeserializeOwned,
26{
27 let response = send(request).await?;
28 response
29 .json::<T>()
30 .await
31 .map_err(|e| Error::Decode(e.to_string()))
32}
33
34pub async fn send_text(request: RequestBuilder) -> Result<String> {
35 let response = send(request).await?;
36 response
37 .text()
38 .await
39 .map_err(|e| Error::Decode(e.to_string()))
40}
41
42pub fn ensure_success(response: Response) -> Result<Response> {
43 if response.status().is_success() {
44 return Ok(response);
45 }
46
47 let status = response.status();
48 let url = response.url().to_string();
49 Err(Error::HttpStatus { status, url })
50}
51
52pub fn default_client() -> Client {
53 Client::builder()
54 .timeout(DEFAULT_HTTP_TIMEOUT)
55 .build()
56 .unwrap_or_else(|_| Client::new())
57}