1
1
use super :: { Api , Result } ;
2
2
use reqwest:: RequestBuilder ;
3
3
use serde_json:: Value ;
4
+ use std:: time:: Duration ;
5
+
6
+ static DEFAULT_USER_AGENT : & str = "api-podcast-rust" ;
4
7
5
8
/// Client for accessing Listen Notes API.
6
9
pub struct Client < ' a > {
7
10
/// HTTP client.
8
11
client : reqwest:: Client ,
9
12
/// API context.
10
13
api : Api < ' a > ,
14
+ /// User Agent Header for API calls.
15
+ user_agent : & ' a str ,
16
+ }
17
+
18
+ #[ derive( Debug ) ]
19
+ /// Response and request context for API call.
20
+ pub struct Response {
21
+ /// HTTP response.
22
+ pub response : reqwest:: Response ,
23
+ /// HTTP request that resulted in this response.
24
+ pub request : reqwest:: Request ,
25
+ }
26
+
27
+ impl Response {
28
+ /// Get JSON data object from [`reqwest::Response`].
29
+ pub async fn json ( self ) -> Result < Value > {
30
+ Ok ( self . response . json ( ) . await ?)
31
+ }
11
32
}
12
33
13
34
impl Client < ' _ > {
14
35
/// Creates new Listen API Client.
15
36
///
37
+ /// Uses default HTTP client with 30 second timeouts.
38
+ ///
16
39
/// To access production API:
17
40
/// ```
18
- /// let client = podcast_api::Client::new(reqwest::Client::new(), Some("YOUR-API-KEY"));
41
+ /// let client = podcast_api::Client::new(Some("YOUR-API-KEY"));
19
42
/// ```
20
43
/// To access mock API:
21
44
/// ```
22
- /// let client = podcast_api::Client::new(reqwest::Client::new(), None);
45
+ /// let client = podcast_api::Client::new(None);
23
46
/// ```
24
- pub fn new ( client : reqwest:: Client , id : Option < & str > ) -> Client {
47
+ pub fn new ( id : Option < & str > ) -> Client {
48
+ Client {
49
+ client : reqwest:: ClientBuilder :: new ( )
50
+ . timeout ( Duration :: from_secs ( 30 ) )
51
+ . build ( )
52
+ . expect ( "Client::new()" ) ,
53
+ api : if let Some ( id) = id {
54
+ Api :: Production ( id)
55
+ } else {
56
+ Api :: Mock
57
+ } ,
58
+ user_agent : DEFAULT_USER_AGENT ,
59
+ }
60
+ }
61
+
62
+ /// Creates new Listen API Client with user provided HTTP Client.
63
+ pub fn new_custom < ' a > (
64
+ client : reqwest:: Client ,
65
+ id : Option < & ' a str > ,
66
+ user_agent : Option < & ' a str > ,
67
+ ) -> Client < ' a > {
25
68
Client {
26
69
client,
27
70
api : if let Some ( id) = id {
28
71
Api :: Production ( id)
29
72
} else {
30
73
Api :: Mock
31
74
} ,
75
+ user_agent : if let Some ( user_agent) = user_agent {
76
+ user_agent
77
+ } else {
78
+ DEFAULT_USER_AGENT
79
+ } ,
32
80
}
33
81
}
34
82
35
83
/// Calls [`GET /search`](https://www.listennotes.com/api/docs/#get-api-v2-search) with supplied parameters.
36
- pub async fn search ( & self , parameters : & Value ) -> Result < Value > {
84
+ pub async fn search ( & self , parameters : & Value ) -> Result < Response > {
37
85
self . get ( "search" , parameters) . await
38
86
}
39
87
40
88
/// Calls [`GET /typeahead`](https://www.listennotes.com/api/docs/#get-api-v2-typeahead) with supplied parameters.
41
- pub async fn typeahead ( & self , parameters : & Value ) -> Result < Value > {
89
+ pub async fn typeahead ( & self , parameters : & Value ) -> Result < Response > {
42
90
self . get ( "typeahead" , parameters) . await
43
91
}
44
92
45
93
/// Calls [`GET /best_podcasts`](https://www.listennotes.com/api/docs/#get-api-v2-best_podcasts) with supplied parameters.
46
- pub async fn fetch_best_podcasts ( & self , parameters : & Value ) -> Result < Value > {
94
+ pub async fn fetch_best_podcasts ( & self , parameters : & Value ) -> Result < Response > {
47
95
self . get ( "best_podcasts" , parameters) . await
48
96
}
49
97
50
98
/// Calls [`GET /podcasts/{id}`](https://www.listennotes.com/api/docs/#get-api-v2-podcasts-id) with supplied parameters.
51
- pub async fn fetch_podcast_by_id ( & self , id : & str , parameters : & Value ) -> Result < Value > {
99
+ pub async fn fetch_podcast_by_id ( & self , id : & str , parameters : & Value ) -> Result < Response > {
52
100
self . get ( & format ! ( "podcasts/{}" , id) , parameters) . await
53
101
}
54
102
55
103
/// Calls [`POST /podcasts`](https://www.listennotes.com/api/docs/#post-api-v2-podcasts) with supplied parameters.
56
- pub async fn batch_fetch_podcasts ( & self , parameters : & Value ) -> Result < Value > {
104
+ pub async fn batch_fetch_podcasts ( & self , parameters : & Value ) -> Result < Response > {
57
105
self . post ( "podcasts" , parameters) . await
58
106
}
59
107
60
108
/// Calls [`GET /episodes/{id}`](https://www.listennotes.com/api/docs/#get-api-v2-episodes-id) with supplied parameters.
61
- pub async fn fetch_episode_by_id ( & self , id : & str , parameters : & Value ) -> Result < Value > {
109
+ pub async fn fetch_episode_by_id ( & self , id : & str , parameters : & Value ) -> Result < Response > {
62
110
self . get ( & format ! ( "episodes/{}" , id) , parameters) . await
63
111
}
64
112
65
113
/// Calls [`POST /episodes`](https://www.listennotes.com/api/docs/#post-api-v2-episodes) with supplied parameters.
66
- pub async fn batch_fetch_episodes ( & self , parameters : & Value ) -> Result < Value > {
114
+ pub async fn batch_fetch_episodes ( & self , parameters : & Value ) -> Result < Response > {
67
115
self . post ( "episodes" , parameters) . await
68
116
}
69
117
70
- async fn get ( & self , endpoint : & str , parameters : & Value ) -> Result < Value > {
118
+ async fn get ( & self , endpoint : & str , parameters : & Value ) -> Result < Response > {
71
119
let request = self
72
120
. client
73
121
. get ( format ! ( "{}/{}" , self . api. url( ) , endpoint) )
@@ -76,7 +124,7 @@ impl Client<'_> {
76
124
Ok ( self . request ( request) . await ?)
77
125
}
78
126
79
- async fn post ( & self , endpoint : & str , parameters : & Value ) -> Result < Value > {
127
+ async fn post ( & self , endpoint : & str , parameters : & Value ) -> Result < Response > {
80
128
let request = self
81
129
. client
82
130
. post ( format ! ( "{}/{}" , self . api. url( ) , endpoint) )
@@ -86,16 +134,19 @@ impl Client<'_> {
86
134
Ok ( self . request ( request) . await ?)
87
135
}
88
136
89
- async fn request ( & self , request : RequestBuilder ) -> Result < Value > {
90
- Ok ( if let Api :: Production ( key) = self . api {
137
+ async fn request ( & self , request : RequestBuilder ) -> Result < Response > {
138
+ let request = if let Api :: Production ( key) = self . api {
91
139
request. header ( "X-ListenAPI-Key" , key)
92
140
} else {
93
141
request
94
142
}
95
- . send ( )
96
- . await ?
97
- . json ( )
98
- . await ?)
143
+ . header ( "User-Agent" , self . user_agent )
144
+ . build ( ) ?;
145
+
146
+ Ok ( Response {
147
+ response : self . client . execute ( request. try_clone ( ) . expect ( "Error can remain unhandled because we're not using streams, which are the try_clone fail condition" ) ) . await ?,
148
+ request,
149
+ } )
99
150
}
100
151
101
152
fn urlencoded_from_json ( json : & Value ) -> String {
0 commit comments