redsunlib/src/search.rs

161 lines
4.5 KiB
Rust
Raw Normal View History

2021-01-01 12:54:13 +13:00
// CRATES
use crate::utils::{catch_random, error, filter_posts, format_num, format_url, get_filters, param, redirect, setting, template, val, Post, Preferences};
use crate::{
client::json,
subreddit::{can_access_quarantine, quarantine},
RequestExt,
};
2021-01-01 12:54:13 +13:00
use askama::Template;
2021-03-18 11:30:33 +13:00
use hyper::{Body, Request, Response};
2021-01-01 12:54:13 +13:00
// STRUCTS
2021-01-03 19:37:54 +13:00
struct SearchParams {
q: String,
sort: String,
t: String,
before: String,
after: String,
restrict_sr: String,
2021-11-24 19:24:23 +13:00
typed: String,
2021-01-03 19:37:54 +13:00
}
2021-01-15 07:22:50 +13:00
// STRUCTS
struct Subreddit {
name: String,
url: String,
icon: String,
2021-01-15 07:22:50 +13:00
description: String,
subscribers: (String, String),
2021-01-15 07:22:50 +13:00
}
2021-01-01 12:54:13 +13:00
#[derive(Template)]
#[template(path = "search.html", escape = "none")]
struct SearchTemplate {
posts: Vec<Post>,
2021-01-15 07:22:50 +13:00
subreddits: Vec<Subreddit>,
2021-01-01 12:54:13 +13:00
sub: String,
2021-01-03 19:37:54 +13:00
params: SearchParams,
2021-01-09 14:35:04 +13:00
prefs: Preferences,
url: String,
/// Whether the subreddit itself is filtered.
is_filtered: bool,
/// Whether all fetched posts are filtered (to differentiate between no posts fetched in the first place,
/// and all fetched posts being filtered).
all_posts_filtered: bool,
2021-01-01 12:54:13 +13:00
}
// SERVICES
2021-03-18 11:30:33 +13:00
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 { "" };
2021-11-30 19:29:41 +13:00
let path = format!("{}.json?{}{}&raw_json=1", req.uri().path(), req.uri().query().unwrap_or_default(), nsfw_results);
2021-09-10 12:28:55 +12:00
let query = param(&path, "q").unwrap_or_default();
if query.is_empty() {
return Ok(redirect("/".to_string()));
}
2021-03-18 11:30:33 +13:00
let sub = req.param("sub").unwrap_or_default();
let quarantined = can_access_quarantine(&req, &sub);
// Handle random subreddits
if let Ok(random) = catch_random(&sub, "/find").await {
return Ok(random);
}
2021-01-15 08:45:04 +13:00
2021-11-24 19:24:23 +13:00
let typed = param(&path, "type").unwrap_or_default();
2021-05-17 04:11:38 +12:00
let sort = param(&path, "sort").unwrap_or_else(|| "relevance".to_string());
let filters = get_filters(&req);
2021-01-15 07:22:50 +13:00
2021-05-21 07:24:06 +12:00
// If search is not restricted to this subreddit, show other subreddits in search results
let subreddits = if param(&path, "restrict_sr").is_none() {
let mut subreddits = search_subreddits(&query, &typed).await;
subreddits.retain(|s| !filters.contains(s.name.as_str()));
subreddits
} else {
Vec::new()
};
2021-01-01 12:54:13 +13:00
let url = String::from(req.uri().path_and_query().map_or("", |val| val.as_str()));
// If all requested subs are filtered, we don't need to fetch posts.
if sub.split('+').all(|s| filters.contains(s)) {
template(SearchTemplate {
posts: Vec::new(),
subreddits,
sub,
params: SearchParams {
2021-02-21 10:59:16 +13:00
q: query.replace('"', "&quot;"),
sort,
t: param(&path, "t").unwrap_or_default(),
before: param(&path, "after").unwrap_or_default(),
after: "".to_string(),
restrict_sr: param(&path, "restrict_sr").unwrap_or_default(),
2021-11-24 19:24:23 +13:00
typed,
},
2021-02-25 18:29:23 +13:00
prefs: Preferences::new(req),
url,
is_filtered: true,
all_posts_filtered: false,
})
} else {
match Post::fetch(&path, quarantined).await {
Ok((mut posts, after)) => {
let all_posts_filtered = filter_posts(&mut posts, &filters);
template(SearchTemplate {
posts,
subreddits,
sub,
params: SearchParams {
q: query.replace('"', "&quot;"),
sort,
t: param(&path, "t").unwrap_or_default(),
before: param(&path, "after").unwrap_or_default(),
after,
restrict_sr: param(&path, "restrict_sr").unwrap_or_default(),
typed,
},
prefs: Preferences::new(req),
url,
is_filtered: false,
all_posts_filtered,
})
}
Err(msg) => {
if msg == "quarantined" {
let sub = req.param("sub").unwrap_or_default();
quarantine(req, sub)
} else {
error(req, msg).await
}
}
}
2021-01-01 12:54:13 +13:00
}
}
2021-01-15 08:45:04 +13:00
2021-11-24 19:24:23 +13:00
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);
2021-01-15 08:45:04 +13:00
// Send a request to the url
2021-05-21 07:24:06 +12:00
json(subreddit_search_path, false).await.unwrap_or_default()["data"]["children"]
.as_array()
.map(ToOwned::to_owned)
.unwrap_or_default()
.iter()
.map(|subreddit| {
// For each subreddit from subreddit list
// Fetch subreddit icon either from the community_icon or icon_img value
2021-11-24 19:24:23 +13:00
let icon = subreddit["data"]["community_icon"].as_str().map_or_else(|| val(subreddit, "icon_img"), ToString::to_string);
2021-05-21 07:24:06 +12:00
Subreddit {
name: val(subreddit, "display_name"),
2021-05-21 07:24:06 +12:00
url: val(subreddit, "url"),
icon: format_url(&icon),
description: val(subreddit, "public_description"),
subscribers: format_num(subreddit["data"]["subscribers"].as_f64().unwrap_or_default() as i64),
2021-01-15 08:45:04 +13:00
}
2021-05-21 07:24:06 +12:00
})
.collect::<Vec<Subreddit>>()
2021-01-15 08:45:04 +13:00
}