Skip to content

Commit 1bb236d

Browse files
authored
Merge pull request #3 from ListenNotes/cameron-develop
Cameron develop
2 parents b421d57 + 583ceb8 commit 1bb236d

File tree

5 files changed

+243
-67
lines changed

5 files changed

+243
-67
lines changed

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ edition = "2018"
77
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
88

99
[dependencies]
10+
form_urlencoded = "1"
11+
http = "0.2"
1012
serde = { version = "1", features = ["derive"] }
1113
serde_json = "1"
1214
tokio = { version = "1", features = ["full"] }

examples/sample/src/main.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ async fn main() {
66
let api_key = None;
77

88
// Create client
9-
let client = podcast_api::Client::new(reqwest::Client::new(), api_key);
9+
let client = podcast_api::Client::new(api_key);
1010

1111
// Call API
1212
match client
@@ -18,8 +18,12 @@ async fn main() {
1818
{
1919
Ok(response) => {
2020
println!("Successfully called \"typeahead\" endpoint.");
21-
println!("Response Body:");
22-
println!("{:?}", response);
21+
if let Ok(body) = response.json().await {
22+
println!("Response Body:");
23+
println!("{:?}", body);
24+
} else {
25+
println!("Response body JSON data parsing error.")
26+
}
2327
}
2428
Err(err) => {
2529
println!("Error calling \"typeahead\" endpoint:");

src/client.rs

Lines changed: 69 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,121 @@
11
use super::{Api, Result};
22
use reqwest::RequestBuilder;
33
use serde_json::Value;
4+
use std::time::Duration;
5+
6+
static DEFAULT_USER_AGENT: &str = "api-podcast-rust";
47

58
/// Client for accessing Listen Notes API.
69
pub struct Client<'a> {
710
/// HTTP client.
811
client: reqwest::Client,
912
/// API context.
1013
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+
}
1132
}
1233

1334
impl Client<'_> {
1435
/// Creates new Listen API Client.
1536
///
37+
/// Uses default HTTP client with 30 second timeouts.
38+
///
1639
/// To access production API:
1740
/// ```
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"));
1942
/// ```
2043
/// To access mock API:
2144
/// ```
22-
/// let client = podcast_api::Client::new(reqwest::Client::new(), None);
45+
/// let client = podcast_api::Client::new(None);
2346
/// ```
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> {
2568
Client {
2669
client,
2770
api: if let Some(id) = id {
2871
Api::Production(id)
2972
} else {
3073
Api::Mock
3174
},
75+
user_agent: if let Some(user_agent) = user_agent {
76+
user_agent
77+
} else {
78+
DEFAULT_USER_AGENT
79+
},
3280
}
3381
}
3482

3583
/// 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> {
3785
self.get("search", parameters).await
3886
}
3987

4088
/// 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> {
4290
self.get("typeahead", parameters).await
4391
}
4492

4593
/// 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> {
4795
self.get("best_podcasts", parameters).await
4896
}
4997

5098
/// 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> {
52100
self.get(&format!("podcasts/{}", id), parameters).await
53101
}
54102

55103
/// 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> {
57105
self.post("podcasts", parameters).await
58106
}
59107

60108
/// 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> {
62110
self.get(&format!("episodes/{}", id), parameters).await
63111
}
64112

65113
/// 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> {
67115
self.post("episodes", parameters).await
68116
}
69117

70-
async fn get(&self, endpoint: &str, parameters: &Value) -> Result<Value> {
118+
async fn get(&self, endpoint: &str, parameters: &Value) -> Result<Response> {
71119
let request = self
72120
.client
73121
.get(format!("{}/{}", self.api.url(), endpoint))
@@ -76,7 +124,7 @@ impl Client<'_> {
76124
Ok(self.request(request).await?)
77125
}
78126

79-
async fn post(&self, endpoint: &str, parameters: &Value) -> Result<Value> {
127+
async fn post(&self, endpoint: &str, parameters: &Value) -> Result<Response> {
80128
let request = self
81129
.client
82130
.post(format!("{}/{}", self.api.url(), endpoint))
@@ -86,16 +134,19 @@ impl Client<'_> {
86134
Ok(self.request(request).await?)
87135
}
88136

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 {
91139
request.header("X-ListenAPI-Key", key)
92140
} else {
93141
request
94142
}
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+
})
99150
}
100151

101152
fn urlencoded_from_json(json: &Value) -> String {

src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
//! let api_key = None;
1212
//!
1313
//! // Create client
14-
//! let client = podcast_api::Client::new(reqwest::Client::new(), api_key);
14+
//! let client = podcast_api::Client::new(api_key);
1515
//!
1616
//! // Call API
1717
//! match client
@@ -42,6 +42,7 @@ mod error;
4242
use api::Api;
4343

4444
pub use client::Client;
45+
pub use client::Response;
4546
pub use error::Error;
4647
/// Result for API calls from [`Client`]
4748
pub type Result<T> = std::result::Result<T, error::Error>;

0 commit comments

Comments
 (0)