use tracing::{debug, error}; use once_cell::sync::Lazy; use std::collections::BTreeMap; pub static BANG_PREFIX: &str = "!"; pub 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("DuckDuckGo", ("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 }); 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, pub start_idx: usize, pub end_idx: usize, } impl<'a> BangLoc<'_> { fn init(url: &'a str, start_idx: usize, end_idx: usize) -> BangLoc { BangLoc {url, start_idx, end_idx} } } 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 } }).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); 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 } }