scrappy_string/
lib.rs

1//! A utl-8 encoded read-only string with Bytes as a storage.
2use std::convert::TryFrom;
3use std::{borrow, fmt, hash, ops, str};
4
5use bytes::Bytes;
6
7/// A utf-8 encoded string with [`Bytes`] as a storage.
8///
9/// [`Bytes`]: https://docs.rs/bytes/0.5.3/bytes/struct.Bytes.html
10#[derive(Clone, Eq, Ord, PartialOrd, Default)]
11pub struct ByteString(Bytes);
12
13impl ByteString {
14    /// Creates a new `ByteString`.
15    pub fn new() -> Self {
16        ByteString(Bytes::new())
17    }
18
19    /// Get a reference to the underlying bytes object.
20    pub fn get_ref(&self) -> &Bytes {
21        &self.0
22    }
23
24    /// Unwraps this `ByteString`, returning the underlying bytes object.
25    pub fn into_inner(self) -> Bytes {
26        self.0
27    }
28
29    /// Creates a new `ByteString` from a static str.
30    pub const fn from_static(src: &'static str) -> ByteString {
31        Self(Bytes::from_static(src.as_bytes()))
32    }
33
34    /// Creates a new `ByteString` from a Bytes.
35    pub const unsafe fn from_bytes_unchecked(src: Bytes) -> ByteString {
36        Self(src)
37    }
38}
39
40impl PartialEq<str> for ByteString {
41    fn eq(&self, other: &str) -> bool {
42        &self[..] == other
43    }
44}
45
46impl<T: AsRef<str>> PartialEq<T> for ByteString {
47    fn eq(&self, other: &T) -> bool {
48        &self[..] == other.as_ref()
49    }
50}
51
52impl AsRef<[u8]> for ByteString {
53    fn as_ref(&self) -> &[u8] {
54        self.0.as_ref()
55    }
56}
57
58impl AsRef<str> for ByteString {
59    fn as_ref(&self) -> &str {
60        &*self
61    }
62}
63
64impl hash::Hash for ByteString {
65    fn hash<H: hash::Hasher>(&self, state: &mut H) {
66        (**self).hash(state);
67    }
68}
69
70impl ops::Deref for ByteString {
71    type Target = str;
72
73    #[inline]
74    fn deref(&self) -> &str {
75        let b = self.0.as_ref();
76        unsafe { str::from_utf8_unchecked(b) }
77    }
78}
79
80impl borrow::Borrow<str> for ByteString {
81    fn borrow(&self) -> &str {
82        &*self
83    }
84}
85
86impl From<String> for ByteString {
87    fn from(value: String) -> Self {
88        Self(Bytes::from(value))
89    }
90}
91
92impl<'a> From<&'a str> for ByteString {
93    fn from(value: &'a str) -> Self {
94        Self(Bytes::copy_from_slice(value.as_ref()))
95    }
96}
97
98impl<'a> TryFrom<&'a [u8]> for ByteString {
99    type Error = str::Utf8Error;
100
101    fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
102        let _ = str::from_utf8(value)?;
103        Ok(ByteString(Bytes::copy_from_slice(value)))
104    }
105}
106
107impl TryFrom<Vec<u8>> for ByteString {
108    type Error = str::Utf8Error;
109
110    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
111        let _ = str::from_utf8(value.as_ref())?;
112        Ok(ByteString(Bytes::from(value)))
113    }
114}
115
116impl TryFrom<Bytes> for ByteString {
117    type Error = str::Utf8Error;
118
119    fn try_from(value: Bytes) -> Result<Self, Self::Error> {
120        let _ = str::from_utf8(value.as_ref())?;
121        Ok(ByteString(value))
122    }
123}
124
125impl TryFrom<bytes::BytesMut> for ByteString {
126    type Error = str::Utf8Error;
127
128    fn try_from(value: bytes::BytesMut) -> Result<Self, Self::Error> {
129        let _ = str::from_utf8(value.as_ref())?;
130        Ok(ByteString(value.freeze()))
131    }
132}
133
134macro_rules! array_impls {
135    ($($len:expr)+) => {
136        $(
137            impl<'a> TryFrom<&'a [u8; $len]> for ByteString {
138                type Error = str::Utf8Error;
139
140                fn try_from(value: &'a [u8; $len]) -> Result<Self, Self::Error> {
141                    ByteString::try_from(&value[..])
142                }
143            }
144        )+
145    }
146}
147
148array_impls!(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16);
149
150impl fmt::Debug for ByteString {
151    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
152        (**self).fmt(fmt)
153    }
154}
155
156impl fmt::Display for ByteString {
157    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
158        (**self).fmt(fmt)
159    }
160}
161
162#[cfg(test)]
163mod test {
164    use super::*;
165    use std::collections::hash_map::DefaultHasher;
166    use std::hash::{Hash, Hasher};
167
168    #[test]
169    fn test_partial_eq() {
170        let s: ByteString = ByteString::from_static("test");
171        assert_eq!(s, "test");
172        assert_eq!(s, *"test");
173        assert_eq!(s, "test".to_string());
174    }
175
176    #[test]
177    fn test_new() {
178        let _: ByteString = ByteString::new();
179    }
180
181    #[test]
182    fn test_hash() {
183        let mut hasher1 = DefaultHasher::default();
184        "str".hash(&mut hasher1);
185
186        let mut hasher2 = DefaultHasher::default();
187        let s = ByteString::from_static("str");
188        s.hash(&mut hasher2);
189        assert_eq!(hasher1.finish(), hasher2.finish());
190    }
191
192    #[test]
193    fn test_from_string() {
194        let s: ByteString = "hello".to_string().into();
195        assert_eq!(&s, "hello");
196        let t: &str = s.as_ref();
197        assert_eq!(t, "hello");
198    }
199
200    #[test]
201    fn test_from_str() {
202        let _: ByteString = "str".into();
203    }
204
205    #[test]
206    fn test_from_static_str() {
207        const _S: ByteString = ByteString::from_static("hello");
208        let _ = ByteString::from_static("str");
209    }
210
211    #[test]
212    fn test_try_from_rbytes() {
213        let _ = ByteString::try_from(b"nice bytes").unwrap();
214    }
215
216    #[test]
217    fn test_try_from_bytes() {
218        let _ = ByteString::try_from(Bytes::from_static(b"nice bytes")).unwrap();
219    }
220
221    #[test]
222    fn test_try_from_bytesmut() {
223        let _ = ByteString::try_from(bytes::BytesMut::from(&b"nice bytes"[..])).unwrap();
224    }
225}