From 9f9ae45f6eaa04f1c88d3b316fef83a624a874c9 Mon Sep 17 00:00:00 2001 From: Matthew Esposito Date: Fri, 19 Jan 2024 20:16:17 -0500 Subject: [PATCH] Add many Clippy's, fix many Clippy's --- src/client.rs | 26 +++++++------- src/config.rs | 6 ++-- src/duplicates.rs | 20 +++++------ src/instance_info.rs | 14 ++++---- src/main.rs | 80 ++++++++++++++++++++++++++++++++------------ src/oauth.rs | 6 ++-- src/post.rs | 24 ++++++------- src/search.rs | 20 +++++------ src/server.rs | 54 +++++++++++++++--------------- src/settings.rs | 12 +++---- src/subreddit.rs | 77 ++++++++++++++++++++---------------------- src/user.rs | 17 +++++----- src/utils.rs | 68 ++++++++++++++++++------------------- 13 files changed, 225 insertions(+), 199 deletions(-) diff --git a/src/client.rs b/src/client.rs index 5dce96a..590777c 100644 --- a/src/client.rs +++ b/src/client.rs @@ -90,12 +90,12 @@ pub async fn canonical_path(path: String) -> Result, String> { } pub async fn proxy(req: Request, format: &str) -> Result, String> { - let mut url = format!("{}?{}", format, req.uri().query().unwrap_or_default()); + let mut url = format!("{format}?{}", req.uri().query().unwrap_or_default()); // For each parameter in request - for (name, value) in req.params().iter() { + for (name, value) in &req.params() { // Fill the parameter value in the url - url = url.replace(&format!("{{{}}}", name), value); + url = url.replace(&format!("{{{name}}}"), value); } stream(&url, &req).await @@ -103,12 +103,12 @@ pub async fn proxy(req: Request, format: &str) -> Result, S async fn stream(url: &str, req: &Request) -> Result, String> { // First parameter is target URL (mandatory). - let uri = url.parse::().map_err(|_| "Couldn't parse URL".to_string())?; + let parsed_uri = url.parse::().map_err(|_| "Couldn't parse URL".to_string())?; // Build the hyper client from the HTTPS connector. - let client: client::Client<_, hyper::Body> = CLIENT.clone(); + let client: Client<_, Body> = CLIENT.clone(); - let mut builder = Request::get(uri); + let mut builder = Request::get(parsed_uri); // Copy useful headers from original request for &key in &["Range", "If-Modified-Since", "Cache-Control"] { @@ -154,15 +154,15 @@ fn reddit_head(path: String, quarantine: bool) -> Boxed, S request(&Method::HEAD, path, false, quarantine) } -/// Makes a request to Reddit. If `redirect` is `true`, request_with_redirect +/// Makes a request to Reddit. If `redirect` is `true`, `request_with_redirect` /// will recurse on the URL that Reddit provides in the Location HTTP header /// in its response. fn request(method: &'static Method, path: String, redirect: bool, quarantine: bool) -> Boxed, String>> { // Build Reddit URL from path. - let url = format!("{}{}", REDDIT_URL_BASE, path); + let url = format!("{REDDIT_URL_BASE}{path}"); // Construct the hyper client from the HTTPS connector. - let client: client::Client<_, hyper::Body> = CLIENT.clone(); + let client: Client<_, Body> = CLIENT.clone(); let (token, vendor_id, device_id, user_agent, loid) = { let client = block_on(OAUTH_CLIENT.read()); @@ -184,7 +184,7 @@ fn request(method: &'static Method, path: String, redirect: bool, quarantine: bo .header("X-Reddit-Device-Id", device_id) .header("x-reddit-loid", loid) .header("Host", "oauth.reddit.com") - .header("Authorization", &format!("Bearer {}", token)) + .header("Authorization", &format!("Bearer {token}")) .header("Accept-Encoding", if method == Method::GET { "gzip" } else { "identity" }) .header("Accept-Language", "en-US,en;q=0.5") .header("Connection", "keep-alive") @@ -227,7 +227,7 @@ fn request(method: &'static Method, path: String, redirect: bool, quarantine: bo // // 2. Percent-encode the path. let new_path = percent_encode(val.as_bytes(), CONTROLS).to_string().trim_start_matches(REDDIT_URL_BASE).to_string(); - format!("{}{}raw_json=1", new_path, if new_path.contains('?') { "&" } else { "?" }) + format!("{new_path}{}raw_json=1", if new_path.contains('?') { "&" } else { "?" }) }) .unwrap_or_default() .to_string(), @@ -302,7 +302,7 @@ pub async fn json(path: String, quarantine: bool) -> Result { // Closure to quickly build errors let err = |msg: &str, e: String| -> Result { // eprintln!("{} - {}: {}", url, msg, e); - Err(format!("{}: {}", msg, e)) + Err(format!("{msg}: {e}")) }; // Fetch the url... @@ -324,7 +324,7 @@ pub async fn json(path: String, quarantine: bool) -> Result { .as_str() .unwrap_or_else(|| { json["message"].as_str().unwrap_or_else(|| { - eprintln!("{}{} - Error parsing reddit error", REDDIT_URL_BASE, path); + eprintln!("{REDDIT_URL_BASE}{path} - Error parsing reddit error"); "Error parsing reddit error" }) }) diff --git a/src/config.rs b/src/config.rs index e0caf95..162b361 100644 --- a/src/config.rs +++ b/src/config.rs @@ -17,7 +17,7 @@ pub const DEFAULT_PUSHSHIFT_FRONTEND: &str = "www.unddit.com"; /// config file. `Config::Default()` contains None for each setting. /// When adding more config settings, add it to `Config::load`, /// `get_setting_from_config`, both below, as well as -/// instance_info::InstanceInfo.to_string(), README.md and app.json. +/// `instance_info::InstanceInfo.to_string`(), README.md and app.json. #[derive(Default, Serialize, Deserialize, Clone, Debug)] pub struct Config { #[serde(rename = "REDLIB_SFW_ONLY")] @@ -103,7 +103,7 @@ impl Config { new_file.ok().and_then(|new_file| toml::from_str::(&new_file).ok()) }; - let config = load_config("redlib.toml").or(load_config("libreddit.toml")).unwrap_or_default(); + let config = load_config("redlib.toml").or_else(|| load_config("libreddit.toml")).unwrap_or_default(); // This function defines the order of preference - first check for // environment variables with "REDLIB", then check the legacy LIBREDDIT @@ -112,7 +112,7 @@ impl Config { // Return the first non-`None` value // If all are `None`, return `None` let legacy_key = key.replace("REDLIB_", "LIBREDDIT_"); - var(key).ok().or(var(legacy_key).ok()).or(get_setting_from_config(key, &config)) + var(key).ok().or_else(|| var(legacy_key).ok()).or_else(|| get_setting_from_config(key, &config)) }; Self { sfw_only: parse("REDLIB_SFW_ONLY"), diff --git a/src/duplicates.rs b/src/duplicates.rs index b099acf..d92e8ab 100644 --- a/src/duplicates.rs +++ b/src/duplicates.rs @@ -12,14 +12,14 @@ use std::borrow::ToOwned; use std::collections::HashSet; use std::vec::Vec; -/// DuplicatesParams contains the parameters in the URL. +/// `DuplicatesParams` contains the parameters in the URL. struct DuplicatesParams { before: String, after: String, sort: String, } -/// DuplicatesTemplate defines an Askama template for rendering duplicate +/// `DuplicatesTemplate` defines an Askama template for rendering duplicate /// posts. #[derive(Template)] #[template(path = "duplicates.html")] @@ -59,7 +59,7 @@ pub async fn item(req: Request) -> Result, String> { // Log the request in debugging mode #[cfg(debug_assertions)] - dbg!(req.param("id").unwrap_or_default()); + req.param("id").unwrap_or_default(); // Send the GET, and await JSON. match json(path, quarantined).await { @@ -189,7 +189,7 @@ pub async fn item(req: Request) -> Result, String> { Err(msg) => { // Abort entirely if we couldn't get the previous // batch. - return error(req, msg).await; + return error(req, &msg).await; } } } else { @@ -197,7 +197,7 @@ pub async fn item(req: Request) -> Result, String> { } } - template(DuplicatesTemplate { + Ok(template(&DuplicatesTemplate { params: DuplicatesParams { before, after, sort }, post, duplicates, @@ -205,28 +205,28 @@ pub async fn item(req: Request) -> Result, String> { url: req_url, num_posts_filtered, all_posts_filtered, - }) + })) } // Process error. Err(msg) => { if msg == "quarantined" || msg == "gated" { let sub = req.param("sub").unwrap_or_default(); - quarantine(req, sub, msg) + Ok(quarantine(&req, sub, &msg)) } else { - error(req, msg).await + error(req, &msg).await } } } } // DUPLICATES -async fn parse_duplicates(json: &serde_json::Value, filters: &HashSet) -> (Vec, u64, bool) { +async fn parse_duplicates(json: &Value, filters: &HashSet) -> (Vec, u64, bool) { let post_duplicates: &Vec = &json["data"]["children"].as_array().map_or(Vec::new(), ToOwned::to_owned); let mut duplicates: Vec = Vec::new(); // Process each post and place them in the Vec. - for val in post_duplicates.iter() { + for val in post_duplicates { let post: Post = parse_post(val).await; duplicates.push(post); } diff --git a/src/instance_info.rs b/src/instance_info.rs index f9fb4ad..462e09c 100644 --- a/src/instance_info.rs +++ b/src/instance_info.rs @@ -24,7 +24,7 @@ pub async fn instance_info(req: Request) -> Result, String> "yaml" | "yml" => info_yaml(), "txt" => info_txt(), "json" => info_json(), - "html" | "" => info_html(req), + "html" | "" => info_html(&req), _ => { let error = ErrorTemplate { msg: "Error: Invalid info extension".into(), @@ -68,13 +68,13 @@ fn info_txt() -> Result, Error> { Response::builder() .status(200) .header("content-type", "text/plain") - .body(Body::from(INSTANCE_INFO.to_string(StringType::Raw))) + .body(Body::from(INSTANCE_INFO.to_string(&StringType::Raw))) } -fn info_html(req: Request) -> Result, Error> { +fn info_html(req: &Request) -> Result, Error> { let message = MessageTemplate { title: String::from("Instance information"), - body: INSTANCE_INFO.to_string(StringType::Html), - prefs: Preferences::new(&req), + body: INSTANCE_INFO.to_string(&StringType::Html), + prefs: Preferences::new(req), url: req.uri().to_string(), } .render() @@ -109,7 +109,7 @@ impl InstanceInfo { } fn to_table(&self) -> String { let mut container = Container::default(); - let convert = |o: &Option| -> String { o.clone().unwrap_or("Unset".to_owned()) }; + let convert = |o: &Option| -> String { o.clone().unwrap_or_else(|| "Unset".to_owned()) }; if let Some(banner) = &self.config.banner { container.add_header(3, "Instance banner"); container.add_raw("
"); @@ -151,7 +151,7 @@ impl InstanceInfo { ); container.to_html_string().replace("", "") } - fn to_string(&self, string_type: StringType) -> String { + fn to_string(&self, string_type: &StringType) -> String { match string_type { StringType::Raw => { format!( diff --git a/src/main.rs b/src/main.rs index d605ad2..2f00330 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,42 @@ // Global specifiers #![forbid(unsafe_code)] -#![allow(clippy::cmp_owned)] +#![deny( + anonymous_parameters, + clippy::all, + illegal_floating_point_literal_pattern, + late_bound_lifetime_arguments, + path_statements, + patterns_in_fns_without_body, + rust_2018_idioms, + trivial_numeric_casts, + unused_extern_crates +)] +#![warn( + clippy::dbg_macro, + clippy::decimal_literal_representation, + clippy::get_unwrap, + clippy::nursery, + clippy::pedantic, + clippy::todo, + clippy::unimplemented, + clippy::use_debug, + clippy::all, + unused_qualifications, + variant_size_differences +)] +#![allow( + clippy::cmp_owned, + clippy::unused_async, + clippy::option_if_let_else, + clippy::items_after_statements, + clippy::cast_possible_truncation, + clippy::cast_possible_wrap, + clippy::cast_precision_loss, + clippy::struct_field_names, + clippy::struct_excessive_bools, + clippy::useless_let_if_seq, + clippy::collection_is_never_read +)] // Reference local files mod config; @@ -193,7 +229,7 @@ async fn main() { }; if let Some(expire_time) = hsts { - if let Ok(val) = HeaderValue::from_str(&format!("max-age={}", expire_time)) { + if let Ok(val) = HeaderValue::from_str(&format!("max-age={expire_time}")) { app.default_headers.insert("Strict-Transport-Security", val); } } @@ -249,11 +285,11 @@ async fn main() { // Browse user profile app .at("/u/:name") - .get(|r| async move { Ok(redirect(format!("/user/{}", r.param("name").unwrap_or_default()))) }.boxed()); + .get(|r| async move { Ok(redirect(&format!("/user/{}", r.param("name").unwrap_or_default()))) }.boxed()); app.at("/u/:name/comments/:id/:title").get(|r| post::item(r).boxed()); app.at("/u/:name/comments/:id/:title/:comment_id").get(|r| post::item(r).boxed()); - app.at("/user/[deleted]").get(|req| error(req, "User has deleted their account".to_string()).boxed()); + app.at("/user/[deleted]").get(|req| error(req, "User has deleted their account").boxed()); app.at("/user/:name").get(|r| user::profile(r).boxed()); app.at("/user/:name/:listing").get(|r| user::profile(r).boxed()); app.at("/user/:name/comments/:id").get(|r| post::item(r).boxed()); @@ -273,7 +309,7 @@ async fn main() { app .at("/r/u_:name") - .get(|r| async move { Ok(redirect(format!("/user/{}", r.param("name").unwrap_or_default()))) }.boxed()); + .get(|r| async move { Ok(redirect(&format!("/user/{}", r.param("name").unwrap_or_default()))) }.boxed()); app.at("/r/:sub/subscribe").post(|r| subreddit::subscriptions_filters(r).boxed()); app.at("/r/:sub/unsubscribe").post(|r| subreddit::subscriptions_filters(r).boxed()); @@ -298,10 +334,10 @@ async fn main() { app .at("/r/:sub/w") - .get(|r| async move { Ok(redirect(format!("/r/{}/wiki", r.param("sub").unwrap_or_default()))) }.boxed()); + .get(|r| async move { Ok(redirect(&format!("/r/{}/wiki", r.param("sub").unwrap_or_default()))) }.boxed()); app .at("/r/:sub/w/*page") - .get(|r| async move { Ok(redirect(format!("/r/{}/wiki/{}", r.param("sub").unwrap_or_default(), r.param("wiki").unwrap_or_default()))) }.boxed()); + .get(|r| async move { Ok(redirect(&format!("/r/{}/wiki/{}", r.param("sub").unwrap_or_default(), r.param("wiki").unwrap_or_default()))) }.boxed()); app.at("/r/:sub/wiki").get(|r| subreddit::wiki(r).boxed()); app.at("/r/:sub/wiki/*page").get(|r| subreddit::wiki(r).boxed()); @@ -313,10 +349,10 @@ async fn main() { app.at("/").get(|r| subreddit::community(r).boxed()); // View Reddit wiki - app.at("/w").get(|_| async { Ok(redirect("/wiki".to_string())) }.boxed()); + app.at("/w").get(|_| async { Ok(redirect("/wiki")) }.boxed()); app .at("/w/*page") - .get(|r| async move { Ok(redirect(format!("/wiki/{}", r.param("page").unwrap_or_default()))) }.boxed()); + .get(|r| async move { Ok(redirect(&format!("/wiki/{}", r.param("page").unwrap_or_default()))) }.boxed()); app.at("/wiki").get(|r| subreddit::wiki(r).boxed()); app.at("/wiki/*page").get(|r| subreddit::wiki(r).boxed()); @@ -324,7 +360,7 @@ async fn main() { app.at("/search").get(|r| search::find(r).boxed()); // Handle about pages - app.at("/about").get(|req| error(req, "About pages aren't added yet".to_string()).boxed()); + app.at("/about").get(|req| error(req, "About pages aren't added yet").boxed()); // Instance info page app.at("/info").get(|r| instance_info::instance_info(r).boxed()); @@ -337,14 +373,14 @@ async fn main() { let sub = req.param("sub").unwrap_or_default(); match req.param("id").as_deref() { // Share link - Some(id) if (8..12).contains(&id.len()) => match canonical_path(format!("/r/{}/s/{}", sub, id)).await { - Ok(Some(path)) => Ok(redirect(path)), + Some(id) if (8..12).contains(&id.len()) => match canonical_path(format!("/r/{sub}/s/{id}")).await { + Ok(Some(path)) => Ok(redirect(&path)), Ok(None) => error(req, "Post ID is invalid. It may point to a post on a community that has been banned.").await, - Err(e) => error(req, e).await, + Err(e) => error(req, &e).await, }, // Error message for unknown pages - _ => error(req, "Nothing here".to_string()).await, + _ => error(req, "Nothing here").await, } }) }); @@ -356,29 +392,29 @@ async fn main() { Some("best" | "hot" | "new" | "top" | "rising" | "controversial") => subreddit::community(req).await, // Short link for post - Some(id) if (5..8).contains(&id.len()) => match canonical_path(format!("/{}", id)).await { + Some(id) if (5..8).contains(&id.len()) => match canonical_path(format!("/{id}")).await { Ok(path_opt) => match path_opt { - Some(path) => Ok(redirect(path)), + Some(path) => Ok(redirect(&path)), None => error(req, "Post ID is invalid. It may point to a post on a community that has been banned.").await, }, - Err(e) => error(req, e).await, + Err(e) => error(req, &e).await, }, // Error message for unknown pages - _ => error(req, "Nothing here".to_string()).await, + _ => error(req, "Nothing here").await, } }) }); // Default service in case no routes match - app.at("/*").get(|req| error(req, "Nothing here".to_string()).boxed()); + app.at("/*").get(|req| error(req, "Nothing here").boxed()); - println!("Running Redlib v{} on {}!", env!("CARGO_PKG_VERSION"), listener); + println!("Running Redlib v{} on {listener}!", env!("CARGO_PKG_VERSION")); - let server = app.listen(listener); + let server = app.listen(&listener); // Run this server for... forever! if let Err(e) = server.await { - eprintln!("Server error: {}", e); + eprintln!("Server error: {e}"); } } diff --git a/src/oauth.rs b/src/oauth.rs index 9bc448d..101dc74 100644 --- a/src/oauth.rs +++ b/src/oauth.rs @@ -46,11 +46,11 @@ impl Oauth { } async fn login(&mut self) -> Option<()> { // Construct URL for OAuth token - let url = format!("{}/api/access_token", AUTH_ENDPOINT); + let url = format!("{AUTH_ENDPOINT}/api/access_token"); let mut builder = Request::builder().method(Method::POST).uri(&url); // Add headers from spoofed client - for (key, value) in self.initial_headers.iter() { + for (key, value) in &self.initial_headers { builder = builder.header(key, value); } // Set up HTTP Basic Auth - basically just the const OAuth ID's with no password, @@ -70,7 +70,7 @@ impl Oauth { let request = builder.body(body).unwrap(); // Send request - let client: client::Client<_, hyper::Body> = CLIENT.clone(); + let client: client::Client<_, Body> = CLIENT.clone(); let resp = client.request(request).await.ok()?; // Parse headers - loid header _should_ be saved sent on subsequent token refreshes. diff --git a/src/post.rs b/src/post.rs index 5cac715..cdf13a6 100644 --- a/src/post.rs +++ b/src/post.rs @@ -27,7 +27,7 @@ struct PostTemplate { comment_query: String, } -static COMMENT_SEARCH_CAPTURE: Lazy = Lazy::new(|| Regex::new(r#"\?q=(.*)&type=comment"#).unwrap()); +static COMMENT_SEARCH_CAPTURE: Lazy = Lazy::new(|| Regex::new(r"\?q=(.*)&type=comment").unwrap()); pub async fn item(req: Request) -> Result, String> { // Build Reddit API path @@ -52,7 +52,7 @@ pub async fn item(req: Request) -> Result, String> { // Log the post ID being fetched in debug mode #[cfg(debug_assertions)] - dbg!(req.param("id").unwrap_or_default()); + req.param("id").unwrap_or_default(); let single_thread = req.param("comment_id").is_some(); let highlighted_comment = &req.param("comment_id").unwrap_or_default(); @@ -83,7 +83,7 @@ pub async fn item(req: Request) -> Result, String> { }; // Use the Post and Comment structs to generate a website to show users - template(PostTemplate { + Ok(template(&PostTemplate { comments, post, url_without_query: url.clone().trim_end_matches(&format!("?q={query}&type=comment")).to_string(), @@ -92,15 +92,15 @@ pub async fn item(req: Request) -> Result, String> { single_thread, url: req_url, comment_query: query, - }) + })) } // If the Reddit API returns an error, exit and send error page to user Err(msg) => { if msg == "quarantined" || msg == "gated" { let sub = req.param("sub").unwrap_or_default(); - quarantine(req, sub, msg) + Ok(quarantine(&req, sub, &msg)) } else { - error(req, msg).await + error(req, &msg).await } } } @@ -139,19 +139,19 @@ fn query_comments( let comments = json["data"]["children"].as_array().map_or(Vec::new(), std::borrow::ToOwned::to_owned); let mut results = Vec::new(); - comments.into_iter().for_each(|comment| { + for comment in comments { let data = &comment["data"]; // If this comment contains replies, handle those too if data["replies"].is_object() { - results.append(&mut query_comments(&data["replies"], post_link, post_author, highlighted_comment, filters, query, req)) + results.append(&mut query_comments(&data["replies"], post_link, post_author, highlighted_comment, filters, query, req)); } let c = build_comment(&comment, data, Vec::new(), post_link, post_author, highlighted_comment, filters, req); if c.body.to_lowercase().contains(&query.to_lowercase()) { results.push(c); } - }); + } results } @@ -170,10 +170,8 @@ fn build_comment( let body = if (val(comment, "author") == "[deleted]" && val(comment, "body") == "[removed]") || val(comment, "body") == "[ Removed by Reddit ]" { format!( - "

[removed] — view removed comment

", - get_setting("REDLIB_PUSHSHIFT_FRONTEND").unwrap_or(String::from(crate::config::DEFAULT_PUSHSHIFT_FRONTEND)), - post_link, - id + "

[removed] — view removed comment

", + get_setting("REDLIB_PUSHSHIFT_FRONTEND").unwrap_or_else(|| String::from(crate::config::DEFAULT_PUSHSHIFT_FRONTEND)), ) } else { rewrite_urls(&val(comment, "body_html")) diff --git a/src/search.rs b/src/search.rs index 35c0f96..2b25983 100644 --- a/src/search.rs +++ b/src/search.rs @@ -65,11 +65,11 @@ pub async fn find(req: Request) -> Result, String> { query = REDDIT_URL_MATCH.replace(&query, "").to_string(); if query.is_empty() { - return Ok(redirect("/".to_string())); + return Ok(redirect("/")); } if query.starts_with("r/") { - return Ok(redirect(format!("/{}", query))); + return Ok(redirect(&format!("/{query}"))); } let sub = req.param("sub").unwrap_or_default(); @@ -97,7 +97,7 @@ pub async fn find(req: Request) -> Result, String> { // If all requested subs are filtered, we don't need to fetch posts. if sub.split('+').all(|s| filters.contains(s)) { - template(SearchTemplate { + Ok(template(&SearchTemplate { posts: Vec::new(), subreddits, sub, @@ -106,7 +106,7 @@ pub async fn find(req: Request) -> Result, String> { sort, t: param(&path, "t").unwrap_or_default(), before: param(&path, "after").unwrap_or_default(), - after: "".to_string(), + after: String::new(), restrict_sr: param(&path, "restrict_sr").unwrap_or_default(), typed, }, @@ -116,14 +116,14 @@ pub async fn find(req: Request) -> Result, String> { all_posts_filtered: false, all_posts_hidden_nsfw: false, no_posts: false, - }) + })) } else { match Post::fetch(&path, quarantined).await { Ok((mut posts, after)) => { let (_, all_posts_filtered) = filter_posts(&mut posts, &filters); let no_posts = posts.is_empty(); let all_posts_hidden_nsfw = !no_posts && (posts.iter().all(|p| p.flags.nsfw) && setting(&req, "show_nsfw") != "on"); - template(SearchTemplate { + Ok(template(&SearchTemplate { posts, subreddits, sub, @@ -142,14 +142,14 @@ pub async fn find(req: Request) -> Result, String> { all_posts_filtered, all_posts_hidden_nsfw, no_posts, - }) + })) } Err(msg) => { if msg == "quarantined" || msg == "gated" { let sub = req.param("sub").unwrap_or_default(); - quarantine(req, sub, msg) + Ok(quarantine(&req, sub, &msg)) } else { - error(req, msg).await + error(req, &msg).await } } } @@ -158,7 +158,7 @@ pub async fn find(req: Request) -> Result, String> { async fn search_subreddits(q: &str, typed: &str) -> Vec { let limit = if typed == "sr_user" { "50" } else { "3" }; - let subreddit_search_path = format!("/subreddits/search.json?q={}&limit={}", q.replace(' ', "+"), limit); + let subreddit_search_path = format!("/subreddits/search.json?q={}&limit={limit}", q.replace(' ', "+")); // Send a request to the url json(subreddit_search_path, false).await.unwrap_or_default()["data"]["children"] diff --git a/src/server.rs b/src/server.rs index 11be461..c7bf45a 100644 --- a/src/server.rs +++ b/src/server.rs @@ -70,7 +70,7 @@ impl ToString for CompressionType { match self { Self::Gzip => "gzip".to_string(), Self::Brotli => "br".to_string(), - _ => String::new(), + Self::Passthrough => String::new(), } } } @@ -104,13 +104,13 @@ pub trait RequestExt { fn params(&self) -> Params; fn param(&self, name: &str) -> Option; fn set_params(&mut self, params: Params) -> Option; - fn cookies(&self) -> Vec; - fn cookie(&self, name: &str) -> Option; + fn cookies(&self) -> Vec>; + fn cookie(&self, name: &str) -> Option>; } pub trait ResponseExt { - fn cookies(&self) -> Vec; - fn insert_cookie(&mut self, cookie: Cookie); + fn cookies(&self) -> Vec>; + fn insert_cookie(&mut self, cookie: Cookie<'_>); fn remove_cookie(&mut self, name: String); } @@ -131,7 +131,7 @@ impl RequestExt for Request { self.extensions_mut().insert(params) } - fn cookies(&self) -> Vec { + fn cookies(&self) -> Vec> { self.headers().get("Cookie").map_or(Vec::new(), |header| { header .to_str() @@ -142,13 +142,13 @@ impl RequestExt for Request { }) } - fn cookie(&self, name: &str) -> Option { + fn cookie(&self, name: &str) -> Option> { self.cookies().into_iter().find(|c| c.name() == name) } } impl ResponseExt for Response { - fn cookies(&self) -> Vec { + fn cookies(&self) -> Vec> { self.headers().get("Cookie").map_or(Vec::new(), |header| { header .to_str() @@ -159,7 +159,7 @@ impl ResponseExt for Response { }) } - fn insert_cookie(&mut self, cookie: Cookie) { + fn insert_cookie(&mut self, cookie: Cookie<'_>) { if let Ok(val) = header::HeaderValue::from_str(&cookie.to_string()) { self.headers_mut().append("Set-Cookie", val); } @@ -176,19 +176,19 @@ impl ResponseExt for Response { } impl Route<'_> { - fn method(&mut self, method: Method, dest: fn(Request) -> BoxResponse) -> &mut Self { + fn method(&mut self, method: &Method, dest: fn(Request) -> BoxResponse) -> &mut Self { self.router.add(&format!("/{}{}", method.as_str(), self.path), dest); self } /// Add an endpoint for `GET` requests pub fn get(&mut self, dest: fn(Request) -> BoxResponse) -> &mut Self { - self.method(Method::GET, dest) + self.method(&Method::GET, dest) } /// Add an endpoint for `POST` requests pub fn post(&mut self, dest: fn(Request) -> BoxResponse) -> &mut Self { - self.method(Method::POST, dest) + self.method(&Method::POST, dest) } } @@ -200,14 +200,14 @@ impl Server { } } - pub fn at(&mut self, path: &str) -> Route { + pub fn at(&mut self, path: &str) -> Route<'_> { Route { path: path.to_owned(), router: &mut self.router, } } - pub fn listen(self, addr: String) -> Boxed> { + pub fn listen(self, addr: &str) -> Boxed> { let make_svc = make_service_fn(move |_conn| { // For correct borrowing, these values need to be borrowed let router = self.router.clone(); @@ -260,7 +260,7 @@ impl Server { }); // Build SocketAddr from provided address - let address = &addr.parse().unwrap_or_else(|_| panic!("Cannot parse {} as address (example format: 0.0.0.0:8080)", addr)); + let address = &addr.parse().unwrap_or_else(|_| panic!("Cannot parse {addr} as address (example format: 0.0.0.0:8080)")); // Bind server to address specified above. Gracefully shut down if CTRL+C is pressed let server = HyperServer::bind(address).serve(make_svc).with_graceful_shutdown(async { @@ -376,7 +376,7 @@ fn determine_compressor(accept_encoding: String) -> Option { // The compressor and q-value (if the latter is defined) // will be delimited by semicolons. - let mut spl: Split = val.split(';'); + let mut spl: Split<'_, char> = val.split(';'); // Get the compressor. For example, in // gzip;q=0.8 @@ -438,10 +438,10 @@ fn determine_compressor(accept_encoding: String) -> Option { }; } - if cur_candidate.q != f64::NEG_INFINITY { - Some(cur_candidate.alg) - } else { + if cur_candidate.q == f64::NEG_INFINITY { None + } else { + Some(cur_candidate.alg) } } @@ -453,16 +453,16 @@ fn determine_compressor(accept_encoding: String) -> Option { /// conditions are met: /// /// 1. the HTTP client requests a compression encoding in the Content-Encoding -/// header (hence the need for the req_headers); +/// header (hence the need for the `req_headers`); /// /// 2. the content encoding corresponds to a compression algorithm we support; /// /// 3. the Media type in the Content-Type response header is text with any /// subtype (e.g. text/plain) or application/json. /// -/// compress_response returns Ok on successful compression, or if not all three +/// `compress_response` returns Ok on successful compression, or if not all three /// conditions above are met. It returns Err if there was a problem decoding -/// any header in either req_headers or res, but res will remain intact. +/// any header in either `req_headers` or res, but res will remain intact. /// /// This function logs errors to stderr, but only in debug mode. No information /// is logged in release builds. @@ -601,7 +601,7 @@ fn compress_body(compressor: CompressionType, body_bytes: Vec) -> Result { + CompressionType::Passthrough => { let msg = "unsupported compressor".to_string(); return Err(msg); } @@ -677,7 +677,7 @@ mod tests { // Perform the compression. if let Err(e) = block_on(compress_response(&req_headers, &mut res)) { - panic!("compress_response(&req_headers, &mut res) => Err(\"{}\")", e); + panic!("compress_response(&req_headers, &mut res) => Err(\"{e}\")"); }; // If the content was compressed, we expect the Content-Encoding @@ -699,7 +699,7 @@ mod tests { // the Response is the same as what with which we start. let body_vec = match block_on(body::to_bytes(res.body_mut())) { Ok(b) => b.to_vec(), - Err(e) => panic!("{}", e), + Err(e) => panic!("{e}"), }; if expected_encoding == CompressionType::Passthrough { @@ -715,7 +715,7 @@ mod tests { let mut decoder: Box = match expected_encoding { CompressionType::Gzip => match gzip::Decoder::new(&mut body_cursor) { Ok(dgz) => Box::new(dgz), - Err(e) => panic!("{}", e), + Err(e) => panic!("{e}"), }, CompressionType::Brotli => Box::new(BrotliDecompressor::new(body_cursor, expected_lorem_ipsum.len())), @@ -725,7 +725,7 @@ mod tests { let mut decompressed = Vec::::new(); if let Err(e) = io::copy(&mut decoder, &mut decompressed) { - panic!("{}", e); + panic!("{e}"); }; assert!(decompressed.eq(&expected_lorem_ipsum)); diff --git a/src/settings.rs b/src/settings.rs index 55bf5b9..6edb059 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -42,10 +42,10 @@ const PREFS: [&str; 15] = [ // Retrieve cookies from request "Cookie" header pub async fn get(req: Request) -> Result, String> { let url = req.uri().to_string(); - template(SettingsTemplate { + Ok(template(&SettingsTemplate { prefs: Preferences::new(&req), url, - }) + })) } // Set cookies using response "Set-Cookie" header @@ -54,7 +54,7 @@ pub async fn set(req: Request) -> Result, String> { let (parts, mut body) = req.into_parts(); // Grab existing cookies - let _cookies: Vec = parts + let _cookies: Vec> = parts .headers .get_all("Cookie") .iter() @@ -73,7 +73,7 @@ pub async fn set(req: Request) -> Result, String> { let form = url::form_urlencoded::parse(&body_bytes).collect::>(); - let mut response = redirect("/settings".to_string()); + let mut response = redirect("/settings"); for &name in &PREFS { match form.get(name) { @@ -96,7 +96,7 @@ fn set_cookies_method(req: Request, remove_cookies: bool) -> Response = parts + let _cookies: Vec> = parts .headers .get_all("Cookie") .iter() @@ -112,7 +112,7 @@ fn set_cookies_method(req: Request, remove_cookies: bool) -> Response "/".to_string(), }; - let mut response = redirect(path); + let mut response = redirect(&path); for name in [PREFS.to_vec(), vec!["subscriptions", "filters"]].concat() { match form.get(name) { diff --git a/src/subreddit.rs b/src/subreddit.rs index 4f366b9..569f84c 100644 --- a/src/subreddit.rs +++ b/src/subreddit.rs @@ -76,7 +76,7 @@ pub async fn community(req: Request) -> Result, String> { } if req.param("sub").is_some() && sub_name.starts_with("u_") { - return Ok(redirect(["/user/", &sub_name[2..]].concat())); + return Ok(redirect(&["/user/", &sub_name[2..]].concat())); } // Request subreddit metadata @@ -117,11 +117,11 @@ pub async fn community(req: Request) -> Result, String> { // If all requested subs are filtered, we don't need to fetch posts. if sub_name.split('+').all(|s| filters.contains(s)) { - template(SubredditTemplate { + Ok(template(&SubredditTemplate { sub, posts: Vec::new(), sort: (sort, param(&path, "t").unwrap_or_default()), - ends: (param(&path, "after").unwrap_or_default(), "".to_string()), + ends: (param(&path, "after").unwrap_or_default(), String::new()), prefs: Preferences::new(&req), url, redirect_url, @@ -129,14 +129,14 @@ pub async fn community(req: Request) -> Result, String> { all_posts_filtered: false, all_posts_hidden_nsfw: false, no_posts: false, - }) + })) } else { match Post::fetch(&path, quarantined).await { Ok((mut posts, after)) => { let (_, all_posts_filtered) = filter_posts(&mut posts, &filters); let no_posts = posts.is_empty(); let all_posts_hidden_nsfw = !no_posts && (posts.iter().all(|p| p.flags.nsfw) && setting(&req, "show_nsfw") != "on"); - template(SubredditTemplate { + Ok(template(&SubredditTemplate { sub, posts, sort: (sort, param(&path, "t").unwrap_or_default()), @@ -148,40 +148,38 @@ pub async fn community(req: Request) -> Result, String> { all_posts_filtered, all_posts_hidden_nsfw, no_posts, - }) + })) } Err(msg) => match msg.as_str() { - "quarantined" | "gated" => quarantine(req, sub_name, msg), - "private" => error(req, format!("r/{} is a private community", sub_name)).await, - "banned" => error(req, format!("r/{} has been banned from Reddit", sub_name)).await, - _ => error(req, msg).await, + "quarantined" | "gated" => Ok(quarantine(&req, sub_name, &msg)), + "private" => error(req, &format!("r/{sub_name} is a private community")).await, + "banned" => error(req, &format!("r/{sub_name} has been banned from Reddit")).await, + _ => error(req, &msg).await, }, } } } -pub fn quarantine(req: Request, sub: String, restriction: String) -> Result, String> { +pub fn quarantine(req: &Request, sub: String, restriction: &str) -> Response { let wall = WallTemplate { - title: format!("r/{} is {}", sub, restriction), + title: format!("r/{sub} is {restriction}"), msg: "Please click the button below to continue to this subreddit.".to_string(), url: req.uri().to_string(), sub, - prefs: Preferences::new(&req), + prefs: Preferences::new(req), }; - Ok( - Response::builder() - .status(403) - .header("content-type", "text/html") - .body(wall.render().unwrap_or_default().into()) - .unwrap_or_default(), - ) + Response::builder() + .status(403) + .header("content-type", "text/html") + .body(wall.render().unwrap_or_default().into()) + .unwrap_or_default() } pub async fn add_quarantine_exception(req: Request) -> Result, String> { let subreddit = req.param("sub").ok_or("Invalid URL")?; let redir = param(&format!("?{}", req.uri().query().unwrap_or_default()), "redir").ok_or("Invalid URL")?; - let mut response = redirect(redir); + let mut response = redirect(&redir); response.insert_cookie( Cookie::build((&format!("allow_quaran_{}", subreddit.to_lowercase()), "true")) .path("/") @@ -206,9 +204,8 @@ pub async fn subscriptions_filters(req: Request) -> Result, if sub == "random" || sub == "randnsfw" { if action.contains(&"filter".to_string()) || action.contains(&"unfilter".to_string()) { return Err("Can't filter random subreddit!".to_string()); - } else { - return Err("Can't subscribe to random subreddit!".to_string()); } + return Err("Can't subscribe to random subreddit!".to_string()); } let query = req.uri().query().unwrap_or_default().to_string(); @@ -219,7 +216,7 @@ pub async fn subscriptions_filters(req: Request) -> Result, // Retrieve list of posts for these subreddits to extract display names - let posts = json(format!("/r/{}/hot.json?raw_json=1", sub), true).await; + let posts = json(format!("/r/{sub}/hot.json?raw_json=1"), true).await; let display_lookup: Vec<(String, &str)> = match &posts { Ok(posts) => posts["data"]["children"] .as_array() @@ -247,7 +244,7 @@ pub async fn subscriptions_filters(req: Request) -> Result, display } else { // This subreddit display name isn't known, retrieve it - let path: String = format!("/r/{}/about.json?raw_json=1", part); + let path: String = format!("/r/{part}/about.json?raw_json=1"); display = json(path, true).await; match &display { Ok(display) => display["data"]["display_name"].as_str(), @@ -282,13 +279,13 @@ pub async fn subscriptions_filters(req: Request) -> Result, // Redirect back to subreddit // check for redirect parameter if unsubscribing/unfiltering from outside sidebar - let path = if let Some(redirect_path) = param(&format!("?{}", query), "redirect") { - format!("/{}", redirect_path) + let path = if let Some(redirect_path) = param(&format!("?{query}"), "redirect") { + format!("/{redirect_path}") } else { - format!("/r/{}", sub) + format!("/r/{sub}") }; - let mut response = redirect(path); + let mut response = redirect(&path); // Delete cookie if empty, else set if sub_list.is_empty() { @@ -326,22 +323,22 @@ pub async fn wiki(req: Request) -> Result, String> { } let page = req.param("page").unwrap_or_else(|| "index".to_string()); - let path: String = format!("/r/{}/wiki/{}.json?raw_json=1", sub, page); + let path: String = format!("/r/{sub}/wiki/{page}.json?raw_json=1"); let url = req.uri().to_string(); match json(path, quarantined).await { - Ok(response) => template(WikiTemplate { + Ok(response) => Ok(template(&WikiTemplate { sub, wiki: rewrite_urls(response["data"]["content_html"].as_str().unwrap_or("

Wiki not found

")), page, prefs: Preferences::new(&req), url, - }), + })), Err(msg) => { if msg == "quarantined" || msg == "gated" { - quarantine(req, sub, msg) + Ok(quarantine(&req, sub, &msg)) } else { - error(req, msg).await + error(req, &msg).await } } } @@ -357,13 +354,13 @@ pub async fn sidebar(req: Request) -> Result, String> { } // Build the Reddit JSON API url - let path: String = format!("/r/{}/about.json?raw_json=1", sub); + let path: String = format!("/r/{sub}/about.json?raw_json=1"); let url = req.uri().to_string(); // Send a request to the url match json(path, quarantined).await { // If success, receive JSON in response - Ok(response) => template(WikiTemplate { + Ok(response) => Ok(template(&WikiTemplate { wiki: rewrite_urls(&val(&response, "description_html")), // wiki: format!( // "{}

Moderators


    {}
", @@ -374,12 +371,12 @@ pub async fn sidebar(req: Request) -> Result, String> { page: "Sidebar".to_string(), prefs: Preferences::new(&req), url, - }), + })), Err(msg) => { if msg == "quarantined" || msg == "gated" { - quarantine(req, sub, msg) + Ok(quarantine(&req, sub, &msg)) } else { - error(req, msg).await + error(req, &msg).await } } } @@ -422,7 +419,7 @@ pub async fn sidebar(req: Request) -> Result, String> { // SUBREDDIT async fn subreddit(sub: &str, quarantined: bool) -> Result { // Build the Reddit JSON API url - let path: String = format!("/r/{}/about.json?raw_json=1", sub); + let path: String = format!("/r/{sub}/about.json?raw_json=1"); // Send a request to the url let res = json(path, quarantined).await?; diff --git a/src/user.rs b/src/user.rs index 0edcfd7..9cf607b 100644 --- a/src/user.rs +++ b/src/user.rs @@ -35,9 +35,8 @@ pub async fn profile(req: Request) -> Result, String> { // Build the Reddit JSON API path let path = format!( - "/user/{}/{}.json?{}&raw_json=1", + "/user/{}/{listing}.json?{}&raw_json=1", req.param("name").unwrap_or_else(|| "reddit".to_string()), - listing, req.uri().query().unwrap_or_default(), ); let url = String::from(req.uri().path_and_query().map_or("", |val| val.as_str())); @@ -60,11 +59,11 @@ pub async fn profile(req: Request) -> Result, String> { let filters = get_filters(&req); if filters.contains(&["u_", &username].concat()) { - template(UserTemplate { + Ok(template(&UserTemplate { user, posts: Vec::new(), sort: (sort, param(&path, "t").unwrap_or_default()), - ends: (param(&path, "after").unwrap_or_default(), "".to_string()), + ends: (param(&path, "after").unwrap_or_default(), String::new()), listing, prefs: Preferences::new(&req), url, @@ -73,7 +72,7 @@ pub async fn profile(req: Request) -> Result, String> { all_posts_filtered: false, all_posts_hidden_nsfw: false, no_posts: false, - }) + })) } else { // Request user posts/comments from Reddit match Post::fetch(&path, false).await { @@ -81,7 +80,7 @@ pub async fn profile(req: Request) -> Result, String> { let (_, all_posts_filtered) = filter_posts(&mut posts, &filters); let no_posts = posts.is_empty(); let all_posts_hidden_nsfw = !no_posts && (posts.iter().all(|p| p.flags.nsfw) && setting(&req, "show_nsfw") != "on"); - template(UserTemplate { + Ok(template(&UserTemplate { user, posts, sort: (sort, param(&path, "t").unwrap_or_default()), @@ -94,10 +93,10 @@ pub async fn profile(req: Request) -> Result, String> { all_posts_filtered, all_posts_hidden_nsfw, no_posts, - }) + })) } // If there is an error show error page - Err(msg) => error(req, msg).await, + Err(msg) => error(req, &msg).await, } } } @@ -105,7 +104,7 @@ pub async fn profile(req: Request) -> Result, String> { // USER async fn user(name: &str) -> Result { // Build the Reddit JSON API path - let path: String = format!("/user/{}/about.json?raw_json=1", name); + let path: String = format!("/user/{name}/about.json?raw_json=1"); // Send a request to the url json(path, false).await.map(|res| { diff --git a/src/utils.rs b/src/utils.rs index 4271793..aa992d6 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -116,8 +116,8 @@ impl Poll { Some(Self { poll_options, - total_vote_count, voting_end_timestamp, + total_vote_count, }) } @@ -327,9 +327,8 @@ impl Post { }; // Fetch the list of posts from the JSON response - let post_list = match res["data"]["children"].as_array() { - Some(list) => list, - None => return Err("No posts found".to_string()), + let Some(post_list) = res["data"]["children"].as_array() else { + return Err("No posts found".to_string()); }; let mut posts: Vec = Vec::new(); @@ -384,7 +383,7 @@ impl Post { alt_url: String::new(), width: data["thumbnail_width"].as_i64().unwrap_or_default(), height: data["thumbnail_height"].as_i64().unwrap_or_default(), - poster: "".to_string(), + poster: String::new(), }, media, domain: val(post, "domain"), @@ -457,7 +456,7 @@ pub struct Award { } impl std::fmt::Display for Award { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{} {} {}", self.name, self.icon_url, self.description) } } @@ -473,8 +472,8 @@ impl std::ops::Deref for Awards { } impl std::fmt::Display for Awards { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - self.iter().fold(Ok(()), |result, award| result.and_then(|_| writeln!(f, "{}", award))) + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.iter().fold(Ok(()), |result, award| result.and_then(|()| writeln!(f, "{award}"))) } } @@ -603,7 +602,7 @@ impl Preferences { let mut themes = vec!["system".to_string()]; for file in ThemeAssets::iter() { let chunks: Vec<&str> = file.as_ref().split(".css").collect(); - themes.push(chunks[0].to_owned()) + themes.push(chunks[0].to_owned()); } Self { available_themes: themes, @@ -657,7 +656,7 @@ pub fn filter_posts(posts: &mut Vec, filters: &HashSet) -> (u64, b } /// Creates a [`Post`] from a provided JSON. -pub async fn parse_post(post: &serde_json::Value) -> Post { +pub async fn parse_post(post: &Value) -> Post { // Grab UTC time as unix timestamp let (rel_time, created) = time(post["data"]["created_utc"].as_f64().unwrap_or_default()); // Parse post score and upvote ratio @@ -675,9 +674,8 @@ pub async fn parse_post(post: &serde_json::Value) -> Post { let body = if val(post, "removed_by_category") == "moderator" { format!( - "

[removed] — view removed post

", - get_setting("REDLIB_PUSHSHIFT_FRONTEND").unwrap_or(String::from(crate::config::DEFAULT_PUSHSHIFT_FRONTEND)), - permalink + "

[removed] — view removed post

", + get_setting("REDLIB_PUSHSHIFT_FRONTEND").unwrap_or_else(|| String::from(crate::config::DEFAULT_PUSHSHIFT_FRONTEND)), ) } else { rewrite_urls(&val(post, "selftext_html")) @@ -753,7 +751,7 @@ pub async fn parse_post(post: &serde_json::Value) -> Post { // Grab a query parameter from a url pub fn param(path: &str, value: &str) -> Option { Some( - Url::parse(format!("https://libredd.it/{}", path).as_str()) + Url::parse(format!("https://libredd.it/{path}").as_str()) .ok()? .query_pairs() .into_owned() @@ -770,7 +768,7 @@ pub fn setting(req: &Request, name: &str) -> String { .cookie(name) .unwrap_or_else(|| { // If there is no cookie for this setting, try receiving a default from the config - if let Some(default) = crate::config::get_setting(&format!("REDLIB_DEFAULT_{}", name.to_uppercase())) { + if let Some(default) = get_setting(&format!("REDLIB_DEFAULT_{}", name.to_uppercase())) { Cookie::new(name, default) } else { Cookie::from(name) @@ -783,21 +781,21 @@ pub fn setting(req: &Request, name: &str) -> String { // Retrieve the value of a setting by name or the default value pub fn setting_or_default(req: &Request, name: &str, default: String) -> String { let value = setting(req, name); - if !value.is_empty() { - value - } else { + if value.is_empty() { default + } else { + value } } // Detect and redirect in the event of a random subreddit pub async fn catch_random(sub: &str, additional: &str) -> Result, String> { if sub == "random" || sub == "randnsfw" { - let new_sub = json(format!("/r/{}/about.json?raw_json=1", sub), false).await?["data"]["display_name"] + let new_sub = json(format!("/r/{sub}/about.json?raw_json=1"), false).await?["data"]["display_name"] .as_str() .unwrap_or_default() .to_string(); - Ok(redirect(format!("/r/{}{}", new_sub, additional))) + Ok(redirect(&format!("/r/{new_sub}{additional}"))) } else { Err("No redirect needed".to_string()) } @@ -963,28 +961,26 @@ pub fn val(j: &Value, k: &str) -> String { // NETWORKING // -pub fn template(t: impl Template) -> Result, String> { - Ok( - Response::builder() - .status(200) - .header("content-type", "text/html") - .body(t.render().unwrap_or_default().into()) - .unwrap_or_default(), - ) +pub fn template(t: &impl Template) -> Response { + Response::builder() + .status(200) + .header("content-type", "text/html") + .body(t.render().unwrap_or_default().into()) + .unwrap_or_default() } -pub fn redirect(path: String) -> Response { +pub fn redirect(path: &str) -> Response { Response::builder() .status(302) .header("content-type", "text/html") - .header("Location", &path) - .body(format!("Redirecting to {0}...", path).into()) + .header("Location", path) + .body(format!("Redirecting to {path}...").into()) .unwrap_or_default() } /// Renders a generic error landing page. -pub async fn error(req: Request, msg: impl ToString) -> Result, String> { - error!("Error page rendered: {}", msg.to_string()); +pub async fn error(req: Request, msg: &str) -> Result, String> { + error!("Error page rendered: {msg}"); let url = req.uri().to_string(); let body = ErrorTemplate { msg: msg.to_string(), @@ -1005,7 +1001,7 @@ pub async fn error(req: Request, msg: impl ToString) -> Result bool { - match crate::config::get_setting("REDLIB_SFW_ONLY") { + match get_setting("REDLIB_SFW_ONLY") { Some(val) => val == "on", None => false, } @@ -1029,7 +1025,7 @@ pub async fn nsfw_landing(req: Request, req_url: String) -> Result, req_url: String) -> Result