1use serde::{Deserialize, Serialize};
2use smol_str::SmolStr;
3use std::{
4 borrow::Cow,
5 fmt,
6 hash::{Hash, Hasher},
7 ops::Deref,
8};
9
10use crate::IntoStatic;
11
12/// Shamelessly copied from https://github.com/bearcove/merde
13/// A copy-on-write immutable string type that uses [`SmolStr`] for
14/// the "owned" variant.
15///
16/// The standard [`Cow`] type cannot be used, since
17/// `<str as ToOwned>::Owned` is `String`, and not `SmolStr`.
18#[derive(Clone)]
19pub enum CowStr<'s> {
20 Borrowed(&'s str),
21 Owned(SmolStr),
22}
23
24impl CowStr<'static> {
25 /// Create a new `CowStr` by copying from a `&str` — this might allocate
26 /// if the `compact_str` feature is disabled, or if the string is longer
27 /// than `MAX_INLINE_SIZE`.
28 pub fn copy_from_str(s: &str) -> Self {
29 Self::Owned(SmolStr::from(s))
30 }
31
32 pub fn new_static(s: &'static str) -> Self {
33 Self::Owned(SmolStr::new_static(s))
34 }
35}
36
37impl<'s> CowStr<'s> {
38 #[inline]
39 pub fn from_utf8(s: &'s [u8]) -> Result<Self, std::str::Utf8Error> {
40 Ok(Self::Borrowed(std::str::from_utf8(s)?))
41 }
42
43 #[inline]
44 pub fn from_utf8_owned(s: Vec<u8>) -> Result<Self, std::str::Utf8Error> {
45 Ok(Self::Owned(SmolStr::new(std::str::from_utf8(&s)?)))
46 }
47
48 #[inline]
49 pub fn from_utf8_lossy(s: &'s [u8]) -> Self {
50 Self::Owned(String::from_utf8_lossy(&s).into())
51 }
52
53 /// # Safety
54 ///
55 /// This function is unsafe because it does not check that the bytes are valid UTF-8.
56 #[inline]
57 pub unsafe fn from_utf8_unchecked(s: &'s [u8]) -> Self {
58 unsafe { Self::Owned(SmolStr::new(std::str::from_utf8_unchecked(s))) }
59 }
60}
61
62impl AsRef<str> for CowStr<'_> {
63 #[inline]
64 fn as_ref(&self) -> &str {
65 match self {
66 CowStr::Borrowed(s) => s,
67 CowStr::Owned(s) => s.as_str(),
68 }
69 }
70}
71
72impl Deref for CowStr<'_> {
73 type Target = str;
74
75 #[inline]
76 fn deref(&self) -> &Self::Target {
77 match self {
78 CowStr::Borrowed(s) => s,
79 CowStr::Owned(s) => s.as_str(),
80 }
81 }
82}
83
84impl<'a> From<Cow<'a, str>> for CowStr<'a> {
85 #[inline]
86 fn from(s: Cow<'a, str>) -> Self {
87 match s {
88 Cow::Borrowed(s) => CowStr::Borrowed(s),
89 #[allow(clippy::useless_conversion)]
90 Cow::Owned(s) => CowStr::Owned(s.into()),
91 }
92 }
93}
94
95impl<'s> From<&'s str> for CowStr<'s> {
96 #[inline]
97 fn from(s: &'s str) -> Self {
98 CowStr::Borrowed(s)
99 }
100}
101
102impl From<String> for CowStr<'_> {
103 #[inline]
104 fn from(s: String) -> Self {
105 #[allow(clippy::useless_conversion)]
106 CowStr::Owned(s.into())
107 }
108}
109
110impl From<Box<str>> for CowStr<'_> {
111 #[inline]
112 fn from(s: Box<str>) -> Self {
113 CowStr::Owned(s.into())
114 }
115}
116
117impl<'s> From<&'s String> for CowStr<'s> {
118 #[inline]
119 fn from(s: &'s String) -> Self {
120 CowStr::Borrowed(s.as_str())
121 }
122}
123
124impl From<CowStr<'_>> for String {
125 #[inline]
126 fn from(s: CowStr<'_>) -> Self {
127 match s {
128 CowStr::Borrowed(s) => s.into(),
129 #[allow(clippy::useless_conversion)]
130 CowStr::Owned(s) => s.into(),
131 }
132 }
133}
134
135impl From<CowStr<'_>> for Box<str> {
136 #[inline]
137 fn from(s: CowStr<'_>) -> Self {
138 match s {
139 CowStr::Borrowed(s) => s.into(),
140 CowStr::Owned(s) => String::from(s).into_boxed_str(),
141 }
142 }
143}
144
145impl<'a> PartialEq<CowStr<'a>> for CowStr<'_> {
146 #[inline]
147 fn eq(&self, other: &CowStr<'a>) -> bool {
148 self.deref() == other.deref()
149 }
150}
151
152impl PartialEq<&str> for CowStr<'_> {
153 #[inline]
154 fn eq(&self, other: &&str) -> bool {
155 self.deref() == *other
156 }
157}
158
159impl PartialEq<CowStr<'_>> for &str {
160 #[inline]
161 fn eq(&self, other: &CowStr<'_>) -> bool {
162 *self == other.deref()
163 }
164}
165
166impl PartialEq<String> for CowStr<'_> {
167 #[inline]
168 fn eq(&self, other: &String) -> bool {
169 self.deref() == other.as_str()
170 }
171}
172
173impl PartialEq<CowStr<'_>> for String {
174 #[inline]
175 fn eq(&self, other: &CowStr<'_>) -> bool {
176 self.as_str() == other.deref()
177 }
178}
179
180impl Eq for CowStr<'_> {}
181
182impl Hash for CowStr<'_> {
183 #[inline]
184 fn hash<H: Hasher>(&self, state: &mut H) {
185 self.deref().hash(state)
186 }
187}
188
189impl fmt::Debug for CowStr<'_> {
190 #[inline]
191 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192 self.deref().fmt(f)
193 }
194}
195
196impl fmt::Display for CowStr<'_> {
197 #[inline]
198 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199 self.deref().fmt(f)
200 }
201}
202
203impl IntoStatic for CowStr<'_> {
204 type Output = CowStr<'static>;
205
206 #[inline]
207 fn into_static(self) -> Self::Output {
208 match self {
209 CowStr::Borrowed(s) => CowStr::Owned((*s).into()),
210 CowStr::Owned(s) => CowStr::Owned(s),
211 }
212 }
213}
214
215impl Serialize for CowStr<'_> {
216 #[inline]
217 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
218 where
219 S: serde::Serializer,
220 {
221 serializer.serialize_str(self)
222 }
223}
224
225impl<'de: 'a, 'a> Deserialize<'de> for CowStr<'a> {
226 #[inline]
227 fn deserialize<D>(deserializer: D) -> Result<CowStr<'a>, D::Error>
228 where
229 D: serde::Deserializer<'de>,
230 {
231 struct CowStrVisitor;
232
233 impl<'de> serde::de::Visitor<'de> for CowStrVisitor {
234 type Value = CowStr<'de>;
235
236 #[inline]
237 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
238 write!(formatter, "a string")
239 }
240
241 #[inline]
242 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
243 where
244 E: serde::de::Error,
245 {
246 Ok(CowStr::copy_from_str(v))
247 }
248
249 #[inline]
250 fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
251 where
252 E: serde::de::Error,
253 {
254 Ok(CowStr::Borrowed(v))
255 }
256
257 #[inline]
258 fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
259 where
260 E: serde::de::Error,
261 {
262 Ok(v.into())
263 }
264 }
265
266 deserializer.deserialize_str(CowStrVisitor)
267 }
268}
269
270#[cfg(test)]
271mod tests {
272 use super::*;
273
274 #[test]
275 fn test_partialeq_with_str() {
276 let cow_str1 = CowStr::Borrowed("hello");
277 let cow_str2 = CowStr::Borrowed("hello");
278 let cow_str3 = CowStr::Borrowed("world");
279
280 assert_eq!(cow_str1, "hello");
281 assert_eq!("hello", cow_str1);
282 assert_eq!(cow_str1, cow_str2);
283 assert_ne!(cow_str1, "world");
284 assert_ne!("world", cow_str1);
285 assert_ne!(cow_str1, cow_str3);
286 }
287}