use std::fmt::{self, Write};
const CUSTOM_EMOJI_CDN_BASE: &str = "https://cdn.discordapp.com/emojis";
const TWEMOJI_CDN_BASE: &str = "https://cdn.jsdelivr.net/gh/jdecked/twemoji@17.0.3/assets/72x72";
pub(crate) fn custom_emoji_image_url(id: impl fmt::Display, animated: bool) -> String {
if animated {
format!("{CUSTOM_EMOJI_CDN_BASE}/{id}.webp?animated=true")
} else {
format!("{CUSTOM_EMOJI_CDN_BASE}/{id}.png")
}
}
pub(crate) fn unicode_emoji_image_url(value: &str) -> Option<String> {
let emoji = emojis::get(value)?;
let canonical = emoji.as_str();
let keep_variation_selectors = canonical.contains('\u{200d}');
let mut codepoints = String::new();
for codepoint in canonical
.chars()
.filter(|codepoint| keep_variation_selectors || *codepoint != '\u{fe0f}')
{
if !codepoints.is_empty() {
codepoints.push('-');
}
write!(&mut codepoints, "{:x}", u32::from(codepoint))
.expect("writing to a String cannot fail");
}
(!codepoints.is_empty()).then(|| format!("{TWEMOJI_CDN_BASE}/{codepoints}.png"))
}
#[cfg(test)]
mod tests {
use super::unicode_emoji_image_url;
#[test]
fn unicode_emoji_urls_follow_twemoji_sequence_names() {
for (emoji, filename) in [
("😀", "1f600.png"),
("❤️", "2764.png"),
("👍🏽", "1f44d-1f3fd.png"),
("👨❤️👨", "1f468-200d-2764-fe0f-200d-1f468.png"),
("🇰🇷", "1f1f0-1f1f7.png"),
("#️⃣", "23-20e3.png"),
] {
let expected = format!(
"https://cdn.jsdelivr.net/gh/jdecked/twemoji@17.0.3/assets/72x72/{filename}"
);
assert_eq!(
unicode_emoji_image_url(emoji).as_deref(),
Some(expected.as_str()),
"{emoji}"
);
}
assert!(unicode_emoji_image_url("not emoji").is_none());
}
}