use crate::cast::Conv;
use crate::event::Key;
use crate::text::format::{FontToken, FormattableText};
use crate::text::{Effect, EffectFlags};
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct AccessString {
text: String,
key: Option<(Key, [Effect; 2])>,
}
impl AccessString {
fn parse(mut s: &str) -> Self {
let mut text = String::with_capacity(s.len());
let mut key = None;
while let Some(mut i) = s.find('&') {
text.push_str(&s[..i]);
i += "&".len();
s = &s[i..];
match s.chars().next() {
None => {
s = &s[0..0];
break;
}
Some(c) if key.is_none() => {
let start = u32::conv(text.len());
text.push(c);
let mut kbuf = [0u8; 4];
let k = c.to_ascii_lowercase().encode_utf8(&mut kbuf);
let k = Key::Character(k.into());
let e0 = Effect {
start,
e: 0,
flags: EffectFlags::UNDERLINE,
};
let i = c.len_utf8();
s = &s[i..];
let e1 = Effect {
start: start + u32::conv(i),
e: 0,
flags: EffectFlags::empty(),
};
key = Some((k, [e0, e1]));
}
Some(c) => {
text.push(c);
let i = c.len_utf8();
s = &s[i..];
}
}
}
text.push_str(s);
AccessString { text, key }
}
pub fn key(&self) -> Option<&(Key, [Effect; 2])> {
self.key.as_ref()
}
pub fn text(&self) -> &str {
&self.text
}
}
impl FormattableText for AccessString {
type FontTokenIter<'a> = std::iter::Empty<FontToken>;
#[inline]
fn as_str(&self) -> &str {
&self.text
}
#[inline]
fn font_tokens(&self, _: f32) -> Self::FontTokenIter<'_> {
std::iter::empty()
}
fn effect_tokens(&self) -> &[Effect] {
&[]
}
}
impl From<String> for AccessString {
fn from(text: String) -> Self {
if text.as_bytes().contains(&b'&') {
Self::parse(&text)
} else {
AccessString {
text,
..Default::default()
}
}
}
}
impl From<&str> for AccessString {
fn from(input: &str) -> Self {
Self::parse(input)
}
}
impl<T: Into<AccessString> + Copy> From<&T> for AccessString {
fn from(input: &T) -> Self {
(*input).into()
}
}