scuffle_bytes_util/cow/string/
mod.rs

1use 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/// 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
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}