diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..222861c --- /dev/null +++ b/.prettierrc @@ -0,0 +1,4 @@ +{ + "tabWidth": 2, + "useTabs": false +} diff --git a/Cargo.lock b/Cargo.lock index cd4ec9b..b14c474 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -505,12 +505,6 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" -[[package]] -name = "hermit-abi" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" - [[package]] name = "html-escape" version = "0.2.13" @@ -661,9 +655,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ca339002caeb0d159cc6e023dff48e199f081e42fa039895c7c6f38b37f2e9d" +checksum = "bdea9aac0dbe5a9240d68cfd9501e2db94222c6dc06843e06640b9e07f0fdc67" dependencies = [ "bytes", "futures-channel", @@ -674,8 +668,6 @@ dependencies = [ "pin-project-lite", "socket2", "tokio", - "tower", - "tower-service", "tracing", ] @@ -726,12 +718,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "libc" version = "0.2.151" @@ -780,15 +766,6 @@ dependencies = [ "tendril", ] -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata 0.1.10", -] - [[package]] name = "matchit" version = "0.7.3" @@ -822,7 +799,6 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tracing-subscriber", "url", "urlencoding", ] @@ -859,26 +835,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "object" version = "0.32.1" @@ -894,12 +850,6 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - [[package]] name = "parking_lot" version = "0.12.1" @@ -1118,17 +1068,8 @@ checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.3", - "regex-syntax 0.8.2", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", + "regex-automata", + "regex-syntax", ] [[package]] @@ -1139,15 +1080,9 @@ checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.2", + "regex-syntax", ] -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - [[package]] name = "regex-syntax" version = "0.8.2" @@ -1370,24 +1305,6 @@ dependencies = [ "stable_deref_trait", ] -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" -dependencies = [ - "libc", -] - [[package]] name = "siphasher" version = "0.3.11" @@ -1517,16 +1434,6 @@ dependencies = [ "utf-8", ] -[[package]] -name = "thread_local" -version = "1.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" -dependencies = [ - "cfg-if", - "once_cell", -] - [[package]] name = "tinyvec" version = "1.6.0" @@ -1552,10 +1459,7 @@ dependencies = [ "bytes", "libc", "mio", - "num_cpus", - "parking_lot", "pin-project-lite", - "signal-hook-registry", "socket2", "tokio-macros", "windows-sys", @@ -1620,7 +1524,6 @@ dependencies = [ "tokio", "tower-layer", "tower-service", - "tracing", ] [[package]] @@ -1641,7 +1544,6 @@ version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "log", "pin-project-lite", "tracing-core", ] @@ -1653,36 +1555,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", ] [[package]] @@ -1753,12 +1625,6 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - [[package]] name = "version_check" version = "0.9.4" @@ -1862,28 +1728,6 @@ version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "windows-sys" version = "0.48.0" diff --git a/Cargo.toml b/Cargo.toml index eefb136..0fc96ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,13 @@ edition = "2021" [dependencies] ammonia = "3.3.0" async-stream = "0.3.5" -axum = { version = "0.7.2", features = ["http2"] } +axum = { version = "0.7.2", default-features = false, features = [ + "tokio", + "http1", + "http2", + "query", + "json", +] } base64 = "0.21.5" bytes = "1.5.0" eyre = "0.6.11" @@ -23,8 +29,7 @@ reqwest = { version = "0.11.23", default-features = false, features = [ scraper = "0.18.1" serde = { version = "1.0.193", features = ["derive"] } serde_json = "1.0.108" -tokio = { version = "1.35.0", features = ["full"] } +tokio = { version = "1.35.0", features = ["rt", "macros"] } tokio-stream = "0.1.14" -tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } url = "2.5.0" urlencoding = "2.1.3" diff --git a/src/engines/answer/wikipedia.rs b/src/engines/answer/wikipedia.rs index c36742c..cf782d1 100644 --- a/src/engines/answer/wikipedia.rs +++ b/src/engines/answer/wikipedia.rs @@ -80,8 +80,7 @@ pub fn parse_response(body: &str) -> eyre::Result { return Ok(EngineResponse::new()); } - // this is present on the wikipedia article for google - let extract = extract.replace("( )", ""); + let extract = extract.replace("( )", "").replace("()", ""); let page_title = title.replace(' ', "_"); let page_url = format!("https://en.wikipedia.org/wiki/{page_title}"); diff --git a/src/engines/mod.rs b/src/engines/mod.rs index a9d7a62..bdf5a1f 100644 --- a/src/engines/mod.rs +++ b/src/engines/mod.rs @@ -10,6 +10,7 @@ use std::{ use futures::future::join_all; use tokio::sync::mpsc; +use url::Url; pub mod answer; pub mod postsearch; @@ -119,11 +120,11 @@ impl Engine { } } - pub fn postsearch_parse_response(&self, body: &str) -> Option { + pub fn postsearch_parse_response(&self, body: &str, url: Url) -> Option { match self { - Engine::StackExchange => postsearch::stackexchange::parse_response(body), - Engine::GitHub => postsearch::github::parse_response(body), - Engine::DocsRs => postsearch::docs_rs::parse_response(body), + Engine::StackExchange => postsearch::stackexchange::parse_response(body, url), + Engine::GitHub => postsearch::github::parse_response(body, url), + Engine::DocsRs => postsearch::docs_rs::parse_response(body, url), _ => None, } } @@ -346,8 +347,9 @@ pub async fn search_with_engines( postsearch_requests.push(async { let response = match request.send().await { Ok(res) => { + let url = res.url().clone(); let body = res.text().await?; - engine.postsearch_parse_response(&body) + engine.postsearch_parse_response(&body, url) } Err(e) => { eprintln!("postsearch request error: {}", e); diff --git a/src/engines/postsearch/docs_rs.rs b/src/engines/postsearch/docs_rs.rs index b6c4924..41171e6 100644 --- a/src/engines/postsearch/docs_rs.rs +++ b/src/engines/postsearch/docs_rs.rs @@ -16,46 +16,58 @@ pub fn request(response: &Response) -> Option { None } -pub fn parse_response(body: &str) -> Option { +pub fn parse_response(body: &str, url: Url) -> Option { let dom = Html::parse_document(body); - let title = dom - .select(&Selector::parse("h2 a").unwrap()) - .next()? - .text() - .collect::(); let version = dom .select(&Selector::parse("h2 .version").unwrap()) .next()? .text() .collect::(); - let url = Url::join( - &Url::parse("https://docs.rs").unwrap(), - &dom.select( - &Selector::parse("ul.pure-menu-list li.pure-menu-item:nth-last-child(2) a").unwrap(), - ) + let page_title = dom + .select(&Selector::parse("h1").unwrap()) .next()? - .value() - .attr("href")? - .replace("/crate/", "/"), - ) - .ok()?; + .text() + .collect::() + .trim() + .to_string(); let doc_query = Selector::parse(".docblock").unwrap(); let doc = dom.select(&doc_query).next()?; + let doc_html = doc.inner_html(); + let item_decl = dom + .select(&Selector::parse(".item-decl").unwrap()) + .next() + .map(|el| el.html()) + .unwrap_or_default(); + let doc_html = ammonia::Builder::default() .link_rel(None) .url_relative(ammonia::UrlRelative::RewriteWithBase(url.clone())) - .clean(&doc_html) + .clean(&format!("{item_decl}{doc_html}")) .to_string(); + let (category, title) = page_title.split_once(' ').unwrap_or(("", &page_title)); + + let title_html = if category == "Crate" { + format!( + r#"

{category} {title} {version}

"#, + url = html_escape::encode_quoted_attribute(&url.to_string()), + title = html_escape::encode_text(&title), + version = html_escape::encode_text(&version), + ) + } else { + format!( + r#"

{category} {title}

"#, + url = html_escape::encode_quoted_attribute(&url.to_string()), + title = html_escape::encode_text(&title), + ) + }; + Some(format!( - r#"

Crate {title} {version}

-
{doc_html}
"#, - url = html_escape::encode_quoted_attribute(&url.to_string()), - title = html_escape::encode_text(&title), + r#"{title_html}
{doc_html}
"# )) } diff --git a/src/engines/postsearch/github.rs b/src/engines/postsearch/github.rs index 6db77ea..7f65261 100644 --- a/src/engines/postsearch/github.rs +++ b/src/engines/postsearch/github.rs @@ -16,7 +16,7 @@ pub fn request(response: &Response) -> Option { None } -pub fn parse_response(body: &str) -> Option { +pub fn parse_response(body: &str, _url: Url) -> Option { let dom = Html::parse_document(body); let url_relative = dom diff --git a/src/engines/postsearch/stackexchange.rs b/src/engines/postsearch/stackexchange.rs index f6342d6..89e891a 100644 --- a/src/engines/postsearch/stackexchange.rs +++ b/src/engines/postsearch/stackexchange.rs @@ -18,7 +18,7 @@ pub fn request(response: &Response) -> Option { None } -pub fn parse_response(body: &str) -> Option { +pub fn parse_response(body: &str, _url: Url) -> Option { let dom = Html::parse_document(body); let title = dom diff --git a/src/main.rs b/src/main.rs index 88336f7..01fc6c4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,10 +5,7 @@ pub mod normalize; pub mod parse; pub mod web; -#[tokio::main] +#[tokio::main(flavor = "current_thread")] async fn main() { - // initialize tracing - tracing_subscriber::fmt::init(); - web::run().await; } diff --git a/src/web/assets/script.js b/src/web/assets/script.js index 05b9116..18a02c8 100644 --- a/src/web/assets/script.js +++ b/src/web/assets/script.js @@ -42,7 +42,8 @@ function renderSuggestions(options) { optionEl.textContent = option; optionEl.className = "search-input-suggestion"; suggestionsEl.appendChild(optionEl); - optionEl.addEventListener("click", () => { + + optionEl.addEventListener("mousedown", () => { searchInputEl.value = option; searchInputEl.focus(); searchInputEl.form.submit(); @@ -136,6 +137,6 @@ searchInputEl.addEventListener("input", () => { // and on focus searchInputEl.addEventListener("focus", updateSuggestions); // on unfocus hide the suggestions -searchInputEl.addEventListener("blur", () => { +searchInputEl.addEventListener("blur", (e) => { suggestionsEl.style.visibility = "hidden"; }); diff --git a/src/web/assets/style.css b/src/web/assets/style.css index b934fcf..9aa0e78 100644 --- a/src/web/assets/style.css +++ b/src/web/assets/style.css @@ -91,10 +91,11 @@ h1 { padding: 0.1em 0 0.3em 0; border: 1px solid #234; border-top: transparent; + z-index: 10; } .search-input-suggestion { cursor: pointer; - padding: 0 0.3em; + padding: 0.3em 0.3em; } .search-input-suggestion.focused { background: #234;