1use crate::multi_value::MultiValue;
2use crate::{
3 exchange::{Exchange, Response},
4 request::Request,
5};
6use http::{StatusCode, header::HeaderMap};
7use std::borrow::Cow;
8use std::collections::HashMap;
9
10#[derive(Debug, thiserror::Error)]
11pub enum Error {
12 #[error("HTTP client error")]
13 Http(#[from] reqwest::Error),
14 #[error("Invalid header")]
15 Header(#[from] crate::request::HeaderError),
16 #[error("Header value serialization error")]
17 HeaderValueToStr(#[from] http::header::ToStrError),
18 #[error("Unexpected status")]
19 UnexpectedStatus {
20 status_code: http::StatusCode,
21 body: Option<String>,
22 },
23}
24
25pub async fn json_send<'a>(
26 client: &reqwest::Client,
27 request: Request<'a>,
28) -> Result<crate::exchange::Exchange<'a, serde_json::Value>, Error> {
29 let builder = build_request(client, &request)?;
30 let response = builder.send().await?;
31 let status_code = response.status();
32 let headers = response.headers();
33 let headers = response_headers_to_index_map(headers)?;
34
35 if status_code == StatusCode::OK {
36 let json = response.json().await?;
37
38 Ok(Exchange {
39 request,
40 response: Response {
41 headers,
42 data: json,
43 },
44 })
45 } else {
46 let body = response.text().await.ok();
48
49 Err(Error::UnexpectedStatus { status_code, body })
50 }
51}
52
53pub async fn text_send<'a>(
54 client: &reqwest::Client,
55 request: Request<'a>,
56) -> Result<crate::exchange::Exchange<'a, String>, Error> {
57 let builder = build_request(client, &request)?;
58 let response = builder.send().await?;
59 let status_code = response.status();
60 let headers = response.headers();
61 let headers = response_headers_to_index_map(headers)?;
62
63 if status_code == StatusCode::OK {
64 let text = response.text().await?;
65
66 Ok(Exchange {
67 request,
68 response: Response {
69 headers,
70 data: text,
71 },
72 })
73 } else {
74 let body = response.text().await.ok();
76
77 Err(Error::UnexpectedStatus { status_code, body })
78 }
79}
80
81fn build_request<'a>(
82 client: &reqwest::Client,
83 request: &'a Request<'a>,
84) -> Result<reqwest::RequestBuilder, crate::request::HeaderError> {
85 let builder = client
86 .request(request.method.clone(), request.url.clone())
87 .headers(request.header_map()?);
88
89 Ok(if let Some(body) = request.body.as_ref() {
90 builder.body(body.to_string())
91 } else {
92 builder
93 })
94}
95
96fn response_headers_to_index_map(
97 response_headers: &HeaderMap,
98) -> Result<HashMap<Cow<'static, str>, MultiValue<'static>>, http::header::ToStrError> {
99 let mut result: HashMap<Cow<'static, str>, MultiValue<'static>> = HashMap::new();
100
101 for (name, value) in response_headers {
102 let value = value.to_str()?;
103
104 match result.entry(name.as_str().to_string().into()) {
105 std::collections::hash_map::Entry::Occupied(mut entry) => {
106 let multi_value = entry.get_mut();
107 multi_value.push(value.to_string());
108 }
109 std::collections::hash_map::Entry::Vacant(entry) => {
110 entry.insert(MultiValue::new(value.to_string()));
111 }
112 }
113 }
114
115 Ok(result)
116}