use alloc::{borrow::Cow, ffi::CString};
use core::{
ffi::{CStr, FromBytesWithNulError},
fmt,
str::FromStr,
};
use crate::inline::{INLINE_CAPACITY, InlineFlexStr, TooLongForInlining, inline_partial_eq_impl};
use flexstr_support::{InteriorNulError, StringToFromBytes};
pub type InlineCStr = InlineFlexStr<CStr>;
#[derive(Debug)]
pub enum TooLongOrNulError {
TooLong(TooLongForInlining),
NulError(InteriorNulError),
}
impl fmt::Display for TooLongOrNulError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TooLongOrNulError::TooLong(e) => e.fmt(f),
TooLongOrNulError::NulError(e) => e.fmt(f),
}
}
}
impl core::error::Error for TooLongOrNulError {}
impl InlineFlexStr<CStr> {
fn try_from_bytes_without_nul(bytes: &[u8]) -> Result<Self, TooLongOrNulError> {
if bytes.len() < INLINE_CAPACITY {
let mut inline = Self::from_bytes(bytes);
inline.append_nul_zero();
Ok(inline)
} else {
Err(TooLongOrNulError::TooLong(TooLongForInlining {
length: bytes.len(),
inline_capacity: INLINE_CAPACITY,
}))
}
}
pub fn try_from_bytes_with_or_without_nul(bytes: &[u8]) -> Result<Self, TooLongOrNulError> {
match CStr::from_bytes_with_nul(bytes) {
Ok(cstr) => Self::try_from_type(cstr).map_err(TooLongOrNulError::TooLong),
Err(FromBytesWithNulError::NotNulTerminated) => Self::try_from_bytes_without_nul(bytes),
Err(FromBytesWithNulError::InteriorNul { position }) => {
Err(TooLongOrNulError::NulError(InteriorNulError { position }))
}
}
}
#[inline]
pub fn as_bytes_with_nul(&self) -> &[u8] {
self.as_raw_bytes()
}
}
impl<'s> TryFrom<&'s CStr> for InlineFlexStr<CStr> {
type Error = TooLongForInlining;
#[inline]
fn try_from(s: &'s CStr) -> Result<Self, Self::Error> {
InlineFlexStr::try_from_type(s)
}
}
impl<'s> TryFrom<&'s str> for InlineFlexStr<CStr> {
type Error = TooLongOrNulError;
#[inline]
fn try_from(s: &'s str) -> Result<Self, Self::Error> {
InlineFlexStr::try_from_bytes_with_or_without_nul(s.as_bytes())
}
}
impl<'s> TryFrom<&'s [u8]> for InlineFlexStr<CStr> {
type Error = TooLongOrNulError;
#[inline]
fn try_from(bytes: &'s [u8]) -> Result<Self, Self::Error> {
InlineFlexStr::try_from_bytes_with_or_without_nul(bytes)
}
}
inline_partial_eq_impl!(CStr, CStr);
inline_partial_eq_impl!(&CStr, CStr);
inline_partial_eq_impl!(CString, CStr);
inline_partial_eq_impl!(Cow<'_, CStr>, CStr);
impl<S: ?Sized + StringToFromBytes> AsRef<CStr> for InlineFlexStr<S>
where
S: AsRef<CStr>,
{
fn as_ref(&self) -> &CStr {
self.as_ref_type().as_ref()
}
}
impl FromStr for InlineFlexStr<CStr> {
type Err = TooLongOrNulError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
InlineFlexStr::try_from_bytes_with_or_without_nul(s.as_bytes())
}
}