use super::keycodes::KeyCode;
use std::fmt;
#[derive(Debug, Clone, Copy)]
pub struct KeyEvent {
pub key_code: KeyCode,
pub is_repeat: bool,
pub mods: KeyModifiers,
text: TinyStr,
unmodified_text: TinyStr,
}
impl KeyEvent {
pub(crate) fn new(
key_code: impl Into<KeyCode>,
is_repeat: bool,
mods: KeyModifiers,
text: impl Into<StrOrChar>,
unmodified_text: impl Into<StrOrChar>,
) -> Self {
let text = match text.into() {
StrOrChar::Char(c) => c.into(),
StrOrChar::Str(s) => TinyStr::new(s),
};
let unmodified_text = match unmodified_text.into() {
StrOrChar::Char(c) => c.into(),
StrOrChar::Str(s) => TinyStr::new(s),
};
KeyEvent {
key_code: key_code.into(),
is_repeat,
mods,
text,
unmodified_text,
}
}
pub fn text(&self) -> Option<&str> {
if self.text.len == 0 {
None
} else {
Some(self.text.as_str())
}
}
pub fn unmod_text(&self) -> Option<&str> {
if self.unmodified_text.len == 0 {
None
} else {
Some(self.unmodified_text.as_str())
}
}
#[doc(hidden)]
pub fn for_test(mods: impl Into<KeyModifiers>, text: &'static str, code: KeyCode) -> Self {
KeyEvent::new(code, false, mods.into(), text, text)
}
}
#[derive(Clone, Copy, Default, PartialEq)]
pub struct KeyModifiers {
pub shift: bool,
pub alt: bool,
pub ctrl: bool,
pub meta: bool,
}
const TINY_STR_CAPACITY: usize = 15;
#[derive(Clone, Copy)]
struct TinyStr {
len: u8,
buf: [u8; TINY_STR_CAPACITY],
}
impl TinyStr {
fn new<S: AsRef<str>>(s: S) -> Self {
let s = s.as_ref();
let len = match s.len() {
l @ 0..=15 => l,
more => {
debug_assert!(
false,
"Err 101, Invalid Assumptions: TinyStr::new \
called with {} (len {}).",
s, more
);
#[cfg(test)]
{
assert!(
false,
"Err 101, Invalid Assumptions: TinyStr::new \
called with {} (len {}).",
s, more
);
}
let mut len = 15;
while !s.is_char_boundary(len) {
len -= 1;
}
len
}
};
let mut buf = [0; 15];
buf[..len].copy_from_slice(&s.as_bytes()[..len]);
TinyStr {
len: len as u8,
buf,
}
}
fn as_str(&self) -> &str {
unsafe { std::str::from_utf8_unchecked(&self.buf[..self.len as usize]) }
}
}
impl From<char> for TinyStr {
fn from(src: char) -> TinyStr {
let len = src.len_utf8();
let mut buf = [0; 15];
src.encode_utf8(&mut buf);
TinyStr {
len: len as u8,
buf,
}
}
}
pub enum StrOrChar {
Char(char),
Str(&'static str),
}
impl From<&'static str> for StrOrChar {
fn from(src: &'static str) -> Self {
StrOrChar::Str(src)
}
}
impl From<char> for StrOrChar {
fn from(src: char) -> StrOrChar {
StrOrChar::Char(src)
}
}
impl From<Option<char>> for StrOrChar {
fn from(src: Option<char>) -> StrOrChar {
match src {
Some(c) => StrOrChar::Char(c),
None => StrOrChar::Str(""),
}
}
}
impl fmt::Display for TinyStr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl fmt::Debug for TinyStr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "TinyStr(\"{}\")", self.as_str())
}
}
impl Default for TinyStr {
fn default() -> Self {
TinyStr::new("")
}
}
#[allow(clippy::useless_let_if_seq)]
impl fmt::Debug for KeyModifiers {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Mods(")?;
let mut has_prev = false;
if self.meta {
write!(f, "meta")?;
has_prev = true;
}
if self.ctrl {
if has_prev {
write!(f, "+")?;
}
write!(f, "ctrl")?;
has_prev = true;
}
if self.alt {
if has_prev {
write!(f, "+")?;
}
write!(f, "alt")?;
has_prev = true;
}
if self.shift {
if has_prev {
write!(f, "+")?;
}
write!(f, "shift")?;
has_prev = true;
}
if !has_prev {
write!(f, "None")?;
}
write!(f, ")")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[should_panic(expected = "Invalid Assumptions")]
fn smallstr() {
let smol = TinyStr::new("hello");
assert_eq!(smol.as_str(), "hello");
let smol = TinyStr::new("");
assert_eq!(smol.as_str(), "");
let s_16 = "😍🥰😘😗";
assert_eq!(s_16.len(), 16);
assert!(!s_16.is_char_boundary(15));
let _too_big = TinyStr::new("😍🥰😘😗");
}
}