use std::borrow::Cow;
use std::collections::HashMap;
use std::sync::LazyLock;
pub static EMPTY_INTERWIKI: LazyLock<InterwikiSettings> =
LazyLock::new(|| InterwikiSettings {
prefixes: hashmap! {},
});
#[allow(rustdoc::bare_urls)]
pub static DEFAULT_INTERWIKI: LazyLock<InterwikiSettings> =
LazyLock::new(|| InterwikiSettings {
prefixes: hashmap! {
cow!("wikipedia") => cow!("https://wikipedia.org/wiki/$$"),
cow!("wp") => cow!("https://wikipedia.org/wiki/$$"),
cow!("commons") => cow!("https://commons.wikimedia.org/wiki/$$"),
cow!("google") => cow!("https://google.com/search?q=$$"),
cow!("duckduckgo") => cow!("https://duckduckgo.com/?q=$$"),
cow!("ddg") => cow!("https://duckduckgo.com/?q=$$"),
cow!("dictionary") => cow!("https://dictionary.com/browse/$$"),
cow!("thesaurus") => cow!("https://thesaurus.com/browse/$$"),
},
});
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
pub struct InterwikiSettings {
#[serde(flatten)]
pub prefixes: HashMap<Cow<'static, str>, Cow<'static, str>>,
}
impl InterwikiSettings {
#[inline]
pub fn new() -> Self {
InterwikiSettings::default()
}
pub fn build(&self, link: &str) -> Option<String> {
match link.find(':') {
Some(0) | None => None,
Some(idx) => {
let (prefix, rest) = link.split_at(idx);
let path = &rest[1..];
if path.is_empty() {
return None;
}
self.prefixes.get(prefix).map(|template| {
let mut url = template.replace("$$", path);
while let Some(idx) = url.find(' ') {
url.replace_range(idx..idx + 1, "%20");
}
url
})
}
}
}
}
#[test]
fn interwiki_prefixes() {
use ref_map::*;
macro_rules! test {
($link:expr, $expected:expr $(,)?) => {{
let actual = DEFAULT_INTERWIKI.build($link);
let expected = $expected;
assert_eq!(
actual.ref_map(|s| s.as_str()),
expected,
"Actual interwiki result doesn't match expected",
);
}};
}
test!("my-link", None);
test!(
"wikipedia:Mallard",
Some("https://wikipedia.org/wiki/Mallard"),
);
test!(
"wikipedia:SCP_Foundation",
Some("https://wikipedia.org/wiki/SCP_Foundation"),
);
test!(
"wikipedia:Special:RecentChanges",
Some("https://wikipedia.org/wiki/Special:RecentChanges"),
);
test!(
"wp:SCP_Foundation",
Some("https://wikipedia.org/wiki/SCP_Foundation"),
);
test!(
"wp:it:SCP_Foundation",
Some("https://wikipedia.org/wiki/it:SCP_Foundation"),
);
test!(
"commons:File:SCP-682.jpg",
Some("https://commons.wikimedia.org/wiki/File:SCP-682.jpg"),
);
test!(
"commons:Category:SCP_Foundation",
Some("https://commons.wikimedia.org/wiki/Category:SCP_Foundation"),
);
test!(
"google:what's+my+ip",
Some("https://google.com/search?q=what's+my+ip"),
);
test!(
"duckduckgo:what's+my+ip",
Some("https://duckduckgo.com/?q=what's+my+ip"),
);
test!(
"ddg:what's+my+ip",
Some("https://duckduckgo.com/?q=what's+my+ip"),
);
test!("dictionary:oak", Some("https://dictionary.com/browse/oak"));
test!("thesaurus:oak", Some("https://thesaurus.com/browse/oak"));
test!("banana:fruit-salad", None);
test!(":empty", None);
test!("no-link:", None);
}