scuffle_bytes_util/cow/
string.rs1use std::borrow::Cow;
2use std::fmt::{Debug, Display};
3use std::hash::Hash;
4use std::str::Utf8Error;
5
6use bytestring::ByteString;
7
8use crate::BytesCow;
9
10#[cfg(feature = "serde")]
11pub(crate) mod serde;
12
13#[derive(Clone, Eq)]
15pub enum StringCow<'a> {
16 Ref(&'a str),
18 StaticRef(&'static str),
20 String(String),
22 Bytes(ByteString),
24}
25
26impl Debug for StringCow<'_> {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 Debug::fmt(self.as_str(), f)
29 }
30}
31
32impl Default for StringCow<'_> {
33 fn default() -> Self {
34 Self::new()
35 }
36}
37
38impl<'a> StringCow<'a> {
39 pub fn new() -> Self {
41 Self::from_static("")
42 }
43
44 pub fn from_static(slice: &'static str) -> Self {
46 StringCow::StaticRef(slice)
47 }
48
49 pub fn from_bytes(bytes: ByteString) -> Self {
51 StringCow::Bytes(bytes)
52 }
53
54 pub fn from_cow(cow: Cow<'a, str>) -> Self {
56 match cow {
57 Cow::Borrowed(slice) => StringCow::Ref(slice),
58 Cow::Owned(string) => StringCow::String(string),
59 }
60 }
61
62 pub fn from_ref(slice: &'a str) -> Self {
64 StringCow::Ref(slice)
65 }
66
67 pub fn from_string(string: String) -> Self {
69 StringCow::String(string)
70 }
71
72 pub fn into_bytes(self) -> ByteString {
74 match self {
75 StringCow::Ref(slice) => ByteString::from(slice),
76 StringCow::StaticRef(slice) => ByteString::from_static(slice),
77 StringCow::String(string) => ByteString::from(string),
78 StringCow::Bytes(bytes) => bytes,
79 }
80 }
81
82 pub fn into_owned(self) -> StringCow<'static> {
84 match self {
85 StringCow::Ref(slice) => StringCow::from(slice.to_owned()),
86 StringCow::StaticRef(slice) => StringCow::StaticRef(slice),
87 StringCow::String(string) => StringCow::String(string),
88 StringCow::Bytes(bytes) => StringCow::Bytes(bytes),
89 }
90 }
91
92 pub fn as_str(&self) -> &str {
94 match self {
95 StringCow::Ref(slice) => slice,
96 StringCow::StaticRef(slice) => slice,
97 StringCow::String(string) => string.as_str(),
98 StringCow::Bytes(bytes) => bytes.as_ref(),
99 }
100 }
101
102 pub fn len(&self) -> usize {
104 match self {
105 StringCow::Ref(slice) => slice.len(),
106 StringCow::StaticRef(slice) => slice.len(),
107 StringCow::String(string) => string.len(),
108 StringCow::Bytes(bytes) => bytes.len(),
109 }
110 }
111
112 pub fn is_empty(&self) -> bool {
114 match self {
115 Self::Ref(slice) => slice.is_empty(),
116 Self::StaticRef(slice) => slice.is_empty(),
117 Self::String(string) => string.is_empty(),
118 Self::Bytes(bytes) => bytes.is_empty(),
119 }
120 }
121}
122
123impl PartialEq<str> for StringCow<'_> {
124 fn eq(&self, other: &str) -> bool {
125 self.as_str() == other
126 }
127}
128
129impl Hash for StringCow<'_> {
130 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
131 self.as_str().hash(state);
132 }
133}
134
135impl PartialOrd for StringCow<'_> {
136 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
137 Some(self.cmp(other))
138 }
139}
140
141impl Ord for StringCow<'_> {
142 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
143 self.as_str().cmp(other.as_str())
144 }
145}
146
147impl<T> PartialEq<T> for StringCow<'_>
148where
149 T: AsRef<str>,
150{
151 fn eq(&self, other: &T) -> bool {
152 self.as_str() == other.as_ref()
153 }
154}
155
156impl Display for StringCow<'_> {
157 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
158 Display::fmt(self.as_str(), f)
159 }
160}
161
162impl AsRef<str> for StringCow<'_> {
163 fn as_ref(&self) -> &str {
164 self.as_str()
165 }
166}
167
168impl<'a> From<Cow<'a, str>> for StringCow<'a> {
169 fn from(cow: Cow<'a, str>) -> Self {
170 StringCow::from_cow(cow)
171 }
172}
173
174impl<'a> From<&'a str> for StringCow<'a> {
175 fn from(slice: &'a str) -> Self {
176 StringCow::from_ref(slice)
177 }
178}
179
180impl From<String> for StringCow<'_> {
181 fn from(string: String) -> Self {
182 StringCow::from_string(string)
183 }
184}
185
186impl From<ByteString> for StringCow<'_> {
187 fn from(bytes: ByteString) -> Self {
188 StringCow::from_bytes(bytes)
189 }
190}
191
192impl<'a> TryFrom<BytesCow<'a>> for StringCow<'a> {
193 type Error = Utf8Error;
194
195 fn try_from(value: BytesCow<'a>) -> Result<Self, Self::Error> {
196 let bytes = ByteString::try_from(value.into_bytes())?;
197 Ok(Self::from_bytes(bytes))
198 }
199}
200
201#[cfg(test)]
202#[cfg_attr(all(test, coverage_nightly), coverage(off))]
203mod tests {
204 use bytestring::ByteString;
205
206 use super::StringCow;
207
208 #[test]
209 fn constructors() {
210 let cow = StringCow::default();
211 assert_eq!(cow.as_str(), "");
212
213 let cow = StringCow::from_static("hello");
214 assert_eq!(cow.as_str(), "hello");
215
216 let cow = StringCow::from_ref("world");
217 assert_eq!(cow.as_str(), "world");
218
219 let cow = StringCow::from_string(String::from("foo"));
220 assert_eq!(cow.as_str(), "foo");
221 let cow = StringCow::from(String::from("bar"));
222 assert_eq!(cow.as_str(), "bar");
223
224 let cow = StringCow::from_bytes(ByteString::from_static("foo"));
225 assert_eq!(cow.as_str(), "foo");
226 let cow = StringCow::from(ByteString::from_static("foo"));
227 assert_eq!(cow.as_str(), "foo");
228
229 let cow = StringCow::from_cow(std::borrow::Cow::Borrowed("bar"));
230 assert_eq!(cow.as_str(), "bar");
231 let cow = StringCow::from_cow(std::borrow::Cow::Owned(String::from("baz")));
232 assert_eq!(cow.as_str(), "baz");
233 let cow = StringCow::from(std::borrow::Cow::Owned(String::from("qux")));
234 assert_eq!(cow.as_str(), "qux");
235 }
236
237 #[test]
238 fn into_bytes() {
239 let cow = StringCow::from_static("hello");
240 assert_eq!(cow.into_bytes(), ByteString::from_static("hello"));
241
242 let cow = StringCow::from_ref("world");
243 assert_eq!(cow.into_bytes(), ByteString::from_static("world"));
244
245 let cow = StringCow::from_string(String::from("foo"));
246 assert_eq!(cow.into_bytes(), ByteString::from_static("foo"));
247
248 let cow = StringCow::from_bytes(ByteString::from_static("foo"));
249 assert_eq!(cow.into_bytes(), ByteString::from_static("foo"));
250
251 let cow = StringCow::from_cow(std::borrow::Cow::Borrowed("bar"));
252 assert_eq!(cow.into_bytes(), ByteString::from_static("bar"));
253
254 let cow = StringCow::from_cow(std::borrow::Cow::Owned(String::from("baz")));
255 assert_eq!(cow.into_bytes(), ByteString::from_static("baz"));
256 }
257
258 #[test]
259 fn as_ref() {
260 let cow = StringCow::from_static("hello");
261 assert_eq!(cow.as_ref(), "hello");
262
263 let cow = StringCow::from_ref("world");
264 assert_eq!(cow.as_ref(), "world");
265
266 let cow = StringCow::from_string(String::from("foo"));
267 assert_eq!(cow.as_ref(), "foo");
268
269 let cow = StringCow::from_bytes(ByteString::from_static("foo"));
270 assert_eq!(cow.as_ref(), "foo");
271 }
272
273 #[test]
274 fn into_owned() {
275 let cow = StringCow::from_static("hello");
276 assert_eq!(cow.into_owned().as_str(), "hello");
277
278 let cow = StringCow::from_ref("world");
279 assert_eq!(cow.into_owned().as_str(), "world");
280
281 let cow = StringCow::from_string(String::from("foo"));
282 assert_eq!(cow.into_owned().as_str(), "foo");
283
284 let cow = StringCow::from_bytes(ByteString::from_static("foo"));
285 assert_eq!(cow.into_owned().as_str(), "foo");
286 }
287
288 #[test]
289 fn partial_eq() {
290 let cow = StringCow::from_static("hello");
291 assert!(cow == "hello");
292 assert!(cow != "world");
293
294 let cow = StringCow::from_ref("world");
295 assert!(cow == "world");
296 assert!(cow != "hello");
297
298 let cow = StringCow::from_string(String::from("foo"));
299 assert!(cow == "foo");
300 assert!(cow != "bar");
301 }
302
303 #[test]
304 fn hash() {
305 use std::collections::hash_map::DefaultHasher;
306 use std::hash::{Hash, Hasher};
307
308 let mut hasher = DefaultHasher::new();
309 "hello".hash(&mut hasher);
310 let expected_hash = hasher.finish();
311
312 let cow = StringCow::from_static("hello");
313 let mut hasher = DefaultHasher::new();
314 cow.hash(&mut hasher);
315 assert_eq!(hasher.finish(), expected_hash);
316 }
317
318 #[test]
319 fn partial_ord() {
320 let cow1 = StringCow::from_static("hello");
321 let cow2 = StringCow::from_static("world");
322 assert!(cow1 < cow2);
323
324 let cow3 = StringCow::from_ref("foo");
325 let cow4 = StringCow::from_string(String::from("bar"));
326 assert!(cow3 > cow4);
327 }
328
329 #[test]
330 fn display() {
331 let cow = StringCow::from_ref("hello");
332 let fmt = format!("{cow}");
333 assert_eq!(fmt, "hello");
334
335 let cow = StringCow::from_static("hello");
336 let fmt = format!("{cow}");
337 assert_eq!(fmt, "hello");
338
339 let cow = StringCow::from_string(String::from("world"));
340 let fmt = format!("{cow}");
341 assert_eq!(fmt, "world");
342
343 let cow = StringCow::from_bytes(ByteString::from_static("foo"));
344 let fmt = format!("{cow}");
345 assert_eq!(fmt, "foo");
346 }
347}