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