"#,
encode_text(query),
encode_unquoted_attribute(query)
)
}
fn render_end_of_html() -> String {
r#""#.to_string()
}
fn render_search_result(result: &engines::SearchResult) -> String {
let engines_html = result
.engines
.iter()
.map(|engine| {
format!(
r#"{}"#,
encode_text(&engine.name())
)
})
.collect::>()
.join("");
format!(
r#"
"#,
url_attr = encode_unquoted_attribute(&result.url),
url = encode_text(&result.url),
title = encode_text(&result.title),
desc = encode_text(&result.description)
)
}
pub async fn route(Query(params): Query>) -> impl IntoResponse {
let query = params
.get("q")
.cloned()
.unwrap_or_default()
.trim()
.replace('\n', " ");
if query.is_empty() {
// redirect to index
return (
StatusCode::FOUND,
[
(header::LOCATION, "/"),
(header::CONTENT_TYPE, "text/html; charset=utf-8"),
],
Body::from("No query provided, click here to go back to index"),
);
}
let s = stream! {
type R = Result;
yield R::Ok(Bytes::from(render_beginning_of_html(&query)));
let (progress_tx, mut progress_rx) = tokio::sync::mpsc::unbounded_channel();
let search_future = tokio::spawn(async move { engines::search(&query, progress_tx).await });
while let Some(progress_update) = progress_rx.recv().await {
let progress_html = format!(
r#"
"); // close progress-updates
second_half.push_str("");
for result in results.search_results {
second_half.push_str(&render_search_result(&result));
}
second_half.push_str(&render_end_of_html());
yield Ok(Bytes::from(second_half));
};
let stream = Body::from_stream(s);
(
StatusCode::OK,
[
(header::CONTENT_TYPE, "text/html; charset=utf-8"),
(header::TRANSFER_ENCODING, "chunked"),
],
stream,
)
}
{}
"#, encode_text(&progress_update.to_string()) ); yield R::Ok(Bytes::from(progress_html)); } let results = match search_future.await? { Ok(results) => results, Err(e) => { let error_html = format!( r#"