From 0054557c869d383e4b822eb5c070062894d72159 Mon Sep 17 00:00:00 2001 From: spikecodes <19519553+spikecodes@users.noreply.github.com> Date: Wed, 18 Nov 2020 18:50:59 -0800 Subject: [PATCH] Make a request() utility --- Cargo.lock | 34 +++++++++++++++++----------------- Cargo.toml | 2 +- src/main.rs | 1 + src/popular.rs | 2 +- src/post.rs | 30 +++++++++++++++++------------- src/subreddit.rs | 44 +++++++++++++++++++++++--------------------- src/user.rs | 25 ++++++++++++++----------- src/utils.rs | 31 +++++++++++++++++++++++++++++++ 8 files changed, 105 insertions(+), 64 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2f36932..98cbf77 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,7 +71,7 @@ dependencies = [ "log", "mime", "percent-encoding", - "pin-project 1.0.1", + "pin-project 1.0.2", "rand", "regex", "serde", @@ -239,7 +239,7 @@ dependencies = [ "fxhash", "log", "mime", - "pin-project 1.0.1", + "pin-project 1.0.2", "regex", "serde", "serde_json", @@ -466,9 +466,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.62" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1770ced377336a88a67c473594ccc14eca6f4559217c34f64aac8f83d641b40" +checksum = "ad9c6140b5a2c7db40ea56eb1821245e5362b44385c05b76288b1a599934ac87" [[package]] name = "cfg-if" @@ -752,7 +752,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project 1.0.1", + "pin-project 1.0.2", "pin-utils", "proc-macro-hack", "proc-macro-nested", @@ -914,7 +914,7 @@ dependencies = [ "httparse", "httpdate", "itoa", - "pin-project 1.0.1", + "pin-project 1.0.2", "socket2", "tokio", "tower-service", @@ -1056,9 +1056,9 @@ checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" [[package]] name = "lock_api" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28247cc5a5be2f05fbcd76dd0cf2c7d3b5400cb978a28042abcd4fa0b3f8261c" +checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" dependencies = [ "scopeguard", ] @@ -1288,9 +1288,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4893845fa2ca272e647da5d0e46660a314ead9c2fdd9a883aabc32e481a8733" +checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" dependencies = [ "instant", "lock_api", @@ -1329,11 +1329,11 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee41d838744f60d959d7074e3afb6b35c7456d0f61cad38a24e35e6553f73841" +checksum = "9ccc2237c2c489783abd8c4c80e5450fc0e98644555b1364da68cc29aa151ca7" dependencies = [ - "pin-project-internal 1.0.1", + "pin-project-internal 1.0.2", ] [[package]] @@ -1349,9 +1349,9 @@ dependencies = [ [[package]] name = "pin-project-internal" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81a4ffa594b66bff340084d4081df649a7dc049ac8d7fc458d8e628bfbbb2f86" +checksum = "f8e8d2bf0b23038a4424865103a4df472855692821aab4e4f5c3312d461d9e5f" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.7", @@ -2096,9 +2096,9 @@ dependencies = [ [[package]] name = "unicode-normalization" -version = "0.1.14" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f98e67a4d84f730d343392f9bfff7d21e3fca562b9cb7a43b768350beeddc6" +checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606" dependencies = [ "tinyvec", ] diff --git a/Cargo.toml b/Cargo.toml index 79865f8..36de6ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,9 +9,9 @@ edition = "2018" [dependencies] actix-web = "3.2.0" +reqwest = "0.10.8" askama = "0.8.0" serde = "1.0.117" serde_json = "1.0" -reqwest = "0.10.8" pulldown-cmark = "0.8.0" chrono = "0.4.19" diff --git a/src/main.rs b/src/main.rs index 8293113..165be97 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,6 +24,7 @@ async fn favicon() -> HttpResponse { async fn main() -> std::io::Result<()> { // start http server println!("Running Libreddit on 0.0.0.0:8080!"); + HttpServer::new(|| { App::new() // GENERAL SERVICES diff --git a/src/popular.rs b/src/popular.rs index 5719150..5181003 100644 --- a/src/popular.rs +++ b/src/popular.rs @@ -20,7 +20,7 @@ struct PopularTemplate { // RENDER async fn render(sub_name: String, sort: String) -> Result { - let posts: Vec = posts(sub_name, &sort).await; + let posts: Vec = posts(sub_name, &sort).await?; let s = PopularTemplate { posts: posts, sort: sort }.render().unwrap(); Ok(HttpResponse::Ok().content_type("text/html").body(s)) diff --git a/src/post.rs b/src/post.rs index f2ab9c4..e902987 100644 --- a/src/post.rs +++ b/src/post.rs @@ -6,7 +6,7 @@ use pulldown_cmark::{html, Options, Parser}; #[path = "utils.rs"] mod utils; -use utils::{val, Comment, Flair, Params, Post}; +use utils::{val, Comment, Flair, Params, Post, request}; // STRUCTS #[derive(Template)] @@ -19,8 +19,8 @@ struct PostTemplate { async fn render(id: String, sort: String) -> Result { println!("id: {}", id); - let post: Post = fetch_post(&id).await; - let comments: Vec = fetch_comments(id, &sort).await.unwrap(); + let post: Post = fetch_post(&id).await?; + let comments: Vec = fetch_comments(id, &sort).await?; let s = PostTemplate { comments: comments, @@ -86,18 +86,19 @@ async fn markdown_to_html(md: &str) -> String { } // POSTS -async fn fetch_post(id: &String) -> Post { +async fn fetch_post(id: &String) -> Result { + // Build the Reddit JSON API url let url: String = format!("https://reddit.com/{}.json", id); - let resp: String = reqwest::get(&url).await.unwrap().text().await.unwrap(); - let data: serde_json::Value = serde_json::from_str(resp.as_str()).expect("Failed to parse JSON"); + // Send a request to the url, receive JSON in response + let res = request(url).await; - let post_data: &serde_json::Value = &data[0]["data"]["children"][0]; + let post_data: &serde_json::Value = &res[0]["data"]["children"][0]; let unix_time: i64 = post_data["data"]["created_utc"].as_f64().unwrap().round() as i64; let score = post_data["data"]["score"].as_i64().unwrap(); - Post { + let post = Post { title: val(post_data, "title").await, community: val(post_data, "subreddit").await, body: markdown_to_html(post_data["data"]["selftext"].as_str().unwrap()).await, @@ -115,17 +116,20 @@ async fn fetch_post(id: &String) -> Post { "white".to_string() }, ), - } + }; + + Ok(post) } // COMMENTS -async fn fetch_comments(id: String, sort: &String) -> Result, Box> { +async fn fetch_comments(id: String, sort: &String) -> Result> { + // Build the Reddit JSON API url let url: String = format!("https://reddit.com/{}.json?sort={}", id, sort); - let resp: String = reqwest::get(&url).await?.text().await?; - let data: serde_json::Value = serde_json::from_str(resp.as_str())?; + // Send a request to the url, receive JSON in response + let res = request(url).await; - let comment_data = data[1]["data"]["children"].as_array().unwrap(); + let comment_data = res[1]["data"]["children"].as_array().unwrap(); let mut comments: Vec = Vec::new(); diff --git a/src/subreddit.rs b/src/subreddit.rs index 71024c2..d6b3a30 100644 --- a/src/subreddit.rs +++ b/src/subreddit.rs @@ -5,7 +5,7 @@ use chrono::{TimeZone, Utc}; #[path = "utils.rs"] mod utils; -pub use utils::{val, Flair, Params, Post, Subreddit}; +pub use utils::{val, Flair, Params, Post, Subreddit, request}; // STRUCTS #[derive(Template)] @@ -17,8 +17,8 @@ struct SubredditTemplate { } async fn render(sub_name: String, sort: String) -> Result { - let mut sub: Subreddit = subreddit(&sub_name).await; - let posts: Vec = posts(sub_name, &sort).await; + let mut sub: Subreddit = subreddit(&sub_name).await?; + let posts: Vec = posts(sub_name, &sort).await?; sub.icon = if sub.icon != "" { format!(r#""#, sub.icon) @@ -41,35 +41,37 @@ async fn page(web::Path(sub): web::Path, params: web::Query) -> } // SUBREDDIT -async fn subreddit(sub: &String) -> Subreddit { - // Make a GET request to the Reddit's JSON API for the metadata of this subreddit +async fn subreddit(sub: &String) -> Result { + // Build the Reddit JSON API url let url: String = format!("https://www.reddit.com/r/{}/about.json", sub); - let resp: String = reqwest::get(&url).await.unwrap().text().await.unwrap(); - // Parse the response from Reddit as JSON - let data: serde_json::Value = serde_json::from_str(resp.as_str()).expect("Failed to parse JSON"); + // Send a request to the url, receive JSON in response + let res = request(url).await; - let icon: String = String::from(data["data"]["community_icon"].as_str().unwrap()); //val(&data, "community_icon"); + let icon: String = String::from(res["data"]["community_icon"].as_str().unwrap()); //val(&data, "community_icon"); let icon_split: std::str::Split<&str> = icon.split("?"); let icon_parts: Vec<&str> = icon_split.collect(); - Subreddit { - name: val(&data, "display_name").await, - title: val(&data, "title").await, - description: val(&data, "public_description").await, + let sub = Subreddit { + name: val(&res, "display_name").await, + title: val(&res, "title").await, + description: val(&res, "public_description").await, icon: String::from(icon_parts[0]), - } + }; + + Ok(sub) } // POSTS -pub async fn posts(sub: String, sort: &String) -> Vec { - // Make a GET request to the Reddit's JSON API for the content of this subreddit +pub async fn posts(sub: String, sort: &String) -> Result> { + // Build the Reddit JSON API url let url: String = format!("https://www.reddit.com/r/{}/{}.json", sub, sort); - let resp: String = reqwest::get(&url).await.unwrap().text().await.unwrap(); - // Parse the response from Reddit as JSON - let popular: serde_json::Value = serde_json::from_str(resp.as_str()).expect("Failed to parse JSON"); - let post_list = popular["data"]["children"].as_array().unwrap(); + // Send a request to the url, receive JSON in response + let res = request(url).await; + + // Fetch the list of posts from the JSON response + let post_list = res["data"]["children"].as_array().unwrap(); let mut posts: Vec = Vec::new(); @@ -101,5 +103,5 @@ pub async fn posts(sub: String, sort: &String) -> Vec { ), }); } - posts + Ok(posts) } diff --git a/src/user.rs b/src/user.rs index efabbf9..457e49b 100644 --- a/src/user.rs +++ b/src/user.rs @@ -5,7 +5,7 @@ use chrono::{TimeZone, Utc}; #[path = "utils.rs"] mod utils; -use utils::{nested_val, val, Flair, Params, Post, User}; +use utils::{nested_val, val, Flair, Params, Post, User, request}; // STRUCTS #[derive(Template)] @@ -35,27 +35,30 @@ async fn page(web::Path(username): web::Path, params: web::Query // USER async fn user(name: &String) -> User { + // Build the Reddit JSON API url let url: String = format!("https://www.reddit.com/user/{}/about.json", name); - let resp: String = reqwest::get(&url).await.unwrap().text().await.unwrap(); - - let data: serde_json::Value = serde_json::from_str(resp.as_str()).expect("Failed to parse JSON"); + + // Send a request to the url, receive JSON in response + let res = request(url).await; User { name: name.to_string(), - icon: nested_val(&data, "subreddit", "icon_img").await, - karma: data["data"]["total_karma"].as_i64().unwrap(), - banner: nested_val(&data, "subreddit", "banner_img").await, - description: nested_val(&data, "subreddit", "public_description").await, + icon: nested_val(&res, "subreddit", "icon_img").await, + karma: res["data"]["total_karma"].as_i64().unwrap(), + banner: nested_val(&res, "subreddit", "banner_img").await, + description: nested_val(&res, "subreddit", "public_description").await, } } // POSTS async fn posts(sub: String, sort: &String) -> Vec { + // Build the Reddit JSON API url let url: String = format!("https://www.reddit.com/u/{}/.json?sort={}", sub, sort); - let resp: String = reqwest::get(&url).await.unwrap().text().await.unwrap(); - let popular: serde_json::Value = serde_json::from_str(resp.as_str()).expect("Failed to parse JSON"); - let post_list = popular["data"]["children"].as_array().unwrap(); + // Send a request to the url, receive JSON in response + let res = request(url).await; + + let post_list = res["data"]["children"].as_array().unwrap(); let mut posts: Vec = Vec::new(); diff --git a/src/utils.rs b/src/utils.rs index 660c040..7bfa6e6 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -61,3 +61,34 @@ pub async fn nested_val(j: &serde_json::Value, n: &str, k: &str) -> String { pub struct Params { pub sort: Option, } + +// Make a request to a Reddit API and parse the JSON response +#[allow(dead_code)] +pub async fn request(url: String) -> serde_json::Value { + + // --- actix-web::client --- + // let client = actix_web::client::Client::default(); + // let res = client + // .get(url) + // .send() + // .await? + // .body() + // .limit(1000000) + // .await?; + + // let body = std::str::from_utf8(res.as_ref())?; // .as_ref converts Bytes to [u8] + + // --- surf --- + // let req = surf::get(url); + // let client = surf::client().with(surf::middleware::Redirect::new(5)); + // let mut res = client.send(req).await.unwrap(); + // let body = res.body_string().await.unwrap(); + + // --- reqwest --- + let resp: String = reqwest::get(&url).await.unwrap().text().await.unwrap(); + + // Parse the response from Reddit as JSON + let json: serde_json::Value = serde_json::from_str(resp.as_str()).expect("Failed to parse JSON"); + + json +}