more more quick answers
* notepad * fix config * mdn docs * search bar fix * remove unnecessary check * notepad fix * make config avaliable to all stages of request * make config more consistent * minecraft wiki infobox * cleanup * engine_list_separator fix * are you happy now mta * change Arc<Config> to &Config in places --------- Co-authored-by: mat <git@matdoes.dev>
This commit is contained in:
parent
39aeae7a69
commit
569922aab7
@ -1,11 +1,13 @@
|
|||||||
bind = "0.0.0.0:28019"
|
bind = "0.0.0.0:28019"
|
||||||
|
|
||||||
|
engine_list_separator = false
|
||||||
|
|
||||||
[engines]
|
[engines]
|
||||||
google = { weight = 1.05 }
|
google = { weight = 1.05 }
|
||||||
bing = { weight = 1.0 }
|
bing = { weight = 1.0 }
|
||||||
brave = { weight = 1.25 }
|
brave = { weight = 1.25 }
|
||||||
|
|
||||||
google-scholar = { enabled = false, weight = 0.50 }
|
google_scholar = { enabled = false, weight = 0.50 }
|
||||||
rightdao = { enabled = false, weight = 0.10 }
|
rightdao = { enabled = false, weight = 0.10 }
|
||||||
stract = { enabled = false, weight = 0.15 }
|
stract = { enabled = false, weight = 0.15 }
|
||||||
yep = { enabled = false, weight = 0.10 }
|
yep = { enabled = false, weight = 0.10 }
|
||||||
@ -17,3 +19,8 @@ fend = { enabled = false, weight = 10 }
|
|||||||
[engines.marginalia]
|
[engines.marginalia]
|
||||||
args = { profile = "corpo", js = "default", adtech = "default" }
|
args = { profile = "corpo", js = "default", adtech = "default" }
|
||||||
weight = 0.15
|
weight = 0.15
|
||||||
|
|
||||||
|
[engines.mdn]
|
||||||
|
# the number of sections of text to display
|
||||||
|
# 1 is just the summary and 0 is no limit
|
||||||
|
max_sections = 1
|
||||||
|
@ -6,9 +6,11 @@ use tracing::info;
|
|||||||
|
|
||||||
use crate::engines::Engine;
|
use crate::engines::Engine;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub bind: SocketAddr,
|
pub bind: SocketAddr,
|
||||||
|
#[serde(default)]
|
||||||
|
pub engine_list_separator: Option<bool>,
|
||||||
pub engines: EnginesConfig,
|
pub engines: EnginesConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,6 +36,8 @@ impl Config {
|
|||||||
// use the default for something.
|
// use the default for something.
|
||||||
pub fn update(&mut self, other: Self) {
|
pub fn update(&mut self, other: Self) {
|
||||||
self.bind = other.bind;
|
self.bind = other.bind;
|
||||||
|
self.engine_list_separator = self.engine_list_separator.or(other.engine_list_separator);
|
||||||
|
assert_ne!(self.engine_list_separator, None);
|
||||||
for (key, value) in other.engines.map {
|
for (key, value) in other.engines.map {
|
||||||
if let Some(existing) = self.engines.map.get_mut(&key) {
|
if let Some(existing) = self.engines.map.get_mut(&key) {
|
||||||
existing.update(value);
|
existing.update(value);
|
||||||
@ -44,7 +48,7 @@ impl Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct EnginesConfig {
|
pub struct EnginesConfig {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub map: HashMap<Engine, DefaultableEngineConfig>,
|
pub map: HashMap<Engine, DefaultableEngineConfig>,
|
||||||
@ -76,7 +80,7 @@ impl EnginesConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Clone)]
|
#[derive(Deserialize, Clone, Debug)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum DefaultableEngineConfig {
|
pub enum DefaultableEngineConfig {
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
@ -99,7 +103,7 @@ impl Default for DefaultableEngineConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Clone)]
|
#[derive(Deserialize, Clone, Debug)]
|
||||||
pub struct FullEngineConfig {
|
pub struct FullEngineConfig {
|
||||||
#[serde(default = "default_true")]
|
#[serde(default = "default_true")]
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
|
@ -50,7 +50,9 @@ pub struct WiktionaryDefinition {
|
|||||||
pub examples: Vec<String>,
|
pub examples: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_response(HttpResponse { res, body }: &HttpResponse) -> eyre::Result<EngineResponse> {
|
pub fn parse_response(
|
||||||
|
HttpResponse { res, body, .. }: &HttpResponse,
|
||||||
|
) -> eyre::Result<EngineResponse> {
|
||||||
let url = res.url();
|
let url = res.url();
|
||||||
|
|
||||||
let Ok(res) = serde_json::from_str::<WiktionaryResponse>(body) else {
|
let Ok(res) = serde_json::from_str::<WiktionaryResponse>(body) else {
|
||||||
|
@ -7,7 +7,8 @@ pub fn request(query: &SearchQuery) -> EngineResponse {
|
|||||||
return EngineResponse::new();
|
return EngineResponse::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
EngineResponse::answer_html(
|
// This allows pasting styles which is undesired behavior, and the
|
||||||
r#"<div contenteditable id='notepad' placeholder='Notes' style='width:100%;color:white;outline:none;min-height:4em;font-size:12px;'></div>"#.to_string()
|
// `contenteditable="plaintext-only"` attribute currently only works on Chrome.
|
||||||
)
|
// This should be updated when the attribute becomes available in more browsers
|
||||||
|
EngineResponse::answer_html(r#"<div contenteditable class="answer-notepad"></div>"#.to_string())
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ pub mod search;
|
|||||||
engines! {
|
engines! {
|
||||||
// search
|
// search
|
||||||
Google = "google",
|
Google = "google",
|
||||||
GoogleScholar = "google-scholar",
|
GoogleScholar = "google_scholar",
|
||||||
Bing = "bing",
|
Bing = "bing",
|
||||||
Brave = "brave",
|
Brave = "brave",
|
||||||
Marginalia = "marginalia",
|
Marginalia = "marginalia",
|
||||||
@ -49,6 +49,7 @@ engines! {
|
|||||||
DocsRs = "docs_rs",
|
DocsRs = "docs_rs",
|
||||||
GitHub = "github",
|
GitHub = "github",
|
||||||
Mdn = "mdn",
|
Mdn = "mdn",
|
||||||
|
MinecraftWiki = "minecraft_wiki",
|
||||||
StackExchange = "stackexchange",
|
StackExchange = "stackexchange",
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,6 +85,7 @@ engine_postsearch_requests! {
|
|||||||
DocsRs => postsearch::docs_rs::request, parse_response,
|
DocsRs => postsearch::docs_rs::request, parse_response,
|
||||||
GitHub => postsearch::github::request, parse_response,
|
GitHub => postsearch::github::request, parse_response,
|
||||||
Mdn => postsearch::mdn::request, parse_response,
|
Mdn => postsearch::mdn::request, parse_response,
|
||||||
|
MinecraftWiki => postsearch::minecraft_wiki::request, parse_response,
|
||||||
StackExchange => postsearch::stackexchange::request, parse_response,
|
StackExchange => postsearch::stackexchange::request, parse_response,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,6 +156,7 @@ impl From<Vec<String>> for RequestAutocompleteResponse {
|
|||||||
pub struct HttpResponse {
|
pub struct HttpResponse {
|
||||||
pub res: reqwest::Response,
|
pub res: reqwest::Response,
|
||||||
pub body: String,
|
pub body: String,
|
||||||
|
pub config: Arc<Config>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a HttpResponse> for &'a str {
|
impl<'a> From<&'a HttpResponse> for &'a str {
|
||||||
@ -302,7 +305,11 @@ pub async fn search(
|
|||||||
start_time,
|
start_time,
|
||||||
))?;
|
))?;
|
||||||
|
|
||||||
let http_response = HttpResponse { res, body };
|
let http_response = HttpResponse {
|
||||||
|
res,
|
||||||
|
body,
|
||||||
|
config: query.config.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
let response = match engine.parse_response(&http_response) {
|
let response = match engine.parse_response(&http_response) {
|
||||||
Ok(response) => response,
|
Ok(response) => response,
|
||||||
@ -339,7 +346,7 @@ pub async fn search(
|
|||||||
join_all(response_futures).await.into_iter().collect();
|
join_all(response_futures).await.into_iter().collect();
|
||||||
let responses = responses_result?;
|
let responses = responses_result?;
|
||||||
|
|
||||||
let response = merge_engine_responses(&query.config, responses);
|
let response = merge_engine_responses(query.config.clone(), responses);
|
||||||
|
|
||||||
let has_infobox = response.infobox.is_some();
|
let has_infobox = response.infobox.is_some();
|
||||||
|
|
||||||
@ -368,7 +375,11 @@ pub async fn search(
|
|||||||
}
|
}
|
||||||
let body = String::from_utf8_lossy(&body_bytes).to_string();
|
let body = String::from_utf8_lossy(&body_bytes).to_string();
|
||||||
|
|
||||||
let http_response = HttpResponse { res, body };
|
let http_response = HttpResponse {
|
||||||
|
res,
|
||||||
|
body,
|
||||||
|
config: query.config.clone(),
|
||||||
|
};
|
||||||
engine.postsearch_parse_response(&http_response)
|
engine.postsearch_parse_response(&http_response)
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -464,6 +475,7 @@ pub struct Response {
|
|||||||
pub featured_snippet: Option<FeaturedSnippet>,
|
pub featured_snippet: Option<FeaturedSnippet>,
|
||||||
pub answer: Option<Answer>,
|
pub answer: Option<Answer>,
|
||||||
pub infobox: Option<Infobox>,
|
pub infobox: Option<Infobox>,
|
||||||
|
pub config: Arc<Config>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -495,7 +507,10 @@ pub struct Infobox {
|
|||||||
pub engine: Engine,
|
pub engine: Engine,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn merge_engine_responses(config: &Config, responses: HashMap<Engine, EngineResponse>) -> Response {
|
fn merge_engine_responses(
|
||||||
|
config: Arc<Config>,
|
||||||
|
responses: HashMap<Engine, EngineResponse>,
|
||||||
|
) -> Response {
|
||||||
let mut search_results: Vec<SearchResult> = Vec::new();
|
let mut search_results: Vec<SearchResult> = Vec::new();
|
||||||
let mut featured_snippet: Option<FeaturedSnippet> = None;
|
let mut featured_snippet: Option<FeaturedSnippet> = None;
|
||||||
let mut answer: Option<Answer> = None;
|
let mut answer: Option<Answer> = None;
|
||||||
@ -596,6 +611,7 @@ fn merge_engine_responses(config: &Config, responses: HashMap<Engine, EngineResp
|
|||||||
featured_snippet,
|
featured_snippet,
|
||||||
answer,
|
answer,
|
||||||
infobox,
|
infobox,
|
||||||
|
config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,4 +5,5 @@
|
|||||||
pub mod docs_rs;
|
pub mod docs_rs;
|
||||||
pub mod github;
|
pub mod github;
|
||||||
pub mod mdn;
|
pub mod mdn;
|
||||||
|
pub mod minecraft_wiki;
|
||||||
pub mod stackexchange;
|
pub mod stackexchange;
|
||||||
|
@ -12,7 +12,7 @@ pub fn request(response: &Response) -> Option<reqwest::RequestBuilder> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_response(HttpResponse { res, body }: &HttpResponse) -> Option<String> {
|
pub fn parse_response(HttpResponse { res, body, .. }: &HttpResponse) -> Option<String> {
|
||||||
let url = res.url().clone();
|
let url = res.url().clone();
|
||||||
|
|
||||||
let dom = Html::parse_document(body);
|
let dom = Html::parse_document(body);
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
use scraper::{Html, Selector};
|
use scraper::{Html, Selector};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use tracing::error;
|
||||||
|
|
||||||
use crate::engines::{HttpResponse, Response, CLIENT};
|
use crate::engines::{Engine, HttpResponse, Response, CLIENT};
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct MdnConfig {
|
||||||
|
pub max_sections: usize,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn request(response: &Response) -> Option<reqwest::RequestBuilder> {
|
pub fn request(response: &Response) -> Option<reqwest::RequestBuilder> {
|
||||||
for search_result in response.search_results.iter().take(8) {
|
for search_result in response.search_results.iter().take(8) {
|
||||||
@ -15,7 +22,16 @@ pub fn request(response: &Response) -> Option<reqwest::RequestBuilder> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_response(HttpResponse { res, body }: &HttpResponse) -> Option<String> {
|
pub fn parse_response(HttpResponse { res, body, config }: &HttpResponse) -> Option<String> {
|
||||||
|
let config_toml = config.engines.get(Engine::Mdn).extra.clone();
|
||||||
|
let config: MdnConfig = match toml::Value::Table(config_toml).try_into() {
|
||||||
|
Ok(args) => args,
|
||||||
|
Err(err) => {
|
||||||
|
error!("Failed to parse Mdn config: {err}");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let url = res.url().clone();
|
let url = res.url().clone();
|
||||||
|
|
||||||
let dom = Html::parse_document(body);
|
let dom = Html::parse_document(body);
|
||||||
@ -30,11 +46,18 @@ pub fn parse_response(HttpResponse { res, body }: &HttpResponse) -> Option<Strin
|
|||||||
|
|
||||||
let doc_query = Selector::parse(".section-content").unwrap();
|
let doc_query = Selector::parse(".section-content").unwrap();
|
||||||
|
|
||||||
|
let max_sections = if config.max_sections == 0 {
|
||||||
|
usize::MAX
|
||||||
|
} else {
|
||||||
|
config.max_sections
|
||||||
|
};
|
||||||
|
|
||||||
let doc_html = dom
|
let doc_html = dom
|
||||||
.select(&doc_query)
|
.select(&doc_query)
|
||||||
.next()
|
|
||||||
.map(|doc| doc.inner_html())
|
.map(|doc| doc.inner_html())
|
||||||
.unwrap_or_default();
|
.take(max_sections)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("<br />");
|
||||||
|
|
||||||
let doc_html = ammonia::Builder::default()
|
let doc_html = ammonia::Builder::default()
|
||||||
.link_rel(None)
|
.link_rel(None)
|
||||||
|
88
src/engines/postsearch/minecraft_wiki.rs
Normal file
88
src/engines/postsearch/minecraft_wiki.rs
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
use scraper::{ElementRef, Html, Selector};
|
||||||
|
|
||||||
|
use crate::engines::{HttpResponse, Response, CLIENT};
|
||||||
|
|
||||||
|
pub fn request(response: &Response) -> Option<reqwest::RequestBuilder> {
|
||||||
|
for search_result in response.search_results.iter().take(8) {
|
||||||
|
if search_result.url.starts_with("https://minecraft.wiki/w/") {
|
||||||
|
return Some(CLIENT.get(search_result.url.as_str()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_response(HttpResponse { res, body, .. }: &HttpResponse) -> Option<String> {
|
||||||
|
let url = res.url().clone();
|
||||||
|
|
||||||
|
let dom = Html::parse_document(body);
|
||||||
|
|
||||||
|
let page_title = dom
|
||||||
|
.select(&Selector::parse("#firstHeading").unwrap())
|
||||||
|
.next()?
|
||||||
|
.text()
|
||||||
|
.collect::<String>()
|
||||||
|
.trim()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let doc_query = Selector::parse(".mw-parser-output").unwrap();
|
||||||
|
|
||||||
|
let doc_html = dom
|
||||||
|
.select(&doc_query)
|
||||||
|
.next()
|
||||||
|
.map(|doc| strip_gallery(doc))
|
||||||
|
.unwrap_or_default()
|
||||||
|
.join("");
|
||||||
|
|
||||||
|
let doc_html = ammonia::Builder::default()
|
||||||
|
.link_rel(None)
|
||||||
|
.add_allowed_classes("div", ["notaninfobox", "mcw-mainpage-icon"])
|
||||||
|
.add_allowed_classes("pre", ["noexcerpt", "navigation-not-searchable"])
|
||||||
|
.url_relative(ammonia::UrlRelative::RewriteWithBase(url.clone()))
|
||||||
|
.clean(&doc_html)
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let title_html = format!(
|
||||||
|
r#"<h2><a href="{url}">{title}</a></h2>"#,
|
||||||
|
url = html_escape::encode_quoted_attribute(&url.to_string()),
|
||||||
|
title = html_escape::encode_safe(&page_title),
|
||||||
|
);
|
||||||
|
|
||||||
|
Some(format!(
|
||||||
|
r#"{title_html}<div class="infobox-minecraft_wiki-article">{doc_html}</div>"#
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn strip_gallery(doc: ElementRef) -> Vec<String> {
|
||||||
|
let mut gallery = false;
|
||||||
|
doc.children()
|
||||||
|
.filter(|elem| {
|
||||||
|
let value = elem.value();
|
||||||
|
if gallery {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
match value {
|
||||||
|
scraper::Node::Element(_) => {
|
||||||
|
let elem = ElementRef::wrap(*elem).unwrap();
|
||||||
|
let is_gallery_title = elem.first_child().map_or(false, |elem| {
|
||||||
|
elem.value().as_element().map_or(false, |_| {
|
||||||
|
let elem = ElementRef::wrap(elem).unwrap();
|
||||||
|
elem.text().collect::<String>() == "Gallery"
|
||||||
|
})
|
||||||
|
});
|
||||||
|
if is_gallery_title {
|
||||||
|
gallery = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => true,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(|elem| {
|
||||||
|
ElementRef::wrap(elem)
|
||||||
|
.map(|elem| elem.html())
|
||||||
|
.unwrap_or_default()
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
@ -113,7 +113,8 @@ document.addEventListener("keydown", (e) => {
|
|||||||
const focusedEl = document.querySelector(":focus");
|
const focusedEl = document.querySelector(":focus");
|
||||||
if (
|
if (
|
||||||
focusedEl &&
|
focusedEl &&
|
||||||
(focusedEl.tagName == "input" ||
|
(focusedEl.tagName.toLowerCase() == "input" ||
|
||||||
|
focusedEl.tagName.toLowerCase() == "textarea" ||
|
||||||
focusedEl.getAttribute("contenteditable") !== null)
|
focusedEl.getAttribute("contenteditable") !== null)
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
|
@ -292,6 +292,18 @@ h3.answer-thesaurus-category-title {
|
|||||||
.answer-thesaurus-list a {
|
.answer-thesaurus-list a {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
.answer-notepad {
|
||||||
|
width: calc( 100% - 4px );
|
||||||
|
height: fit-content;
|
||||||
|
overflow-y: show;
|
||||||
|
background-color: transparent;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
min-height: 4em;
|
||||||
|
font-size: 12px;
|
||||||
|
resize: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* infobox */
|
/* infobox */
|
||||||
.infobox {
|
.infobox {
|
||||||
@ -353,3 +365,12 @@ h3.answer-thesaurus-category-title {
|
|||||||
.postsearch-infobox p {
|
.postsearch-infobox p {
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
|
.infobox-minecraft_wiki-article > .notaninfobox {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
.noexcerpt, .navigation-not-searchable {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
.mcw-mainpage-icon {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
@ -45,18 +45,29 @@ fn render_end_of_html() -> String {
|
|||||||
r"</main></div></body></html>".to_string()
|
r"</main></div></body></html>".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_engine_list(engines: &[engines::Engine]) -> String {
|
fn render_engine_list(engines: &[engines::Engine], config: &Config) -> String {
|
||||||
let mut html = String::new();
|
let mut html = String::new();
|
||||||
|
let mut first_iter = true;
|
||||||
for engine in engines {
|
for engine in engines {
|
||||||
|
if config.engine_list_separator.unwrap() && !first_iter {
|
||||||
|
html.push_str(" · ");
|
||||||
|
}
|
||||||
|
first_iter = false;
|
||||||
|
let raw_engine_id = &engine.id();
|
||||||
|
let engine_id = if config.engine_list_separator.unwrap() {
|
||||||
|
raw_engine_id.replace('_', " ")
|
||||||
|
} else {
|
||||||
|
raw_engine_id.to_string()
|
||||||
|
};
|
||||||
html.push_str(&format!(
|
html.push_str(&format!(
|
||||||
r#"<span class="engine-list-item">{engine}</span>"#,
|
r#"<span class="engine-list-item">{engine}</span>"#,
|
||||||
engine = encode_text(&engine.id())
|
engine = encode_text(&engine_id)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
format!(r#"<div class="engine-list">{html}</div>"#)
|
format!(r#"<div class="engine-list">{html}</div>"#)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_search_result(result: &engines::SearchResult) -> String {
|
fn render_search_result(result: &engines::SearchResult, config: &Config) -> String {
|
||||||
format!(
|
format!(
|
||||||
r#"<div class="search-result">
|
r#"<div class="search-result">
|
||||||
<a class="search-result-anchor" rel="noreferrer" href="{url_attr}">
|
<a class="search-result-anchor" rel="noreferrer" href="{url_attr}">
|
||||||
@ -71,11 +82,12 @@ fn render_search_result(result: &engines::SearchResult) -> String {
|
|||||||
url = encode_text(&result.url),
|
url = encode_text(&result.url),
|
||||||
title = encode_text(&result.title),
|
title = encode_text(&result.title),
|
||||||
desc = encode_text(&result.description),
|
desc = encode_text(&result.description),
|
||||||
engines_html = render_engine_list(&result.engines.iter().copied().collect::<Vec<_>>())
|
engines_html =
|
||||||
|
render_engine_list(&result.engines.iter().copied().collect::<Vec<_>>(), config)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_featured_snippet(featured_snippet: &engines::FeaturedSnippet) -> String {
|
fn render_featured_snippet(featured_snippet: &engines::FeaturedSnippet, config: &Config) -> String {
|
||||||
format!(
|
format!(
|
||||||
r#"<div class="featured-snippet">
|
r#"<div class="featured-snippet">
|
||||||
<p class="search-result-description">{desc}</p>
|
<p class="search-result-description">{desc}</p>
|
||||||
@ -90,7 +102,7 @@ fn render_featured_snippet(featured_snippet: &engines::FeaturedSnippet) -> Strin
|
|||||||
url_attr = encode_unquoted_attribute(&featured_snippet.url),
|
url_attr = encode_unquoted_attribute(&featured_snippet.url),
|
||||||
url = encode_text(&featured_snippet.url),
|
url = encode_text(&featured_snippet.url),
|
||||||
title = encode_text(&featured_snippet.title),
|
title = encode_text(&featured_snippet.title),
|
||||||
engines_html = render_engine_list(&[featured_snippet.engine])
|
engines_html = render_engine_list(&[featured_snippet.engine], config)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,21 +112,21 @@ fn render_results(response: Response) -> String {
|
|||||||
html.push_str(&format!(
|
html.push_str(&format!(
|
||||||
r#"<div class="infobox">{infobox_html}{engines_html}</div>"#,
|
r#"<div class="infobox">{infobox_html}{engines_html}</div>"#,
|
||||||
infobox_html = &infobox.html,
|
infobox_html = &infobox.html,
|
||||||
engines_html = render_engine_list(&[infobox.engine])
|
engines_html = render_engine_list(&[infobox.engine], &response.config)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if let Some(answer) = &response.answer {
|
if let Some(answer) = &response.answer {
|
||||||
html.push_str(&format!(
|
html.push_str(&format!(
|
||||||
r#"<div class="answer">{answer_html}{engines_html}</div>"#,
|
r#"<div class="answer">{answer_html}{engines_html}</div>"#,
|
||||||
answer_html = &answer.html,
|
answer_html = &answer.html,
|
||||||
engines_html = render_engine_list(&[answer.engine])
|
engines_html = render_engine_list(&[answer.engine], &response.config)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if let Some(featured_snippet) = &response.featured_snippet {
|
if let Some(featured_snippet) = &response.featured_snippet {
|
||||||
html.push_str(&render_featured_snippet(featured_snippet));
|
html.push_str(&render_featured_snippet(featured_snippet, &response.config));
|
||||||
}
|
}
|
||||||
for result in &response.search_results {
|
for result in &response.search_results {
|
||||||
html.push_str(&render_search_result(result));
|
html.push_str(&render_search_result(result, &response.config));
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.infobox.is_none()
|
if response.infobox.is_none()
|
||||||
@ -187,7 +199,7 @@ pub async fn route(
|
|||||||
|| addr.ip().to_string(),
|
|| addr.ip().to_string(),
|
||||||
|ip| ip.to_str().unwrap_or_default().to_string(),
|
|ip| ip.to_str().unwrap_or_default().to_string(),
|
||||||
),
|
),
|
||||||
config,
|
config: config.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let s = stream! {
|
let s = stream! {
|
||||||
@ -230,7 +242,7 @@ pub async fn route(
|
|||||||
third_part.push_str(&format!(
|
third_part.push_str(&format!(
|
||||||
r#"<div class="infobox postsearch-infobox">{infobox_html}{engines_html}</div>"#,
|
r#"<div class="infobox postsearch-infobox">{infobox_html}{engines_html}</div>"#,
|
||||||
infobox_html = &infobox.html,
|
infobox_html = &infobox.html,
|
||||||
engines_html = render_engine_list(&[infobox.engine])
|
engines_html = render_engine_list(&[infobox.engine], &config)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user