rocstr/
rocstr.rs

1//! An immutable fixed capacity stack based generic copy string.
2
3use core::fmt::Debug;
4use core::fmt::Display;
5use core::fmt::Formatter;
6use core::fmt::Result;
7use core::hash::Hash;
8use core::hash::Hasher;
9use core::ops::Add;
10use core::ops::Div;
11use core::ops::Mul;
12use core::ops::Neg;
13use core::ops::Sub;
14use core::str::from_utf8;
15
16#[derive(Copy, Clone, Eq, PartialOrd, Ord)]
17pub struct RocStr<const SIZE: usize> {
18    inner: [u8; SIZE],
19    len: usize,
20}
21
22impl<const SIZE: usize> RocStr<SIZE> {
23    /// Extracts a slice of bytes containing the entire [`RocStr`].
24    ///
25    /// # Examples
26    /// ```
27    /// # use rocstr::RocStr;
28    /// let s = RocStr::<3>::from("foo");
29    /// assert_eq!(b"foo", s.as_bytes());
30    /// ```
31    pub fn as_bytes(&self) -> &[u8] {
32        self.into()
33    }
34
35    /// Extracts a string slice containing the entire [`RocStr`].
36    ///
37    /// # Examples
38    /// ```
39    /// # use rocstr::RocStr;
40    /// let s = RocStr::<3>::from("foo");
41    /// assert_eq!("foo", s.as_str());
42    /// ```
43    pub fn as_str(&self) -> &str {
44        self.into()
45    }
46
47    /// Return the capacity of the [`RocStr`].
48    ///
49    /// # Examples
50    ///
51    /// Basic usage:
52    ///
53    /// ```
54    /// # use rocstr::RocStr;
55    /// let string = RocStr::<16>::from("");
56    /// assert_eq!(string.capacity(), 16);
57    /// ```
58    #[inline]
59    #[must_use]
60    pub const fn capacity(&self) -> usize {
61        SIZE
62    }
63
64    /// Returns `true` if this [`RocStr`] is an empty string.
65    ///
66    /// # Examples
67    ///
68    /// Basic usage:
69    ///
70    /// ```
71    /// # use rocstr::RocStr;
72    /// let s = RocStr::<16>::from("");
73    /// assert!(s.is_empty());
74    ///
75    /// let s = RocStr::<16>::from("foo");
76    /// assert!(!s.is_empty());
77    /// ```
78    #[inline]
79    #[must_use]
80    pub const fn is_empty(&self) -> bool {
81        self.len == 0
82    }
83
84    /// Returns the length of this [`RocStr`], in bytes, not [`char`]s or graphemes.
85    ///
86    /// In other words, it might not be what a human considers the length of the string.
87    ///
88    /// # Examples
89    /// ```
90    /// # use rocstr::RocStr;
91    /// let s = RocStr::<16>::from("foo");
92    /// assert_eq!(s.len(), 3);
93    ///
94    /// let fancy_f = RocStr::<16>::from("ƒoo");
95    /// assert_eq!(fancy_f.len(), 4);
96    /// assert_eq!(fancy_f.as_str().chars().count(), 3);
97    /// ```
98    #[inline]
99    #[must_use]
100    pub const fn len(&self) -> usize {
101        self.len
102    }
103
104    /// Replaces all matches of a pattern with another string.
105    ///
106    /// `replace` creates a new [`RocStr`], and copies the data from this [`RocStr`] into it.
107    /// While doing so, it attempts to find matches of a pattern.
108    /// If it finds any, it replaces them with the replacement string.
109    ///
110    /// If replacing with the replacement string make this [`RocStr`] overflow its capacity,
111    /// the string will be trim to at most the capacity.
112    ///
113    /// # Examples
114    ///
115    /// Basic usage:
116    ///
117    /// ```
118    /// # use rocstr::RocStr;
119    ///
120    /// let s = RocStr::<16>::from("this is old");
121    ///
122    /// assert_eq!(RocStr::<16>::from("this is new"), s.replace("old", "new"));
123    /// assert_eq!(RocStr::<16>::from("than an old"), s.replace("is", "an"));
124    /// ```
125    ///
126    /// When the pattern doesn't match, it returns this [`RocStr`]:
127    ///
128    /// ```
129    /// let s = "this is old";
130    /// assert_eq!(s, s.replace("cookie monster", "little lamb"));
131    /// ```
132    pub fn replace(&self, from: &str, to: &str) -> Self {
133        if from.is_empty() {
134            *self
135        } else {
136            let pattern = from.as_bytes();
137            let mut len = 0;
138            let mut skip = 0;
139
140            let mut inner = [b' '; SIZE];
141            let frames = self.inner[..self.len].windows(from.len()).enumerate();
142            for (i, frame) in frames {
143                if skip == 0 {
144                    // Nothing to skip
145                    if frame == pattern {
146                        let end = len + to.len();
147                        if end <= SIZE {
148                            inner[len..end].copy_from_slice(to.as_bytes());
149                            len += to.len();
150                            // skip the from.len() bytes minus the one we are in
151                            skip = from.len() - 1;
152                        } else {
153                            let remaining_slots = SIZE - len;
154                            inner[len..SIZE].copy_from_slice(&to.as_bytes()[0..remaining_slots]);
155                            len = SIZE;
156                            break;
157                        }
158                    } else if len < SIZE {
159                        inner[len] = self.inner[i];
160                        len += 1;
161                    } else {
162                        break;
163                    }
164                } else {
165                    skip -= 1;
166                    continue;
167                }
168            }
169
170            // add the remaining bytes, the last frame, only if it remains some space
171            if len < SIZE && skip == 0 {
172                let remaining_slots = SIZE - len;
173                let remaining_bytes = &self.inner[self.len - from.len() + 1..self.len];
174                let remaining_bytes = if remaining_slots > remaining_bytes.len() {
175                    remaining_bytes
176                } else {
177                    &remaining_bytes[..remaining_slots]
178                };
179                inner[len..len + remaining_bytes.len()].copy_from_slice(remaining_bytes);
180                len += remaining_bytes.len();
181            }
182
183            Self { inner, len }
184        }
185    }
186
187    /// Returns a copy of this [`RocStr`] with capacity set to `LEN`.
188    ///
189    /// It will silently trim this [`RocStr`] if its length is greater than `LEN`.
190    ///
191    /// # Examples
192    /// ```
193    /// # use rocstr::RocStr;
194    /// let s = RocStr::<16>::from("foo");
195    /// assert_eq!(s.reshape::<8>().capacity(), 8);
196    ///
197    /// let s = RocStr::<16>::from("foo bar");
198    /// let t = RocStr::<4>::from("foo ");
199    /// assert_eq!(s.reshape::<4>(), t);
200    /// ```
201    #[inline]
202    #[must_use]
203    pub fn reshape<const LEN: usize>(&self) -> RocStr<LEN> {
204        let mut inner = [b' '; LEN];
205        let slice = extract_utf8_within(&self.inner[..self.len], LEN);
206        let len = slice.len();
207        inner[..len].copy_from_slice(slice);
208
209        RocStr { inner, len }
210    }
211
212    /// Returns `true` if the given `&str` matches a prefix of this RocStr.
213    ///
214    /// Returns `false` if it does not.
215    ///
216    /// # Examples
217    ///
218    /// ```
219    /// # use rocstr::RocStr;
220    /// let bananas = RocStr::<16>::from("bananas");
221    ///
222    /// assert!(bananas.starts_with("bana"));
223    /// assert!(!bananas.starts_with("nana"));
224    /// ```
225    pub fn starts_with(&self, pattern: &str) -> bool {
226        self.as_bytes().starts_with(pattern.as_bytes())
227    }
228
229    /// Returns a [`RocStr`] with a valid utf-8 string with at most `len` bytes.
230    ///
231    /// The source [`RocStr`] remains unchanged.
232    ///
233    /// # Examples
234    ///
235    /// ```
236    /// # use rocstr::RocStr;
237    /// let s = RocStr::<32>::from("Löwe 老虎 Léopard");
238    ///
239    /// /* first byte of `ö` is not utf-8 boundary */
240    /// assert_eq!(s.truncate(2), "L");
241    ///
242    /// /* second byte of `老`is not utf-8 boundary */
243    /// assert_eq!(s.truncate(8), "Löwe ");
244    /// ```
245    #[inline]
246    #[must_use]
247    pub fn truncate(&self, len: usize) -> Self {
248        let slice = extract_utf8_within(self.as_bytes(), len);
249        let len = slice.len();
250        let mut inner = [b' '; SIZE];
251        inner[..len].copy_from_slice(slice);
252
253        Self { inner, len }
254    }
255}
256
257impl<const SIZE: usize> Debug for RocStr<SIZE> {
258    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
259        let inner: &str = self.into();
260        f.debug_struct("RocStr")
261            .field("inner", &inner)
262            .field("len", &self.len)
263            .finish()
264    }
265}
266
267impl<const SIZE: usize> Default for RocStr<SIZE> {
268    fn default() -> Self {
269        Self {
270            inner: [0; SIZE],
271            len: Default::default(),
272        }
273    }
274}
275
276impl<const SIZE: usize> Display for RocStr<SIZE> {
277    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
278        write!(f, "{}", Into::<&str>::into(self))
279    }
280}
281
282// Ideally, the signature should be
283//     `fn from(value: T) -> Self where T: AsRef<str>`
284// But this conflict with other `From`` implementation.
285impl<const SIZE: usize> From<&str> for RocStr<SIZE> {
286    #[inline]
287    #[must_use]
288    fn from(value: &str) -> Self {
289        let bytes = value.as_bytes();
290        let slice = extract_utf8_within(bytes, SIZE);
291        let len = slice.len();
292
293        let mut inner = [0; SIZE];
294        inner[..len].copy_from_slice(slice);
295        Self { inner, len }
296    }
297}
298
299impl<'a, const SIZE: usize> From<&'a RocStr<SIZE>> for &'a str {
300    #[inline]
301    #[must_use]
302    fn from(value: &'a RocStr<SIZE>) -> Self {
303        match from_utf8(value.inner[..value.len].as_ref()) {
304            Ok(string) => string,
305            // Unless unsafe use, this should never happen.
306            // This data is immutable and can only be initialized from a valid utf-8 string.
307            Err(_) => unreachable!(),
308        }
309    }
310}
311
312impl<'a, const SIZE: usize> From<&'a RocStr<SIZE>> for &'a [u8] {
313    #[inline]
314    #[must_use]
315    fn from(value: &'a RocStr<SIZE>) -> Self {
316        &value.inner[..value.len]
317    }
318}
319
320impl<const SIZE: usize> Hash for RocStr<SIZE> {
321    #[inline]
322    fn hash<H: Hasher>(&self, hasher: &mut H) {
323        hasher.write(&self.inner[..self.len]);
324        hasher.write_u8(0xff);
325    }
326}
327
328impl<const SIZE: usize> PartialEq<str> for RocStr<SIZE> {
329    #[inline]
330    #[must_use]
331    fn eq(&self, other: &str) -> bool {
332        self.len == other.len() && &self.inner[..self.len] == other.as_bytes()
333    }
334}
335
336impl<const SIZE: usize, T> PartialEq<T> for RocStr<SIZE>
337where
338    T: AsRef<str>,
339{
340    #[inline]
341    #[must_use]
342    fn eq(&self, other: &T) -> bool {
343        let other = other.as_ref();
344        self.eq(other)
345    }
346}
347
348impl<const SIZE: usize> PartialEq<RocStr<SIZE>> for &str {
349    #[inline]
350    #[must_use]
351    fn eq(&self, other: &RocStr<SIZE>) -> bool {
352        self.len() == other.len && self.as_bytes() == &other.inner[..other.len]
353    }
354}
355
356impl<const SIZE: usize, const LEN: usize> PartialEq<RocStr<SIZE>> for RocStr<LEN> {
357    #[inline]
358    #[must_use]
359    fn eq(&self, other: &RocStr<SIZE>) -> bool {
360        self.len() == other.len && self.inner[..self.len] == other.inner[..other.len]
361    }
362}
363
364impl<const SIZE: usize, T> Add<T> for RocStr<SIZE>
365where
366    T: AsRef<str>,
367{
368    type Output = Self;
369
370    fn add(self, rhs: T) -> Self::Output {
371        let rhs = rhs.as_ref();
372        let mut inner = self.inner;
373        let (slice, len) = if self.len + rhs.len() > SIZE {
374            let available_len = SIZE - self.len;
375            let slice = extract_utf8_within(rhs.as_bytes(), available_len);
376            (slice, SIZE)
377        } else {
378            let len = self.len + rhs.len();
379            let slice = rhs.as_bytes();
380            (slice, len)
381        };
382
383        inner[self.len..len].copy_from_slice(slice);
384
385        Self { inner, len }
386    }
387}
388
389impl<const SIZE: usize, const LEN: usize> Add<RocStr<LEN>> for RocStr<SIZE> {
390    type Output = Self;
391
392    fn add(self, rhs: RocStr<LEN>) -> Self::Output {
393        let mut inner = self.inner;
394        let (slice, len) = if self.len + rhs.len > SIZE {
395            let available_len = SIZE - self.len;
396            let slice = extract_utf8_within(&rhs.inner, available_len);
397            (slice, SIZE)
398        } else {
399            let len = self.len + rhs.len;
400            let slice = &rhs.inner[..rhs.len];
401            (slice, len)
402        };
403
404        inner[self.len..len].copy_from_slice(slice);
405
406        Self { inner, len }
407    }
408}
409
410/// Extract a valid utf-8 string from this byte array with at most `len` bytes.
411///
412/// # Examples
413/// let s = "Löwe 老虎 Léopard";
414///
415/// /* first byte of `ö` is not utf-8 boundary */
416/// assert_eq!("L", extract_utf8_within(s.as_bytes(), 2));
417///
418/// /* second byte of `老`is not utf-8 boundary */
419/// assert_eq!("Löwe ", extract_utf8_within(s.as_bytes(), 8));
420#[inline]
421#[must_use]
422fn extract_utf8_within(bytes: &[u8], len: usize) -> &[u8] {
423    let bytes_len = bytes.len();
424    let boundary = if len < bytes_len {
425        // This is bit magic equivalent to: b < 128 || b >= 192
426        if (bytes[len] as i8) >= -0x40 {
427            len
428        } else {
429            let mut boundary = len - 1;
430            // This is bit magic equivalent to: b >= 128 && b <= 192
431            while (bytes[boundary] as i8) < -0x40 {
432                boundary -= 1;
433            }
434            boundary
435        }
436    } else {
437        bytes_len
438    };
439
440    &bytes[..boundary]
441}
442
443trait Zero {
444    fn zero() -> Self;
445
446    fn zero_as_rocstr<const SIZE: usize>() -> RocStr<SIZE> {
447        let len = 1;
448        let mut inner = [b' '; SIZE];
449        inner[0] = b'0';
450        RocStr { inner, len }
451    }
452}
453
454trait Ten {
455    fn ten() -> Self;
456}
457
458trait AsDigit {
459    fn as_digit(&self) -> u8;
460}
461
462const ROCSTR_MIN_I8: RocStr<4> = RocStr {
463    inner: *b"-128",
464    len: 4,
465};
466const ROCSTR_MIN_I16: RocStr<6> = RocStr {
467    inner: *b"-32768",
468    len: 6,
469};
470const ROCSTR_MIN_I32: RocStr<11> = RocStr {
471    inner: *b"-2147483648",
472    len: 11,
473};
474const ROCSTR_MIN_I64: RocStr<20> = RocStr {
475    inner: *b"-9223372036854775808",
476    len: 20,
477};
478const ROCSTR_MIN_ISIZE: RocStr<20> = RocStr {
479    inner: *b"-9223372036854775808",
480    len: 20,
481};
482
483fn next_char<T>(value: T) -> (T, u8)
484where
485    T: Copy + Eq + Div<Output = T> + Mul<Output = T> + Sub<Output = T> + Zero + Ten + AsDigit,
486{
487    let next = value / T::ten();
488    let mask = next * T::ten();
489    let digit = (value - mask).as_digit();
490    let char = match digit {
491        0 => b'0',
492        1 => b'1',
493        2 => b'2',
494        3 => b'3',
495        4 => b'4',
496        5 => b'5',
497        6 => b'6',
498        7 => b'7',
499        8 => b'8',
500        9 => b'9',
501        // Unreachable beaucause digit is the remainder of the division by 10
502        _ => unreachable!(),
503    };
504
505    (next, char)
506}
507
508fn from_signed<const SIZE: usize, T>(value: T) -> RocStr<SIZE>
509where
510    T: Copy
511        + Eq
512        + Neg<Output = T>
513        + Ord
514        + Div<Output = T>
515        + Mul<Output = T>
516        + Sub<Output = T>
517        + Zero
518        + Ten
519        + AsDigit,
520{
521    if value == T::zero() {
522        T::zero_as_rocstr()
523    } else {
524        let mut value = value;
525        let mut len = 0;
526        let mut buffer = [b' '; SIZE];
527
528        // Backup the sign
529        let negative = value < T::zero();
530        // Get absolute value
531        if negative {
532            value = -value;
533        }
534
535        while value > T::zero() {
536            len += 1;
537            let (next, char) = next_char(value);
538            buffer[SIZE - len] = char;
539            value = next;
540        }
541
542        // Add the sign at the beginning
543        if negative {
544            len += 1;
545            buffer[SIZE - len] = b'-';
546        }
547
548        let mut inner = [b' '; SIZE];
549        inner[..len].copy_from_slice(&buffer[SIZE - len..]);
550
551        RocStr { inner, len }
552    }
553}
554
555fn from_unsigned<const SIZE: usize, T>(value: T) -> RocStr<SIZE>
556where
557    T: Copy + Eq + Ord + Div<Output = T> + Mul<Output = T> + Sub<Output = T> + Zero + Ten + AsDigit,
558{
559    if value == T::zero() {
560        T::zero_as_rocstr()
561    } else {
562        let mut value = value;
563        let mut len = 0;
564        let mut buffer = [b' '; SIZE];
565        while value > T::zero() {
566            len += 1;
567            let (next, char) = next_char(value);
568            buffer[SIZE - len] = char;
569            value = next;
570        }
571        let mut inner = [b' '; SIZE];
572        inner[..len].copy_from_slice(&buffer[SIZE - len..]);
573
574        RocStr { inner, len }
575    }
576}
577
578impl Zero for u8 {
579    fn zero() -> Self {
580        0
581    }
582}
583
584impl Zero for u16 {
585    fn zero() -> Self {
586        0
587    }
588}
589
590impl Zero for u32 {
591    fn zero() -> Self {
592        0
593    }
594}
595
596impl Zero for u64 {
597    fn zero() -> Self {
598        0
599    }
600}
601
602impl Zero for u128 {
603    fn zero() -> Self {
604        0
605    }
606}
607
608impl Zero for usize {
609    fn zero() -> Self {
610        0
611    }
612}
613
614impl Zero for i8 {
615    fn zero() -> Self {
616        0
617    }
618}
619
620impl Zero for i16 {
621    fn zero() -> Self {
622        0
623    }
624}
625
626impl Zero for i32 {
627    fn zero() -> Self {
628        0
629    }
630}
631
632impl Zero for i64 {
633    fn zero() -> Self {
634        0
635    }
636}
637
638impl Zero for i128 {
639    fn zero() -> Self {
640        0
641    }
642}
643
644impl Zero for isize {
645    fn zero() -> Self {
646        0
647    }
648}
649
650impl Ten for u8 {
651    fn ten() -> Self {
652        10
653    }
654}
655
656impl Ten for u16 {
657    fn ten() -> Self {
658        10
659    }
660}
661
662impl Ten for u32 {
663    fn ten() -> Self {
664        10
665    }
666}
667
668impl Ten for u64 {
669    fn ten() -> Self {
670        10
671    }
672}
673
674impl Ten for u128 {
675    fn ten() -> Self {
676        10
677    }
678}
679
680impl Ten for usize {
681    fn ten() -> Self {
682        10
683    }
684}
685
686impl Ten for i8 {
687    fn ten() -> Self {
688        10
689    }
690}
691
692impl Ten for i16 {
693    fn ten() -> Self {
694        10
695    }
696}
697
698impl Ten for i32 {
699    fn ten() -> Self {
700        10
701    }
702}
703
704impl Ten for i64 {
705    fn ten() -> Self {
706        10
707    }
708}
709
710impl Ten for i128 {
711    fn ten() -> Self {
712        10
713    }
714}
715
716impl Ten for isize {
717    fn ten() -> Self {
718        10
719    }
720}
721
722impl AsDigit for u8 {
723    fn as_digit(&self) -> u8 {
724        *self
725    }
726}
727
728impl AsDigit for u16 {
729    fn as_digit(&self) -> u8 {
730        *self as u8
731    }
732}
733
734impl AsDigit for u32 {
735    fn as_digit(&self) -> u8 {
736        *self as u8
737    }
738}
739
740impl AsDigit for u64 {
741    fn as_digit(&self) -> u8 {
742        *self as u8
743    }
744}
745
746impl AsDigit for u128 {
747    fn as_digit(&self) -> u8 {
748        *self as u8
749    }
750}
751
752impl AsDigit for usize {
753    fn as_digit(&self) -> u8 {
754        *self as u8
755    }
756}
757
758impl AsDigit for i8 {
759    fn as_digit(&self) -> u8 {
760        *self as u8
761    }
762}
763
764impl AsDigit for i16 {
765    fn as_digit(&self) -> u8 {
766        *self as u8
767    }
768}
769
770impl AsDigit for i32 {
771    fn as_digit(&self) -> u8 {
772        *self as u8
773    }
774}
775
776impl AsDigit for i64 {
777    fn as_digit(&self) -> u8 {
778        *self as u8
779    }
780}
781
782impl AsDigit for i128 {
783    fn as_digit(&self) -> u8 {
784        *self as u8
785    }
786}
787
788impl AsDigit for isize {
789    fn as_digit(&self) -> u8 {
790        *self as u8
791    }
792}
793
794impl From<u8> for RocStr<3> {
795    fn from(value: u8) -> Self {
796        from_unsigned(value)
797    }
798}
799
800impl From<u16> for RocStr<5> {
801    fn from(value: u16) -> Self {
802        from_unsigned(value)
803    }
804}
805
806impl From<u32> for RocStr<10> {
807    fn from(value: u32) -> Self {
808        from_unsigned(value)
809    }
810}
811
812impl From<u64> for RocStr<20> {
813    fn from(value: u64) -> Self {
814        from_unsigned(value)
815    }
816}
817
818impl From<u128> for RocStr<39> {
819    fn from(value: u128) -> Self {
820        from_unsigned(value)
821    }
822}
823
824impl From<usize> for RocStr<20> {
825    fn from(value: usize) -> Self {
826        from_unsigned(value)
827    }
828}
829
830impl From<i8> for RocStr<4> {
831    fn from(value: i8) -> Self {
832        if value == i8::MIN {
833            ROCSTR_MIN_I8
834        } else {
835            from_signed(value)
836        }
837    }
838}
839
840impl From<i16> for RocStr<6> {
841    fn from(value: i16) -> Self {
842        if value == i16::MIN {
843            ROCSTR_MIN_I16
844        } else {
845            from_signed(value)
846        }
847    }
848}
849
850impl From<i32> for RocStr<11> {
851    fn from(value: i32) -> Self {
852        if value == i32::MIN {
853            ROCSTR_MIN_I32
854        } else {
855            from_signed(value)
856        }
857    }
858}
859
860impl From<i64> for RocStr<20> {
861    fn from(value: i64) -> Self {
862        if value == i64::MIN {
863            ROCSTR_MIN_I64
864        } else {
865            from_signed(value)
866        }
867    }
868}
869
870impl From<i128> for RocStr<40> {
871    fn from(value: i128) -> Self {
872        if value == i128::MIN {
873            RocStr::from("-170141183460469231731687303715884105728")
874        } else {
875            from_signed(value)
876        }
877    }
878}
879
880impl From<isize> for RocStr<20> {
881    fn from(value: isize) -> Self {
882        if value == isize::MIN {
883            ROCSTR_MIN_ISIZE
884        } else {
885            from_signed(value)
886        }
887    }
888}
889
890#[cfg(feature = "std")]
891mod standard_rocstr {
892    extern crate std;
893
894    use super::RocStr;
895
896    use std::string::String;
897
898    impl<const SIZE: usize> From<String> for RocStr<SIZE> {
899        #[inline]
900        #[must_use]
901        fn from(value: String) -> Self {
902            Self::from(value.as_str())
903        }
904    }
905
906    impl<const SIZE: usize> From<&String> for RocStr<SIZE> {
907        #[inline]
908        #[must_use]
909        fn from(value: &String) -> Self {
910            Self::from(value.as_str())
911        }
912    }
913
914    impl<const SIZE: usize> PartialEq<RocStr<SIZE>> for String {
915        fn eq(&self, other: &RocStr<SIZE>) -> bool {
916            self.eq(other.as_str())
917        }
918    }
919}
920
921#[cfg(test)]
922mod tests {
923    use super::*;
924
925    #[test]
926    fn str_could_be_compared_to_rocstr() {
927        let s = RocStr::<16>::from("foo");
928        assert!("foo" == s);
929    }
930
931    #[test]
932    fn rocstr_as_str_should_be_inner_str() {
933        let s = RocStr::<16>::from("foo");
934        assert_eq!(s.as_str(), "foo");
935    }
936
937    #[test]
938    fn rocstr_should_equal_inner_str() {
939        let s = RocStr::<16>::from("foo");
940        assert_eq!(s, "foo");
941    }
942
943    #[test]
944    fn rocstr_ref_should_equal_inner_str() {
945        let s = RocStr::<16>::from("foo");
946        assert_eq!(&s, "foo");
947    }
948
949    #[test]
950    fn rocstr_capacity_should_be_its_generic_parameter_size() {
951        let string = RocStr::<16>::from("");
952        assert_eq!(string.capacity(), 16);
953    }
954
955    #[test]
956    fn empty_rocstr_should_say_it_is_empty() {
957        let s = RocStr::<16>::from("");
958        assert!(s.is_empty());
959    }
960
961    #[test]
962    fn not_empty_rocstr_should_say_it_is_not_empty() {
963        let s = RocStr::<16>::from("foo");
964        assert!(!s.is_empty());
965    }
966
967    #[test]
968    fn rocstr_len_should_count_the_number_of_bytes() {
969        let s = RocStr::<16>::from("foo");
970        assert_eq!(s.len(), 3);
971    }
972
973    #[test]
974    fn reshaped_rocstr_should_have_the_new_capacity() {
975        let s = RocStr::<16>::from("foo");
976        assert_eq!(s.reshape::<8>().capacity(), 8);
977    }
978
979    #[test]
980    fn rocstr_starts_with_should_return_true_if_it_starts_with() {
981        let bananas = RocStr::<16>::from("bananas");
982        assert!(bananas.starts_with("bana"));
983    }
984
985    #[test]
986    fn rocstr_starts_with_should_return_false_if_it_does_not_start_with() {
987        let bananas = RocStr::<16>::from("bananas");
988        assert!(!bananas.starts_with("nana"));
989    }
990
991    #[test]
992    fn bytes_from_rocstr_should_be_the_bytes_of_the_inner_str() {
993        let s = RocStr::<16>::from("foo");
994        let bytes: &[u8] = (&s).into();
995        assert_eq!(bytes, b"foo");
996    }
997
998    #[test]
999    fn rocstr_as_bytes_should_be_the_bytes_of_the_inner_str() {
1000        let s = RocStr::<16>::from("foo");
1001        let bytes = s.as_bytes();
1002        assert_eq!(bytes, b"foo");
1003    }
1004
1005    #[test]
1006    fn concat_two_rocstrs_first_with_enough_capacity_should_be_the_concateenation() {
1007        let s = RocStr::<32>::from("Löwe 老虎 ");
1008        let t = RocStr::<16>::from("Léopard Gepardi");
1009        let expected = RocStr::<32>::from("Löwe 老虎 Léopard Gepardi");
1010
1011        assert_eq!(s + t, expected);
1012    }
1013
1014    #[test]
1015    fn concat_two_rocstrs_first_without_enough_capacity_should_be_trimmed_concateenation() {
1016        let s = RocStr::<16>::from("Löwe 老虎 ");
1017        let t = RocStr::<16>::from("Léopard Gepardi");
1018        let expected = RocStr::<16>::from("Löwe 老虎 Lé");
1019
1020        assert_eq!(s + t, expected);
1021    }
1022
1023    #[test]
1024    fn concat_a_str_to_rocstr_first_with_enough_capacity_should_be_the_concateenation() {
1025        let s = RocStr::<32>::from("Löwe 老虎 ");
1026        let t = "Léopard Gepardi";
1027        let expected = RocStr::<32>::from("Löwe 老虎 Léopard Gepardi");
1028
1029        assert_eq!(s + t, expected);
1030    }
1031
1032    #[test]
1033    fn concat_a_str_to_rocstr_first_without_enough_capacity_should_be_trimmed_concateenation() {
1034        let s = RocStr::<16>::from("Löwe 老虎 ");
1035        let t = "Léo";
1036        let expected = RocStr::<16>::from("Löwe 老虎 Lé");
1037
1038        assert_eq!(s + t, expected);
1039
1040        let s = RocStr::<16>::from("Löwe 老虎 ");
1041        let t = "Léopard Gepardi";
1042        let expected = RocStr::<16>::from("Löwe 老虎 Lé");
1043
1044        assert_eq!(s + t, expected);
1045    }
1046
1047    #[test]
1048    fn concat_an_empty_rocstr_to_rocstr_should_be_a_noop() {
1049        let s = RocStr::<32>::from("Löwe 老虎 ");
1050        let t = RocStr::<32>::default();
1051        let expected = RocStr::<32>::from("Löwe 老虎 ");
1052
1053        assert_eq!(s + t, expected);
1054    }
1055
1056    #[test]
1057    fn concat_an_empty_str_to_rocstr_should_be_a_noop() {
1058        let s = RocStr::<32>::from("Löwe 老虎 ");
1059        let t = "";
1060        let expected = RocStr::<32>::from("Löwe 老虎 ");
1061
1062        assert_eq!(s + t, expected);
1063    }
1064
1065    #[test]
1066    fn extract_utf8_within_should_return_the_string_if_len_is_greater_than() {
1067        let s = "Löwe 老虎 Léopard";
1068        assert_eq!(extract_utf8_within(s.as_bytes(), 32), s.as_bytes());
1069    }
1070
1071    #[test]
1072    fn extract_utf8_within_should_a_valid_utf8_with_len_lower_or_eq_than_len() {
1073        let s = "Löwe 老虎 Léopard";
1074        let len = 8;
1075        let extracted = extract_utf8_within(s.as_bytes(), len);
1076        assert_eq!(extracted, "Löwe ".as_bytes());
1077        assert!(extracted.len() <= len);
1078    }
1079
1080    #[test]
1081    fn convert_max_u8_to_rocstr_should_be_max_u8_as_str() {
1082        let expected = "255";
1083        let converted = RocStr::from(u8::MAX);
1084
1085        assert_eq!(converted, expected);
1086    }
1087
1088    #[test]
1089    fn convert_max_u16_to_rocstr_should_be_max_u16_as_str() {
1090        let expected = "65535";
1091        let converted = RocStr::from(u16::MAX);
1092
1093        assert_eq!(converted, expected);
1094    }
1095
1096    #[test]
1097    fn convert_max_u32_to_rocstr_should_be_max_u32_as_str() {
1098        let expected = "4294967295";
1099        let converted = RocStr::from(u32::MAX);
1100
1101        assert_eq!(converted, expected);
1102    }
1103
1104    #[test]
1105    fn convert_max_u64_to_rocstr_should_be_max_u64_as_str() {
1106        let expected = "18446744073709551615";
1107        let converted = RocStr::from(u64::MAX);
1108
1109        assert_eq!(converted, expected);
1110    }
1111
1112    #[test]
1113    fn convert_max_u128_to_rocstr_should_be_max_u128_as_str() {
1114        let expected = "340282366920938463463374607431768211455";
1115        let converted = RocStr::from(u128::MAX);
1116
1117        assert_eq!(converted, expected);
1118    }
1119
1120    #[test]
1121    fn convert_max_usize_to_rocstr_should_be_max_usize_as_str() {
1122        let expected = "18446744073709551615";
1123        let converted = RocStr::from(usize::MAX);
1124
1125        assert_eq!(converted, expected);
1126    }
1127
1128    #[test]
1129    fn convert_max_i8_to_rocstr_should_be_max_i8_as_str() {
1130        let expected = "127";
1131        let converted = RocStr::from(i8::MAX);
1132
1133        assert_eq!(converted, expected);
1134    }
1135
1136    #[test]
1137    fn convert_max_i16_to_rocstr_should_be_max_i16_as_str() {
1138        let expected = "32767";
1139        let converted = RocStr::from(i16::MAX);
1140
1141        assert_eq!(converted, expected);
1142    }
1143
1144    #[test]
1145    fn convert_max_i32_to_rocstr_should_be_max_i32_as_str() {
1146        let expected = "2147483647";
1147        let converted = RocStr::from(i32::MAX);
1148
1149        assert_eq!(converted, expected);
1150    }
1151
1152    #[test]
1153    fn convert_max_i64_to_rocstr_should_be_max_i64_as_str() {
1154        let expected = "9223372036854775807";
1155        let converted = RocStr::from(i64::MAX);
1156
1157        assert_eq!(converted, expected);
1158    }
1159
1160    #[test]
1161    fn convert_max_i128_to_rocstr_should_be_max_i128_as_str() {
1162        let expected = "170141183460469231731687303715884105727";
1163        let converted = RocStr::from(i128::MAX);
1164
1165        assert_eq!(converted, expected);
1166    }
1167
1168    #[test]
1169    fn convert_max_isize_to_rocstr_should_be_max_isize_as_str() {
1170        let expected = "9223372036854775807";
1171        let converted = RocStr::from(isize::MAX);
1172
1173        assert_eq!(converted, expected);
1174    }
1175
1176    #[test]
1177    fn convert_min_u8_to_rocstr_should_be_min_u8_as_str() {
1178        let expected = "0";
1179        let converted = RocStr::from(u8::MIN);
1180
1181        assert_eq!(converted, expected);
1182    }
1183
1184    #[test]
1185    fn convert_min_u16_to_rocstr_should_be_min_u16_as_str() {
1186        let expected = "0";
1187        let converted = RocStr::from(u16::MIN);
1188
1189        assert_eq!(converted, expected);
1190    }
1191
1192    #[test]
1193    fn convert_min_u32_to_rocstr_should_be_min_u32_as_str() {
1194        let expected = "0";
1195        let converted = RocStr::from(u32::MIN);
1196
1197        assert_eq!(converted, expected);
1198    }
1199
1200    #[test]
1201    fn convert_min_u64_to_rocstr_should_be_min_u64_as_str() {
1202        let expected = "0";
1203        let converted = RocStr::from(u64::MIN);
1204
1205        assert_eq!(converted, expected);
1206    }
1207
1208    #[test]
1209    fn convert_min_u128_to_rocstr_should_be_min_u128_as_str() {
1210        let expected = "0";
1211        let converted = RocStr::from(u128::MIN);
1212
1213        assert_eq!(converted, expected);
1214    }
1215
1216    #[test]
1217    fn convert_min_usize_to_rocstr_should_be_min_usize_as_str() {
1218        let expected = "0";
1219        let converted = RocStr::from(usize::MIN);
1220
1221        assert_eq!(converted, expected);
1222    }
1223
1224    #[test]
1225    fn convert_min_i8_to_rocstr_should_be_min_i8_as_str() {
1226        let expected = "-128";
1227        let converted = RocStr::from(i8::MIN);
1228
1229        assert_eq!(converted, expected);
1230    }
1231
1232    #[test]
1233    fn convert_min_i16_to_rocstr_should_be_min_i16_as_str() {
1234        let expected = "-32768";
1235        let converted = RocStr::from(i16::MIN);
1236
1237        assert_eq!(converted, expected);
1238    }
1239
1240    #[test]
1241    fn convert_min_i32_to_rocstr_should_be_min_i32_as_str() {
1242        let expected = "-2147483648";
1243        let converted = RocStr::from(i32::MIN);
1244
1245        assert_eq!(converted, expected);
1246    }
1247
1248    #[test]
1249    fn convert_min_i64_to_rocstr_should_be_min_i64_as_str() {
1250        let expected = "-9223372036854775808";
1251        let converted = RocStr::from(i64::MIN);
1252
1253        assert_eq!(converted, expected);
1254    }
1255
1256    #[test]
1257    fn convert_min_i128_to_rocstr_should_be_min_i128_as_str() {
1258        let expected = "-170141183460469231731687303715884105728";
1259        let converted = RocStr::from(i128::MIN);
1260
1261        assert_eq!(converted, expected);
1262    }
1263
1264    #[test]
1265    fn convert_min_i128_plus_one_to_rocstr_should_be_min_i128_plus_one_as_str() {
1266        let expected = "-170141183460469231731687303715884105727";
1267        let converted = RocStr::from(i128::MIN + 1);
1268
1269        assert_eq!(converted, expected);
1270    }
1271
1272    #[test]
1273    fn convert_min_isize_to_rocstr_should_be_min_isize_as_str() {
1274        let expected = "-9223372036854775808";
1275        let converted = RocStr::from(isize::MIN);
1276
1277        assert_eq!(converted, expected);
1278    }
1279
1280    #[test]
1281    fn convert_zero_i8_to_rocstr_should_be_str_zero() {
1282        let converted = RocStr::from(0i8);
1283        assert_eq!(converted, "0");
1284    }
1285
1286    #[test]
1287    fn convert_zero_i16_to_rocstr_should_be_str_zero() {
1288        let converted = RocStr::from(0i16);
1289        assert_eq!(converted, "0");
1290    }
1291
1292    #[test]
1293    fn convert_zero_i32_to_rocstr_should_be_str_zero() {
1294        let converted = RocStr::from(0i32);
1295        assert_eq!(converted, "0");
1296    }
1297
1298    #[test]
1299    fn convert_zero_i64_to_rocstr_should_be_str_zero() {
1300        let converted = RocStr::from(0i64);
1301        assert_eq!(converted, "0");
1302    }
1303
1304    #[test]
1305    fn convert_zero_isize_to_rocstr_should_be_str_zero() {
1306        let converted = RocStr::from(0isize);
1307        assert_eq!(converted, "0");
1308    }
1309
1310    #[test]
1311    fn convert_negative_i8_to_rocstr_should_start_with_a_minus_sign() {
1312        let converted = RocStr::from(-42i8);
1313
1314        assert_eq!(converted.inner[0], b'-');
1315        assert_eq!(converted, "-42");
1316    }
1317
1318    #[test]
1319    fn convert_negative_i16_to_rocstr_should_start_with_a_minus_sign() {
1320        let converted = RocStr::from(-42i16);
1321
1322        assert_eq!(converted.inner[0], b'-');
1323        assert_eq!(converted, "-42");
1324    }
1325
1326    #[test]
1327    fn convert_negative_i32_to_rocstr_should_start_with_a_minus_sign() {
1328        let converted = RocStr::from(-42i32);
1329
1330        assert_eq!(converted.inner[0], b'-');
1331        assert_eq!(converted, "-42");
1332    }
1333
1334    #[test]
1335    fn convert_negative_i64_to_rocstr_should_start_with_a_minus_sign() {
1336        let converted = RocStr::from(-42i64);
1337
1338        assert_eq!(converted.inner[0], b'-');
1339        assert_eq!(converted, "-42");
1340    }
1341
1342    #[test]
1343    fn convert_negative_isize_to_rocstr_should_start_with_a_minus_sign() {
1344        let converted = RocStr::from(-42isize);
1345
1346        assert_eq!(converted.inner[0], b'-');
1347        assert_eq!(converted, "-42");
1348    }
1349
1350    #[test]
1351    fn rocerr_debug_info_should_display_inner_field_as_str() {
1352        extern crate std;
1353        use std::format;
1354        use std::string::ToString;
1355
1356        let s = RocStr::<16>::from("foo");
1357        assert_eq!(
1358            format!("{s:?}"),
1359            "RocStr { inner: \"foo\", len: 3 }".to_string()
1360        );
1361    }
1362
1363    #[test]
1364    fn hash_rocstr_should_be_the_hash_of_the_inner_str() {
1365        extern crate std;
1366        use std::hash::{DefaultHasher, Hasher};
1367
1368        let mut hasher = DefaultHasher::new();
1369        "foo".hash(&mut hasher);
1370        let exptected = hasher.finish();
1371
1372        let mut hasher = DefaultHasher::new();
1373        let s = RocStr::<16>::from("foo");
1374        s.hash(&mut hasher);
1375        let hash = hasher.finish();
1376
1377        assert_eq!(hash, exptected);
1378    }
1379
1380    #[test]
1381    fn replace_an_str_at_the_begining_of_a_rocstr_should_be_the_rocstr_with_str_replaced() {
1382        let s = RocStr::<16>::from("this is old");
1383        assert_eq!(RocStr::<16>::from("that is old"), s.replace("this", "that"));
1384    }
1385
1386    #[test]
1387    fn replace_an_str_at_the_end_of_a_rocstr_should_be_the_rocstr_with_str_replaced() {
1388        let s = RocStr::<16>::from("this is old");
1389        assert_eq!(RocStr::<16>::from("this is new"), s.replace("old", "new"));
1390    }
1391
1392    #[test]
1393    fn replace_an_str_in_a_rocstr_should_be_the_rocstr_with_str_replaced() {
1394        let s = RocStr::<16>::from("this is old");
1395        assert_eq!(RocStr::<16>::from("than an old"), s.replace("is", "an"));
1396    }
1397
1398    #[test]
1399    fn replace_an_str_in_a_rocstr_that_overflow_should_be_the_truncated_str_replaced() {
1400        let s = RocStr::<16>::from("this is old");
1401        let replaced = s.replace("old", "obvously overflowing");
1402
1403        assert!(
1404            replaced.len <= replaced.capacity(),
1405            "Len of replaced rocstr is greater than its capacity"
1406        );
1407
1408        assert_eq!(RocStr::<16>::from("this is obvously"), replaced);
1409    }
1410
1411    #[test]
1412    fn replace_an_str_inside_a_rocstr_that_overflow_should_be_the_truncated_str_replaced() {
1413        let s = RocStr::<16>::from("this is old");
1414        let replaced = s.replace("is", "is obvously");
1415
1416        assert!(
1417            replaced.len <= replaced.capacity(),
1418            "Len of replaced rocstr is greater than its capacity"
1419        );
1420
1421        assert_eq!(RocStr::<16>::from("this obvously is"), replaced);
1422    }
1423
1424    #[test]
1425    fn replace_an_str_inside_a_rocstr_that_overflow_at_last_should_be_the_truncated_str_replaced() {
1426        let s = RocStr::<16>::from("this is old");
1427        let replaced = s.replace(" is", " is obvously");
1428
1429        assert!(
1430            replaced.len <= replaced.capacity(),
1431            "Len of replaced rocstr is greater than its capacity"
1432        );
1433
1434        assert_eq!(RocStr::<16>::from("this is obvously is"), replaced);
1435    }
1436
1437    #[test]
1438    fn truncate_rocstr_should_contain_a_valid_utf8_with_at_most_len_bytes() {
1439        let s = RocStr::<32>::from("Löwe 老虎 Léopard");
1440
1441        /* second byte of `老`is not utf-8 boundary */
1442        assert_eq!(s.truncate(8), "Löwe ");
1443    }
1444
1445    #[cfg(feature = "std")]
1446    mod standard_rocstr {
1447        extern crate std;
1448
1449        use std::string::String;
1450
1451        use super::RocStr;
1452
1453        #[test]
1454        fn rocstr_from_string_with_size_greater_than_string_len_should_be_the_string() {
1455            let s = String::from("This is a short enough string");
1456
1457            assert_eq!(s, RocStr::<32>::from(&s));
1458        }
1459    }
1460}