stackstring/
string.rs

1use core::{
2    borrow::Borrow,
3    cmp::{Eq, Ord, PartialEq, PartialOrd},
4    convert::TryFrom,
5    fmt,
6    hash::{Hash, Hasher},
7    ops::{self, Deref, DerefMut, Index, IndexMut},
8    str,
9};
10
11use self::builder::StringBuilder;
12use crate::error::Error;
13
14pub mod builder;
15#[cfg(feature = "rkyv-derive")]
16pub mod rkyv;
17#[cfg(feature = "serde-derive")]
18pub mod serde;
19
20#[derive(Copy, Clone, Eq, PartialOrd, Ord)]
21pub struct String<const L: usize>(pub(crate) [u8; L]);
22
23/// A non-zero sized inlined `String`. It doesn't keep any length, the assumption is
24/// that the capacity is always utilised.
25impl<const L: usize> String<L> {
26    pub const fn empty() -> Self {
27        Self([b' '; L])
28    }
29
30    /// Constructs a `String<L>` from an _at most_ `L` bytes-long string slice,
31    /// left-padding if the slice is less than `L` bytes-long.
32    ///
33    /// # Example
34    ///
35    /// ```
36    /// # use stackstring::String;
37    /// let s = "three";
38    /// let string = String::<9>::try_from_str_padded(s).unwrap();
39    ///
40    /// assert_eq!(string, "three    ");
41    ///
42    /// let string_err = String::<3>::try_from_str_padded(s);
43    ///
44    /// assert!(string_err.is_err());
45    /// ```
46    pub fn try_from_str_padded(s: impl AsRef<str>) -> Result<Self, Error> {
47        Self::try_from_bytes_padded(s.as_ref().as_bytes())
48    }
49
50    /// Constructs a `String<L>` from _at most_ `L` bytes.
51    /// left-padding if the number of bytes is less than `L`.
52    ///
53    /// # Example
54    ///
55    /// ```
56    /// # use stackstring::String;
57    /// let bytes = b"three";
58    /// let string = String::<9>::try_from_bytes_padded(bytes).unwrap();
59    ///
60    /// assert_eq!(string.as_bytes(), b"three    ");
61    /// ```
62    pub fn try_from_bytes_padded(bytes: impl AsRef<[u8]>) -> Result<Self, Error> {
63        let bytes = bytes.as_ref();
64
65        if bytes.len() > L {
66            return Err(Error::new(L, bytes.len()));
67        }
68
69        let mut builder = Self::builder();
70        unsafe {
71            builder.push_bytes_unchecked(bytes);
72        }
73
74        Ok(builder.build())
75    }
76
77    pub const fn builder() -> StringBuilder<L> {
78        StringBuilder::empty()
79    }
80
81    pub fn as_str(&self) -> &str {
82        &self
83    }
84
85    pub fn as_bytes(&self) -> &[u8; L] {
86        &self.0
87    }
88
89    pub fn as_slice(&self) -> &[u8] {
90        self.as_str().as_bytes()
91    }
92
93    /// Turns the `String` into its underlying bytes.
94    pub fn into_bytes(self) -> [u8; L] {
95        self.0
96    }
97
98    /// Returns whether all bytes in the `String` are empty.
99    pub fn is_empty(&self) -> bool {
100        self.0.iter().all(|&x| x == b' ')
101    }
102
103    /// Returns whether the first byte of the `String` is empty.
104    /// Can be used as a faster version of `is_empty`.
105    pub fn starts_empty(&self) -> bool {
106        self.0[0] == b' '
107    }
108}
109
110impl<const L: usize> Default for String<L> {
111    fn default() -> Self {
112        Self::empty()
113    }
114}
115
116impl<const L: usize> fmt::Debug for String<L> {
117    #[inline]
118    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119        fmt::Debug::fmt(&**self, f)
120    }
121}
122
123impl<const L: usize> fmt::Display for String<L> {
124    #[inline]
125    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126        fmt::Display::fmt(&**self, f)
127    }
128}
129
130impl<const L: usize> Deref for String<L> {
131    type Target = str;
132
133    fn deref(&self) -> &str {
134        unsafe { core::str::from_utf8_unchecked(&self.0) }
135    }
136}
137
138impl<const L: usize> DerefMut for String<L> {
139    #[inline]
140    fn deref_mut(&mut self) -> &mut str {
141        unsafe { str::from_utf8_unchecked_mut(&mut self.0) }
142    }
143}
144
145impl<const L: usize> From<[u8; L]> for String<L> {
146    fn from(buf: [u8; L]) -> Self {
147        String(buf)
148    }
149}
150
151impl<const L: usize> Hash for String<L> {
152    #[inline]
153    fn hash<H: Hasher>(&self, hasher: &mut H) {
154        (**self).hash(hasher)
155    }
156}
157
158impl<const L: usize> TryFrom<&str> for String<L> {
159    type Error = crate::error::Error;
160
161    fn try_from(s: &str) -> Result<Self, Self::Error> {
162        if s.len() != L {
163            return Err(Error::new(L, s.len()));
164        }
165
166        let mut res = Self::empty();
167
168        res.0.copy_from_slice(s.as_bytes());
169
170        Ok(res)
171    }
172}
173
174impl<const L: usize> Borrow<str> for String<L> {
175    #[inline]
176    fn borrow(&self) -> &str {
177        &self
178    }
179}
180
181impl<const L: usize> PartialEq for String<L> {
182    #[inline]
183    fn eq(&self, other: &String<L>) -> bool {
184        PartialEq::eq(&self[..], &other[..])
185    }
186
187    #[inline]
188    fn ne(&self, other: &String<L>) -> bool {
189        PartialEq::ne(&self[..], &other[..])
190    }
191}
192
193// == str eq ==
194impl<const L: usize> PartialEq<str> for String<L> {
195    #[inline]
196    fn eq(&self, other: &str) -> bool {
197        PartialEq::eq(&self[..], &other[..])
198    }
199
200    #[inline]
201    fn ne(&self, other: &str) -> bool {
202        PartialEq::ne(&self[..], &other[..])
203    }
204}
205
206impl<const L: usize> PartialEq<&str> for String<L> {
207    #[inline]
208    fn eq(&self, other: &&str) -> bool {
209        PartialEq::eq(&self[..], &other[..])
210    }
211
212    #[inline]
213    fn ne(&self, other: &&str) -> bool {
214        PartialEq::ne(&self[..], &other[..])
215    }
216}
217
218impl<const L: usize> PartialEq<String<L>> for &str {
219    #[inline]
220    fn eq(&self, other: &String<L>) -> bool {
221        PartialEq::eq(&self[..], &other[..])
222    }
223
224    #[inline]
225    fn ne(&self, other: &String<L>) -> bool {
226        PartialEq::ne(&self[..], &other[..])
227    }
228}
229
230// == std String eq ==
231impl<const L: usize> PartialEq<std::string::String> for String<L> {
232    #[inline]
233    fn eq(&self, other: &std::string::String) -> bool {
234        PartialEq::eq(&self[..], &other[..])
235    }
236
237    #[inline]
238    fn ne(&self, other: &std::string::String) -> bool {
239        PartialEq::ne(&self[..], &other[..])
240    }
241}
242
243impl<const L: usize> PartialEq<String<L>> for std::string::String {
244    #[inline]
245    fn eq(&self, other: &String<L>) -> bool {
246        PartialEq::eq(&self[..], &other[..])
247    }
248
249    #[inline]
250    fn ne(&self, other: &String<L>) -> bool {
251        PartialEq::ne(&self[..], &other[..])
252    }
253}
254
255impl<const L: usize> ops::Index<ops::Range<usize>> for String<L> {
256    type Output = str;
257
258    #[inline]
259    fn index(&self, index: ops::Range<usize>) -> &str {
260        &self[..][index]
261    }
262}
263
264impl<const L: usize> ops::Index<ops::RangeTo<usize>> for String<L> {
265    type Output = str;
266
267    #[inline]
268    fn index(&self, index: ops::RangeTo<usize>) -> &str {
269        &self[..][index]
270    }
271}
272
273impl<const L: usize> ops::Index<ops::RangeFrom<usize>> for String<L> {
274    type Output = str;
275
276    #[inline]
277    fn index(&self, index: ops::RangeFrom<usize>) -> &str {
278        &self[..][index]
279    }
280}
281
282impl<const L: usize> ops::Index<ops::RangeFull> for String<L> {
283    type Output = str;
284
285    #[inline]
286    fn index(&self, _index: ops::RangeFull) -> &str {
287        unsafe { str::from_utf8_unchecked(&self.0) }
288    }
289}
290
291impl<const L: usize> ops::Index<ops::RangeInclusive<usize>> for String<L> {
292    type Output = str;
293
294    #[inline]
295    fn index(&self, index: ops::RangeInclusive<usize>) -> &str {
296        Index::index(&**self, index)
297    }
298}
299
300impl<const L: usize> ops::Index<ops::RangeToInclusive<usize>> for String<L> {
301    type Output = str;
302
303    #[inline]
304    fn index(&self, index: ops::RangeToInclusive<usize>) -> &str {
305        Index::index(&**self, index)
306    }
307}
308
309impl<const L: usize> ops::IndexMut<ops::Range<usize>> for String<L> {
310    #[inline]
311    fn index_mut(&mut self, index: ops::Range<usize>) -> &mut str {
312        &mut self[..][index]
313    }
314}
315
316impl<const L: usize> ops::IndexMut<ops::RangeTo<usize>> for String<L> {
317    #[inline]
318    fn index_mut(&mut self, index: ops::RangeTo<usize>) -> &mut str {
319        &mut self[..][index]
320    }
321}
322
323impl<const L: usize> ops::IndexMut<ops::RangeFrom<usize>> for String<L> {
324    #[inline]
325    fn index_mut(&mut self, index: ops::RangeFrom<usize>) -> &mut str {
326        &mut self[..][index]
327    }
328}
329
330impl<const L: usize> ops::IndexMut<ops::RangeFull> for String<L> {
331    #[inline]
332    fn index_mut(&mut self, _index: ops::RangeFull) -> &mut str {
333        unsafe { str::from_utf8_unchecked_mut(&mut self.0) }
334    }
335}
336
337impl<const L: usize> ops::IndexMut<ops::RangeInclusive<usize>> for String<L> {
338    #[inline]
339    fn index_mut(&mut self, index: ops::RangeInclusive<usize>) -> &mut str {
340        IndexMut::index_mut(&mut **self, index)
341    }
342}
343
344impl<const L: usize> ops::IndexMut<ops::RangeToInclusive<usize>> for String<L> {
345    #[inline]
346    fn index_mut(&mut self, index: ops::RangeToInclusive<usize>) -> &mut str {
347        IndexMut::index_mut(&mut **self, index)
348    }
349}
350
351#[cfg(test)]
352mod tests {
353    use super::*;
354    use std::collections::hash_map::DefaultHasher;
355    use std::collections::HashSet;
356    use std::hash::{Hash, Hasher};
357
358    #[test]
359    fn deref() {
360        let s_ = "abcde";
361        let s = String::<5>::try_from(s_).unwrap();
362
363        assert_eq!(s_, s);
364    }
365
366    #[test]
367    fn slice() {
368        let s = String::<3>::try_from("abc").unwrap();
369
370        assert_eq!(&s[..2], "ab");
371    }
372
373    #[test]
374    fn eq_impls() {
375        let s_ = "abcde";
376        let s = String::<5>::try_from(s_).unwrap();
377
378        assert_eq!(s_, s);
379
380        let s_ = s_.to_owned();
381        assert_eq!(s_, s);
382    }
383
384    #[test]
385    fn hash_set_contains() {
386        let s_ = "abcde";
387        let s = String::<5>::try_from(s_).unwrap();
388
389        assert_eq!(<String<5> as Borrow<str>>::borrow(&s), s_);
390
391        let mut hasher = DefaultHasher::new();
392        s_.hash(&mut hasher);
393        let s_hash = hasher.finish();
394        let mut hasher = DefaultHasher::new();
395        s.hash(&mut hasher);
396        let ss_hash = hasher.finish();
397
398        assert_eq!(s_hash, ss_hash);
399
400        let set = HashSet::from([s]);
401
402        assert!(set.contains(s_));
403
404        let s_ = s_.to_owned();
405        assert!(set.contains(s_.as_str()));
406    }
407}