#![no_std]
#[cfg(feature = "std")]
extern crate std;
mod search;
use core::cmp;
use core::convert;
use core::fmt;
use core::ops;
pub use crate::generated::Group;
#[cfg(feature = "search")]
pub use crate::search::search;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Emoji {
id: usize,
emoji: &'static str,
name: &'static str,
group: Group,
aliases: Option<&'static [&'static str]>,
variations: &'static [&'static str],
skin_tones: &'static [&'static str],
}
impl Emoji {
pub const fn as_str(&self) -> &str {
self.emoji
}
pub const fn name(&self) -> &str {
self.name
}
pub const fn group(&self) -> Group {
self.group
}
pub fn shortcode(&self) -> Option<&str> {
self.aliases.and_then(|aliases| aliases.first().copied())
}
}
impl cmp::PartialEq<str> for Emoji {
fn eq(&self, s: &str) -> bool {
cmp::PartialEq::eq(self.as_str(), s)
}
}
impl cmp::PartialEq<&str> for Emoji {
fn eq(&self, s: &&str) -> bool {
cmp::PartialEq::eq(self.as_str(), *s)
}
}
impl cmp::PartialOrd for Emoji {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(cmp::Ord::cmp(self, other))
}
}
impl cmp::Ord for Emoji {
fn cmp(&self, other: &Self) -> cmp::Ordering {
cmp::Ord::cmp(&self.id, &other.id)
}
}
impl convert::AsRef<str> for Emoji {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl fmt::Display for Emoji {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self.as_str(), f)
}
}
impl ops::Deref for Emoji {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
pub fn iter() -> impl Iterator<Item = &'static Emoji> {
crate::generated::EMOJIS.iter()
}
pub fn lookup(query: &str) -> Option<Emoji> {
crate::generated::EMOJIS
.iter()
.find(|&e| {
e == query
|| e.variations.contains(&query)
|| e.skin_tones.contains(&query)
|| e.aliases
.map(|aliases| aliases.contains(&query))
.unwrap_or(false)
})
.copied()
}
impl Group {
pub fn emojis(&self) -> impl Iterator<Item = &'static Emoji> {
let group = *self;
iter()
.skip_while(move |emoji| emoji.group != group)
.take_while(move |emoji| emoji.group == group)
}
}
mod generated;
#[cfg(test)]
mod tests {
use super::*;
use core::fmt::Write;
#[test]
fn emoji_ordering() {
let grinning_face = lookup("π");
let winking_face = lookup("π");
assert!(grinning_face < winking_face);
assert!(winking_face > grinning_face);
assert_eq!(grinning_face, lookup("π"));
}
#[test]
fn emoji_display() {
let mut buf = String::<[u8; 4]>::default();
let grinning_face = lookup("π").unwrap();
write!(buf, "{}", grinning_face).unwrap();
assert_eq!(buf.as_str(), "π");
}
#[test]
fn lookup_variation() {
assert_eq!(lookup("βΉ"), lookup("βΉοΈ"));
}
#[test]
fn lookup_skin_tone() {
assert_eq!(lookup("ππ½"), lookup("π"));
}
#[derive(Default)]
struct String<T> {
buf: T,
pos: usize,
}
impl<const N: usize> String<[u8; N]> {
fn as_str(&self) -> &str {
core::str::from_utf8(&self.buf[..self.pos]).unwrap()
}
}
impl<const N: usize> fmt::Write for String<[u8; N]> {
fn write_str(&mut self, s: &str) -> fmt::Result {
let bytes = s.as_bytes();
let end = self.pos + bytes.len();
if end > self.buf.len() {
panic!("buffer overflow");
}
self.buf[self.pos..end].copy_from_slice(bytes);
self.pos = end;
Ok(())
}
}
}