1use 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 pub fn as_bytes(&self) -> &[u8] {
32 self.into()
33 }
34
35 pub fn as_str(&self) -> &str {
44 self.into()
45 }
46
47 #[inline]
59 #[must_use]
60 pub const fn capacity(&self) -> usize {
61 SIZE
62 }
63
64 #[inline]
79 #[must_use]
80 pub const fn is_empty(&self) -> bool {
81 self.len == 0
82 }
83
84 #[inline]
99 #[must_use]
100 pub const fn len(&self) -> usize {
101 self.len
102 }
103
104 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 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 = 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 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 #[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 pub fn starts_with(&self, pattern: &str) -> bool {
226 self.as_bytes().starts_with(pattern.as_bytes())
227 }
228
229 #[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
282impl<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 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#[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 if (bytes[len] as i8) >= -0x40 {
427 len
428 } else {
429 let mut boundary = len - 1;
430 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!(),
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 let negative = value < T::zero();
530 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 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 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}