use {
beef::lean::Cow,
derive_more::{Display, Error},
std::{
ffi::{c_char, CStr, FromBytesWithNulError},
fmt::{Debug, Display},
str::Utf8Error,
},
};
pub(crate) type ItemType = c_char;
#[derive(PartialEq, Eq)]
#[repr(transparent)]
pub struct TStr {
pub(super) inner: CStr,
}
impl TStr {
pub const fn from_impl(cstr: &CStr) -> &Self {
unsafe { &*(cstr as *const CStr as *const Self) }
}
pub const fn as_ptr(&self) -> *const c_char {
self.inner.as_ptr()
}
pub fn from_slice(slice: &[u8]) -> Result<&Self, impl std::error::Error> {
if let Err(e) = std::str::from_utf8(slice) {
return Err(FromSliceError::Unicode(e));
}
let cstr = CStr::from_bytes_with_nul(slice).map_err(FromSliceError::Nul)?;
Ok(Self::from_impl(cstr))
}
pub unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a Self {
let cstr = unsafe { CStr::from_ptr(ptr) };
Self::from_impl(cstr)
}
pub unsafe fn from_ptr_optional<'a>(ptr: *const c_char) -> Option<&'a Self> {
(!ptr.is_null()).then(|| unsafe { Self::from_ptr(ptr) })
}
pub fn to_str(&self) -> Cow<str> {
self.as_str().into()
}
pub const fn len(&self) -> usize {
self.inner.to_bytes().len()
}
pub const fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn chars(&self) -> impl Iterator<Item = char> + '_ {
self.as_str().chars()
}
fn as_str(&self) -> &str {
let string = unsafe { &*(self as *const Self as *const str) };
&string[0..string.len() - 1]
}
}
#[derive(Clone, Debug, Display, Error)]
pub enum FromSliceError {
Unicode(Utf8Error),
Nul(FromBytesWithNulError),
}
impl Debug for TStr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Debug::fmt(self.as_str(), f)
}
}
impl Display for TStr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(self.as_str(), f)
}
}