use crate::*;
impl<Kind: StringletKind, const SIZE: usize, const LEN: usize> StringletBase<Kind, SIZE, LEN>
where
Self: ConfigBase<Kind, SIZE, LEN>,
{
#[inline(always)]
pub const fn new() -> Self {
const {
if Self::FIXED {
assert!(SIZE == 0, "Stringlet<1> or longer cannot be empty");
} else if Self::TRIM {
assert!(SIZE <= 1, "TrimStringlet<2> or longer cannot be empty");
}
}
unsafe { Self::from_utf8_unchecked(&[]) }
}
pub const fn from_str(str: &str) -> Result<Self, ()> {
if Self::fits(str.len()) {
Ok(unsafe { Self::from_str_unchecked(str) })
} else {
Err(())
}
}
pub const unsafe fn from_str_unchecked(str: &str) -> Self {
unsafe { Self::from_utf8_unchecked(str.as_bytes()) }
}
pub fn from_utf8_bytes(str: [u8; SIZE]) -> Result<Self, core::str::Utf8Error> {
str::from_utf8(&str)?;
Ok(unsafe { Self::from_utf8_bytes_unchecked(str) })
}
pub const unsafe fn from_utf8_bytes_unchecked(str: [u8; SIZE]) -> Self {
Self {
str,
len: [str.len() as _; _],
_kind: PhantomData,
}
}
pub fn from_utf8(bytes: &[u8]) -> Result<Self, core::str::Utf8Error> {
assert!(
Self::fits(bytes.len()),
"{}::from_utf8(): cannot store {} characters",
Self::type_name(),
bytes.len()
);
str::from_utf8(bytes)?;
Ok(unsafe { Self::from_utf8_unchecked(bytes) })
}
#[doc(hidden)]
#[inline]
pub const fn _from_macro(str: &str) -> Self {
if Self::fits(str.len()) {
unsafe { Self::from_utf8_unchecked(str.as_bytes()) }
} else if Self::FIXED {
panic!("stringlet!(...): parameter too short or too long.")
} else if Self::TRIM {
panic!("stringlet!(trim ...): parameter too short or too long.")
} else {
panic!("stringlet!(var|slim ...): parameter too long.")
}
}
pub const unsafe fn from_utf8_unchecked(bytes: &[u8]) -> Self {
let bytes_len = bytes.len();
let mut str_uninit = core::mem::MaybeUninit::uninit();
let str = str_uninit.as_mut_ptr() as *mut u8;
Self {
str: unsafe {
core::ptr::copy_nonoverlapping(bytes.as_ptr(), str, bytes_len);
if !Self::FIXED {
let tail = TAG | (SIZE - bytes_len) as u8;
str.add(bytes_len).write_bytes(tail, SIZE - bytes_len);
}
str_uninit.assume_init()
},
len: [bytes_len as _; _],
_kind: PhantomData,
}
}
#[inline(always)]
pub(crate) const fn fits(len: usize) -> bool {
if Self::FIXED {
len == SIZE
} else if Self::TRIM {
len == SIZE || len + 1 == SIZE
} else {
len <= SIZE
}
}
}
impl_for! {
bound Default:
fn default() -> Self {
Self::new()
}
}
#[cfg(doctest)]
mod doctests {
fn test_stringlet_1_new_compile_fail() {}
fn test_stringlet_1_default_compile_fail() {}
fn test_trim_stringlet_2_new_compile_fail() {}
fn test_trim_stringlet_2_default_compile_fail() {}
fn test_var_stringlet_256_new_compile_fail() {}
fn test_var_stringlet_256_default_compile_fail() {}
fn test_slim_stringlet_65_new_compile_fail() {}
fn test_slim_stringlet_65_default_compile_fail() {}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fits() {
assert!(Stringlet::<0>::fits(0));
assert!(!Stringlet::<0>::fits(1));
assert!(Stringlet::<1>::fits(1));
assert!(!Stringlet::<1>::fits(0));
assert!(Stringlet::<256>::fits(256));
assert!(VarStringlet::<0>::fits(0));
assert!(!VarStringlet::<0>::fits(1));
assert!(VarStringlet::<1>::fits(1));
assert!(VarStringlet::<1>::fits(0));
assert!(VarStringlet::<2>::fits(0));
assert!(VarStringlet::<255>::fits(0));
assert!(VarStringlet::<255>::fits(255));
assert!(TrimStringlet::<0>::fits(0));
assert!(!TrimStringlet::<0>::fits(1));
assert!(TrimStringlet::<1>::fits(0));
assert!(TrimStringlet::<1>::fits(1));
assert!(!TrimStringlet::<2>::fits(0));
assert!(TrimStringlet::<256>::fits(255));
assert!(TrimStringlet::<256>::fits(256));
assert!(SlimStringlet::<0>::fits(0));
assert!(!SlimStringlet::<0>::fits(1));
assert!(SlimStringlet::<1>::fits(1));
assert!(SlimStringlet::<1>::fits(0));
assert!(SlimStringlet::<2>::fits(0));
assert!(SlimStringlet::<64>::fits(0));
assert!(SlimStringlet::<64>::fits(64));
}
#[test]
fn test_new() {
let s = Stringlet::<0>::new();
assert!(s.is_empty());
let s = TrimStringlet::<0>::new();
assert!(s.is_empty());
let s = TrimStringlet::<1>::new();
assert!(s.is_empty());
let s = VarStringlet::<8>::new();
assert!(s.is_empty());
let s = SlimStringlet::<8>::new();
assert!(s.is_empty());
}
#[test]
fn test_default() {
let s: Stringlet<0> = Default::default();
assert!(s.is_empty());
let s: TrimStringlet<0> = Default::default();
assert!(s.is_empty());
let s: TrimStringlet<1> = Default::default();
assert!(s.is_empty());
let s: VarStringlet = Default::default();
assert!(s.is_empty());
let s: SlimStringlet = Default::default();
assert!(s.is_empty());
}
}