From bac21898c9636695ad27b6ce786d454814239591 Mon Sep 17 00:00:00 2001 From: Book-reader Date: Thu, 8 May 2025 18:40:38 +1200 Subject: [PATCH] just make the bangs better --- asklyphe-frontend/src/bangs.rs | 169 +++++++++++-------------- asklyphe-frontend/src/routes/search.rs | 5 +- 2 files changed, 81 insertions(+), 93 deletions(-) diff --git a/asklyphe-frontend/src/bangs.rs b/asklyphe-frontend/src/bangs.rs index 8307961..83eb1e8 100644 --- a/asklyphe-frontend/src/bangs.rs +++ b/asklyphe-frontend/src/bangs.rs @@ -1,113 +1,98 @@ use tracing::{debug, error}; use once_cell::sync::Lazy; use std::collections::BTreeMap; +use url_encoded_data; pub static BANG_PREFIX: &str = "!"; -pub static BUILTIN_BANGS: Lazy> = Lazy::new(|| { +#[derive(Debug)] +struct Bang<'a> { + pub url: &'a str, + pub keys: &'a [&'a str] +} + +impl<'a> Bang<'_> { + fn new(url: &'a str, keys: &'a [&'a str]) -> Bang<'a> { + Bang {url, keys} + } +} + +static BUILTIN_BANGS: Lazy> = Lazy::new(|| { let mut bangs = BTreeMap::new(); - bangs.insert("Google", ("https://google.com/search?q={}", &["g", "google"] as &[&str])); + bangs.insert("Google", Bang::new("https://google.com/search?q={}", &["g", "google"] as &[&str])); - bangs.insert("DuckDuckGo", ("https://duckduckgo.com/?q={}", &["d", "ddg", "duckduckgo"] as &[&str])); + bangs.insert("DuckDuckGo", Bang::new("https://duckduckgo.com/?q={}", &["d", "ddg", "duckduckgo"] as &[&str])); - bangs.insert("Wikipedia", ("https://wikipedia.org/w/index.php?search={}", &["w", "wiki", "wikipedia"] as &[&str])); + bangs.insert("Wikipedia", Bang::new("https://wikipedia.org/w/index.php?search={}", &["w", "wiki", "wikipedia"] as &[&str])); bangs }); -enum Bangs { - Google, - DuckDuckGo, - Wikipedia, -} - -impl Bangs { - pub fn get_suffix(self) -> &'static [&'static str] { - match self { - Bangs::Google => &["g", "google"], - Bangs::DuckDuckGo => &["d", "ddg", "duckduckgo"], - Bangs::Wikipedia => &["w", "wiki", "wikipedia"], - } - } - - pub fn get_url_format(self) -> &'static str { - match self { - Bangs::Google => "https://google.com/search?q={}", - Bangs::DuckDuckGo => "https://duckduckgo.com/?q={}", - Bangs::Wikipedia => "https://wikipedia.org/w/index.php?search={}", - } - } - -} - #[derive(Debug, Clone)] -struct BangLoc<'a> { - pub url: &'a str, +struct BangLoc<'b> { + pub url: &'b str, pub start_idx: usize, - pub end_idx: usize, + pub len: usize } -impl<'a> BangLoc<'_> { - fn init(url: &'a str, start_idx: usize, end_idx: usize) -> BangLoc { - BangLoc {url, start_idx, end_idx} + +impl<'b> BangLoc<'_> { + fn new(url: &'b str, start_idx: usize, len: usize) -> BangLoc<'b> { + BangLoc {url, start_idx, len} } } -pub fn do_bangs_or_whatever(query: &String) -> Option { - // TODO: make this a little more procedural and not as confusing - error!("Hello, World!, you searched \"{}\"", query); - return match query.match_indices(BANG_PREFIX).map(|idx| { - let bang_start_idx = idx.0; - match bang_start_idx == 0 || query.chars().nth(bang_start_idx - 1).unwrap().is_ascii_whitespace() { - true => match query.get(bang_start_idx + 1 .. query.len()) { - Some(rest) => - { - debug!("rest is {}", rest); - match BUILTIN_BANGS.iter().map( - |bang_full| - match bang_full.1.1.iter() - .filter(|short| rest.starts_with(**short)) - .max_by(|a, b| a.len().cmp(&b.len())) { - Some(bang_text) => { - let bang_end_idx = bang_start_idx + 1 + bang_text.len(); - match query.chars().nth(bang_end_idx).unwrap_or(' ').is_ascii_whitespace() { - true => Some(BangLoc::init(bang_full.1.0, bang_start_idx, bang_end_idx + 1)), - false => None - } - } - None => None, - } - ).filter(|i| i.is_some()).map(|bang| bang.unwrap()) - .collect::>().last() - { - Some(bang) => { - debug!("got:'{:?}'", bang); - Some(bang.clone()) - }, - None => None - } - }, - None => None - } - false => None +#[derive(Debug)] +pub struct BangRedirect { + pub url: String +} + +pub fn do_bangs_or_whatever(query: &String) -> Option { + error!("Hello, World!"); + if !query.contains(BANG_PREFIX) { + return None; + } + let bangs = query.match_indices(BANG_PREFIX).filter(|(bang_start_idx, _)| { + if *bang_start_idx == 0 || query.chars().nth(*bang_start_idx - 1).unwrap().is_whitespace() { + true + } else { + false } - }).filter(|bang| bang.is_some()).map(|bang| bang.unwrap()).collect::>().first() { - Some(bang) => { - debug!("Initial query: \"{}\"", query); - let query_split = query.split_once(query.get(bang.start_idx..bang.end_idx).unwrap()).unwrap(); - debug!("Split query: {:?}", query_split); - let query_trimmed = format!("{}{}", query_split.0, query_split.1); - debug!("Trimmed query: \"{}\"", query_trimmed); + }).map(|(bang_start_idx, _)| { + let rest = query.get(bang_start_idx + 1..query.len()).unwrap(); + BUILTIN_BANGS.iter().map(|(_, bang)| { + let key = bang.keys.iter() + .filter(|key| rest.starts_with(**key)) + .filter( + |key| rest.chars() + .nth(key.len()) + .unwrap_or(' ') + .is_whitespace()) + .max_by(|a, b| a.len().cmp(&b.len()))?; + Some(BangLoc::new(bang.url, bang_start_idx, key.len())) + }).filter(|bang| bang.is_some()).map(|bang| bang.unwrap()).next() + }).filter(|bang| bang.is_some()) + .map(|bang| bang.unwrap()) + .collect::>(); + debug!("Result: {:?}", bangs); - let bang_url_split = bang.url.split_once("{}").unwrap(); - debug!("Split bang URL: {:?}", bang_url_split); - let bang_url = format!( - "{}{}{}", - bang_url_split.0, - query_trimmed, - bang_url_split.1 - ); - debug!("Final URL: \"{}\"", bang_url); - Some(bang_url) - }, - None => None - } + let bang = bangs.first()?; + let end_idx = bang.start_idx + 1 + bang.len; + debug!("Initial query: \"{}\"", query); + let query_split = query.split_once(query.get(bang.start_idx..end_idx).unwrap()).unwrap(); + debug!("Split query: {:?}", query_split); + let query_trimmed = format!("{}{}", query_split.0, query_split.1); + debug!("Trimmed query: \"{}\"", query_trimmed); + let query_encoded = url_encoded_data::stringify(&[("", query_trimmed.as_str())]); + let query_encoded = query_encoded.get(1..query_encoded.len()).unwrap().to_owned(); + debug!("Encoded query: {:?}", query_encoded); + + let bang_url_split = bang.url.split_once("{}").unwrap(); + debug!("Split bang URL: {:?}", bang_url_split); + let bang_url = format!( + "{}{}{}", + bang_url_split.0, + query_encoded, + bang_url_split.1 + ); + debug!("Final URL: \"{}\"", bang_url); + Some(BangRedirect{url: bang_url}) } diff --git a/asklyphe-frontend/src/routes/search.rs b/asklyphe-frontend/src/routes/search.rs index 78172c5..af33081 100644 --- a/asklyphe-frontend/src/routes/search.rs +++ b/asklyphe-frontend/src/routes/search.rs @@ -19,6 +19,7 @@ use crate::unit_converter::UnitConversion; use crate::wikipedia::WikipediaSummary; use crate::{wikipedia, Opts, ALPHA, BUILT_ON, GIT_COMMIT, VERSION, YEAR}; use crate::bangs; +use crate::bangs::BangRedirect; use askama::Template; use asklyphe_common::nats; use asklyphe_common::nats::bingservice::{ @@ -69,6 +70,7 @@ pub struct Complications { disabled: bool, wikipedia: Option, unit_converter: Option, + bang: Option } pub async fn search( @@ -174,7 +176,8 @@ pub async fn search_js( /*if let Some(bang, result_query) = bangs::do_bangs_or_whatever(&query) { error!("Would redirect to '{}', but it's not implemented", format!(bang, result_query)); }*/ - let _ = bangs::do_bangs_or_whatever(&query); + let bang_redirect = bangs::do_bangs_or_whatever(&query); + complications.bang = bang_redirect; } else { complications.disabled = true; query = query.replace("-complications", "");