Skip to content

Commit 144e1b9

Browse files
authored
Merge pull request #1 from ListenNotes/cameron-develop
Initial architecture review
2 parents e0a1ecb + 1753450 commit 144e1b9

File tree

9 files changed

+188
-9
lines changed

9 files changed

+188
-9
lines changed

.gitignore

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,2 @@
1-
# Generated by Cargo
2-
# will have compiled files and executables
3-
/target/
4-
5-
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
6-
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
1+
/target
72
Cargo.lock
8-
9-
# These are backup files generated by rustfmt
10-
**/*.rs.bk

Cargo.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[package]
2+
name = "podcast-api"
3+
version = "0.1.0"
4+
authors = ["Cameron Fyfe <cameron.j.fyfe@gmail.com>"]
5+
edition = "2018"
6+
7+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8+
9+
[dependencies]
10+
serde = { version = "1", features = ["derive"] }
11+
serde_json = "1"
12+
tokio = { version = "1", features = ["full"] }
13+
reqwest = { version = "0.11", features = ["json"] }

examples/sample/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/target

examples/sample/Cargo.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[package]
2+
name = "sample"
3+
version = "0.1.0"
4+
authors = ["Cameron Fyfe <cameron.j.fyfe@gmail.com>"]
5+
edition = "2018"
6+
7+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8+
9+
[dependencies]
10+
podcast-api = { version = "^0", path = "../../" }
11+
reqwest = { version = "0.11", features = ["json"] }
12+
serde = { version = "1", features = ["derive"] }
13+
serde_json = "1"
14+
tokio = { version = "1", features = ["full"] }

examples/sample/src/main.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
use serde_json::json;
2+
3+
#[tokio::main]
4+
async fn main() {
5+
// Api Key (None => Test API, Some(key) => Production API)
6+
let api_key = None;
7+
8+
// Create client
9+
let client = podcast_api::Client::new(reqwest::Client::new(), api_key);
10+
11+
// Call API
12+
match client
13+
.typeahead(&json!({
14+
"q": "startup",
15+
"show_podcasts": 1
16+
}))
17+
.await
18+
{
19+
Ok(response) => {
20+
println!("Successfully called \"typeahead\" endpoint.");
21+
println!("Response Body:");
22+
println!("{:?}", response);
23+
}
24+
Err(err) => {
25+
println!("Error calling \"typeahead\" endpoint:");
26+
println!("{:?},", err);
27+
}
28+
};
29+
}

src/api.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
pub enum Api {
2+
Production(String),
3+
Mock,
4+
}
5+
6+
impl Api {
7+
pub fn url(&self) -> &str {
8+
match &self {
9+
Api::Production(_) => "https://listen-api.listennotes.com/api/v2",
10+
Api::Mock => "https://listen-api-test.listennotes.com/api/v2",
11+
}
12+
}
13+
}

src/client.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
use super::{Api, Result};
2+
use serde_json::{json, Value};
3+
4+
pub struct Client {
5+
client: reqwest::Client,
6+
api: Api,
7+
}
8+
9+
impl Client {
10+
pub fn new(client: reqwest::Client, id: Option<String>) -> Client {
11+
Client {
12+
client,
13+
api: if let Some(id) = id {
14+
Api::Production(id)
15+
} else {
16+
Api::Mock
17+
},
18+
}
19+
}
20+
21+
pub async fn search(&self, parameters: &Value) -> Result<Value> {
22+
self.get("search", parameters).await
23+
}
24+
25+
pub async fn typeahead(&self, parameters: &Value) -> Result<Value> {
26+
self.get("typeahead", parameters).await
27+
}
28+
29+
pub async fn episode_by_id(&self, id: &str, parameters: &Value) -> Result<Value> {
30+
self.get(&format!("episodes/{}", id), parameters).await
31+
}
32+
33+
pub async fn episodes(&self, ids: &[&str], parameters: &Value) -> Result<Value> {
34+
self.post("episodes", &parameters.with("ids", &ids.join(",").as_str()))
35+
.await
36+
}
37+
38+
pub async fn genres(&self, parameters: &Value) -> Result<Value> {
39+
self.get("genres", parameters).await
40+
}
41+
42+
async fn get(&self, endpoint: &str, parameters: &Value) -> Result<Value> {
43+
Ok(self
44+
.client
45+
.get(format!("{}/{}", self.api.url(), endpoint))
46+
.query(parameters)
47+
.send()
48+
.await?
49+
.json()
50+
.await?)
51+
}
52+
53+
async fn post(&self, endpoint: &str, parameters: &Value) -> Result<Value> {
54+
Ok(self
55+
.client
56+
.post(format!("{}/{}", self.api.url(), endpoint))
57+
.header("Content-Type", "application/x-www-form-urlencoded")
58+
.body(serde_json::to_string(&parameters)?) // TODO: switch to URL encoding
59+
.send()
60+
.await?
61+
.json()
62+
.await?)
63+
}
64+
}
65+
66+
trait AddField {
67+
fn with(&self, key: &str, value: &str) -> Self;
68+
}
69+
70+
impl AddField for Value {
71+
fn with(&self, key: &str, value: &str) -> Self {
72+
let mut p = self.clone();
73+
p[key] = json!(value);
74+
p
75+
}
76+
}

src/error.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#[derive(Debug)]
2+
pub enum Error {
3+
Reqwest(reqwest::Error),
4+
Json(serde_json::Error),
5+
}
6+
7+
impl From<reqwest::Error> for Error {
8+
fn from(e: reqwest::Error) -> Error {
9+
Error::Reqwest(e)
10+
}
11+
}
12+
13+
impl From<serde_json::Error> for Error {
14+
fn from(e: serde_json::Error) -> Error {
15+
Error::Json(e)
16+
}
17+
}
18+
19+
impl std::error::Error for Error {
20+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
21+
match *self {
22+
Error::Reqwest(ref e) => Some(e),
23+
Error::Json(ref e) => Some(e),
24+
}
25+
}
26+
}
27+
28+
impl std::fmt::Display for Error {
29+
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
30+
write!(f, "{}", *self)
31+
}
32+
}

src/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
mod api;
2+
mod client;
3+
mod error;
4+
5+
use api::Api;
6+
7+
pub use client::Client;
8+
pub use error::Error;
9+
pub type Result<T> = std::result::Result<T, error::Error>;

0 commit comments

Comments
 (0)