just make the bangs better

This commit is contained in:
Book-reader 2025-05-08 18:40:38 +12:00
parent 245744a317
commit bac21898c9
2 changed files with 81 additions and 93 deletions

View file

@ -1,113 +1,98 @@
use tracing::{debug, error}; use tracing::{debug, error};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use url_encoded_data;
pub static BANG_PREFIX: &str = "!"; pub static BANG_PREFIX: &str = "!";
pub static BUILTIN_BANGS: Lazy<BTreeMap<&str, (&str, &[&str])>> = 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<BTreeMap<&str, Bang>> = Lazy::new(|| {
let mut bangs = BTreeMap::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 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)] #[derive(Debug, Clone)]
struct BangLoc<'a> { struct BangLoc<'b> {
pub url: &'a str, pub url: &'b str,
pub start_idx: usize, 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 { impl<'b> BangLoc<'_> {
BangLoc {url, start_idx, end_idx} 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<String> { #[derive(Debug)]
// TODO: make this a little more procedural and not as confusing pub struct BangRedirect {
error!("Hello, World!, you searched \"{}\"", query); pub url: String
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() { pub fn do_bangs_or_whatever(query: &String) -> Option<BangRedirect> {
true => match query.get(bang_start_idx + 1 .. query.len()) { error!("Hello, World!");
Some(rest) => if !query.contains(BANG_PREFIX) {
{ return None;
debug!("rest is {}", rest); }
match BUILTIN_BANGS.iter().map( let bangs = query.match_indices(BANG_PREFIX).filter(|(bang_start_idx, _)| {
|bang_full| if *bang_start_idx == 0 || query.chars().nth(*bang_start_idx - 1).unwrap().is_whitespace() {
match bang_full.1.1.iter() true
.filter(|short| rest.starts_with(**short)) } else {
.max_by(|a, b| a.len().cmp(&b.len())) { false
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::<Vec<_>>().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::<Vec<_>>().first() { }).map(|(bang_start_idx, _)| {
Some(bang) => { let rest = query.get(bang_start_idx + 1..query.len()).unwrap();
debug!("Initial query: \"{}\"", query); BUILTIN_BANGS.iter().map(|(_, bang)| {
let query_split = query.split_once(query.get(bang.start_idx..bang.end_idx).unwrap()).unwrap(); let key = bang.keys.iter()
debug!("Split query: {:?}", query_split); .filter(|key| rest.starts_with(**key))
let query_trimmed = format!("{}{}", query_split.0, query_split.1); .filter(
debug!("Trimmed query: \"{}\"", query_trimmed); |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::<Vec<_>>();
debug!("Result: {:?}", bangs);
let bang_url_split = bang.url.split_once("{}").unwrap(); let bang = bangs.first()?;
debug!("Split bang URL: {:?}", bang_url_split); let end_idx = bang.start_idx + 1 + bang.len;
let bang_url = format!( debug!("Initial query: \"{}\"", query);
"{}{}{}", let query_split = query.split_once(query.get(bang.start_idx..end_idx).unwrap()).unwrap();
bang_url_split.0, debug!("Split query: {:?}", query_split);
query_trimmed, let query_trimmed = format!("{}{}", query_split.0, query_split.1);
bang_url_split.1 debug!("Trimmed query: \"{}\"", query_trimmed);
); let query_encoded = url_encoded_data::stringify(&[("", query_trimmed.as_str())]);
debug!("Final URL: \"{}\"", bang_url); let query_encoded = query_encoded.get(1..query_encoded.len()).unwrap().to_owned();
Some(bang_url) debug!("Encoded query: {:?}", query_encoded);
},
None => None 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})
} }

View file

@ -19,6 +19,7 @@ use crate::unit_converter::UnitConversion;
use crate::wikipedia::WikipediaSummary; use crate::wikipedia::WikipediaSummary;
use crate::{wikipedia, Opts, ALPHA, BUILT_ON, GIT_COMMIT, VERSION, YEAR}; use crate::{wikipedia, Opts, ALPHA, BUILT_ON, GIT_COMMIT, VERSION, YEAR};
use crate::bangs; use crate::bangs;
use crate::bangs::BangRedirect;
use askama::Template; use askama::Template;
use asklyphe_common::nats; use asklyphe_common::nats;
use asklyphe_common::nats::bingservice::{ use asklyphe_common::nats::bingservice::{
@ -69,6 +70,7 @@ pub struct Complications {
disabled: bool, disabled: bool,
wikipedia: Option<WikipediaSummary>, wikipedia: Option<WikipediaSummary>,
unit_converter: Option<UnitConversion>, unit_converter: Option<UnitConversion>,
bang: Option<BangRedirect>
} }
pub async fn search( 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) { /*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)); 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 { } else {
complications.disabled = true; complications.disabled = true;
query = query.replace("-complications", ""); query = query.replace("-complications", "");