use crate::*;
macro_rules! consts {
($($fn:ident -> $const:ident);+ $(;)?) => {
$(
#[allow(unused)]
pub(crate) const $const: bool =
Kind::ABBR == stringify!($const).as_bytes()[0];
#[allow(unused)]
pub(crate) const fn $fn(&self) -> bool {
Self::$const
}
)+
};
}
impl<Kind: StringletKind, const SIZE: usize, const LEN: usize> StringletBase<Kind, SIZE, LEN> {
consts! {
is_fixed -> FIXED;
is_trim -> TRIM;
is_var -> VAR;
is_slim -> SLIM;
}
#[inline(always)]
pub const fn len(&self) -> usize {
if Self::FIXED || SIZE == 0 {
return SIZE;
} else if Self::VAR {
return self.len[0] as _;
}
let last = self.last();
if SIZE == 1 {
(last < TAG) as _
} else if Self::TRIM {
SIZE - (last > TAG) as usize
}
else if SIZE == 64 {
SIZE - (last == TAG) as usize * SIZE - (last > TAG) as usize * (last ^ TAG) as usize
} else {
SIZE - (last > TAG) as usize * (last ^ TAG) as usize
}
}
#[inline(always)]
pub const fn is_empty(&self) -> bool {
if SIZE == 0 {
return true;
}
let last = self.last();
if Self::FIXED {
false
} else if Self::TRIM {
SIZE == 1 && last > TAG
} else if Self::VAR {
self.len[0] == 0
}
else if SIZE == 64 {
last == TAG
} else {
last == TAG | SIZE as u8
}
}
#[inline(always)]
pub const fn as_bytes(&self) -> &[u8] {
if Self::FIXED {
&self.str
} else {
unsafe { core::slice::from_raw_parts(self.str.as_ptr(), self.len()) }
}
}
#[inline(always)]
pub const fn as_str(&self) -> &str {
unsafe { str::from_utf8_unchecked(self.as_bytes()) }
}
pub(crate) fn type_name() -> String {
use core::fmt::Write;
let mut ret = String::with_capacity(20) + if Self::FIXED {
"Stringlet"
} else if Self::VAR {
"VarStringlet"
} else if Self::TRIM {
"TrimStringlet"
} else {
"SlimStringlet"
};
if SIZE != 16 {
_ = write!(ret, "<{}>", SIZE);
}
ret
}
#[inline(always)]
pub(crate) const fn last(&self) -> u8 {
debug_assert!(SIZE != 0, "unchecked call");
self.str[SIZE - 1]
}
}
#[cfg(test)]
mod tests {
use core::convert::Into;
use super::*;
#[test]
fn test_big() {
let _f: Stringlet<1024>;
let _v: VarStringlet<255>;
let _s: SlimStringlet<64>;
}
#[test]
fn test_as_str() {
let f: Stringlet<7> = "A123456".into();
assert_eq!(f.as_str(), "A123456");
let v: VarStringlet = "A123456".into();
assert_eq!(v.as_str(), "A123456");
let s: SlimStringlet = "A123456".into();
assert_eq!(s.as_str(), "A123456");
}
#[test]
fn test_const() {
const ABCD: Stringlet<4> =
unsafe { Stringlet::<_>::from_utf8_bytes_unchecked([b'A', b'b', b'c', b'd']) };
assert_eq!(&ABCD, "Abcd");
const A123456: Stringlet<7> = stringlet!("A123456");
assert_eq!(&A123456, "A123456");
}
fn test_all_lengths<const SIZE: usize>()
where
VarStringlet<SIZE>: VarConfig<SIZE>,
SlimStringlet<SIZE>: SlimConfig<SIZE>,
{
const STR64: &str = "0123456789_123456789_123456789_123456789_123456789_123456789_123";
for len in 0..=SIZE {
let str: VarStringlet<SIZE> = (&STR64[..len]).into();
assert_eq!(str.is_empty(), len == 0);
assert_eq!(str.len(), len);
let str: SlimStringlet<SIZE> = (&STR64[..len]).into();
assert_eq!(str.is_empty(), len == 0);
assert_eq!(str.len(), len);
}
let fixed: Stringlet<SIZE> = (&STR64[..SIZE]).into();
assert_eq!(fixed.is_empty(), SIZE == 0);
assert_eq!(fixed.len(), SIZE);
let trim: TrimStringlet<SIZE> = (&STR64[..SIZE]).into();
assert_eq!(trim.is_empty(), SIZE == 0);
assert_eq!(trim.len(), SIZE);
}
#[test]
fn test_len() {
macro_rules! test_all_lengths {
($($size:literal),+) => {
$(test_all_lengths::<$size>();)+
};
}
test_all_lengths![
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45,
46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64
];
}
#[test]
fn test_empty() {
assert!(stringlet!("").is_empty());
assert!(!stringlet!("a").is_empty());
assert!(!stringlet!("ab").is_empty());
assert!(stringlet!(trim: "").is_empty());
assert!(stringlet!(trim 1: "").is_empty());
assert!(!stringlet!(trim: "a").is_empty());
assert!(!stringlet!(trim 2: "a").is_empty());
assert!(!stringlet!(trim: "ab").is_empty());
assert!(!stringlet!(trim 3: "ab").is_empty());
assert!(stringlet!(var: "").is_empty());
assert!(stringlet!(var 1: "").is_empty());
assert!(stringlet!(var 2: "").is_empty());
assert!(!stringlet!(var: "a").is_empty());
assert!(!stringlet!(var: "ab").is_empty());
assert!(stringlet!(slim: "").is_empty());
assert!(stringlet!(slim 1: "").is_empty());
assert!(stringlet!(slim 2: "").is_empty());
assert!(!stringlet!(slim: "a").is_empty());
assert!(!stringlet!(slim: "ab").is_empty());
}
#[test]
fn test_all_type_names() {
macro_rules! test_it {
(1 $ty:ty) => {
assert_eq!(<$ty>::type_name(), stringify!($ty).replace(' ', ""));
};
([$($size:literal),+] $ty:tt) => {
$(
test_it!(1 $ty<$size>);
)+
};
($ty:tt) => {
test_it!(1 $ty); test_it!([
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64
] $ty);
};
}
test_it!(Stringlet);
test_it!(VarStringlet);
test_it!(TrimStringlet);
test_it!(SlimStringlet);
}
}