use crate::{CharDecoder, ZStr, ZStringError};
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct ArrayZString<const N: usize>([u8; N]);
impl<const N: usize> ArrayZString<N> {
#[inline]
#[must_use]
pub const fn const_default() -> Self {
Self([0_u8; N])
}
#[inline]
#[must_use]
pub const fn as_zstr(&self) -> ZStr<'_> {
assert!(N > 0);
unsafe { core::mem::transmute::<*const u8, ZStr<'_>>(self.0.as_ptr()) }
}
#[inline]
#[must_use]
#[track_caller]
pub fn as_str(&self) -> &str {
let null_position = self.0.iter().position(|&b| b == 0).unwrap();
core::str::from_utf8(&self.0[..null_position]).unwrap()
}
#[inline]
pub fn bytes(&self) -> impl Iterator<Item = u8> + '_ {
self.0.iter().copied().take_while(|&b| b != 0)
}
#[inline]
pub fn chars(&self) -> impl Iterator<Item = char> + '_ {
CharDecoder::from(self.bytes())
}
#[inline]
#[must_use]
pub const fn as_ptr(self) -> *const u8 {
self.0.as_ptr()
}
}
impl<const N: usize> Default for ArrayZString<N> {
#[inline]
#[must_use]
fn default() -> Self {
Self::const_default()
}
}
impl<const N: usize> TryFrom<&str> for ArrayZString<N> {
type Error = Option<ZStringError>;
#[inline]
fn try_from(value: &str) -> Result<Self, Self::Error> {
let trimmed = value.trim_end_matches('\0');
if trimmed.as_bytes().iter().copied().any(|b| b == 0) {
Err(Some(ZStringError::InteriorNulls))
} else if trimmed.len() <= (N - 1) {
let mut out = Self::const_default();
out.0[..trimmed.len()].copy_from_slice(trimmed.as_bytes());
Ok(out)
} else {
Err(None)
}
}
}
impl<const N: usize> core::fmt::Display for ArrayZString<N> {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::Display::fmt(&self.as_zstr(), f)
}
}
impl<const N: usize> core::fmt::Debug for ArrayZString<N> {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::Debug::fmt(&self.as_zstr(), f)
}
}
impl<const N: usize, const X: usize> PartialEq<ArrayZString<X>>
for ArrayZString<N>
{
#[inline]
#[must_use]
fn eq(&self, other: &ArrayZString<X>) -> bool {
self.bytes().eq(other.bytes())
}
}
impl<const N: usize, const X: usize> PartialOrd<ArrayZString<X>>
for ArrayZString<N>
{
#[inline]
#[must_use]
fn partial_cmp(
&self, other: &ArrayZString<X>,
) -> Option<core::cmp::Ordering> {
Some(self.bytes().cmp(other.bytes()))
}
}
impl<const N: usize> Eq for ArrayZString<N> {}
impl<const N: usize> Ord for ArrayZString<N> {
#[inline]
#[must_use]
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.partial_cmp(other).unwrap()
}
}
impl<const N: usize> PartialEq<ZStr<'_>> for ArrayZString<N> {
#[inline]
#[must_use]
fn eq(&self, other: &ZStr<'_>) -> bool {
self.bytes().eq(other.bytes())
}
}
impl<const N: usize> PartialOrd<ZStr<'_>> for ArrayZString<N> {
#[inline]
#[must_use]
fn partial_cmp(&self, other: &ZStr<'_>) -> Option<core::cmp::Ordering> {
Some(self.bytes().cmp(other.bytes()))
}
}
#[cfg(feature = "alloc")]
impl<const N: usize> PartialEq<crate::ZString> for ArrayZString<N> {
#[inline]
#[must_use]
fn eq(&self, other: &crate::ZString) -> bool {
self.eq(&other.as_zstr())
}
}
#[cfg(feature = "alloc")]
impl<const N: usize> PartialOrd<crate::ZString> for ArrayZString<N> {
#[inline]
#[must_use]
fn partial_cmp(&self, other: &crate::ZString) -> Option<core::cmp::Ordering> {
self.partial_cmp(&other.as_zstr())
}
}
impl<const N: usize> core::hash::Hash for ArrayZString<N> {
#[inline]
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
for b in self.bytes() {
state.write_u8(b)
}
}
}