// CRATES use crate::utils::{request, val, Comment, ErrorTemplate, Flair, Params, Post}; use actix_web::{get, http::StatusCode, web, HttpResponse, Result}; use askama::Template; use chrono::{TimeZone, Utc}; use pulldown_cmark::{html, Options, Parser}; #[cfg(feature = "proxy")] use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC}; // STRUCTS #[derive(Template)] #[template(path = "post.html", escape = "none")] struct PostTemplate { comments: Vec, post: Post, sort: String, } async fn render(id: String, sort: String) -> Result { // Log the post ID being fetched println!("id: {}", id); // 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 req = request(url).await; // If the Reddit API returns an error, exit and send error page to user if req.is_err() { let s = ErrorTemplate { message: req.err().unwrap().to_string(), } .render() .unwrap(); return Ok(HttpResponse::Ok().status(StatusCode::NOT_FOUND).content_type("text/html").body(s)); } // Otherwise, grab the JSON output from the request let res = req.unwrap(); // Parse the JSON into Post and Comment structs let post = parse_post(res.clone()).await; let comments = parse_comments(res).await; // Use the Post and Comment structs to generate a website to show users let s = PostTemplate { comments: comments.unwrap(), post: post.unwrap(), sort: sort, } .render() .unwrap(); Ok(HttpResponse::Ok().content_type("text/html").body(s)) } // SERVICES #[get("/{id}")] async fn short(web::Path(id): web::Path) -> Result { render(id.to_string(), "confidence".to_string()).await } #[get("/r/{sub}/comments/{id}/{title}/")] async fn page(web::Path((_sub, id)): web::Path<(String, String)>, params: web::Query) -> Result { match ¶ms.sort { Some(sort) => render(id, sort.to_string()).await, None => render(id, "confidence".to_string()).await, } } async fn format_url(url: &str) -> String { #[cfg(feature = "proxy")] return utf8_percent_encode(url, NON_ALPHANUMERIC).to_string(); #[cfg(not(feature = "proxy"))] return url.to_string(); } // UTILITIES async fn media(data: &serde_json::Value) -> String { let post_hint: &str = data["data"]["post_hint"].as_str().unwrap_or(""); let has_media: bool = data["data"]["media"].is_object(); let prefix = if cfg!(feature = "proxy") { "/imageproxy/" } else { "" }; let media: String = if !has_media { format!(r#"

{u}

"#, u = data["data"]["url"].as_str().unwrap()) } else { format!( r#""#, prefix, format_url(data["data"]["url"].as_str().unwrap()).await ) }; match post_hint { "hosted:video" => format!( r#"