Many clippy changes
This commit is contained in:
parent
fade305f90
commit
d49a3fd38a
9 changed files with 205 additions and 187 deletions
|
@ -51,12 +51,12 @@ pub async fn canonical_path(path: String) -> Result<Option<String>, String> {
|
|||
}
|
||||
|
||||
pub async fn proxy(req: Request<Body>, format: &str) -> Result<Response<Body>, 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() {
|
||||
// Fill the parameter value in the url
|
||||
url = url.replace(&format!("{{{}}}", name), value);
|
||||
url = url.replace(&format!("{{{name}}}"), value);
|
||||
}
|
||||
|
||||
stream(&url, &req).await
|
||||
|
@ -70,7 +70,7 @@ async fn stream(url: &str, req: &Request<Body>) -> Result<Response<Body>, String
|
|||
let https = hyper_rustls::HttpsConnectorBuilder::new().with_native_roots().https_only().enable_http1().build();
|
||||
|
||||
// Build the hyper client from the HTTPS connector.
|
||||
let client: client::Client<_, hyper::Body> = client::Client::builder().build(https);
|
||||
let client: client::Client<_, Body> = client::Client::builder().build(https);
|
||||
|
||||
let mut builder = Request::get(uri);
|
||||
|
||||
|
@ -116,18 +116,18 @@ fn reddit_head(path: String, quarantine: bool) -> Boxed<Result<Response<Body>, 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<Result<Response<Body>, String>> {
|
||||
// Build Reddit URL from path.
|
||||
let url = format!("{}{}", REDDIT_URL_BASE, path);
|
||||
let url = format!("{REDDIT_URL_BASE}{path}");
|
||||
|
||||
// Prepare the HTTPS connector.
|
||||
let https = hyper_rustls::HttpsConnectorBuilder::new().with_native_roots().https_or_http().enable_http1().build();
|
||||
|
||||
// Construct the hyper client from the HTTPS connector.
|
||||
let client: client::Client<_, hyper::Body> = client::Client::builder().build(https);
|
||||
let client: client::Client<_, Body> = client::Client::builder().build(https);
|
||||
|
||||
// Build request to Reddit. When making a GET, request gzip compression.
|
||||
// (Reddit doesn't do brotli yet.)
|
||||
|
@ -161,7 +161,7 @@ fn request(method: &'static Method, path: String, redirect: bool, quarantine: bo
|
|||
.get("Location")
|
||||
.map(|val| {
|
||||
let new_url = percent_encode(val.as_bytes(), CONTROLS).to_string();
|
||||
format!("{}{}raw_json=1", new_url, if new_url.contains('?') { "&" } else { "?" })
|
||||
format!("{new_url}{}raw_json=1", if new_url.contains('?') { "&" } else { "?" })
|
||||
})
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
|
@ -236,7 +236,7 @@ pub async fn json(path: String, quarantine: bool) -> Result<Value, String> {
|
|||
// Closure to quickly build errors
|
||||
let err = |msg: &str, e: String| -> Result<Value, String> {
|
||||
// eprintln!("{} - {}: {}", url, msg, e);
|
||||
Err(format!("{}: {}", msg, e))
|
||||
Err(format!("{msg}: {e}"))
|
||||
};
|
||||
|
||||
// Fetch the url...
|
||||
|
@ -258,7 +258,7 @@ pub async fn json(path: String, quarantine: bool) -> Result<Value, String> {
|
|||
.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"
|
||||
})
|
||||
})
|
||||
|
|
62
src/main.rs
62
src/main.rs
|
@ -1,3 +1,35 @@
|
|||
#![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(elided_lifetimes_in_paths)]
|
||||
#![allow(clippy::unused_async)]
|
||||
#![allow(clippy::similar_names)]
|
||||
#![allow(clippy::items_after_statements)]
|
||||
#![allow(clippy::cast_possible_truncation)]
|
||||
#![allow(clippy::cast_precision_loss)]
|
||||
#![allow(clippy::match_wildcard_for_single_variants)]
|
||||
|
||||
// Global specifiers
|
||||
#![forbid(unsafe_code)]
|
||||
#![allow(clippy::cmp_owned)]
|
||||
|
@ -143,9 +175,9 @@ async fn main() {
|
|||
)
|
||||
.get_matches();
|
||||
|
||||
let address = matches.get_one("address").map(|m: &String| m.as_str()).unwrap_or("0.0.0.0");
|
||||
let port = std::env::var("PORT").unwrap_or_else(|_| matches.get_one("port").map(|m: &String| m.as_str()).unwrap_or("8080").to_string());
|
||||
let hsts = matches.get_one("hsts").map(|m: &String| m.as_str());
|
||||
let address = matches.get_one("address").map_or("0.0.0.0", std::string::String::as_str);
|
||||
let port = std::env::var("PORT").unwrap_or_else(|_| matches.get_one("port").map_or("8080", std::string::String::as_str).to_string());
|
||||
let hsts = matches.get_one("hsts").map(std::string::String::as_str);
|
||||
|
||||
let listener = [address, ":", &port].concat();
|
||||
|
||||
|
@ -163,7 +195,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);
|
||||
}
|
||||
}
|
||||
|
@ -204,7 +236,7 @@ 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());
|
||||
|
||||
|
@ -228,7 +260,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());
|
||||
|
@ -248,10 +280,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());
|
||||
|
||||
|
@ -263,10 +295,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());
|
||||
|
||||
|
@ -283,9 +315,9 @@ async fn main() {
|
|||
Some("best" | "hot" | "new" | "top" | "rising" | "controversial") => subreddit::community(req).await,
|
||||
|
||||
// Short link for post
|
||||
Some(id) if (5..7).contains(&id.len()) => match canonical_path(format!("/{}", id)).await {
|
||||
Some(id) if (5..7).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,
|
||||
|
@ -300,12 +332,12 @@ async fn main() {
|
|||
// Default service in case no routes match
|
||||
app.at("/*").get(|req| error(req, "Nothing here".to_string()).boxed());
|
||||
|
||||
println!("Running Libreddit v{} on {}!", env!("CARGO_PKG_VERSION"), listener);
|
||||
println!("Running Libreddit 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}");
|
||||
}
|
||||
}
|
||||
|
|
14
src/post.rs
14
src/post.rs
|
@ -37,14 +37,14 @@ pub async fn item(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||
if default_sort.is_empty() {
|
||||
String::new()
|
||||
} else {
|
||||
path = format!("{}.json?{}&sort={}&raw_json=1", req.uri().path(), req.uri().query().unwrap_or_default(), default_sort);
|
||||
path = format!("{}.json?{}&sort={default_sort}&raw_json=1", req.uri().path(), req.uri().query().unwrap_or_default());
|
||||
default_sort
|
||||
}
|
||||
});
|
||||
|
||||
// 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();
|
||||
|
@ -59,20 +59,20 @@ pub async fn item(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||
let url = req.uri().to_string();
|
||||
|
||||
// Use the Post and Comment structs to generate a website to show users
|
||||
template(PostTemplate {
|
||||
Ok(template(&PostTemplate {
|
||||
comments,
|
||||
post,
|
||||
sort,
|
||||
prefs: Preferences::new(req),
|
||||
prefs: Preferences::new(&req),
|
||||
single_thread,
|
||||
url,
|
||||
})
|
||||
}))
|
||||
}
|
||||
// If the Reddit API returns an error, exit and send error page to user
|
||||
Err(msg) => {
|
||||
if msg == "quarantined" {
|
||||
let sub = req.param("sub").unwrap_or_default();
|
||||
quarantine(req, sub)
|
||||
Ok(quarantine(&req, sub))
|
||||
} else {
|
||||
error(req, msg).await
|
||||
}
|
||||
|
@ -137,7 +137,7 @@ async fn parse_post(json: &serde_json::Value) -> Post {
|
|||
alt_url: String::new(),
|
||||
width: post["data"]["thumbnail_width"].as_i64().unwrap_or_default(),
|
||||
height: post["data"]["thumbnail_height"].as_i64().unwrap_or_default(),
|
||||
poster: "".to_string(),
|
||||
poster: String::new(),
|
||||
},
|
||||
flair: Flair {
|
||||
flair_parts: FlairPart::parse(
|
||||
|
|
|
@ -49,15 +49,15 @@ struct SearchTemplate {
|
|||
// SERVICES
|
||||
pub async fn find(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||
let nsfw_results = if setting(&req, "show_nsfw") == "on" { "&include_over_18=on" } else { "" };
|
||||
let path = format!("{}.json?{}{}&raw_json=1", req.uri().path(), req.uri().query().unwrap_or_default(), nsfw_results);
|
||||
let path = format!("{}.json?{}{nsfw_results}&raw_json=1", req.uri().path(), req.uri().query().unwrap_or_default());
|
||||
let query = param(&path, "q").unwrap_or_default();
|
||||
|
||||
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();
|
||||
|
@ -85,7 +85,7 @@ pub async fn find(req: Request<Body>) -> Result<Response<Body>, 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,
|
||||
|
@ -94,22 +94,22 @@ pub async fn find(req: Request<Body>) -> Result<Response<Body>, 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,
|
||||
},
|
||||
prefs: Preferences::new(req),
|
||||
prefs: Preferences::new(&req),
|
||||
url,
|
||||
is_filtered: true,
|
||||
all_posts_filtered: false,
|
||||
all_posts_hidden_nsfw: false,
|
||||
})
|
||||
}))
|
||||
} else {
|
||||
match Post::fetch(&path, quarantined).await {
|
||||
Ok((mut posts, after)) => {
|
||||
let all_posts_filtered = filter_posts(&mut posts, &filters);
|
||||
let all_posts_hidden_nsfw = posts.iter().all(|p| p.flags.nsfw) && setting(&req, "show_nsfw") != "on";
|
||||
template(SearchTemplate {
|
||||
Ok(template(&SearchTemplate {
|
||||
posts,
|
||||
subreddits,
|
||||
sub,
|
||||
|
@ -122,17 +122,17 @@ pub async fn find(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||
restrict_sr: param(&path, "restrict_sr").unwrap_or_default(),
|
||||
typed,
|
||||
},
|
||||
prefs: Preferences::new(req),
|
||||
prefs: Preferences::new(&req),
|
||||
url,
|
||||
is_filtered: false,
|
||||
all_posts_filtered,
|
||||
all_posts_hidden_nsfw,
|
||||
})
|
||||
}))
|
||||
}
|
||||
Err(msg) => {
|
||||
if msg == "quarantined" {
|
||||
let sub = req.param("sub").unwrap_or_default();
|
||||
quarantine(req, sub)
|
||||
Ok(quarantine(&req, sub))
|
||||
} else {
|
||||
error(req, msg).await
|
||||
}
|
||||
|
@ -143,7 +143,7 @@ pub async fn find(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||
|
||||
async fn search_subreddits(q: &str, typed: &str) -> Vec<Subreddit> {
|
||||
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"]
|
||||
|
|
|
@ -46,11 +46,11 @@ impl CompressionType {
|
|||
/// Returns a `CompressionType` given a content coding
|
||||
/// in [RFC 7231](https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.4)
|
||||
/// format.
|
||||
fn parse(s: &str) -> Option<CompressionType> {
|
||||
fn parse(s: &str) -> Option<Self> {
|
||||
let c = match s {
|
||||
// Compressors we support.
|
||||
"gzip" => CompressionType::Gzip,
|
||||
"br" => CompressionType::Brotli,
|
||||
"gzip" => Self::Gzip,
|
||||
"br" => Self::Brotli,
|
||||
|
||||
// The wildcard means that we can choose whatever
|
||||
// compression we prefer. In this case, use the
|
||||
|
@ -68,8 +68,8 @@ impl CompressionType {
|
|||
impl ToString for CompressionType {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
CompressionType::Gzip => "gzip".to_string(),
|
||||
CompressionType::Brotli => "br".to_string(),
|
||||
Self::Gzip => "gzip".to_string(),
|
||||
Self::Brotli => "br".to_string(),
|
||||
_ => String::new(),
|
||||
}
|
||||
}
|
||||
|
@ -176,25 +176,25 @@ impl ResponseExt for Response<Body> {
|
|||
}
|
||||
|
||||
impl Route<'_> {
|
||||
fn method(&mut self, method: Method, dest: fn(Request<Body>) -> BoxResponse) -> &mut Self {
|
||||
fn method(&mut self, method: &Method, dest: fn(Request<Body>) -> 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<Body>) -> 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<Body>) -> BoxResponse) -> &mut Self {
|
||||
self.method(Method::POST, dest)
|
||||
self.method(&Method::POST, dest)
|
||||
}
|
||||
}
|
||||
|
||||
impl Server {
|
||||
pub fn new() -> Self {
|
||||
Server {
|
||||
Self {
|
||||
default_headers: HeaderMap::new(),
|
||||
router: Router::new(),
|
||||
}
|
||||
|
@ -207,7 +207,7 @@ impl Server {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn listen(self, addr: String) -> Boxed<Result<(), hyper::Error>> {
|
||||
pub fn listen(self, addr: &str) -> Boxed<Result<(), hyper::Error>> {
|
||||
let make_svc = make_service_fn(move |_conn| {
|
||||
// For correct borrowing, these values need to be borrowed
|
||||
let router = self.router.clone();
|
||||
|
@ -231,7 +231,7 @@ impl Server {
|
|||
}
|
||||
|
||||
// Match the visited path with an added route
|
||||
match router.recognize(&format!("/{}{}", req.method().as_str(), path)) {
|
||||
match router.recognize(&format!("/{}{path}", req.method().as_str())) {
|
||||
// If a route was configured for this path
|
||||
Ok(found) => {
|
||||
let mut parammed = req;
|
||||
|
@ -243,7 +243,7 @@ impl Server {
|
|||
match func.await {
|
||||
Ok(mut res) => {
|
||||
res.headers_mut().extend(def_headers);
|
||||
let _ = compress_response(req_headers, &mut res).await;
|
||||
drop(compress_response(req_headers, &mut res).await);
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -282,7 +282,7 @@ async fn new_boilerplate(
|
|||
) -> Result<Response<Body>, String> {
|
||||
match Response::builder().status(status).body(body) {
|
||||
Ok(mut res) => {
|
||||
let _ = compress_response(req_headers, &mut res).await;
|
||||
drop(compress_response(req_headers, &mut res).await);
|
||||
|
||||
res.headers_mut().extend(default_headers.clone());
|
||||
Ok(res)
|
||||
|
@ -445,10 +445,10 @@ fn determine_compressor(accept_encoding: &str) -> Option<CompressionType> {
|
|||
};
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -460,16 +460,16 @@ fn determine_compressor(accept_encoding: &str) -> Option<CompressionType> {
|
|||
/// 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.
|
||||
|
@ -506,21 +506,18 @@ async fn compress_response(req_headers: HeaderMap<header::HeaderValue>, res: &mu
|
|||
// Quick and dirty closure for extracting a header from the request and
|
||||
// returning it as a &str.
|
||||
let get_req_header = |k: header::HeaderName| -> Option<&str> {
|
||||
match req_headers.get(k) {
|
||||
Some(hdr) => match from_utf8(hdr.as_bytes()) {
|
||||
Ok(val) => Some(val),
|
||||
req_headers.get(k).and_then(|hdr| match from_utf8(hdr.as_bytes()) {
|
||||
Ok(val) => Some(val),
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
Err(e) => {
|
||||
dbg_msg!(e);
|
||||
None
|
||||
}
|
||||
#[cfg(debug_assertions)]
|
||||
Err(e) => {
|
||||
dbg_msg!(e);
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
Err(_) => None,
|
||||
},
|
||||
None => None,
|
||||
}
|
||||
#[cfg(not(debug_assertions))]
|
||||
Err(_) => None,
|
||||
})
|
||||
};
|
||||
|
||||
// Check to see which compressor is requested, and if we can use it.
|
||||
|
@ -692,7 +689,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
|
||||
|
|
|
@ -38,10 +38,10 @@ const PREFS: [&str; 11] = [
|
|||
// Retrieve cookies from request "Cookie" header
|
||||
pub async fn get(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||
let url = req.uri().to_string();
|
||||
template(SettingsTemplate {
|
||||
prefs: Preferences::new(req),
|
||||
Ok(template(&SettingsTemplate {
|
||||
prefs: Preferences::new(&req),
|
||||
url,
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
// Set cookies using response "Set-Cookie" header
|
||||
|
@ -69,7 +69,7 @@ pub async fn set(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||
|
||||
let form = url::form_urlencoded::parse(&body_bytes).collect::<HashMap<_, _>>();
|
||||
|
||||
let mut response = redirect("/settings".to_string());
|
||||
let mut response = redirect("/settings");
|
||||
|
||||
for &name in &PREFS {
|
||||
match form.get(name) {
|
||||
|
@ -103,12 +103,11 @@ fn set_cookies_method(req: Request<Body>, remove_cookies: bool) -> Response<Body
|
|||
|
||||
let form = url::form_urlencoded::parse(query).collect::<HashMap<_, _>>();
|
||||
|
||||
let path = match form.get("redirect") {
|
||||
Some(value) => format!("/{}", value.replace("%26", "&").replace("%23", "#")),
|
||||
None => "/".to_string(),
|
||||
};
|
||||
let path = form
|
||||
.get("redirect")
|
||||
.map_or_else(|| "/".to_string(), |value| format!("/{}", value.replace("%26", "&").replace("%23", "#")));
|
||||
|
||||
let mut response = redirect(path);
|
||||
let mut response = redirect(&path);
|
||||
|
||||
for name in [PREFS.to_vec(), vec!["subscriptions", "filters"]].concat() {
|
||||
match form.get(name) {
|
||||
|
|
|
@ -74,7 +74,7 @@ pub async fn community(req: Request<Body>) -> Result<Response<Body>, 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
|
||||
|
@ -96,75 +96,73 @@ pub async fn community(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||
}
|
||||
};
|
||||
|
||||
let path = format!("/r/{}/{}.json?{}&raw_json=1", sub_name.clone(), sort, req.uri().query().unwrap_or_default());
|
||||
let path = format!("/r/{}/{sort}.json?{}&raw_json=1", sub_name.clone(), req.uri().query().unwrap_or_default());
|
||||
let url = String::from(req.uri().path_and_query().map_or("", |val| val.as_str()));
|
||||
let redirect_url = url[1..].replace('?', "%3F").replace('&', "%26").replace('+', "%2B");
|
||||
let filters = get_filters(&req);
|
||||
|
||||
// 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()),
|
||||
prefs: Preferences::new(req),
|
||||
ends: (param(&path, "after").unwrap_or_default(), String::new()),
|
||||
prefs: Preferences::new(&req),
|
||||
url,
|
||||
redirect_url,
|
||||
is_filtered: true,
|
||||
all_posts_filtered: false,
|
||||
all_posts_hidden_nsfw: false,
|
||||
})
|
||||
}))
|
||||
} else {
|
||||
match Post::fetch(&path, quarantined).await {
|
||||
Ok((mut posts, after)) => {
|
||||
let all_posts_filtered = filter_posts(&mut posts, &filters);
|
||||
let all_posts_hidden_nsfw = 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()),
|
||||
ends: (param(&path, "after").unwrap_or_default(), after),
|
||||
prefs: Preferences::new(req),
|
||||
prefs: Preferences::new(&req),
|
||||
url,
|
||||
redirect_url,
|
||||
is_filtered: false,
|
||||
all_posts_filtered,
|
||||
all_posts_hidden_nsfw,
|
||||
})
|
||||
}))
|
||||
}
|
||||
Err(msg) => match msg.as_str() {
|
||||
"quarantined" => quarantine(req, sub_name),
|
||||
"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,
|
||||
"quarantined" => Ok(quarantine(&req, sub_name)),
|
||||
"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<Body>, sub: String) -> Result<Response<Body>, String> {
|
||||
pub fn quarantine(req: &Request<Body>, sub: String) -> Response<Body> {
|
||||
let wall = WallTemplate {
|
||||
title: format!("r/{} is quarantined", sub),
|
||||
title: format!("r/{sub} is quarantined"),
|
||||
msg: "Please click the button below to continue to this subreddit.".to_string(),
|
||||
url: req.uri().to_string(),
|
||||
sub,
|
||||
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<Body>) -> Result<Response<Body>, 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("/")
|
||||
|
@ -189,19 +187,18 @@ pub async fn subscriptions_filters(req: Request<Body>) -> Result<Response<Body>,
|
|||
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();
|
||||
|
||||
let preferences = Preferences::new(req);
|
||||
let preferences = Preferences::new(&req);
|
||||
let mut sub_list = preferences.subscriptions;
|
||||
let mut filters = preferences.filters;
|
||||
|
||||
// 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)> = posts["data"]["children"]
|
||||
.as_array()
|
||||
.map(|list| {
|
||||
|
@ -226,7 +223,7 @@ pub async fn subscriptions_filters(req: Request<Body>) -> Result<Response<Body>,
|
|||
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?;
|
||||
display["data"]["display_name"].as_str().ok_or_else(|| "Failed to query subreddit name".to_string())?
|
||||
};
|
||||
|
@ -257,13 +254,9 @@ pub async fn subscriptions_filters(req: Request<Body>) -> Result<Response<Body>,
|
|||
|
||||
// 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)
|
||||
} else {
|
||||
format!("/r/{}", sub)
|
||||
};
|
||||
let path = param(&format!("?{query}"), "redirect").map_or_else(|| format!("/r/{sub}"), |redirect_path| format!("/{redirect_path}"));
|
||||
|
||||
let mut response = redirect(path);
|
||||
let mut response = redirect(&path);
|
||||
|
||||
// Delete cookie if empty, else set
|
||||
if sub_list.is_empty() {
|
||||
|
@ -301,20 +294,20 @@ pub async fn wiki(req: Request<Body>) -> Result<Response<Body>, 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("<h3>Wiki not found</h3>")),
|
||||
page,
|
||||
prefs: Preferences::new(req),
|
||||
prefs: Preferences::new(&req),
|
||||
url,
|
||||
}),
|
||||
})),
|
||||
Err(msg) => {
|
||||
if msg == "quarantined" {
|
||||
quarantine(req, sub)
|
||||
Ok(quarantine(&req, sub))
|
||||
} else {
|
||||
error(req, msg).await
|
||||
}
|
||||
|
@ -332,13 +325,13 @@ pub async fn sidebar(req: Request<Body>) -> Result<Response<Body>, 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!(
|
||||
// "{}<hr><h1>Moderators</h1><br><ul>{}</ul>",
|
||||
|
@ -347,12 +340,12 @@ pub async fn sidebar(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||
// ),
|
||||
sub,
|
||||
page: "Sidebar".to_string(),
|
||||
prefs: Preferences::new(req),
|
||||
prefs: Preferences::new(&req),
|
||||
url,
|
||||
}),
|
||||
})),
|
||||
Err(msg) => {
|
||||
if msg == "quarantined" {
|
||||
quarantine(req, sub)
|
||||
Ok(quarantine(&req, sub))
|
||||
} else {
|
||||
error(req, msg).await
|
||||
}
|
||||
|
@ -397,7 +390,7 @@ pub async fn sidebar(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||
// SUBREDDIT
|
||||
async fn subreddit(sub: &str, quarantined: bool) -> Result<Subreddit, 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");
|
||||
|
||||
// Send a request to the url
|
||||
let res = json(path, quarantined).await?;
|
||||
|
|
16
src/user.rs
16
src/user.rs
|
@ -49,38 +49,38 @@ pub async fn profile(req: Request<Body>) -> Result<Response<Body>, 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),
|
||||
prefs: Preferences::new(&req),
|
||||
url,
|
||||
redirect_url,
|
||||
is_filtered: true,
|
||||
all_posts_filtered: false,
|
||||
all_posts_hidden_nsfw: false,
|
||||
})
|
||||
}))
|
||||
} else {
|
||||
// Request user posts/comments from Reddit
|
||||
match Post::fetch(&path, false).await {
|
||||
Ok((mut posts, after)) => {
|
||||
let all_posts_filtered = filter_posts(&mut posts, &filters);
|
||||
let all_posts_hidden_nsfw = 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()),
|
||||
ends: (param(&path, "after").unwrap_or_default(), after),
|
||||
listing,
|
||||
prefs: Preferences::new(req),
|
||||
prefs: Preferences::new(&req),
|
||||
url,
|
||||
redirect_url,
|
||||
is_filtered: false,
|
||||
all_posts_filtered,
|
||||
all_posts_hidden_nsfw,
|
||||
})
|
||||
}))
|
||||
}
|
||||
// If there is an error show error page
|
||||
Err(msg) => error(req, msg).await,
|
||||
|
@ -91,7 +91,7 @@ pub async fn profile(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||
// USER
|
||||
async fn user(name: &str) -> Result<User, String> {
|
||||
// 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| {
|
||||
|
|
95
src/utils.rs
95
src/utils.rs
|
@ -48,8 +48,8 @@ impl FlairPart {
|
|||
// Parse type of flair
|
||||
match flair_type {
|
||||
// If flair contains emojis and text
|
||||
"richtext" => match rich_flair {
|
||||
Some(rich) => rich
|
||||
"richtext" => rich_flair.map_or_else(Vec::new, |rich| {
|
||||
rich
|
||||
.iter()
|
||||
// For each part of the flair, extract text and emojis
|
||||
.map(|part| {
|
||||
|
@ -63,17 +63,18 @@ impl FlairPart {
|
|||
},
|
||||
}
|
||||
})
|
||||
.collect::<Vec<Self>>(),
|
||||
None => Vec::new(),
|
||||
},
|
||||
.collect::<Vec<Self>>()
|
||||
}),
|
||||
// If flair contains only text
|
||||
"text" => match text_flair {
|
||||
Some(text) => vec![Self {
|
||||
flair_part_type: "text".to_string(),
|
||||
value: text.to_string(),
|
||||
}],
|
||||
None => Vec::new(),
|
||||
},
|
||||
"text" => text_flair.map_or_else(
|
||||
Vec::new,
|
||||
|text| {
|
||||
vec![Self {
|
||||
flair_part_type: "text".to_string(),
|
||||
value: text.to_string(),
|
||||
}]
|
||||
},
|
||||
),
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
@ -299,7 +300,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"),
|
||||
|
@ -383,7 +384,7 @@ 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)))
|
||||
self.iter().fold(Ok(()), |result, award| result.and_then(|_| writeln!(f, "{award}")))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -479,29 +480,29 @@ pub struct ThemeAssets;
|
|||
|
||||
impl Preferences {
|
||||
// Build preferences from cookies
|
||||
pub fn new(req: Request<Body>) -> Self {
|
||||
pub fn new(req: &Request<Body>) -> Self {
|
||||
// Read available theme names from embedded css files.
|
||||
// Always make the default "system" theme available.
|
||||
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,
|
||||
theme: setting(&req, "theme"),
|
||||
front_page: setting(&req, "front_page"),
|
||||
layout: setting(&req, "layout"),
|
||||
wide: setting(&req, "wide"),
|
||||
show_nsfw: setting(&req, "show_nsfw"),
|
||||
blur_nsfw: setting(&req, "blur_nsfw"),
|
||||
use_hls: setting(&req, "use_hls"),
|
||||
hide_hls_notification: setting(&req, "hide_hls_notification"),
|
||||
autoplay_videos: setting(&req, "autoplay_videos"),
|
||||
comment_sort: setting(&req, "comment_sort"),
|
||||
post_sort: setting(&req, "post_sort"),
|
||||
subscriptions: setting(&req, "subscriptions").split('+').map(String::from).filter(|s| !s.is_empty()).collect(),
|
||||
filters: setting(&req, "filters").split('+').map(String::from).filter(|s| !s.is_empty()).collect(),
|
||||
theme: setting(req, "theme"),
|
||||
front_page: setting(req, "front_page"),
|
||||
layout: setting(req, "layout"),
|
||||
wide: setting(req, "wide"),
|
||||
show_nsfw: setting(req, "show_nsfw"),
|
||||
blur_nsfw: setting(req, "blur_nsfw"),
|
||||
use_hls: setting(req, "use_hls"),
|
||||
hide_hls_notification: setting(req, "hide_hls_notification"),
|
||||
autoplay_videos: setting(req, "autoplay_videos"),
|
||||
comment_sort: setting(req, "comment_sort"),
|
||||
post_sort: setting(req, "post_sort"),
|
||||
subscriptions: setting(req, "subscriptions").split('+').map(String::from).filter(|s| !s.is_empty()).collect(),
|
||||
filters: setting(req, "filters").split('+').map(String::from).filter(|s| !s.is_empty()).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -530,7 +531,7 @@ pub fn filter_posts(posts: &mut Vec<Post>, filters: &HashSet<String>) -> bool {
|
|||
// Grab a query parameter from a url
|
||||
pub fn param(path: &str, value: &str) -> Option<String> {
|
||||
Some(
|
||||
Url::parse(format!("https://libredd.it/{}", path).as_str())
|
||||
Url::parse(format!("https://libredd.it/{path}").as_str())
|
||||
.ok()?
|
||||
.query_pairs()
|
||||
.into_owned()
|
||||
|
@ -547,11 +548,7 @@ pub fn setting(req: &Request<Body>, name: &str) -> String {
|
|||
.cookie(name)
|
||||
.unwrap_or_else(|| {
|
||||
// If there is no cookie for this setting, try receiving a default from an environment variable
|
||||
if let Ok(default) = std::env::var(format!("LIBREDDIT_DEFAULT_{}", name.to_uppercase())) {
|
||||
Cookie::new(name, default)
|
||||
} else {
|
||||
Cookie::named(name)
|
||||
}
|
||||
std::env::var(format!("LIBREDDIT_DEFAULT_{}", name.to_uppercase())).map_or_else(|_| Cookie::named(name), |default| Cookie::new(name, default))
|
||||
})
|
||||
.value()
|
||||
.to_string()
|
||||
|
@ -560,11 +557,11 @@ pub fn setting(req: &Request<Body>, name: &str) -> String {
|
|||
// Detect and redirect in the event of a random subreddit
|
||||
pub async fn catch_random(sub: &str, additional: &str) -> Result<Response<Body>, 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())
|
||||
}
|
||||
|
@ -699,22 +696,22 @@ pub fn val(j: &Value, k: &str) -> String {
|
|||
// NETWORKING
|
||||
//
|
||||
|
||||
pub fn template(t: impl Template) -> Result<Response<Body>, 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<Body> {
|
||||
// Ok(
|
||||
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<Body> {
|
||||
pub fn redirect(path: &str) -> Response<Body> {
|
||||
Response::builder()
|
||||
.status(302)
|
||||
.header("content-type", "text/html")
|
||||
.header("Location", &path)
|
||||
.body(format!("Redirecting to <a href=\"{0}\">{0}</a>...", path).into())
|
||||
.header("Location", path)
|
||||
.body(format!("Redirecting to <a href=\"{path}\">{path}</a>...").into())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
|
@ -723,7 +720,7 @@ pub async fn error(req: Request<Body>, msg: impl ToString) -> Result<Response<Bo
|
|||
let url = req.uri().to_string();
|
||||
let body = ErrorTemplate {
|
||||
msg: msg.to_string(),
|
||||
prefs: Preferences::new(req),
|
||||
prefs: Preferences::new(&req),
|
||||
url,
|
||||
}
|
||||
.render()
|
||||
|
|
Loading…
Add table
Reference in a new issue