scuffle_bytes_util/cow/string/
mod.rs

1use std::borrow::{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/// A [`Cow`] type for strings.
11#[derive(Debug, Clone, Eq)]
12pub enum StringCow<'a> {
13    /// A borrowed [`ByteString`] object.
14    Ref(&'a str),
15    /// A staticly borrowed [`ByteString`] object.
16    StaticRef(&'static str),
17    /// An owned [`String`] object.
18    String(String),
19    /// An owned [`ByteString`] object.
20    Bytes(ByteString),
21}
22
23impl Default for StringCow<'_> {
24    fn default() -> Self {
25        Self::new()
26    }
27}
28
29impl<'a> StringCow<'a> {
30    /// Creates an empty [`StringCow`] object.
31    pub fn new() -> Self {
32        Self::from_static("")
33    }
34
35    /// Creates a new [`StringCow`] from a static slice.
36    pub fn from_static(slice: &'static str) -> Self {
37        StringCow::StaticRef(slice)
38    }
39
40    /// Creates a new [`StringCow`] from a [`ByteString`] object.
41    pub fn from_bytes(bytes: ByteString) -> Self {
42        StringCow::Bytes(bytes)
43    }
44
45    /// Creates a new [`StringCow`] from a [`Cow`] of a [`str`] object.
46    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    /// Creates a new [`StringCow`] from a static slice.
54    pub fn from_ref(slice: &'a str) -> Self {
55        StringCow::Ref(slice)
56    }
57
58    /// Creates a new [`StringCow`] from a [`String`] object.
59    pub fn from_string(string: String) -> Self {
60        StringCow::String(string)
61    }
62
63    /// Converts the object into a [`ByteString`] object.
64    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    /// Converts this [`StringCow`] into an owned version with a static lifetime.
74    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    /// Returns a reference to the inner data as a slice.
84    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
168impl Borrow<str> for StringCow<'_> {
169    fn borrow(&self) -> &str {
170        self.as_str()
171    }
172}
173
174#[cfg(test)]
175#[cfg_attr(all(test, coverage_nightly), coverage(off))]
176mod tests {
177    use bytestring::ByteString;
178
179    use super::StringCow;
180
181    #[test]
182    fn constructors() {
183        let cow = StringCow::default();
184        assert_eq!(cow.as_str(), "");
185
186        let cow = StringCow::from_static("hello");
187        assert_eq!(cow.as_str(), "hello");
188
189        let cow = StringCow::from_ref("world");
190        assert_eq!(cow.as_str(), "world");
191
192        let cow = StringCow::from_string(String::from("foo"));
193        assert_eq!(cow.as_str(), "foo");
194        let cow = StringCow::from(String::from("bar"));
195        assert_eq!(cow.as_str(), "bar");
196
197        let cow = StringCow::from_bytes(ByteString::from_static("foo"));
198        assert_eq!(cow.as_str(), "foo");
199        let cow = StringCow::from(ByteString::from_static("foo"));
200        assert_eq!(cow.as_str(), "foo");
201
202        let cow = StringCow::from_cow(std::borrow::Cow::Borrowed("bar"));
203        assert_eq!(cow.as_str(), "bar");
204        let cow = StringCow::from_cow(std::borrow::Cow::Owned(String::from("baz")));
205        assert_eq!(cow.as_str(), "baz");
206        let cow = StringCow::from(std::borrow::Cow::Owned(String::from("qux")));
207        assert_eq!(cow.as_str(), "qux");
208    }
209
210    #[test]
211    fn into_bytes() {
212        let cow = StringCow::from_static("hello");
213        assert_eq!(cow.into_bytes(), ByteString::from_static("hello"));
214
215        let cow = StringCow::from_ref("world");
216        assert_eq!(cow.into_bytes(), ByteString::from_static("world"));
217
218        let cow = StringCow::from_string(String::from("foo"));
219        assert_eq!(cow.into_bytes(), ByteString::from_static("foo"));
220
221        let cow = StringCow::from_bytes(ByteString::from_static("foo"));
222        assert_eq!(cow.into_bytes(), ByteString::from_static("foo"));
223
224        let cow = StringCow::from_cow(std::borrow::Cow::Borrowed("bar"));
225        assert_eq!(cow.into_bytes(), ByteString::from_static("bar"));
226
227        let cow = StringCow::from_cow(std::borrow::Cow::Owned(String::from("baz")));
228        assert_eq!(cow.into_bytes(), ByteString::from_static("baz"));
229    }
230
231    #[test]
232    fn as_ref() {
233        let cow = StringCow::from_static("hello");
234        assert_eq!(cow.as_ref(), "hello");
235
236        let cow = StringCow::from_ref("world");
237        assert_eq!(cow.as_ref(), "world");
238
239        let cow = StringCow::from_string(String::from("foo"));
240        assert_eq!(cow.as_ref(), "foo");
241
242        let cow = StringCow::from_bytes(ByteString::from_static("foo"));
243        assert_eq!(cow.as_ref(), "foo");
244    }
245
246    #[test]
247    fn into_owned() {
248        let cow = StringCow::from_static("hello");
249        assert_eq!(cow.into_owned().as_str(), "hello");
250
251        let cow = StringCow::from_ref("world");
252        assert_eq!(cow.into_owned().as_str(), "world");
253
254        let cow = StringCow::from_string(String::from("foo"));
255        assert_eq!(cow.into_owned().as_str(), "foo");
256
257        let cow = StringCow::from_bytes(ByteString::from_static("foo"));
258        assert_eq!(cow.into_owned().as_str(), "foo");
259    }
260
261    #[test]
262    fn partial_eq() {
263        let cow = StringCow::from_static("hello");
264        assert!(cow == "hello");
265        assert!(cow != "world");
266
267        let cow = StringCow::from_ref("world");
268        assert!(cow == "world");
269        assert!(cow != "hello");
270
271        let cow = StringCow::from_string(String::from("foo"));
272        assert!(cow == "foo");
273        assert!(cow != "bar");
274    }
275
276    #[test]
277    fn hash() {
278        use std::collections::hash_map::DefaultHasher;
279        use std::hash::{Hash, Hasher};
280
281        let mut hasher = DefaultHasher::new();
282        "hello".hash(&mut hasher);
283        let expected_hash = hasher.finish();
284
285        let cow = StringCow::from_static("hello");
286        let mut hasher = DefaultHasher::new();
287        cow.hash(&mut hasher);
288        assert_eq!(hasher.finish(), expected_hash);
289    }
290
291    #[test]
292    fn partial_ord() {
293        let cow1 = StringCow::from_static("hello");
294        let cow2 = StringCow::from_static("world");
295        assert!(cow1 < cow2);
296
297        let cow3 = StringCow::from_ref("foo");
298        let cow4 = StringCow::from_string(String::from("bar"));
299        assert!(cow3 > cow4);
300    }
301
302    #[test]
303    fn display() {
304        let cow = StringCow::from_ref("hello");
305        let fmt = format!("{cow}");
306        assert_eq!(fmt, "hello");
307
308        let cow = StringCow::from_static("hello");
309        let fmt = format!("{cow}");
310        assert_eq!(fmt, "hello");
311
312        let cow = StringCow::from_string(String::from("world"));
313        let fmt = format!("{cow}");
314        assert_eq!(fmt, "world");
315
316        let cow = StringCow::from_bytes(ByteString::from_static("foo"));
317        let fmt = format!("{cow}");
318        assert_eq!(fmt, "foo");
319    }
320}