string/
lib.rs

1#![deny(warnings, missing_docs, missing_debug_implementations, clippy::all)]
2#![doc(html_root_url = "https://docs.rs/string/0.3.1")]
3
4//! A UTF-8 encoded string with configurable byte storage.
5//!
6//! This crate provides `String`, a type similar to its std counterpart, but
7//! with one significant difference: the underlying byte storage is
8//! configurable. In other words, `String<T>` is a marker type wrapping `T`,
9//! indicating that it represents a UTF-8 encoded string.
10//!
11//! For example, one can represent small strings (stack allocated) by wrapping
12//! an array:
13//!
14//! ```
15//! # use string::*;
16//! let s: String<[u8; 2]> = String::try_from([b'h', b'i']).unwrap();
17//! assert_eq!(&s[..], "hi");
18//! ```
19
20use std::{borrow, default::Default, fmt, hash, ops, str};
21
22/// A UTF-8 encoded string with configurable byte storage.
23///
24/// This type differs from `std::String` in that it is generic over the
25/// underlying byte storage, enabling it to use `Vec<[u8]>`, `&[u8]`, or third
26/// party types, such as [`Bytes`].
27///
28/// In order to construct `String` via any of the non-unsafe constructors,
29/// the backing storage needs to implement the `StableAsRef` marker trait.
30/// If you wish to construct `String` with a type that does not implement `StableAsRef`,
31/// you can use the `from_utf8_unchecked` constructor.
32///
33/// [`Bytes`]: https://docs.rs/bytes/0.4.8/bytes/struct.Bytes.html
34#[derive(Clone, Eq, PartialEq, Ord, PartialOrd)]
35pub struct String<T = Vec<u8>> {
36    value: T,
37}
38
39impl<T> String<T> {
40    /// Get a reference to the underlying byte storage.
41    ///
42    /// # Examples
43    ///
44    /// ```
45    /// # use string::*;
46    /// let s = String::new();
47    /// let vec = s.get_ref();
48    /// ```
49    pub fn get_ref(&self) -> &T {
50        &self.value
51    }
52
53    /// Get a mutable reference to the underlying byte storage.
54    ///
55    /// # Safety
56    ///
57    /// It is inadvisable to directly manipulate the byte storage. This function
58    /// is unsafe as the bytes could no longer be valid UTF-8 after mutation.
59    ///
60    /// # Examples
61    ///
62    /// ```
63    /// # use string::*;
64    /// let mut s = String::new();
65    ///
66    /// unsafe {
67    ///     let vec = s.get_mut();
68    /// }
69    /// ```
70    pub unsafe fn get_mut(&mut self) -> &mut T {
71        &mut self.value
72    }
73
74    /// Unwraps this `String`, returning the underlying byte storage.
75    ///
76    /// # Examples
77    ///
78    /// ```
79    /// # use string::*;
80    /// let s = String::new();
81    /// let vec = s.into_inner();
82    /// ```
83    pub fn into_inner(self) -> T {
84        self.value
85    }
86
87    /// Creates a new `String` from a &str.
88    ///
89    /// Use `TryFrom` for conversion from &[u8].
90    ///
91    /// ```
92    /// # use string::*;
93    /// let _: String<Vec<u8>> = String::from_str("nice str");
94    /// ```
95    #[allow(clippy::should_implement_trait)]
96    pub fn from_str<'a>(src: &'a str) -> String<T>
97    where
98        T: From<&'a [u8]> + StableAsRef,
99    {
100        let value: T = src.as_bytes().into();
101        Self { value }
102    }
103}
104
105impl String {
106    /// Creates a new empty `String`.
107    ///
108    /// Given that the `String` is empty, this will not allocate.
109    ///
110    /// # Examples
111    ///
112    /// Basic usage
113    ///
114    /// ```
115    /// let s = String::new();
116    /// assert_eq!(s, "");
117    /// ```
118    pub fn new() -> String {
119        String::default()
120    }
121}
122
123impl<T> String<T>
124where
125    T: AsRef<[u8]>,
126{
127    /// Converts the provided value to a `String` without checking that the
128    /// given value is valid UTF-8.
129    ///
130    /// Use `TryFrom` for a safe conversion.
131    ///
132    /// # Safety
133    ///
134    /// You must ensure that:
135    ///
136    /// 1. The backing storage type `T` adheres to the contract as documented on the `StableAsRef`
137    ///    marker trait.
138    /// 2. If `T` implements `AsRef<[u8]>` and/or `AsMut<[u8]>`, the byte slice returned
139    ///    by calling `as_ref` and/or `as_mut` on the provided value represents valid utf-8.
140    pub unsafe fn from_utf8_unchecked(value: T) -> String<T> {
141        String { value }
142    }
143}
144
145impl<T> PartialEq<str> for String<T>
146where
147    T: AsRef<[u8]>,
148{
149    fn eq(&self, other: &str) -> bool {
150        &self[..] == other
151    }
152}
153
154#[allow(clippy::derive_hash_xor_eq)]
155impl<T> hash::Hash for String<T>
156where
157    T: AsRef<[u8]>,
158{
159    fn hash<H: hash::Hasher>(&self, state: &mut H) {
160        ops::Deref::deref(self).hash(state);
161    }
162}
163
164impl<T> ops::Deref for String<T>
165where
166    T: AsRef<[u8]>,
167{
168    type Target = str;
169
170    #[inline]
171    fn deref(&self) -> &str {
172        let b = self.value.as_ref();
173        // SAFETY: The `StableAsRef` marker trait ensures that
174        //         the impl of `AsRef<[u8]>` for `T` behaves sanely.
175        unsafe { str::from_utf8_unchecked(b) }
176    }
177}
178
179impl<T> ops::DerefMut for String<T>
180where
181    T: AsRef<[u8]> + AsMut<[u8]>,
182{
183    #[inline]
184    fn deref_mut(&mut self) -> &mut str {
185        let b = self.value.as_mut();
186        // SAFETY: The `StableAsRef` marker trait ensures that
187        //         the impl of `AsMut<[u8]>` for `T` behaves sanely.
188        unsafe { str::from_utf8_unchecked_mut(b) }
189    }
190}
191
192impl<T> borrow::Borrow<str> for String<T>
193where
194    T: AsRef<[u8]>,
195{
196    fn borrow(&self) -> &str {
197        &*self
198    }
199}
200
201impl From<::std::string::String> for String<::std::string::String> {
202    fn from(value: ::std::string::String) -> Self {
203        String { value }
204    }
205}
206
207impl<T> Default for String<T>
208where
209    T: Default + StableAsRef,
210{
211    fn default() -> Self {
212        String {
213            value: T::default(),
214        }
215    }
216}
217
218impl<T> TryFrom<T> for String<T>
219where
220    T: AsRef<[u8]> + StableAsRef,
221{
222    type Error = str::Utf8Error;
223
224    fn try_from(value: T) -> Result<Self, Self::Error> {
225        let _ = str::from_utf8(value.as_ref())?;
226        Ok(String { value })
227    }
228}
229
230impl<T: AsRef<[u8]>> fmt::Debug for String<T> {
231    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
232        (**self).fmt(fmt)
233    }
234}
235
236impl<T: AsRef<[u8]>> fmt::Display for String<T> {
237    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
238        (**self).fmt(fmt)
239    }
240}
241
242#[cfg(feature = "valuable")]
243impl<T> inspect::Valuable for String<T>
244where
245    T: AsRef<[u8]>,
246{
247    fn as_value(&self) -> inspect::Value<'_> {
248        inspect::Value::String(&self[..])
249    }
250
251    fn visit(&self, visit: &mut dyn inspect::Visit) {
252        visit.visit_value(inspect::Value::String(self));
253    }
254}
255
256/// Attempt to construct `Self` via a conversion.
257///
258/// This trait will be deprecated in favor of [std::convert::TryFrom] once it
259/// reaches stable Rust.
260pub trait TryFrom<T>: Sized + sealed::Sealed {
261    /// The type returned in the event of a conversion error.
262    type Error;
263
264    /// Performs the conversion.
265    fn try_from(value: T) -> Result<Self, Self::Error>;
266}
267
268impl<T> sealed::Sealed for String<T> {}
269
270mod sealed {
271    /// Private trait to this crate to prevent traits from being implemented in
272    /// downstream crates.
273    pub trait Sealed {}
274}
275
276/// Marker trait that indicates that a type is guaranteed safe to use as backing storage
277/// for `String`.
278///
279/// In order to be safe, a storage type `T` needs to guarantee the following:
280///
281/// - If `T` implements `AsRef<[u8]>` and/or `AsMut<[u8]>`, the contents of `T` as visible
282///   the byte slice returned by `as_ref` and `as_mut` may only be mutated through mutable
283///   references or owned access. In other words, no use of interior mutability.
284///
285/// - If `T` implements `AsRef<[u8]>`, the `as_ref` method must always return the same
286///   slice of bytes (unless the storage is mutated).
287///
288/// - If `T` implements `AsRef<[u8]>` and `AsMut<[u8]>`, the `as_mut` method must return
289///   a mutable reference to the same slice of bytes as the `as_ref` method returns.
290///
291/// - If `T` implements `AsRef<[u8]>` and `Default`, the default value must represent the
292///   empty byte sequence. In other words, `T::default().as_ref().len() == 0`.
293///
294/// - If `T` implements `AsRef<[u8]>` and `From<&[u8]>`, it must do so in such a way that
295///   the byte slice returned by `as_ref` is equal to the byte slice provided to the `from`
296///   method.
297pub unsafe trait StableAsRef {}
298
299unsafe impl<'a, T> StableAsRef for &'a T where T: StableAsRef {}
300unsafe impl<'a, T> StableAsRef for &'a mut T where T: StableAsRef {}
301unsafe impl<T> StableAsRef for Box<T> where T: StableAsRef {}
302unsafe impl<T> StableAsRef for std::rc::Rc<T> where T: StableAsRef {}
303unsafe impl<T> StableAsRef for std::sync::Arc<T> where T: StableAsRef {}
304
305unsafe impl StableAsRef for std::string::String {}
306unsafe impl StableAsRef for str {}
307unsafe impl StableAsRef for Vec<u8> {}
308unsafe impl StableAsRef for [u8] {}
309
310#[cfg(feature = "bytes")]
311unsafe impl StableAsRef for bytes::Bytes {}
312
313#[cfg(feature = "bytes")]
314unsafe impl StableAsRef for bytes::BytesMut {}
315
316macro_rules! array_impls {
317    ($($len:expr)+) => {
318        $(
319            unsafe impl StableAsRef for [u8; $len] {}
320        )+
321    }
322}
323
324array_impls!(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16);
325
326#[cfg(test)]
327mod test {
328    use super::*;
329
330    #[test]
331    fn test_from_std_string() {
332        let s: String<_> = "hello".to_string().into();
333        assert_eq!(&s, "hello");
334    }
335
336    #[test]
337    fn test_from_str() {
338        let _: String<Vec<u8>> = String::from_str("nice str");
339    }
340
341    #[test]
342    fn test_try_from_bytes() {
343        let _ = String::try_from(b"nice bytes").unwrap();
344    }
345}