scuffle_bytes_util/cow/
string.rs

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