1use std::{borrow::Cow, fmt};
2
3use crate::inline::*;
4use crate::KStringCow;
5use crate::KStringRef;
6
7pub(crate) type StdString = std::string::String;
8type BoxedStr = Box<str>;
9#[cfg(feature = "arc")]
10pub(crate) type OwnedStr = std::sync::Arc<str>;
11#[cfg(not(feature = "arc"))]
12pub(crate) type OwnedStr = Box<str>;
13
14#[derive(Clone)]
16#[repr(transparent)]
17pub struct KString {
18 pub(crate) inner: KStringInner,
19}
20
21#[derive(Debug)]
22pub(crate) enum KStringInner {
23 Singleton(&'static str),
24 Inline(InlineString),
25 Owned(OwnedStr),
26}
27
28impl KString {
29 #[inline]
31 pub fn new() -> Self {
32 Default::default()
33 }
34
35 #[inline]
37 pub fn from_boxed(other: BoxedStr) -> Self {
38 Self {
39 inner: KStringInner::Owned(OwnedStr::from(other)),
40 }
41 }
42
43 #[inline]
45 pub fn from_string(other: StdString) -> Self {
46 let inner = if (0..=CAPACITY).contains(&other.len()) {
47 KStringInner::Inline(InlineString::new(other.as_str()))
48 } else {
49 KStringInner::Owned(OwnedStr::from(other.into_boxed_str()))
50 };
51 Self { inner }
52 }
53
54 #[inline]
56 pub fn from_ref(other: &str) -> Self {
57 let inner = if (0..=CAPACITY).contains(&other.len()) {
58 KStringInner::Inline(InlineString::new(other))
59 } else {
60 KStringInner::Owned(OwnedStr::from(other))
61 };
62 Self { inner }
63 }
64
65 #[inline]
67 pub fn from_static(other: &'static str) -> Self {
68 Self {
69 inner: KStringInner::Singleton(other),
70 }
71 }
72
73 #[inline]
75 pub fn as_ref(&self) -> KStringRef<'_> {
76 self.inner.as_ref()
77 }
78
79 #[inline]
81 pub fn as_str(&self) -> &str {
82 self.inner.as_str()
83 }
84
85 #[inline]
87 pub fn into_string(self) -> StdString {
88 String::from(self.into_boxed_str())
89 }
90
91 #[inline]
93 pub fn into_boxed_str(self) -> BoxedStr {
94 self.inner.into_boxed_str()
95 }
96
97 #[inline]
99 pub fn into_cow_str(self) -> Cow<'static, str> {
100 self.inner.into_cow_str()
101 }
102}
103
104impl KStringInner {
105 #[inline]
106 fn as_ref(&self) -> KStringRef<'_> {
107 match self {
108 Self::Singleton(s) => KStringRef::from_static(s),
109 Self::Inline(s) => KStringRef::from_ref(s.as_str()),
110 Self::Owned(s) => KStringRef::from_ref(s),
111 }
112 }
113
114 #[inline]
115 fn as_str(&self) -> &str {
116 match self {
117 Self::Singleton(s) => s,
118 Self::Inline(s) => s.as_str(),
119 Self::Owned(s) => &s,
120 }
121 }
122
123 #[inline]
124 fn into_boxed_str(self) -> BoxedStr {
125 match self {
126 Self::Singleton(s) => BoxedStr::from(s),
127 Self::Inline(s) => s.to_boxed_str(),
128 Self::Owned(s) => BoxedStr::from(s.as_ref()),
129 }
130 }
131
132 #[inline]
134 fn into_cow_str(self) -> Cow<'static, str> {
135 match self {
136 Self::Singleton(s) => Cow::Borrowed(s),
137 Self::Inline(s) => Cow::Owned(s.to_boxed_str().into()),
138 Self::Owned(s) => Cow::Owned(s.as_ref().into()),
139 }
140 }
141}
142
143impl Clone for KStringInner {
152 fn clone(&self) -> Self {
153 match self {
154 Self::Singleton(s) => Self::Singleton(s),
155 Self::Inline(s) => Self::Inline(*s),
156 Self::Owned(s) => Self::Owned(s.clone()),
157 }
158 }
159}
160
161impl std::ops::Deref for KString {
162 type Target = str;
163
164 #[inline]
165 fn deref(&self) -> &str {
166 self.as_str()
167 }
168}
169
170impl Eq for KString {}
171
172impl<'s> PartialEq<KString> for KString {
173 #[inline]
174 fn eq(&self, other: &KString) -> bool {
175 PartialEq::eq(self.as_str(), other.as_str())
176 }
177}
178
179impl<'s> PartialEq<str> for KString {
180 #[inline]
181 fn eq(&self, other: &str) -> bool {
182 PartialEq::eq(self.as_str(), other)
183 }
184}
185
186impl<'s> PartialEq<&'s str> for KString {
187 #[inline]
188 fn eq(&self, other: &&str) -> bool {
189 PartialEq::eq(self.as_str(), *other)
190 }
191}
192
193impl<'s> PartialEq<String> for KString {
194 #[inline]
195 fn eq(&self, other: &StdString) -> bool {
196 PartialEq::eq(self.as_str(), other.as_str())
197 }
198}
199
200impl Ord for KString {
201 #[inline]
202 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
203 self.as_str().cmp(other.as_str())
204 }
205}
206
207impl PartialOrd for KString {
208 #[inline]
209 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
210 self.as_str().partial_cmp(other.as_str())
211 }
212}
213
214impl std::hash::Hash for KString {
215 #[inline]
216 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
217 self.as_str().hash(state);
218 }
219}
220
221impl fmt::Debug for KString {
222 #[inline]
223 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
224 fmt::Debug::fmt(&self.inner, f)
225 }
226}
227
228impl fmt::Display for KString {
229 #[inline]
230 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
231 fmt::Display::fmt(self.as_str(), f)
232 }
233}
234
235impl AsRef<str> for KString {
236 #[inline]
237 fn as_ref(&self) -> &str {
238 self.as_str()
239 }
240}
241
242impl AsRef<[u8]> for KString {
243 #[inline]
244 fn as_ref(&self) -> &[u8] {
245 self.as_bytes()
246 }
247}
248
249impl AsRef<std::ffi::OsStr> for KString {
250 #[inline]
251 fn as_ref(&self) -> &std::ffi::OsStr {
252 (&**self).as_ref()
253 }
254}
255
256impl AsRef<std::path::Path> for KString {
257 #[inline]
258 fn as_ref(&self) -> &std::path::Path {
259 std::path::Path::new(self)
260 }
261}
262
263impl std::borrow::Borrow<str> for KString {
264 #[inline]
265 fn borrow(&self) -> &str {
266 self.as_str()
267 }
268}
269
270impl Default for KString {
271 #[inline]
272 fn default() -> Self {
273 Self::from_static("")
274 }
275}
276
277impl<'s> From<KStringRef<'s>> for KString {
278 #[inline]
279 fn from(other: KStringRef<'s>) -> Self {
280 other.to_owned()
281 }
282}
283
284impl<'s> From<&'s KStringRef<'s>> for KString {
285 #[inline]
286 fn from(other: &'s KStringRef<'s>) -> Self {
287 other.to_owned()
288 }
289}
290
291impl<'s> From<KStringCow<'s>> for KString {
292 #[inline]
293 fn from(other: KStringCow<'s>) -> Self {
294 other.into_owned()
295 }
296}
297
298impl<'s> From<&'s KStringCow<'s>> for KString {
299 #[inline]
300 fn from(other: &'s KStringCow<'s>) -> Self {
301 other.clone().into_owned()
302 }
303}
304
305impl From<StdString> for KString {
306 #[inline]
307 fn from(other: StdString) -> Self {
308 Self::from_string(other)
309 }
310}
311
312impl<'s> From<&'s StdString> for KString {
313 #[inline]
314 fn from(other: &'s StdString) -> Self {
315 Self::from_ref(other)
316 }
317}
318
319impl From<BoxedStr> for KString {
320 #[inline]
321 fn from(other: BoxedStr) -> Self {
322 Self::from_boxed(other)
323 }
324}
325
326impl<'s> From<&'s BoxedStr> for KString {
327 #[inline]
328 fn from(other: &'s BoxedStr) -> Self {
329 Self::from_ref(other)
330 }
331}
332
333impl From<&'static str> for KString {
334 #[inline]
335 fn from(other: &'static str) -> Self {
336 Self::from_static(other)
337 }
338}
339
340impl std::str::FromStr for KString {
341 type Err = std::convert::Infallible;
342 #[inline]
343 fn from_str(s: &str) -> Result<Self, Self::Err> {
344 Ok(KString::from_ref(s))
345 }
346}
347
348#[cfg(feature = "serde")]
349impl serde::Serialize for KString {
350 #[inline]
351 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
352 where
353 S: serde::Serializer,
354 {
355 serializer.serialize_str(self.as_str())
356 }
357}
358
359#[cfg(feature = "serde")]
360impl<'de> serde::Deserialize<'de> for KString {
361 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
362 where
363 D: serde::Deserializer<'de>,
364 {
365 deserializer.deserialize_string(StringVisitor)
366 }
367}
368
369#[cfg(feature = "serde")]
370struct StringVisitor;
371
372#[cfg(feature = "serde")]
373impl<'de> serde::de::Visitor<'de> for StringVisitor {
374 type Value = KString;
375
376 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
377 formatter.write_str("a string")
378 }
379
380 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
381 where
382 E: serde::de::Error,
383 {
384 Ok(KString::from_ref(v))
385 }
386
387 fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
388 where
389 E: serde::de::Error,
390 {
391 Ok(KString::from_string(v))
392 }
393
394 fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
395 where
396 E: serde::de::Error,
397 {
398 match std::str::from_utf8(v) {
399 Ok(s) => Ok(KString::from_ref(s)),
400 Err(_) => Err(serde::de::Error::invalid_value(
401 serde::de::Unexpected::Bytes(v),
402 &self,
403 )),
404 }
405 }
406
407 fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
408 where
409 E: serde::de::Error,
410 {
411 match String::from_utf8(v) {
412 Ok(s) => Ok(KString::from_string(s)),
413 Err(e) => Err(serde::de::Error::invalid_value(
414 serde::de::Unexpected::Bytes(&e.into_bytes()),
415 &self,
416 )),
417 }
418 }
419}
420
421#[cfg(test)]
422mod test {
423 use super::*;
424
425 #[test]
426 fn test_size() {
427 println!("KString: {}", std::mem::size_of::<KString>());
428 }
429}