1use alloc::{borrow::Cow, ffi::CString};
2use core::{
3 ffi::{CStr, FromBytesWithNulError},
4 fmt,
5 str::FromStr,
6};
7
8use crate::inline::{INLINE_CAPACITY, InlineFlexStr, TooLongForInlining, inline_partial_eq_impl};
9
10use flexstr_support::{InteriorNulError, StringToFromBytes};
11
12pub type InlineCStr = InlineFlexStr<CStr>;
14
15#[derive(Debug)]
19pub enum TooLongOrNulError {
20 TooLong(TooLongForInlining),
22 NulError(InteriorNulError),
24}
25
26impl fmt::Display for TooLongOrNulError {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28 match self {
29 TooLongOrNulError::TooLong(e) => e.fmt(f),
30 TooLongOrNulError::NulError(e) => e.fmt(f),
31 }
32 }
33}
34
35impl core::error::Error for TooLongOrNulError {}
36
37impl InlineFlexStr<CStr> {
40 fn try_from_bytes_without_nul(bytes: &[u8]) -> Result<Self, TooLongOrNulError> {
41 if bytes.len() < INLINE_CAPACITY {
42 let mut inline = Self::from_bytes(bytes);
43 inline.append_nul_zero();
44 Ok(inline)
45 } else {
46 Err(TooLongOrNulError::TooLong(TooLongForInlining {
47 length: bytes.len(),
48 inline_capacity: INLINE_CAPACITY,
49 }))
50 }
51 }
52
53 pub fn try_from_bytes_with_or_without_nul(bytes: &[u8]) -> Result<Self, TooLongOrNulError> {
55 match CStr::from_bytes_with_nul(bytes) {
56 Ok(cstr) => Self::try_from_type(cstr).map_err(TooLongOrNulError::TooLong),
57 Err(FromBytesWithNulError::NotNulTerminated) => Self::try_from_bytes_without_nul(bytes),
58 Err(FromBytesWithNulError::InteriorNul { position }) => {
59 Err(TooLongOrNulError::NulError(InteriorNulError { position }))
60 }
61 }
62 }
63
64 #[inline]
66 pub fn as_bytes_with_nul(&self) -> &[u8] {
67 self.as_raw_bytes()
68 }
69}
70
71impl<'s> TryFrom<&'s CStr> for InlineFlexStr<CStr> {
75 type Error = TooLongForInlining;
76
77 #[inline]
78 fn try_from(s: &'s CStr) -> Result<Self, Self::Error> {
79 InlineFlexStr::try_from_type(s)
80 }
81}
82
83impl<'s> TryFrom<&'s str> for InlineFlexStr<CStr> {
84 type Error = TooLongOrNulError;
85
86 #[inline]
87 fn try_from(s: &'s str) -> Result<Self, Self::Error> {
88 InlineFlexStr::try_from_bytes_with_or_without_nul(s.as_bytes())
89 }
90}
91
92impl<'s> TryFrom<&'s [u8]> for InlineFlexStr<CStr> {
93 type Error = TooLongOrNulError;
94
95 #[inline]
96 fn try_from(bytes: &'s [u8]) -> Result<Self, Self::Error> {
97 InlineFlexStr::try_from_bytes_with_or_without_nul(bytes)
98 }
99}
100
101inline_partial_eq_impl!(CStr, CStr);
104inline_partial_eq_impl!(&CStr, CStr);
105inline_partial_eq_impl!(CString, CStr);
106inline_partial_eq_impl!(Cow<'_, CStr>, CStr);
107
108impl<S: ?Sized + StringToFromBytes> AsRef<CStr> for InlineFlexStr<S>
111where
112 S: AsRef<CStr>,
113{
114 fn as_ref(&self) -> &CStr {
115 self.as_ref_type().as_ref()
116 }
117}
118
119impl FromStr for InlineFlexStr<CStr> {
122 type Err = TooLongOrNulError;
123
124 fn from_str(s: &str) -> Result<Self, Self::Err> {
125 InlineFlexStr::try_from_bytes_with_or_without_nul(s.as_bytes())
126 }
127}