cheetah_string/
cheetah_string.rs

1use core::fmt;
2use std::borrow::{Borrow, Cow};
3use std::cmp::Ordering;
4use std::fmt::Display;
5use std::hash::Hash;
6use std::ops::Deref;
7use std::str::FromStr;
8use std::sync::Arc;
9
10pub const EMPTY_STRING: &str = "";
11
12#[derive(Clone)]
13#[repr(transparent)]
14pub struct CheetahString {
15    pub(super) inner: InnerString,
16}
17
18impl Default for CheetahString {
19    fn default() -> Self {
20        CheetahString {
21            inner: InnerString::Empty,
22        }
23    }
24}
25
26impl From<String> for CheetahString {
27    #[inline]
28    fn from(s: String) -> Self {
29        CheetahString::from_string(s)
30    }
31}
32
33impl From<Arc<String>> for CheetahString {
34    #[inline]
35    fn from(s: Arc<String>) -> Self {
36        CheetahString::from_arc_string(s)
37    }
38}
39
40impl<'a> From<&'a str> for CheetahString {
41    #[inline]
42    fn from(s: &'a str) -> Self {
43        CheetahString::from_slice(s)
44    }
45}
46
47impl From<&[u8]> for CheetahString {
48    #[inline]
49    fn from(b: &[u8]) -> Self {
50        CheetahString::from_slice(unsafe { std::str::from_utf8_unchecked(b) })
51    }
52}
53
54impl FromStr for CheetahString {
55    type Err = std::string::ParseError;
56    #[inline]
57    fn from_str(s: &str) -> Result<Self, Self::Err> {
58        Ok(CheetahString::from_slice(s))
59    }
60}
61
62impl From<Vec<u8>> for CheetahString {
63    #[inline]
64    fn from(v: Vec<u8>) -> Self {
65        CheetahString::from_slice(unsafe { std::str::from_utf8_unchecked(&v) })
66    }
67}
68
69impl From<Cow<'static, str>> for CheetahString {
70    #[inline]
71    fn from(cow: Cow<'static, str>) -> Self {
72        match cow {
73            Cow::Borrowed(s) => CheetahString::from_static_str(s),
74            Cow::Owned(s) => CheetahString::from_string(s),
75        }
76    }
77}
78
79impl From<Cow<'_, String>> for CheetahString {
80    #[inline]
81    fn from(cow: Cow<'_, String>) -> Self {
82        match cow {
83            Cow::Borrowed(s) => CheetahString::from_slice(s),
84            Cow::Owned(s) => CheetahString::from_string(s),
85        }
86    }
87}
88
89impl From<char> for CheetahString {
90    /// Allocates an owned [`CheetahString`] from a single character.
91    ///
92    /// # Example
93    /// ```rust
94    /// use cheetah_string::CheetahString;
95    /// let c: char = 'a';
96    /// let s: CheetahString = CheetahString::from(c);
97    /// assert_eq!("a", &s[..]);
98    /// ```
99    #[inline]
100    fn from(c: char) -> Self {
101        CheetahString::from_string(c.to_string())
102    }
103}
104
105impl<'a> FromIterator<&'a char> for CheetahString {
106    #[inline]
107    fn from_iter<T: IntoIterator<Item = &'a char>>(iter: T) -> CheetahString {
108        let mut buf = String::new();
109        buf.extend(iter);
110        CheetahString::from_string(buf)
111    }
112}
113
114impl<'a> FromIterator<&'a str> for CheetahString {
115    fn from_iter<I: IntoIterator<Item = &'a str>>(iter: I) -> CheetahString {
116        let mut buf = String::new();
117        buf.extend(iter);
118        CheetahString::from_string(buf)
119    }
120}
121
122impl FromIterator<String> for CheetahString {
123    #[inline]
124    fn from_iter<T: IntoIterator<Item = String>>(iter: T) -> Self {
125        let mut buf = String::new();
126        buf.extend(iter);
127        CheetahString::from_string(buf)
128    }
129}
130
131impl<'a> FromIterator<&'a String> for CheetahString {
132    #[inline]
133    fn from_iter<T: IntoIterator<Item = &'a String>>(iter: T) -> Self {
134        let mut buf = String::new();
135        buf.extend(iter.into_iter().map(|s| s.as_str()));
136        CheetahString::from_string(buf)
137    }
138}
139
140#[cfg(feature = "bytes")]
141impl From<bytes::Bytes> for CheetahString {
142    #[inline]
143    fn from(b: bytes::Bytes) -> Self {
144        CheetahString::from_bytes(b)
145    }
146}
147
148impl From<&CheetahString> for CheetahString {
149    #[inline]
150    fn from(s: &CheetahString) -> Self {
151        s.clone()
152    }
153}
154
155impl From<CheetahString> for String {
156    #[inline]
157    fn from(s: CheetahString) -> Self {
158        match s {
159            CheetahString {
160                inner: InnerString::ArcString(s),
161            } => s.as_ref().clone(),
162            CheetahString {
163                inner: InnerString::StaticStr(s),
164            } => s.to_string(),
165            CheetahString {
166                inner: InnerString::ArcVecString(s),
167            } => unsafe { String::from_utf8_unchecked(s.to_vec()) },
168            #[cfg(feature = "bytes")]
169            CheetahString {
170                inner: InnerString::Bytes(b),
171            } => unsafe { String::from_utf8_unchecked(b.to_vec()) },
172            CheetahString {
173                inner: InnerString::Empty,
174            } => String::new(),
175        }
176    }
177}
178
179impl Deref for CheetahString {
180    type Target = str;
181
182    #[inline]
183    fn deref(&self) -> &Self::Target {
184        self.as_str()
185    }
186}
187
188impl AsRef<str> for CheetahString {
189    #[inline]
190    fn as_ref(&self) -> &str {
191        self.as_str()
192    }
193}
194
195impl AsRef<[u8]> for CheetahString {
196    #[inline]
197    fn as_ref(&self) -> &[u8] {
198        self.as_bytes()
199    }
200}
201
202impl AsRef<CheetahString> for CheetahString {
203    #[inline]
204    fn as_ref(&self) -> &CheetahString {
205        self
206    }
207}
208
209impl From<&String> for CheetahString {
210    #[inline]
211    fn from(s: &String) -> Self {
212        CheetahString::from_slice(s)
213    }
214}
215
216impl CheetahString {
217    #[inline]
218    pub const fn empty() -> Self {
219        CheetahString {
220            inner: InnerString::Empty,
221        }
222    }
223
224    #[inline]
225    pub fn new() -> Self {
226        CheetahString::default()
227    }
228
229    #[inline]
230    pub const fn from_static_str(s: &'static str) -> Self {
231        CheetahString {
232            inner: InnerString::StaticStr(s),
233        }
234    }
235
236    #[inline]
237    pub fn from_vec(s: Vec<u8>) -> Self {
238        CheetahString {
239            inner: InnerString::ArcVecString(Arc::new(s)),
240        }
241    }
242
243    #[inline]
244    pub fn from_arc_vec(s: Arc<Vec<u8>>) -> Self {
245        CheetahString {
246            inner: InnerString::ArcVecString(s),
247        }
248    }
249
250    #[inline]
251    pub fn from_slice(s: &str) -> Self {
252        CheetahString {
253            inner: InnerString::ArcString(Arc::new(s.to_owned())),
254        }
255    }
256
257    #[inline]
258    pub fn from_string(s: String) -> Self {
259        CheetahString {
260            inner: InnerString::ArcString(Arc::new(s)),
261        }
262    }
263    #[inline]
264    pub fn from_arc_string(s: Arc<String>) -> Self {
265        CheetahString {
266            inner: InnerString::ArcString(s),
267        }
268    }
269
270    #[inline]
271    #[cfg(feature = "bytes")]
272    pub fn from_bytes(b: bytes::Bytes) -> Self {
273        CheetahString {
274            inner: InnerString::Bytes(b),
275        }
276    }
277
278    #[inline]
279    pub fn as_str(&self) -> &str {
280        match &self.inner {
281            InnerString::ArcString(s) => s.as_str(),
282            InnerString::StaticStr(s) => s,
283            InnerString::ArcVecString(s) => std::str::from_utf8(s.as_ref()).unwrap(),
284            #[cfg(feature = "bytes")]
285            InnerString::Bytes(b) => std::str::from_utf8(b.as_ref()).unwrap(),
286            InnerString::Empty => EMPTY_STRING,
287        }
288    }
289
290    #[inline]
291    pub fn as_bytes(&self) -> &[u8] {
292        match &self.inner {
293            InnerString::ArcString(s) => s.as_bytes(),
294            InnerString::StaticStr(s) => s.as_bytes(),
295            InnerString::ArcVecString(s) => s.as_ref(),
296            #[cfg(feature = "bytes")]
297            InnerString::Bytes(b) => b.as_ref(),
298            InnerString::Empty => &[],
299        }
300    }
301
302    #[inline]
303    pub fn len(&self) -> usize {
304        match &self.inner {
305            InnerString::ArcString(s) => s.len(),
306            InnerString::StaticStr(s) => s.len(),
307            InnerString::ArcVecString(s) => s.len(),
308            #[cfg(feature = "bytes")]
309            InnerString::Bytes(b) => b.len(),
310            InnerString::Empty => 0,
311        }
312    }
313
314    #[inline]
315    pub fn is_empty(&self) -> bool {
316        match &self.inner {
317            InnerString::ArcString(s) => s.is_empty(),
318            InnerString::StaticStr(s) => s.is_empty(),
319            InnerString::ArcVecString(s) => s.is_empty(),
320            #[cfg(feature = "bytes")]
321            InnerString::Bytes(b) => b.is_empty(),
322            InnerString::Empty => true,
323        }
324    }
325}
326
327impl PartialEq for CheetahString {
328    #[inline]
329    fn eq(&self, other: &Self) -> bool {
330        self.as_str() == other.as_str()
331    }
332}
333
334impl PartialEq<str> for CheetahString {
335    #[inline]
336    fn eq(&self, other: &str) -> bool {
337        self.as_str() == other
338    }
339}
340
341impl PartialEq<String> for CheetahString {
342    #[inline]
343    fn eq(&self, other: &String) -> bool {
344        self.as_str() == other.as_str()
345    }
346}
347
348impl PartialEq<Vec<u8>> for CheetahString {
349    #[inline]
350    fn eq(&self, other: &Vec<u8>) -> bool {
351        self.as_bytes() == other.as_slice()
352    }
353}
354
355impl<'a> PartialEq<&'a str> for CheetahString {
356    #[inline]
357    fn eq(&self, other: &&'a str) -> bool {
358        self.as_str() == *other
359    }
360}
361
362impl PartialEq<CheetahString> for str {
363    #[inline]
364    fn eq(&self, other: &CheetahString) -> bool {
365        self == other.as_str()
366    }
367}
368
369impl PartialEq<CheetahString> for String {
370    #[inline]
371    fn eq(&self, other: &CheetahString) -> bool {
372        self.as_str() == other.as_str()
373    }
374}
375
376impl PartialEq<CheetahString> for &str {
377    #[inline]
378    fn eq(&self, other: &CheetahString) -> bool {
379        *self == other.as_str()
380    }
381}
382
383impl Eq for CheetahString {}
384
385impl PartialOrd for CheetahString {
386    #[inline]
387    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
388        Some(self.cmp(other))
389    }
390}
391
392impl Ord for CheetahString {
393    #[inline]
394    fn cmp(&self, other: &Self) -> Ordering {
395        self.as_str().cmp(other.as_str())
396    }
397}
398
399impl Hash for CheetahString {
400    #[inline]
401    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
402        self.as_str().hash(state);
403    }
404}
405
406impl Display for CheetahString {
407    #[inline]
408    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
409        self.as_str().fmt(f)
410    }
411}
412
413impl std::fmt::Debug for CheetahString {
414    #[inline]
415    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
416        fmt::Debug::fmt(self.as_str(), f)
417    }
418}
419
420impl Borrow<str> for CheetahString {
421    #[inline]
422    fn borrow(&self) -> &str {
423        self.as_str()
424    }
425}
426
427/// The `InnerString` enum represents different types of string storage.
428///
429/// Variants:
430///
431/// * `ArcString(Arc<String>)` - A reference-counted string.
432/// * `StaticStr(&'static str)` - A static string slice.
433/// * `Bytes(bytes::Bytes)` - A byte buffer (available when the "bytes" feature is enabled).
434/// * `Empty` - An empty string.
435#[derive(Clone)]
436pub(super) enum InnerString {
437    ArcString(Arc<String>),
438    StaticStr(&'static str),
439    ArcVecString(Arc<Vec<u8>>),
440    #[cfg(feature = "bytes")]
441    Bytes(bytes::Bytes),
442    Empty,
443}