1use crate::{
11 EMPTY,
12 MAX_LENGTH,
13 MAX_LENGTH_SMALL,
14 MAX_LENGTH_SMALL_ADD1,
15 PREFIX_LENGTH,
16 StringPtr,
17 TkStrError,
18 TokenString,
19};
20
21extern crate alloc;
22
23use core::{fmt, marker, mem, ptr, slice};
24
25#[derive(Debug, Clone, Copy)]
37pub struct Builder<'a, const N: usize> {
38 strings: [*const TokenString; N],
40 total_size: usize,
42 num_strings: usize,
44 lifetime: marker::PhantomData<&'a ()>,
46}
47
48#[derive(Debug)]
52pub struct BuilderIter<'a, const N: usize> {
53 builder: &'a Builder<'a, N>,
55 idx: usize,
57}
58
59impl<const N: usize> fmt::Display for Builder<'_, N> {
60 #[inline]
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 write!(f, "Builder < ")?;
63 for idx in 0 .. self.num_strings {
64 if idx == 0 {
65 write!(f, "'{}'", unsafe { &*self.strings[idx] })?;
69 } else {
70 write!(f, " + '{}'", unsafe { &*self.strings[idx] })?;
74 }
75 }
76 write!(f, " >")
77 }
78}
79
80impl<'a, const N: usize> IntoIterator for &'a Builder<'a, N> {
81 type IntoIter = BuilderIter<'a, N>;
82 type Item = &'a TokenString;
83
84 #[inline]
85 fn into_iter(self) -> Self::IntoIter {
86 Self::IntoIter {
87 builder: self,
88 idx: 0,
89 }
90 }
91}
92
93impl<'a, const N: usize> Iterator for BuilderIter<'a, N> {
94 type Item = &'a TokenString;
95
96 #[inline]
97 fn next(&mut self) -> Option<Self::Item> {
98 debug_assert!(
99 self.idx <= self.builder.num_strings,
100 "Builder iterator index is out of bounds"
101 );
102 self.idx += 1;
103 if self.idx - 1 == self.builder.num_strings {
104 None
105 } else {
106 Some(unsafe { &*self.builder.strings[self.idx - 1] })
110 }
111 }
112}
113
114impl<'a, const N: usize> Builder<'a, N> {
115 #[inline]
116 #[must_use]
117 pub fn iter(&'a self) -> BuilderIter<'a, N> {
118 <&Self as IntoIterator>::into_iter(self)
119 }
120
121 #[inline]
129 #[must_use]
130 pub const fn new(s: &'a TokenString) -> Self {
131 assert!(N > 0, "the number of elements must not be 0");
132 let mut ret_val = Self {
133 total_size: s.len as usize,
134 strings: [ptr::null(); N],
135 num_strings: 1,
136 lifetime: marker::PhantomData,
137 };
138 ret_val.strings[0] = s;
139 ret_val
140 }
141
142 #[inline]
152 #[must_use]
153 pub const fn concat_unchecked(&mut self, s: &'a TokenString) -> &mut Self {
154 assert!(
155 (self.num_strings < N),
156 "more strings concatenated than reserved space in Builder"
157 );
158 self.total_size += s.len as usize;
159 self.strings[self.num_strings] = s;
160 self.num_strings += 1;
161
162 self
163 }
164
165 #[inline]
177 pub const fn concat_checked(
178 &mut self,
179 s: &'a TokenString,
180 ) -> Result<&mut Self, TkStrError> {
181 if self.num_strings == N {
182 return Err(TkStrError::TooMany(N));
183 }
184 if self.total_size + s.len as usize > MAX_LENGTH {
185 return Err(TkStrError::TooBig(self.total_size + s.len as usize));
186 }
187 self.total_size += s.len as usize;
188 self.strings[self.num_strings] = s;
189 self.num_strings += 1;
190
191 Ok(self)
192 }
193
194 #[inline]
201 pub fn collect_checked(self) -> Result<TokenString, TkStrError> {
202 match self.total_size {
203 | 0 => Ok(EMPTY),
204 | 1 ..= MAX_LENGTH_SMALL => Ok(self.collect_to_small()),
205 | MAX_LENGTH_SMALL_ADD1 ..= MAX_LENGTH =>
206 Ok(self.collect_to_alloc()),
207 | _ => Err(TkStrError::TooBig(self.total_size)),
208 }
209 }
210
211 #[inline]
218 #[must_use]
219 pub fn collect_unchecked(self) -> TokenString {
220 match self.total_size {
221 | 0 => EMPTY,
222 | 1 ..= MAX_LENGTH_SMALL => self.collect_to_small(),
223 | MAX_LENGTH_SMALL_ADD1 ..= MAX_LENGTH => self.collect_to_alloc(),
224 | _ => panic!(
225 "the result of this builder would be bigger than `MAX_LENGTH`!"
226 ),
227 }
228 }
229
230 fn collect_to_small(&self) -> TokenString {
232 let mut ret_val = EMPTY;
233 #[expect(
234 clippy::cast_possible_truncation,
235 reason = "We checked for overflow in the parent function"
236 )]
237 let total = self.total_size as u16;
238 ret_val.len = total;
239 let dest: &mut [u8] = unsafe {
243 slice::from_raw_parts_mut(
244 ret_val.prefix.as_mut_ptr(),
245 self.total_size,
246 )
247 };
248 let mut curr_end = 0;
249 for b in self {
250 let len = b.len as usize;
251 let idx = curr_end;
252 curr_end += len;
253 dest[idx .. curr_end].copy_from_slice(&b.as_bytes()[.. len]);
254 }
255
256 ret_val
257 }
258
259 fn collect_to_alloc(&self) -> TokenString {
261 let mut ret_val = EMPTY;
262 #[expect(
263 clippy::cast_possible_truncation,
264 reason = "We checked for overflow in the parent function"
265 )]
266 let total = self.total_size as u16;
267 ret_val.u.ptr =
268 mem::ManuallyDrop::new(StringPtr::alloc_manually(self.total_size));
269 ret_val.len = total;
270 let mut curr_end = 0;
271 for b in self {
272 let len = b.len as usize;
273 let idx = curr_end;
274 curr_end += len;
275 unsafe {
278 (*ret_val.u.ptr).copy_manually(idx, b.as_bytes());
279 }
280 }
281 ret_val.prefix.copy_from_slice(unsafe {
284 &(*ret_val.u.ptr).as_slice_manually_mut(self.total_size)
285 [.. PREFIX_LENGTH]
286 });
287 ret_val
288 }
289}
290
291pub trait Concat<T> {
294 type Output;
295
296 fn concat(self, s: T) -> Result<Self::Output, TkStrError>;
305}
306
307impl<'a, const N: usize> Concat<&'a TokenString>
308 for Result<&'a mut Builder<'a, N>, TkStrError>
309{
310 type Output = &'a mut Builder<'a, N>;
311
312 #[inline]
313 fn concat(self, s: &'a TokenString) -> Result<Self::Output, TkStrError> {
314 self?.concat_checked(s)
315 }
316}
317
318impl<'a, const N: usize> Concat<&'a TokenString> for &'a mut Builder<'a, N> {
319 type Output = &'a mut Builder<'a, N>;
320
321 #[inline]
322 fn concat(self, s: &'a TokenString) -> Result<Self::Output, TkStrError> {
323 self.concat_checked(s)
324 }
325}
326
327pub trait Collect {
330 fn collect(self) -> Result<TokenString, TkStrError>;
337}
338
339impl<'a, const N: usize> Collect
340 for Result<&'a mut Builder<'a, N>, TkStrError>
341{
342 #[inline]
343 fn collect(self) -> Result<TokenString, TkStrError> {
344 self?.collect_checked()
345 }
346}
347
348impl<'a, const N: usize> Collect for &'a mut Builder<'a, N> {
349 #[inline]
350 fn collect(self) -> Result<TokenString, TkStrError> {
351 self.collect_checked()
352 }
353}
354
355#[macro_export]
356macro_rules! num_args {
358 () => { 0_usize };
359 ($_x:tt $($xs:tt)*) => { 1_usize + $crate::num_args!($($xs)*) };
360}
361
362#[macro_export]
378macro_rules! concat {
379 ( $x0:expr, $($xs:expr),+ ) => {{
380 $crate::Builder::<'_, {$crate::num_args!($x0 $($xs)*)}>::new($x0)
381 $( .concat($xs) )+.collect()
382 }};
383}
384
385#[cfg(test)]
391mod prefix {
392 extern crate std;
393 use assert2::check;
394
395 use crate::{Builder, TokenString};
396
397 #[test]
398 fn concat_empty_unchecked() {
399 let s1 = TokenString::default();
400 let res = Builder::<6>::new(&s1).collect_unchecked();
401 check!(res.prefix[0] == 0);
402 check!(res.len == 0);
403 }
404
405 #[test]
406 fn concat_2_empty_unchecked() {
407 let s1 = TokenString::default();
408 let s2 = TokenString::default();
409 let res = Builder::<6>::new(&s1)
410 .concat_unchecked(&s2)
411 .collect_unchecked();
412 check!(res.prefix[0] == 0);
413 check!(res.len == 0);
414 }
415
416 #[test]
417 fn concat_1_unchecked() {
418 let s1 = TokenString::from_str_unchecked("12");
419 let res = Builder::<6>::new(&s1).collect_unchecked();
420 check!(&res.prefix[.. 2] == b"12");
421 check!(res.len == 2);
422 }
423
424 #[test]
425 fn concat_1_empty_unchecked() {
426 let s1 = TokenString::from_str_unchecked("12");
427 let s2 = TokenString::default();
428 let res = Builder::<6>::new(&s1)
429 .concat_unchecked(&s2)
430 .collect_unchecked();
431 check!(&res.prefix[.. 2] == b"12");
432 check!(res.len == 2);
433 }
434
435 #[test]
436 fn concat_empty_1_unchecked() {
437 let s1 = TokenString::default();
438 let s2 = TokenString::from_str_unchecked("12");
439 let res = Builder::<6>::new(&s1)
440 .concat_unchecked(&s2)
441 .collect_unchecked();
442 check!(&res.prefix[.. 2] == b"12");
443 }
444
445
446 #[test]
447 fn concat_2_unchecked() {
448 let s1 = TokenString::from_str_unchecked("12");
449 let s2 = TokenString::from_str_unchecked("34");
450 let res = Builder::<6>::new(&s1)
451 .concat_unchecked(&s2)
452 .collect_unchecked();
453 check!(&res.prefix[.. 4] == b"1234");
454 check!(res.len == 4);
455 }
456
457 #[test]
458 fn concat_1_empty_1_unchecked() {
459 let s1 = TokenString::from_str_unchecked("12");
460 let s2 = TokenString::from_str_unchecked("");
461 let s3 = TokenString::from_str_unchecked("34");
462 let res = Builder::<6>::new(&s1)
463 .concat_unchecked(&s2)
464 .concat_unchecked(&s3)
465 .collect_unchecked();
466 check!(&res.prefix[.. 4] == b"1234");
467 check!(res.len == 4);
468 }
469
470 #[test]
471 fn concat_3_unchecked() {
472 let s1 = TokenString::from_str_unchecked("12");
473 let s2 = TokenString::from_str_unchecked("34");
474 let s3 = TokenString::from_str_unchecked("56");
475 let res = Builder::<6>::new(&s1)
476 .concat_unchecked(&s2)
477 .concat_unchecked(&s3)
478 .collect_unchecked();
479 check!(&res.prefix == b"123456");
480 check!(res.len == 6);
481 }
482 #[test]
483 fn concat_empty_checked() {
484 let s1 = TokenString::default();
485 let res = Builder::<6>::new(&s1).collect_checked().unwrap();
486 check!(res.prefix[0] == 0);
487 check!(res.len == 0);
488 }
489
490 #[test]
491 fn concat_2_empty_checked() {
492 let s1 = TokenString::default();
493 let s2 = TokenString::default();
494 let res = Builder::<6>::new(&s1)
495 .concat_checked(&s2)
496 .unwrap()
497 .collect_checked()
498 .unwrap();
499 check!(res.prefix[0] == 0);
500 check!(res.len == 0);
501 }
502
503 #[test]
504 fn concat_1_checked() {
505 let s1 = TokenString::from_str_unchecked("12");
506 let res = Builder::<6>::new(&s1).collect_checked().unwrap();
507 check!(&res.prefix[.. 2] == b"12");
508 check!(res.len == 2);
509 }
510
511 #[test]
512 fn concat_1_empty_checked() {
513 let s1 = TokenString::from_str_unchecked("12");
514 let s2 = TokenString::default();
515 let res = Builder::<6>::new(&s1)
516 .concat_checked(&s2)
517 .unwrap()
518 .collect_checked()
519 .unwrap();
520 check!(&res.prefix[.. 2] == b"12");
521 check!(res.len == 2);
522 }
523
524 #[test]
525 fn concat_empty_1_checked() {
526 let s1 = TokenString::default();
527 let s2 = TokenString::from_str_unchecked("12");
528 let res = Builder::<6>::new(&s1)
529 .concat_checked(&s2)
530 .unwrap()
531 .collect_checked()
532 .unwrap();
533 check!(&res.prefix[.. 2] == b"12");
534 }
535
536
537 #[test]
538 fn concat_2_checked() {
539 let s1 = TokenString::from_str_unchecked("12");
540 let s2 = TokenString::from_str_unchecked("34");
541 let res = Builder::<6>::new(&s1)
542 .concat_checked(&s2)
543 .unwrap()
544 .collect_checked()
545 .unwrap();
546 check!(&res.prefix[.. 4] == b"1234");
547 check!(res.len == 4);
548 }
549
550 #[test]
551 fn concat_1_empty_1_checked() {
552 let s1 = TokenString::from_str_unchecked("12");
553 let s2 = TokenString::from_str_unchecked("");
554 let s3 = TokenString::from_str_unchecked("34");
555 let res = Builder::<6>::new(&s1)
556 .concat_checked(&s2)
557 .unwrap()
558 .concat_checked(&s3)
559 .unwrap()
560 .collect_checked()
561 .unwrap();
562 check!(&res.prefix[.. 4] == b"1234");
563 check!(res.len == 4);
564 }
565
566 #[test]
567 fn concat_3_checked() {
568 let s1 = TokenString::from_str_unchecked("12");
569 let s2 = TokenString::from_str_unchecked("34");
570 let s3 = TokenString::from_str_unchecked("56");
571 let res = Builder::<6>::new(&s1)
572 .concat_checked(&s2)
573 .unwrap()
574 .concat_checked(&s3)
575 .unwrap()
576 .collect_checked()
577 .unwrap();
578 check!(&res.prefix == b"123456");
579 check!(res.len == 6);
580 }
581}
582
583#[cfg(test)]
584mod small {
585 extern crate std;
586 use assert2::check;
587
588 use crate::{Builder, TokenString};
589
590
591 #[test]
592 fn concat_1_unchecked() {
593 let s1 = TokenString::from_str_unchecked("1234567");
594 let res = Builder::<6>::new(&s1).collect_unchecked();
595 check!(&res.prefix == b"123456");
596 check!(unsafe { res.u.small[0] } == b'7');
599 check!(res.len == 7);
600 }
601
602 #[test]
603 fn concat_1_empty_unchecked() {
604 let s1 = TokenString::from_str_unchecked("1234567");
605 let s2 = TokenString::default();
606 let res = Builder::<6>::new(&s1)
607 .concat_unchecked(&s2)
608 .collect_unchecked();
609 check!(&res.prefix == b"123456");
610 check!(unsafe { res.u.small[0] } == b'7');
613 check!(res.len == 7);
614 }
615
616 #[test]
617 fn concat_empty_1_unchecked() {
618 let s1 = TokenString::default();
619 let s2 = TokenString::from_str_unchecked("1234567");
620 let res = Builder::<6>::new(&s1)
621 .concat_unchecked(&s2)
622 .collect_unchecked();
623 check!(&res.prefix == b"123456");
624 check!(unsafe { res.u.small[0] } == b'7');
627 check!(res.len == 7);
628 }
629
630
631 #[test]
632 fn concat_2_unchecked() {
633 let s1 = TokenString::from_str_unchecked("1234");
634 let s2 = TokenString::from_str_unchecked("5678");
635 let res = Builder::<6>::new(&s1)
636 .concat_unchecked(&s2)
637 .collect_unchecked();
638 check!(&res.prefix[.. 6] == b"123456");
639 check!(unsafe { &res.u.small[.. 2] } == b"78");
642 check!(res.len == 8);
643 }
644
645 #[test]
646 fn concat_2_pref_unchecked() {
647 let s1 = TokenString::from_str_unchecked("1");
648 let s2 = TokenString::from_str_unchecked("2345678");
649 let res = Builder::<6>::new(&s1)
650 .concat_unchecked(&s2)
651 .collect_unchecked();
652 check!(&res.prefix[.. 6] == b"123456");
653 check!(unsafe { &res.u.small[.. 2] } == b"78");
656 check!(res.len == 8);
657 }
658
659 #[test]
660 fn concat_1_empty_1_unchecked() {
661 let s1 = TokenString::from_str_unchecked("1234");
662 let s2 = TokenString::from_str_unchecked("");
663 let s3 = TokenString::from_str_unchecked("5678");
664 let res = Builder::<6>::new(&s1)
665 .concat_unchecked(&s2)
666 .concat_unchecked(&s3)
667 .collect_unchecked();
668 check!(&res.prefix[.. 6] == b"123456");
669 check!(unsafe { &res.u.small[.. 2] } == b"78");
672 check!(res.len == 8);
673 }
674
675 #[test]
676 fn concat_3_unchecked() {
677 let s1 = TokenString::from_str_unchecked("1234");
678 let s2 = TokenString::from_str_unchecked("5678");
679 let s3 = TokenString::from_str_unchecked("90ABCD");
680 let res = Builder::<6>::new(&s1)
681 .concat_unchecked(&s2)
682 .concat_unchecked(&s3)
683 .collect_unchecked();
684 check!(&res.prefix[.. 6] == b"123456");
685 check!(unsafe { &res.u.small[.. 8] } == b"7890ABCD");
688 check!(res.len == 14);
689 }
690
691 #[test]
692 fn concat_1_checked() {
693 let s1 = TokenString::from_str_unchecked("1234567");
694 let res = Builder::<6>::new(&s1).collect_checked().unwrap();
695 check!(&res.prefix == b"123456");
696 check!(unsafe { res.u.small[0] } == b'7');
699 check!(res.len == 7);
700 }
701
702 #[test]
703 fn concat_1_empty_checked() {
704 let s1 = TokenString::from_str_unchecked("1234567");
705 let s2 = TokenString::default();
706 let res = Builder::<6>::new(&s1)
707 .concat_checked(&s2)
708 .unwrap()
709 .collect_checked()
710 .unwrap();
711 check!(&res.prefix == b"123456");
712 check!(unsafe { res.u.small[0] } == b'7');
715 check!(res.len == 7);
716 }
717
718 #[test]
719 fn concat_empty_1_checked() {
720 let s1 = TokenString::default();
721 let s2 = TokenString::from_str_unchecked("1234567");
722 let res = Builder::<6>::new(&s1)
723 .concat_checked(&s2)
724 .unwrap()
725 .collect_checked()
726 .unwrap();
727 check!(&res.prefix == b"123456");
728 check!(unsafe { res.u.small[0] } == b'7');
731 check!(res.len == 7);
732 }
733
734
735 #[test]
736 fn concat_2_checked() {
737 let s1 = TokenString::from_str_unchecked("1234");
738 let s2 = TokenString::from_str_unchecked("5678");
739 let res = Builder::<6>::new(&s1)
740 .concat_checked(&s2)
741 .unwrap()
742 .collect_checked()
743 .unwrap();
744 check!(&res.prefix[.. 6] == b"123456");
745 check!(unsafe { &res.u.small[.. 2] } == b"78");
748 check!(res.len == 8);
749 }
750
751 #[test]
752 fn concat_2_pref_checked() {
753 let s1 = TokenString::from_str_unchecked("1");
754 let s2 = TokenString::from_str_unchecked("2345678");
755 let res = Builder::<6>::new(&s1)
756 .concat_checked(&s2)
757 .unwrap()
758 .collect_checked()
759 .unwrap();
760 check!(&res.prefix[.. 6] == b"123456");
761 check!(unsafe { &res.u.small[.. 2] } == b"78");
764 check!(res.len == 8);
765 }
766
767 #[test]
768 fn concat_1_empty_1_checked() {
769 let s1 = TokenString::from_str_unchecked("1234");
770 let s2 = TokenString::from_str_unchecked("");
771 let s3 = TokenString::from_str_unchecked("5678");
772 let res = Builder::<6>::new(&s1)
773 .concat_checked(&s2)
774 .unwrap()
775 .concat_checked(&s3)
776 .unwrap()
777 .collect_checked()
778 .unwrap();
779 check!(&res.prefix[.. 6] == b"123456");
780 check!(unsafe { &res.u.small[.. 2] } == b"78");
783 check!(res.len == 8);
784 }
785
786 #[test]
787 fn concat_3_checked() {
788 let s1 = TokenString::from_str_unchecked("1234");
789 let s2 = TokenString::from_str_unchecked("5678");
790 let s3 = TokenString::from_str_unchecked("90ABCD");
791 let res = Builder::<6>::new(&s1)
792 .concat_checked(&s2)
793 .unwrap()
794 .concat_checked(&s3)
795 .unwrap()
796 .collect_checked()
797 .unwrap();
798 check!(&res.prefix[.. 6] == b"123456");
799 check!(unsafe { &res.u.small[.. 8] } == b"7890ABCD");
802 check!(res.len == 14);
803 }
804}
805
806#[cfg(test)]
807mod heap {
808 extern crate std;
809 use assert2::check;
810
811 use crate::{Builder, TokenString};
812
813
814 #[test]
815 fn concat_1_unchecked() {
816 let s1 = TokenString::from_str_unchecked("1234567890ABCDE");
817 let res = Builder::<6>::new(&s1).collect_unchecked();
818 check!(&res.prefix == b"123456");
819 check!(
820 unsafe { res.u.ptr.as_string_manually(res.len as usize) }
823 == "1234567890ABCDE"
824 );
825 check!(res.len == 15);
826 }
827
828 #[test]
829 fn concat_1_empty_unchecked() {
830 let s1 = TokenString::from_str_unchecked("1234567890ABCDE");
831 let s2 = TokenString::default();
832 let res = Builder::<6>::new(&s1)
833 .concat_unchecked(&s2)
834 .collect_unchecked();
835 check!(&res.prefix == b"123456");
836 check!(
837 unsafe { res.u.ptr.as_string_manually(res.len as usize) }
840 == "1234567890ABCDE"
841 );
842 check!(res.len == 15);
843 }
844
845 #[test]
846 fn concat_empty_1_unchecked() {
847 let s1 = TokenString::default();
848 let s2 = TokenString::from_str_unchecked("1234567890ABCDE");
849 let res = Builder::<6>::new(&s1)
850 .concat_unchecked(&s2)
851 .collect_unchecked();
852 check!(&res.prefix == b"123456");
853 check!(
854 unsafe { res.u.ptr.as_string_manually(res.len as usize) }
857 == "1234567890ABCDE"
858 );
859 check!(res.len == 15);
860 }
861
862
863 #[test]
864 fn concat_2_unchecked() {
865 let s1 = TokenString::from_str_unchecked("1234");
866 let s2 = TokenString::from_str_unchecked("567890ABCDE");
867 let res = Builder::<6>::new(&s1)
868 .concat_unchecked(&s2)
869 .collect_unchecked();
870 check!(&res.prefix[.. 6] == b"123456");
871 check!(
872 unsafe { res.u.ptr.as_string_manually(res.len as usize) }
875 == "1234567890ABCDE"
876 );
877 check!(res.len == 15);
878 }
879
880 #[test]
881 fn concat_1_empty_1_unchecked() {
882 let s1 = TokenString::from_str_unchecked("1234");
883 let s2 = TokenString::from_str_unchecked("");
884 let s3 = TokenString::from_str_unchecked("567890ABCDE");
885 let res = Builder::<6>::new(&s1)
886 .concat_unchecked(&s2)
887 .concat_unchecked(&s3)
888 .collect_unchecked();
889 check!(&res.prefix[.. 6] == b"123456");
890 check!(
891 unsafe { res.u.ptr.as_string_manually(res.len as usize) }
894 == "1234567890ABCDE"
895 );
896 check!(res.len == 15);
897 }
898
899 #[test]
900 fn concat_3_unchecked() {
901 let s1 = TokenString::from_str_unchecked("1234");
902 let s2 = TokenString::from_str_unchecked("5678");
903 let s3 = TokenString::from_str_unchecked("90ABCDE");
904 let res = Builder::<6>::new(&s1)
905 .concat_unchecked(&s2)
906 .concat_unchecked(&s3)
907 .collect_unchecked();
908 check!(&res.prefix[.. 6] == b"123456");
909 check!(
910 unsafe { res.u.ptr.as_string_manually(res.len as usize) }
913 == "1234567890ABCDE"
914 );
915 check!(res.len == 15);
916 }
917
918 #[test]
919 fn concat_1_checked() {
920 let s1 = TokenString::from_str_unchecked("1234567890ABCDE");
921 let res = Builder::<6>::new(&s1).collect_checked().unwrap();
922 check!(&res.prefix == b"123456");
923 check!(
924 unsafe { res.u.ptr.as_string_manually(res.len as usize) }
927 == "1234567890ABCDE"
928 );
929 check!(res.len == 15);
930 }
931
932 #[test]
933 fn concat_1_empty_checked() {
934 let s1 = TokenString::from_str_unchecked("1234567890ABCDE");
935 let s2 = TokenString::default();
936 let res = Builder::<6>::new(&s1)
937 .concat_checked(&s2)
938 .unwrap()
939 .collect_checked()
940 .unwrap();
941 check!(&res.prefix == b"123456");
942 check!(
943 unsafe { res.u.ptr.as_string_manually(res.len as usize) }
946 == "1234567890ABCDE"
947 );
948 check!(res.len == 15);
949 }
950
951 #[test]
952 fn concat_empty_1_checked() {
953 let s1 = TokenString::default();
954 let s2 = TokenString::from_str_unchecked("1234567890ABCDE");
955 let res = Builder::<6>::new(&s1)
956 .concat_checked(&s2)
957 .unwrap()
958 .collect_checked()
959 .unwrap();
960 check!(&res.prefix == b"123456");
961 check!(
962 unsafe { res.u.ptr.as_string_manually(res.len as usize) }
965 == "1234567890ABCDE"
966 );
967 check!(res.len == 15);
968 }
969
970
971 #[test]
972 fn concat_2_checked() {
973 let s1 = TokenString::from_str_unchecked("1234");
974 let s2 = TokenString::from_str_unchecked("567890ABCDE");
975 let res = Builder::<6>::new(&s1)
976 .concat_checked(&s2)
977 .unwrap()
978 .collect_checked()
979 .unwrap();
980 check!(&res.prefix[.. 6] == b"123456");
981 check!(
982 unsafe { res.u.ptr.as_string_manually(res.len as usize) }
985 == "1234567890ABCDE"
986 );
987 check!(res.len == 15);
988 }
989
990 #[test]
991 fn concat_1_empty_1_checked() {
992 let s1 = TokenString::from_str_unchecked("1234");
993 let s2 = TokenString::from_str_unchecked("");
994 let s3 = TokenString::from_str_unchecked("567890ABCDE");
995 let res = Builder::<6>::new(&s1)
996 .concat_checked(&s2)
997 .unwrap()
998 .concat_checked(&s3)
999 .unwrap()
1000 .collect_checked()
1001 .unwrap();
1002 check!(&res.prefix[.. 6] == b"123456");
1003 check!(
1004 unsafe { res.u.ptr.as_string_manually(res.len as usize) }
1007 == "1234567890ABCDE"
1008 );
1009 check!(res.len == 15);
1010 }
1011
1012 #[test]
1013 fn concat_3_checked() {
1014 let s1 = TokenString::from_str_unchecked("1234");
1015 let s2 = TokenString::from_str_unchecked("5678");
1016 let s3 = TokenString::from_str_unchecked("90ABCDE");
1017 let res = Builder::<6>::new(&s1)
1018 .concat_checked(&s2)
1019 .unwrap()
1020 .concat_checked(&s3)
1021 .unwrap()
1022 .collect_checked()
1023 .unwrap();
1024 check!(&res.prefix[.. 6] == b"123456");
1025 check!(
1026 unsafe { res.u.ptr.as_string_manually(res.len as usize) }
1029 == "1234567890ABCDE"
1030 );
1031 check!(res.len == 15);
1032 }
1033}