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