Skip to main content

cheetah_string/
cheetah_str.rs

1use alloc::borrow::Cow;
2use alloc::string::{ParseError, String, ToString};
3use alloc::sync::Arc;
4use core::borrow::Borrow;
5use core::cmp::Ordering;
6use core::fmt;
7use core::hash::{Hash, Hasher};
8use core::ops::Deref;
9use core::str::FromStr;
10
11use crate::inline::InlineStr;
12use crate::CheetahString;
13
14/// Immutable, clone-cheap string value for key/name/topic style workloads.
15///
16/// `CheetahStr` intentionally has no mutation API. Use [`CheetahBuilder`] or
17/// [`CheetahString`] when the value is still being constructed.
18///
19/// [`CheetahBuilder`]: crate::CheetahBuilder
20#[derive(Clone)]
21pub struct CheetahStr {
22    inner: Repr,
23}
24
25#[derive(Clone)]
26enum Repr {
27    Inline(InlineStr),
28    Static(&'static str),
29    Shared(Arc<str>),
30}
31
32impl CheetahStr {
33    /// Creates an empty immutable string.
34    #[inline]
35    pub const fn empty() -> Self {
36        Self {
37            inner: Repr::Inline(InlineStr::empty()),
38        }
39    }
40
41    /// Creates an empty immutable string.
42    #[inline]
43    pub fn new() -> Self {
44        Self::empty()
45    }
46
47    /// Creates a zero-copy immutable string from a static string slice.
48    #[inline]
49    pub const fn from_static_str(s: &'static str) -> Self {
50        Self {
51            inner: Repr::Static(s),
52        }
53    }
54
55    /// Creates an immutable string from a borrowed string slice.
56    #[inline]
57    pub fn from_slice(s: &str) -> Self {
58        if let Some(inline) = InlineStr::from_str(s) {
59            Self {
60                inner: Repr::Inline(inline),
61            }
62        } else {
63            Self {
64                inner: Repr::Shared(Arc::from(s)),
65            }
66        }
67    }
68
69    /// Creates an immutable string from owned storage.
70    #[inline]
71    pub fn from_string(s: String) -> Self {
72        if let Some(inline) = InlineStr::from_str(&s) {
73            Self {
74                inner: Repr::Inline(inline),
75            }
76        } else {
77            Self {
78                inner: Repr::Shared(s.into_boxed_str().into()),
79            }
80        }
81    }
82
83    /// Returns the string slice.
84    #[inline]
85    pub fn as_str(&self) -> &str {
86        match &self.inner {
87            Repr::Inline(inline) => inline.as_str(),
88            Repr::Static(s) => s,
89            Repr::Shared(s) => s.as_ref(),
90        }
91    }
92
93    /// Returns the UTF-8 bytes.
94    #[inline]
95    pub fn as_bytes(&self) -> &[u8] {
96        self.as_str().as_bytes()
97    }
98
99    /// Returns the byte length.
100    #[inline]
101    pub fn len(&self) -> usize {
102        self.as_str().len()
103    }
104
105    /// Returns whether this string is empty.
106    #[inline]
107    pub fn is_empty(&self) -> bool {
108        self.as_str().is_empty()
109    }
110}
111
112impl Default for CheetahStr {
113    #[inline]
114    fn default() -> Self {
115        Self::empty()
116    }
117}
118
119impl From<&str> for CheetahStr {
120    #[inline]
121    fn from(value: &str) -> Self {
122        Self::from_slice(value)
123    }
124}
125
126impl From<String> for CheetahStr {
127    #[inline]
128    fn from(value: String) -> Self {
129        Self::from_string(value)
130    }
131}
132
133impl From<Cow<'static, str>> for CheetahStr {
134    #[inline]
135    fn from(value: Cow<'static, str>) -> Self {
136        match value {
137            Cow::Borrowed(s) => Self::from_static_str(s),
138            Cow::Owned(s) => Self::from_string(s),
139        }
140    }
141}
142
143impl From<&CheetahString> for CheetahStr {
144    #[inline]
145    fn from(value: &CheetahString) -> Self {
146        Self::from_slice(value.as_str())
147    }
148}
149
150impl From<CheetahString> for CheetahStr {
151    #[inline]
152    fn from(value: CheetahString) -> Self {
153        Self::from_string(String::from(value))
154    }
155}
156
157impl From<CheetahStr> for String {
158    #[inline]
159    fn from(value: CheetahStr) -> Self {
160        value.as_str().to_string()
161    }
162}
163
164impl FromStr for CheetahStr {
165    type Err = ParseError;
166
167    #[inline]
168    fn from_str(s: &str) -> Result<Self, Self::Err> {
169        Ok(Self::from_slice(s))
170    }
171}
172
173impl Deref for CheetahStr {
174    type Target = str;
175
176    #[inline]
177    fn deref(&self) -> &Self::Target {
178        self.as_str()
179    }
180}
181
182impl AsRef<str> for CheetahStr {
183    #[inline]
184    fn as_ref(&self) -> &str {
185        self.as_str()
186    }
187}
188
189impl AsRef<[u8]> for CheetahStr {
190    #[inline]
191    fn as_ref(&self) -> &[u8] {
192        self.as_bytes()
193    }
194}
195
196impl Borrow<str> for CheetahStr {
197    #[inline]
198    fn borrow(&self) -> &str {
199        self.as_str()
200    }
201}
202
203impl fmt::Display for CheetahStr {
204    #[inline]
205    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
206        self.as_str().fmt(f)
207    }
208}
209
210impl fmt::Debug for CheetahStr {
211    #[inline]
212    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213        fmt::Debug::fmt(self.as_str(), f)
214    }
215}
216
217impl Hash for CheetahStr {
218    #[inline]
219    fn hash<H: Hasher>(&self, state: &mut H) {
220        self.as_str().hash(state);
221    }
222}
223
224impl PartialEq for CheetahStr {
225    #[inline]
226    fn eq(&self, other: &Self) -> bool {
227        self.as_str() == other.as_str()
228    }
229}
230
231impl PartialEq<str> for CheetahStr {
232    #[inline]
233    fn eq(&self, other: &str) -> bool {
234        self.as_str() == other
235    }
236}
237
238impl PartialEq<&str> for CheetahStr {
239    #[inline]
240    fn eq(&self, other: &&str) -> bool {
241        self.as_str() == *other
242    }
243}
244
245impl PartialEq<CheetahStr> for str {
246    #[inline]
247    fn eq(&self, other: &CheetahStr) -> bool {
248        self == other.as_str()
249    }
250}
251
252impl PartialEq<CheetahStr> for &str {
253    #[inline]
254    fn eq(&self, other: &CheetahStr) -> bool {
255        *self == other.as_str()
256    }
257}
258
259impl Eq for CheetahStr {}
260
261impl PartialOrd for CheetahStr {
262    #[inline]
263    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
264        Some(self.cmp(other))
265    }
266}
267
268impl Ord for CheetahStr {
269    #[inline]
270    fn cmp(&self, other: &Self) -> Ordering {
271        self.as_str().cmp(other.as_str())
272    }
273}