#[cfg(feature = "alloc")]
use alloc::{ffi::CString, str::Chars, string::ToString};
use crate::{
error::{TextosError as Error, TextosResult as Result},
macros::impl_sized_alias,
unicode::char::*,
};
use core::{fmt, ops::Deref};
use devela::codegen::paste;
#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct StaticU8String<const CAP: usize> {
arr: [u8; CAP],
len: u8,
}
impl_sized_alias![
String, StaticU8String,
"UTF-8-encoded string, with fixed capacity of ", ".":
"A" 16, 1 "";
"A" 24, 2 "s";
"A" 32, 3 "s";
"A" 40, 4 "s";
"A" 48, 5 "s";
"A" 56, 6 "s";
"A" 64, 7 "s";
"A" 128, 15 "s";
"A" 256, 31 "s";
"A" 512, 63 "s";
"A" 1024, 127 "s";
"A" 2048, 255 "s"
];
impl<const CAP: usize> StaticU8String<CAP> {
#[inline]
pub const fn new() -> Self {
assert![CAP <= 255];
Self {
arr: [0; CAP],
len: 0,
}
}
#[inline]
pub const fn from_char7(c: Char7) -> Self {
let mut new = Self::new();
new.arr[0] = c.to_utf8_bytes()[0];
new.len = 1;
new
}
#[inline]
pub const fn from_char8(c: Char8) -> Self {
let mut new = Self::new();
let bytes = c.to_utf8_bytes();
new.len = char_utf8_2bytes_len(bytes);
new.arr[0] = bytes[0];
if new.len > 1 {
new.arr[1] = bytes[1];
}
new
}
#[inline]
pub const fn from_char16(c: Char16) -> Self {
let mut new = Self::new();
let bytes = c.to_utf8_bytes();
new.len = char_utf8_3bytes_len(bytes);
new.arr[0] = bytes[0];
if new.len > 1 {
new.arr[1] = bytes[1];
}
if new.len > 2 {
new.arr[2] = bytes[2];
}
new
}
#[inline]
pub const fn from_char24(c: Char24) -> Self {
let mut new = Self::new();
let bytes = c.to_utf8_bytes();
new.len = char_utf8_4bytes_len(bytes);
new.arr[0] = bytes[0];
if new.len > 1 {
new.arr[1] = bytes[1];
}
if new.len > 2 {
new.arr[2] = bytes[2];
}
if new.len > 3 {
new.arr[3] = bytes[3];
}
new
}
#[inline]
pub const fn from_char32(c: Char32) -> Self {
let mut new = Self::new();
let bytes = c.to_utf8_bytes();
new.len = char_utf8_4bytes_len(bytes);
new.arr[0] = bytes[0];
if new.len > 1 {
new.arr[1] = bytes[1];
}
if new.len > 2 {
new.arr[2] = bytes[2];
}
if new.len > 3 {
new.arr[3] = bytes[3];
}
new
}
#[inline]
pub const fn from_char(c: char) -> Self {
Self::from_char32(Char32(c))
}
#[inline]
pub const fn capacity() -> usize {
CAP
}
#[inline]
pub const fn remaining_capacity(&self) -> usize {
CAP - self.len as usize
}
#[inline]
pub const fn len(&self) -> usize {
self.len as usize
}
#[inline]
pub const fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
pub const fn is_full(&self) -> bool {
self.len == CAP as u8
}
#[inline]
pub fn clear(&mut self) {
self.len = 0;
}
#[inline]
pub fn reset(&mut self) {
self.arr = [0; CAP];
self.len = 0;
}
#[inline]
pub fn as_bytes(&self) -> &[u8] {
#[cfg(feature = "unsafe")]
unsafe {
self.arr.get_unchecked(0..self.len as usize)
}
#[cfg(not(feature = "unsafe"))]
self.arr
.get(0..self.len as usize)
.expect("len must be <= arr.len()")
}
#[inline]
#[cfg(feature = "unsafe")]
#[cfg_attr(feature = "nightly", doc(cfg(feature = "unsafe")))]
pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8] {
self.arr.get_unchecked_mut(0..self.len as usize)
}
#[inline]
pub const fn as_array(&self) -> [u8; CAP] {
self.arr
}
#[inline]
pub const fn into_array(self) -> [u8; CAP] {
self.arr
}
pub fn as_str(&self) -> &str {
#[cfg(feature = "unsafe")]
unsafe {
core::str::from_utf8_unchecked(
self.arr
.get(0..self.len as usize)
.expect("len must be <= arr.len()"),
)
}
#[cfg(not(feature = "unsafe"))]
core::str::from_utf8(
self.arr
.get(0..self.len as usize)
.expect("len must be <= arr.len()"),
)
.expect("must be valid utf-8")
}
#[cfg(feature = "unsafe")]
#[cfg_attr(feature = "nightly", doc(cfg(feature = "unsafe")))]
pub fn as_str_mut(&mut self) -> &mut str {
unsafe { &mut *(self.as_bytes_mut() as *mut [u8] as *mut str) }
}
#[cfg(feature = "alloc")]
#[cfg_attr(feature = "nightly", doc(cfg(feature = "alloc")))]
pub fn chars(&self) -> Chars {
self.as_str().chars()
}
#[inline]
#[cfg(feature = "alloc")]
#[cfg_attr(feature = "nightly", doc(cfg(feature = "alloc")))]
pub fn to_cstring(&self) -> CString {
CString::new(self.to_string()).unwrap()
}
#[inline]
pub fn pop(&mut self) -> Option<char> {
self.as_str().chars().last().map(|c| {
self.len -= c.len_utf8() as u8;
c
})
}
#[inline]
pub fn try_pop(&mut self) -> Result<char> {
self.as_str()
.chars()
.last()
.map(|c| {
self.len -= c.len_utf8() as u8;
c
})
.ok_or(Error::NotEnoughElements(1))
}
pub fn push(&mut self, character: char) -> usize {
let char_len = character.len_utf8();
if self.remaining_capacity() >= char_len {
let beg = self.len as usize;
let end = beg + char_len;
let _ = character.encode_utf8(&mut self.arr[beg..end]);
self.len += char_len as u8;
char_len
} else {
0
}
}
pub fn try_push(&mut self, character: char) -> Result<usize> {
let char_len = character.len_utf8();
if self.remaining_capacity() >= char_len {
let beg = self.len as usize;
let end = beg + char_len;
let _ = character.encode_utf8(&mut self.arr[beg..end]);
self.len += char_len as u8;
Ok(char_len)
} else {
Err(Error::NotEnoughCapacity(char_len))
}
}
}
impl<const CAP: usize> Default for StaticU8String<CAP> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<const CAP: usize> fmt::Display for StaticU8String<CAP> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl<const CAP: usize> fmt::Debug for StaticU8String<CAP> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self.as_str())
}
}
impl<const CAP: usize> Deref for StaticU8String<CAP> {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
macro_rules! impl_from_char {
( $char:ty => $for_name:ident: $( $for_bit:expr ),+ ) => {
$( impl_from_char![@$char => $for_name: $for_bit]; )+
};
( @$char:ty => $for_name:ident: $for_bit:expr ) => { paste! {
impl From<$char> for [< $for_name $for_bit >] {
fn from(c: $char) -> [< $for_name $for_bit >] {
let mut s = Self::default();
let _ = s.push(c.into());
s
}
}
}};
( try $char:ty => $for_name:ident: $( $for_bit:expr ),+ ) => {
$( impl_from_char![@try $char => $for_name: $for_bit]; )+
};
( @try $char:ty => $for_name:ident: $for_bit:expr ) => { paste! {
impl TryFrom<$char> for [< $for_name $for_bit >] {
type Error = Error;
fn try_from(c: $char) -> Result<[< $for_name $for_bit >]> {
let mut s = Self::default();
s.try_push(c.into())?;
Ok(s)
}
}
}};
}
impl_from_char![Char7 => String: 16, 24, 32, 40, 48, 56, 64, 128, 256, 512, 1024, 2048];
impl_from_char![Char8 => String: 24, 32, 40, 48, 56, 64, 128, 256, 512, 1024, 2048];
impl_from_char![try Char8 => String: 16];
impl_from_char![Char16 => String: 32, 40, 48, 56, 64, 128, 256, 512, 1024, 2048];
impl_from_char![try Char16 => String: 16, 24];
impl_from_char![Char24 => String: 40, 48, 56, 64, 128, 256, 512, 1024, 2048];
impl_from_char![try Char24 => String: 16, 24, 32];
impl_from_char![Char32 => String: 40, 48, 56, 64, 128, 256, 512, 1024, 2048];
impl_from_char![try Char32 => String: 16, 24, 32];
impl_from_char![char => String: 40, 48, 56, 64, 128, 256, 512, 1024, 2048];
impl_from_char![try char => String: 16, 24, 32];
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn push() {
let mut s = String32::new();
assert![s.try_push('ñ').is_ok()];
assert_eq![2, s.len()];
assert![s.try_push('ñ').is_err()];
assert_eq![2, s.len()];
assert![s.try_push('a').is_ok()];
assert_eq![3, s.len()];
}
#[test]
fn pop() {
let mut s = String32::new();
s.push('ñ');
s.push('a');
assert_eq![Some('a'), s.pop()];
assert_eq![Some('ñ'), s.pop()];
assert_eq![None, s.pop()];
}
}