Error Pages
This commit is contained in:
parent
7a176c6804
commit
1960e8a0fb
7 changed files with 204 additions and 63 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -958,9 +958,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.8"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb1fc4429a33e1f80d41dc9fea4d108a88bec1de8053878898ae448a0b52f613"
|
||||
checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
|
|
@ -8,7 +8,7 @@ use subreddit::{posts, Post};
|
|||
|
||||
#[path = "utils.rs"]
|
||||
mod utils;
|
||||
use utils::Params;
|
||||
use utils::{ErrorTemplate, Params};
|
||||
|
||||
// STRUCTS
|
||||
#[derive(Template)]
|
||||
|
@ -33,7 +33,17 @@ async fn render(sub_name: String, sort: Option<String>, ends: (Option<String>, O
|
|||
},
|
||||
};
|
||||
|
||||
let items = posts(url).await?;
|
||||
let items_result = posts(url).await;
|
||||
|
||||
if items_result.is_err() {
|
||||
let s = ErrorTemplate {
|
||||
message: items_result.err().unwrap().to_string(),
|
||||
}
|
||||
.render()
|
||||
.unwrap();
|
||||
Ok(HttpResponse::Ok().content_type("text/html").body(s))
|
||||
} else {
|
||||
let items = items_result.unwrap();
|
||||
|
||||
let s = PopularTemplate {
|
||||
posts: items.0,
|
||||
|
@ -43,6 +53,7 @@ async fn render(sub_name: String, sort: Option<String>, ends: (Option<String>, O
|
|||
.render()
|
||||
.unwrap();
|
||||
Ok(HttpResponse::Ok().content_type("text/html").body(s))
|
||||
}
|
||||
}
|
||||
|
||||
// SERVICES
|
||||
|
|
46
src/post.rs
46
src/post.rs
|
@ -6,7 +6,7 @@ use pulldown_cmark::{html, Options, Parser};
|
|||
|
||||
#[path = "utils.rs"]
|
||||
mod utils;
|
||||
use utils::{request, val, Comment, Flair, Params, Post};
|
||||
use utils::{request, val, Comment, ErrorTemplate, Flair, Params, Post};
|
||||
|
||||
// STRUCTS
|
||||
#[derive(Template)]
|
||||
|
@ -19,20 +19,26 @@ struct PostTemplate {
|
|||
|
||||
async fn render(id: String, sort: String) -> Result<HttpResponse> {
|
||||
println!("id: {}", id);
|
||||
let post: Post = fetch_post(&id).await?;
|
||||
let comments: Vec<Comment> = fetch_comments(id, &sort).await?;
|
||||
let post = fetch_post(&id).await;
|
||||
let comments = fetch_comments(id, &sort).await;
|
||||
|
||||
if post.is_err() || comments.is_err() {
|
||||
let s = ErrorTemplate {
|
||||
message: post.err().unwrap().to_string(),
|
||||
}
|
||||
.render()
|
||||
.unwrap();
|
||||
Ok(HttpResponse::Ok().content_type("text/html").body(s))
|
||||
} else {
|
||||
let s = PostTemplate {
|
||||
comments: comments,
|
||||
post: post,
|
||||
comments: comments.unwrap(),
|
||||
post: post.unwrap(),
|
||||
sort: sort,
|
||||
}
|
||||
.render()
|
||||
.unwrap();
|
||||
|
||||
// println!("{}", s);
|
||||
|
||||
Ok(HttpResponse::Ok().content_type("text/html").body(s))
|
||||
}
|
||||
}
|
||||
|
||||
// SERVICES
|
||||
|
@ -86,12 +92,20 @@ async fn markdown_to_html(md: &str) -> String {
|
|||
}
|
||||
|
||||
// POSTS
|
||||
async fn fetch_post(id: &String) -> Result<Post> {
|
||||
async fn fetch_post(id: &String) -> Result<Post, &'static str> {
|
||||
// Build the Reddit JSON API url
|
||||
let url: String = format!("https://reddit.com/{}.json", id);
|
||||
|
||||
// Send a request to the url, receive JSON in response
|
||||
let res = request(url).await;
|
||||
let req = request(url).await;
|
||||
|
||||
// If the Reddit API returns an error, exit this function
|
||||
if req.is_err() {
|
||||
return Err(req.err().unwrap());
|
||||
}
|
||||
|
||||
// Otherwise, grab the JSON output from the request
|
||||
let res = req.unwrap();
|
||||
|
||||
let post_data: &serde_json::Value = &res[0]["data"]["children"][0];
|
||||
|
||||
|
@ -122,12 +136,20 @@ async fn fetch_post(id: &String) -> Result<Post> {
|
|||
}
|
||||
|
||||
// COMMENTS
|
||||
async fn fetch_comments(id: String, sort: &String) -> Result<Vec<Comment>> {
|
||||
async fn fetch_comments(id: String, sort: &String) -> Result<Vec<Comment>, &'static str> {
|
||||
// Build the Reddit JSON API url
|
||||
let url: String = format!("https://reddit.com/{}.json?sort={}", id, sort);
|
||||
|
||||
// Send a request to the url, receive JSON in response
|
||||
let res = request(url).await;
|
||||
let req = request(url).await;
|
||||
|
||||
// If the Reddit API returns an error, exit this function
|
||||
if req.is_err() {
|
||||
return Err(req.err().unwrap());
|
||||
}
|
||||
|
||||
// Otherwise, grab the JSON output from the request
|
||||
let res = req.unwrap();
|
||||
|
||||
let comment_data = res[1]["data"]["children"].as_array().unwrap();
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ use chrono::{TimeZone, Utc};
|
|||
|
||||
#[path = "utils.rs"]
|
||||
mod utils;
|
||||
pub use utils::{request, val, Flair, Params, Post, Subreddit};
|
||||
pub use utils::{request, val, ErrorTemplate, Flair, Params, Post, Subreddit};
|
||||
|
||||
// STRUCTS
|
||||
#[derive(Template)]
|
||||
|
@ -37,8 +37,19 @@ pub async fn render(sub_name: String, sort: Option<String>, ends: (Option<String
|
|||
},
|
||||
};
|
||||
|
||||
let mut sub: Subreddit = subreddit(&sub_name).await?;
|
||||
let items = posts(url).await?;
|
||||
let sub_result = subreddit(&sub_name).await;
|
||||
let items_result = posts(url).await;
|
||||
|
||||
if sub_result.is_err() || items_result.is_err() {
|
||||
let s = ErrorTemplate {
|
||||
message: sub_result.err().unwrap().to_string(),
|
||||
}
|
||||
.render()
|
||||
.unwrap();
|
||||
Ok(HttpResponse::Ok().content_type("text/html").body(s))
|
||||
} else {
|
||||
let mut sub = sub_result.unwrap();
|
||||
let items = items_result.unwrap();
|
||||
|
||||
sub.icon = if sub.icon != "" {
|
||||
format!(r#"<img class="subreddit_icon" src="{}">"#, sub.icon)
|
||||
|
@ -55,15 +66,24 @@ pub async fn render(sub_name: String, sort: Option<String>, ends: (Option<String
|
|||
.render()
|
||||
.unwrap();
|
||||
Ok(HttpResponse::Ok().content_type("text/html").body(s))
|
||||
}
|
||||
}
|
||||
|
||||
// SUBREDDIT
|
||||
async fn subreddit(sub: &String) -> Result<Subreddit> {
|
||||
async fn subreddit(sub: &String) -> Result<Subreddit, &'static str> {
|
||||
// Build the Reddit JSON API url
|
||||
let url: String = format!("https://www.reddit.com/r/{}/about.json", sub);
|
||||
|
||||
// Send a request to the url, receive JSON in response
|
||||
let res = request(url).await;
|
||||
let req = request(url).await;
|
||||
|
||||
// If the Reddit API returns an error, exit this function
|
||||
if req.is_err() {
|
||||
return Err(req.err().unwrap());
|
||||
}
|
||||
|
||||
// Otherwise, grab the JSON output from the request
|
||||
let res = req.unwrap();
|
||||
|
||||
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("?");
|
||||
|
@ -80,9 +100,17 @@ async fn subreddit(sub: &String) -> Result<Subreddit> {
|
|||
}
|
||||
|
||||
// POSTS
|
||||
pub async fn posts(url: String) -> Result<(Vec<Post>, String)> {
|
||||
pub async fn posts(url: String) -> Result<(Vec<Post>, String), &'static str> {
|
||||
// Send a request to the url, receive JSON in response
|
||||
let res = request(url).await;
|
||||
let req = request(url).await;
|
||||
|
||||
// If the Reddit API returns an error, exit this function
|
||||
if req.is_err() {
|
||||
return Err(req.err().unwrap());
|
||||
}
|
||||
|
||||
// Otherwise, grab the JSON output from the request
|
||||
let res = req.unwrap();
|
||||
|
||||
// Fetch the list of posts from the JSON response
|
||||
let post_list = res["data"]["children"].as_array().unwrap();
|
||||
|
|
54
src/user.rs
54
src/user.rs
|
@ -5,7 +5,7 @@ use chrono::{TimeZone, Utc};
|
|||
|
||||
#[path = "utils.rs"]
|
||||
mod utils;
|
||||
use utils::{nested_val, request, val, Flair, Params, Post, User};
|
||||
use utils::{nested_val, request, val, ErrorTemplate, Flair, Params, Post, User};
|
||||
|
||||
// STRUCTS
|
||||
#[derive(Template)]
|
||||
|
@ -17,11 +17,26 @@ struct UserTemplate {
|
|||
}
|
||||
|
||||
async fn render(username: String, sort: String) -> Result<HttpResponse> {
|
||||
let user: User = user(&username).await;
|
||||
let posts: Vec<Post> = posts(username, &sort).await;
|
||||
let user = user(&username).await;
|
||||
let posts = posts(username, &sort).await;
|
||||
|
||||
let s = UserTemplate { user: user, posts: posts, sort: sort }.render().unwrap();
|
||||
if user.is_err() || posts.is_err() {
|
||||
let s = ErrorTemplate {
|
||||
message: user.err().unwrap().to_string(),
|
||||
}
|
||||
.render()
|
||||
.unwrap();
|
||||
Ok(HttpResponse::Ok().content_type("text/html").body(s))
|
||||
} else {
|
||||
let s = UserTemplate {
|
||||
user: user.unwrap(),
|
||||
posts: posts.unwrap(),
|
||||
sort: sort,
|
||||
}
|
||||
.render()
|
||||
.unwrap();
|
||||
Ok(HttpResponse::Ok().content_type("text/html").body(s))
|
||||
}
|
||||
}
|
||||
|
||||
// SERVICES
|
||||
|
@ -34,29 +49,46 @@ async fn page(web::Path(username): web::Path<String>, params: web::Query<Params>
|
|||
}
|
||||
|
||||
// USER
|
||||
async fn user(name: &String) -> User {
|
||||
async fn user(name: &String) -> Result<User, &'static str> {
|
||||
// Build the Reddit JSON API url
|
||||
let url: String = format!("https://www.reddit.com/user/{}/about.json", name);
|
||||
|
||||
// Send a request to the url, receive JSON in response
|
||||
let res = request(url).await;
|
||||
let req = request(url).await;
|
||||
|
||||
User {
|
||||
// If the Reddit API returns an error, exit this function
|
||||
if req.is_err() {
|
||||
return Err(req.err().unwrap());
|
||||
}
|
||||
|
||||
// Otherwise, grab the JSON output from the request
|
||||
let res = req.unwrap();
|
||||
|
||||
// Parse the JSON output into a User struct
|
||||
Ok(User {
|
||||
name: name.to_string(),
|
||||
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<Post> {
|
||||
async fn posts(sub: String, sort: &String) -> Result<Vec<Post>, &'static str> {
|
||||
// Build the Reddit JSON API url
|
||||
let url: String = format!("https://www.reddit.com/u/{}/.json?sort={}", sub, sort);
|
||||
|
||||
// Send a request to the url, receive JSON in response
|
||||
let res = request(url).await;
|
||||
let req = request(url).await;
|
||||
|
||||
// If the Reddit API returns an error, exit this function
|
||||
if req.is_err() {
|
||||
return Err(req.err().unwrap());
|
||||
}
|
||||
|
||||
// Otherwise, grab the JSON output from the request
|
||||
let res = req.unwrap();
|
||||
|
||||
let post_list = res["data"]["children"].as_array().unwrap();
|
||||
|
||||
|
@ -93,5 +125,5 @@ async fn posts(sub: String, sort: &String) -> Vec<Post> {
|
|||
});
|
||||
}
|
||||
|
||||
posts
|
||||
Ok(posts)
|
||||
}
|
||||
|
|
38
src/utils.rs
38
src/utils.rs
|
@ -1,3 +1,6 @@
|
|||
//
|
||||
// STRUCTS
|
||||
//
|
||||
#[allow(dead_code)]
|
||||
// Post flair with text, background color and foreground color
|
||||
pub struct Flair(pub String, pub String, pub String);
|
||||
|
@ -52,6 +55,17 @@ pub struct Params {
|
|||
pub before: Option<String>,
|
||||
}
|
||||
|
||||
// Error template
|
||||
#[derive(askama::Template)]
|
||||
#[template(path = "error.html", escape = "none")]
|
||||
pub struct ErrorTemplate {
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
//
|
||||
// JSON PARSING
|
||||
//
|
||||
|
||||
#[allow(dead_code)]
|
||||
// val() function used to parse JSON from Reddit APIs
|
||||
pub async fn val(j: &serde_json::Value, k: &str) -> String {
|
||||
|
@ -64,9 +78,13 @@ pub async fn nested_val(j: &serde_json::Value, n: &str, k: &str) -> String {
|
|||
String::from(j["data"][n][k].as_str().unwrap())
|
||||
}
|
||||
|
||||
//
|
||||
// NETWORKING
|
||||
//
|
||||
|
||||
// Make a request to a Reddit API and parse the JSON response
|
||||
#[allow(dead_code)]
|
||||
pub async fn request(url: String) -> serde_json::Value {
|
||||
pub async fn request(url: String) -> Result<serde_json::Value, &'static str> {
|
||||
// --- actix-web::client ---
|
||||
// let client = actix_web::client::Client::default();
|
||||
// let res = client
|
||||
|
@ -86,10 +104,22 @@ pub async fn request(url: String) -> serde_json::Value {
|
|||
// let body = res.body_string().await.unwrap();
|
||||
|
||||
// --- reqwest ---
|
||||
let resp: String = reqwest::get(&url).await.unwrap().text().await.unwrap();
|
||||
let res = reqwest::get(&url).await.unwrap();
|
||||
// Read the status from the response
|
||||
let success = res.status().is_success();
|
||||
// Read the body of the response
|
||||
let body = res.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");
|
||||
let json: serde_json::Value = serde_json::from_str(body.as_str()).unwrap_or(serde_json::Value::Null);
|
||||
|
||||
json
|
||||
if !success {
|
||||
Ok(json)
|
||||
} else if json == serde_json::Value::Null {
|
||||
println!("! {} - {}", url, "Failed to parse page JSON data");
|
||||
Err("Failed to parse page JSON data")
|
||||
} else {
|
||||
println!("! {} - {}", url, "Page not found");
|
||||
Err("Page not found")
|
||||
}
|
||||
}
|
||||
|
|
18
templates/error.html
Normal file
18
templates/error.html
Normal file
|
@ -0,0 +1,18 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Page not found.</title>
|
||||
<meta name="description" content="View on Libreddit, an alternative private front-end to Reddit.">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<a href="/"><span style="color:white">lib</span>reddit.</a>
|
||||
<a style="color:white" href="https://github.com/spikecodes/libreddit">GITHUB</a>
|
||||
</header>
|
||||
<main>
|
||||
<h1 style="text-align: center; font-size: 50px;">{{ message }}</h1>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in a new issue