use crate::{
FILLER,
FILLER_CHAR,
Character,
};
#[cfg(feature = "utf8")]
use core::fmt::Display;
#[cfg(feature = "utf8")]
#[derive(Debug)]
pub struct Utf8Charray<'a>(&'a [char]);
#[cfg(feature = "utf8")]
impl Display for Utf8Charray<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
for ch in self.0 {
write!(f, "{}", ch)?;
}
Ok(())
}
}
#[cfg(feature = "utf8")]
impl PartialEq<&str> for Utf8Charray<'_> {
fn eq(&self, other: &&str) -> bool {
let mut other_chars = other.chars();
for &ch in self.0.iter() {
let other_char = other_chars.next();
if other_char.is_none() || ch != other_char.unwrap() {
return false;
}
}
true
}
}
#[cfg(feature = "utf8")]
impl Utf8Charray<'_> {
pub fn iter(&self) -> impl Iterator<Item = &char> {
self.0.iter()
}
}
pub struct Message<const MSG_MAX: usize> {
chars: [Character; MSG_MAX],
edit_pos: usize,
last_change_index: usize,
clamp_edit_pos: bool,
}
impl<const MSG_MAX: usize> Default for Message<MSG_MAX> {
fn default() -> Self {
Self {
chars: [FILLER; MSG_MAX],
edit_pos: 0,
last_change_index: 0,
clamp_edit_pos: false,
}
}
}
impl<const MSG_MAX: usize> Message<MSG_MAX> {
pub const POS_MAX: usize = MSG_MAX - 1;
pub fn new(message_str: &str, edit_pos_end: bool, clamp_edit_pos: bool) -> Self {
let mut new_self = Self {
chars: Self::str_to_chars(message_str),
clamp_edit_pos,
..Self::default()
};
if edit_pos_end {
new_self.edit_pos = new_self.len().clamp(0, Self::POS_MAX);
}
new_self
}
#[cfg(not(feature = "utf8"))]
fn str_to_chars(str: &str) -> [u8; MSG_MAX] {
let mut str_iter = str.chars()
.take(MSG_MAX)
.filter(|ch| ch.is_ascii());
core::array::from_fn(|_|
str_iter.next()
.unwrap_or(FILLER_CHAR)
.to_ascii_uppercase() as u8
)
}
#[cfg(feature = "utf8")]
fn str_to_chars(str: &str) -> [Character; MSG_MAX] {
let mut str_iter = str.chars()
.take(MSG_MAX);
core::array::from_fn(|_|
str_iter.next()
.unwrap_or(FILLER_CHAR)
.to_uppercase()
.next()
.unwrap()
)
}
}
impl<const MSG_MAX: usize> Message<MSG_MAX> {
fn last_char_index(&self) -> Option<usize> {
self.chars.iter().rposition(|ch| *ch != FILLER)
}
fn update_empty_chars(&mut self) {
if let Some(last_index) = self.last_char_index() {
self.chars.iter_mut().enumerate().for_each(|(index, ch)| {
if *ch == FILLER && index < last_index {
*ch = ' ' as Character;
}
});
}
}
}
impl<const MSG_MAX: usize> Message<MSG_MAX> {
pub fn iter(&self) -> MessageIterator<MSG_MAX> {
MessageIterator {
message: self,
index: 0,
}
}
pub fn set_edit_pos(&mut self, pos: usize) {
self.edit_pos = pos.clamp(0, Self::POS_MAX);
}
pub fn set_edit_position_clamp(&mut self, clamp: bool) {
self.clamp_edit_pos = clamp;
}
pub fn is_edit_clamped(&self) -> bool {
self.clamp_edit_pos
}
pub fn get_edit_pos(&self) -> usize {
self.edit_pos
}
pub fn get_last_changed_index(&self) -> usize {
self.last_change_index
}
pub fn get_last_changed_char(&self) -> Character {
self.chars[self.last_change_index]
}
pub fn shift_edit_left(&mut self) {
self.edit_pos = match self.edit_pos {
0 => if self.clamp_edit_pos { 0 } else { Self::POS_MAX },
p => p - 1,
}
}
pub fn shift_edit_right(&mut self) {
self.edit_pos = match self.edit_pos {
p if p == Self::POS_MAX => if self.clamp_edit_pos { Self::POS_MAX } else { 0 },
p => p + 1,
}
}
pub fn add_char(&mut self, ch: Character) {
self.chars[self.edit_pos] = ch;
self.update_empty_chars();
self.last_change_index = self.edit_pos;
}
pub fn put_char_at(&mut self, index: usize, ch: Character) -> Result<(), &str> {
if index < MSG_MAX {
self.chars[index] = ch;
self.update_empty_chars();
self.last_change_index = index;
Ok(())
} else {
Err("Put char index doesn't fit into message length")
}
}
pub fn char_at(&self, index: usize) -> Character {
self.chars[index]
}
pub fn len(&self) -> usize {
let index = self.last_char_index();
match index {
Some(i) if i < MSG_MAX => i + 1,
Some(i) if i == MSG_MAX => MSG_MAX,
_ => 0,
}
}
pub fn is_empty(&self) -> bool {
self.last_char_index().is_none()
}
pub fn set_message(&mut self, message_str: &str, edit_pos_end: bool) -> Result<(), &str> {
if message_str.len() > MSG_MAX {
Err("Message string can't be longer than MSG_MAX.")
} else {
self.chars = Self::str_to_chars(message_str);
if edit_pos_end {
self.edit_pos = self.len().clamp(0, Self::POS_MAX);
} else {
self.edit_pos = 0;
}
self.last_change_index = self.edit_pos;
Ok(())
}
}
pub fn as_charray(&self) -> [Character; MSG_MAX] {
self.chars
}
#[cfg(not(feature = "utf8"))]
pub fn as_str(&self) -> &str {
core::str::from_utf8(self.chars[0..self.len()].as_ref()).unwrap()
}
#[cfg(feature = "utf8")]
pub fn as_str(&self) -> Utf8Charray {
Utf8Charray(self.chars[..self.len()].as_ref())
}
pub fn clear(&mut self) {
self.chars = [FILLER; MSG_MAX];
self.edit_pos = 0;
}
}
pub struct MessageIterator<'a, const MSG_MAX: usize> {
message: &'a Message<MSG_MAX>,
index: usize,
}
impl<'a, const MSG_MAX: usize> Iterator for MessageIterator<'a, MSG_MAX> {
type Item = &'a Character;
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.message.len() {
let result = Some(&self.message.chars[self.index]);
self.index += 1;
result
} else {
None
}
}
}