use core::ffi::{CStr, c_char};
use core::fmt;
use core::hash::{Hash, Hasher};
use core::ops::Deref;
use crate::CStrTooLargeForStaticArray;
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct ArrayCStr<const N: usize>([c_char; N]);
impl<const N: usize> ArrayCStr<N> {
#[inline]
pub fn as_c_str(&self) -> &CStr {
let bytes = unsafe { core::slice::from_raw_parts(self.0.as_ptr().cast::<u8>(), N) };
CStr::from_bytes_until_nul(bytes).expect("ArrayCStr: no NUL byte found")
}
#[inline]
pub fn write_c_str(
&mut self,
s: &CStr,
) -> core::result::Result<(), CStrTooLargeForStaticArray> {
let bytes = s.to_bytes_with_nul();
let src =
unsafe { core::slice::from_raw_parts(bytes.as_ptr().cast::<c_char>(), bytes.len()) };
self.0
.get_mut(..src.len())
.ok_or(CStrTooLargeForStaticArray {
static_array_size: N,
c_str_size: src.len(),
})?
.copy_from_slice(src);
Ok(())
}
}
impl<const N: usize> Default for ArrayCStr<N> {
#[inline]
fn default() -> Self {
Self([0; N])
}
}
impl<const N: usize> Deref for ArrayCStr<N> {
type Target = CStr;
#[inline]
fn deref(&self) -> &CStr {
self.as_c_str()
}
}
impl<const N: usize> AsRef<CStr> for ArrayCStr<N> {
#[inline]
fn as_ref(&self) -> &CStr {
self.as_c_str()
}
}
impl<const N: usize> fmt::Debug for ArrayCStr<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self.as_c_str(), f)
}
}
impl<const N: usize> fmt::Display for ArrayCStr<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for &byte in self.as_c_str().to_bytes() {
let ch = byte as char;
fmt::Write::write_char(f, ch)?;
}
Ok(())
}
}
impl<const N: usize> PartialEq for ArrayCStr<N> {
fn eq(&self, other: &Self) -> bool {
self.as_c_str() == other.as_c_str()
}
}
impl<const N: usize> Eq for ArrayCStr<N> {}
impl<const N: usize> Hash for ArrayCStr<N> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_c_str().hash(state);
}
}