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")]
8#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
9pub(crate) mod serde;
10
11/// A [`Cow`] type for strings.
12#[derive(Debug, Clone, Eq)]
13pub enum StringCow<'a> {
14    /// A borrowed [`ByteString`] object.
15    Ref(&'a str),
16    /// A staticly borrowed [`ByteString`] object.
17    StaticRef(&'static str),
18    /// An owned [`String`] object.
19    String(String),
20    /// An owned [`ByteString`] object.
21    Bytes(ByteString),
22}
23
24impl Default for StringCow<'_> {
25    fn default() -> Self {
26        Self::new()
27    }
28}
29
30impl<'a> StringCow<'a> {
31    /// Creates an empty [`StringCow`] object.
32    pub fn new() -> Self {
33        Self::from_static("")
34    }
35
36    /// Creates a new [`StringCow`] from a static slice.
37    pub fn from_static(slice: &'static str) -> Self {
38        StringCow::StaticRef(slice)
39    }
40
41    /// Creates a new [`StringCow`] from a [`ByteString`] object.
42    pub fn from_bytes(bytes: ByteString) -> Self {
43        StringCow::Bytes(bytes)
44    }
45
46    /// Creates a new [`StringCow`] from a [`Cow`] of a [`str`] object.
47    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    /// Creates a new [`StringCow`] from a static slice.
55    pub fn from_ref(slice: &'a str) -> Self {
56        StringCow::Ref(slice)
57    }
58
59    /// Creates a new [`StringCow`] from a [`String`] object.
60    pub fn from_string(string: String) -> Self {
61        StringCow::String(string)
62    }
63
64    /// Converts the object into a [`ByteString`] object.
65    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    /// Converts this [`StringCow`] into an owned version with a static lifetime.
75    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    /// Returns a reference to the inner data as a slice.
85    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}