22// SPDX-License-Identifier: Apache-2.0
33// SPDX-License-Identifier: MIT
44
5- use bytes:: Bytes ;
6- use reqwest:: { header:: HeaderName , redirect:: Policy , Method } ;
5+ use http:: { header:: HeaderName , Method } ;
76use serde:: { Deserialize , Serialize } ;
87use serde_json:: Value ;
98use serde_repr:: { Deserialize_repr , Serialize_repr } ;
109
1110use std:: { collections:: HashMap , path:: PathBuf , time:: Duration } ;
1211
1312/// Client builder.
14- #[ derive( Default , Deserialize ) ]
13+ #[ derive( Clone , Default , Deserialize ) ]
1514#[ serde( rename_all = "camelCase" ) ]
1615pub struct ClientBuilder {
1716 /// Max number of redirections to follow
@@ -38,12 +37,19 @@ impl ClientBuilder {
3837 self
3938 }
4039
41- /// Builds the ClientOptions.
40+ /// Builds the Client.
41+ #[ cfg( not( feature = "reqwest-client" ) ) ]
42+ pub fn build ( self ) -> crate :: api:: Result < Client > {
43+ Ok ( Client ( self ) )
44+ }
45+
46+ /// Builds the Client.
47+ #[ cfg( feature = "reqwest-client" ) ]
4248 pub fn build ( self ) -> crate :: api:: Result < Client > {
4349 let mut client_builder = reqwest:: Client :: builder ( ) ;
4450
4551 if let Some ( max_redirections) = self . max_redirections {
46- client_builder = client_builder. redirect ( Policy :: limited ( max_redirections) )
52+ client_builder = client_builder. redirect ( reqwest :: redirect :: Policy :: limited ( max_redirections) )
4753 }
4854
4955 if let Some ( connect_timeout) = self . connect_timeout {
@@ -56,16 +62,80 @@ impl ClientBuilder {
5662}
5763
5864/// The HTTP client.
65+ #[ cfg( feature = "reqwest-client" ) ]
5966#[ derive( Clone ) ]
6067pub struct Client ( reqwest:: Client ) ;
6168
69+ /// The HTTP client.
70+ #[ cfg( not( feature = "reqwest-client" ) ) ]
71+ #[ derive( Clone ) ]
72+ pub struct Client ( ClientBuilder ) ;
73+
74+ #[ cfg( not( feature = "reqwest-client" ) ) ]
75+ impl Client {
76+ /// Executes an HTTP request
77+ ///
78+ /// The response will be transformed to String,
79+ /// If reading the response as binary, the byte array will be serialized using serde_json.
80+ pub async fn send ( & self , request : HttpRequestBuilder ) -> crate :: api:: Result < Response > {
81+ let method = Method :: from_bytes ( request. method . to_uppercase ( ) . as_bytes ( ) ) ?;
82+
83+ let mut request_builder = attohttpc:: RequestBuilder :: try_new ( method, & request. url ) ?;
84+
85+ if let Some ( query) = request. query {
86+ request_builder = request_builder. params ( & query) ;
87+ }
88+
89+ if let Some ( headers) = request. headers {
90+ for ( header, header_value) in headers. iter ( ) {
91+ request_builder =
92+ request_builder. header ( HeaderName :: from_bytes ( header. as_bytes ( ) ) ?, header_value) ;
93+ }
94+ }
95+
96+ if let Some ( timeout) = request. timeout {
97+ request_builder = request_builder. timeout ( Duration :: from_secs ( timeout) ) ;
98+ }
99+
100+ let response = if let Some ( body) = request. body {
101+ match body {
102+ Body :: Bytes ( data) => request_builder. body ( attohttpc:: body:: Bytes ( data) ) . send ( ) ?,
103+ Body :: Text ( text) => request_builder. body ( attohttpc:: body:: Bytes ( text) ) . send ( ) ?,
104+ Body :: Json ( json) => request_builder. json ( & json) ?. send ( ) ?,
105+ Body :: Form ( form_body) => {
106+ let mut form = Vec :: new ( ) ;
107+ for ( name, part) in form_body. 0 {
108+ match part {
109+ FormPart :: Bytes ( bytes) => form. push ( ( name, serde_json:: to_string ( & bytes) ?) ) ,
110+ FormPart :: File ( file_path) => form. push ( ( name, serde_json:: to_string ( & file_path) ?) ) ,
111+ FormPart :: Text ( text) => form. push ( ( name, text) ) ,
112+ }
113+ }
114+ request_builder. form ( & form) ?. send ( ) ?
115+ }
116+ }
117+ } else {
118+ request_builder. send ( ) ?
119+ } ;
120+
121+ let response = response. error_for_status ( ) ?;
122+ Ok ( Response (
123+ request. response_type . unwrap_or ( ResponseType :: Json ) ,
124+ response,
125+ request. url ,
126+ ) )
127+ }
128+ }
129+
130+ #[ cfg( feature = "reqwest-client" ) ]
62131impl Client {
63132 /// Executes an HTTP request
64133 ///
65134 /// The response will be transformed to String,
66- /// If reading the response as binary, the byte array will be serialized using serde_json
135+ /// If reading the response as binary, the byte array will be serialized using serde_json.
67136 pub async fn send ( & self , request : HttpRequestBuilder ) -> crate :: api:: Result < Response > {
68137 let method = Method :: from_bytes ( request. method . to_uppercase ( ) . as_bytes ( ) ) ?;
138+
69139 let mut request_builder = self . 0 . request ( method, & request. url ) ;
70140
71141 if let Some ( query) = request. query {
@@ -85,8 +155,18 @@ impl Client {
85155
86156 let response = if let Some ( body) = request. body {
87157 match body {
88- Body :: Bytes ( data) => request_builder. body ( Bytes :: from ( data) ) . send ( ) . await ?,
89- Body :: Text ( text) => request_builder. body ( Bytes :: from ( text) ) . send ( ) . await ?,
158+ Body :: Bytes ( data) => {
159+ request_builder
160+ . body ( bytes:: Bytes :: from ( data) )
161+ . send ( )
162+ . await ?
163+ }
164+ Body :: Text ( text) => {
165+ request_builder
166+ . body ( bytes:: Bytes :: from ( text) )
167+ . send ( )
168+ . await ?
169+ }
90170 Body :: Json ( json) => request_builder. json ( & json) . send ( ) . await ?,
91171 Body :: Form ( form_body) => {
92172 let mut form = Vec :: new ( ) ;
@@ -249,24 +329,50 @@ impl HttpRequestBuilder {
249329}
250330
251331/// The HTTP response.
332+ #[ cfg( feature = "reqwest-client" ) ]
252333pub struct Response ( ResponseType , reqwest:: Response ) ;
334+ /// The HTTP response.
335+ #[ cfg( not( feature = "reqwest-client" ) ) ]
336+ pub struct Response ( ResponseType , attohttpc:: Response , String ) ;
253337
254338impl Response {
339+ /// Reads the response as raw bytes.
340+ pub async fn bytes ( self ) -> crate :: api:: Result < RawResponse > {
341+ let status = self . 1 . status ( ) . as_u16 ( ) ;
342+ #[ cfg( feature = "reqwest-client" ) ]
343+ let data = self . 1 . bytes ( ) . await ?. to_vec ( ) ;
344+ #[ cfg( not( feature = "reqwest-client" ) ) ]
345+ let data = self . 1 . bytes ( ) ?;
346+ Ok ( RawResponse { status, data } )
347+ }
348+
255349 /// Reads the response and returns its info.
256350 pub async fn read ( self ) -> crate :: api:: Result < ResponseData > {
351+ #[ cfg( feature = "reqwest-client" ) ]
257352 let url = self . 1 . url ( ) . to_string ( ) ;
353+ #[ cfg( not( feature = "reqwest-client" ) ) ]
354+ let url = self . 2 ;
355+
258356 let mut headers = HashMap :: new ( ) ;
259357 for ( name, value) in self . 1 . headers ( ) {
260358 headers. insert ( name. as_str ( ) . to_string ( ) , value. to_str ( ) ?. to_string ( ) ) ;
261359 }
262360 let status = self . 1 . status ( ) . as_u16 ( ) ;
263361
362+ #[ cfg( feature = "reqwest-client" ) ]
264363 let data = match self . 0 {
265364 ResponseType :: Json => self . 1 . json ( ) . await ?,
266365 ResponseType :: Text => Value :: String ( self . 1 . text ( ) . await ?) ,
267366 ResponseType :: Binary => Value :: String ( serde_json:: to_string ( & self . 1 . bytes ( ) . await ?) ?) ,
268367 } ;
269368
369+ #[ cfg( not( feature = "reqwest-client" ) ) ]
370+ let data = match self . 0 {
371+ ResponseType :: Json => self . 1 . json ( ) ?,
372+ ResponseType :: Text => Value :: String ( self . 1 . text ( ) ?) ,
373+ ResponseType :: Binary => Value :: String ( serde_json:: to_string ( & self . 1 . bytes ( ) ?) ?) ,
374+ } ;
375+
270376 Ok ( ResponseData {
271377 url,
272378 status,
@@ -276,12 +382,26 @@ impl Response {
276382 }
277383}
278384
385+ /// A response with raw bytes.
386+ #[ non_exhaustive]
387+ pub struct RawResponse {
388+ /// Response status code.
389+ pub status : u16 ,
390+ /// Response bytes.
391+ pub data : Vec < u8 > ,
392+ }
393+
279394/// The response type.
280395#[ derive( Serialize ) ]
281396#[ serde( rename_all = "camelCase" ) ]
397+ #[ non_exhaustive]
282398pub struct ResponseData {
283- url : String ,
284- status : u16 ,
285- headers : HashMap < String , String > ,
286- data : Value ,
399+ /// Response URL. Useful if it followed redirects.
400+ pub url : String ,
401+ /// Response status code.
402+ pub status : u16 ,
403+ /// Response headers.
404+ pub headers : HashMap < String , String > ,
405+ /// Response data.
406+ pub data : Value ,
287407}
0 commit comments