1use core::fmt::{Debug, Write};
2use core::ops::{
3 Index, IndexMut, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive,
4};
5use core::{borrow::Borrow, fmt, ops::Deref};
6
7use bytes::Bytes;
8
9#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
15#[repr(transparent)]
16pub struct Component<const MCL: usize>([u8]);
17
18impl<const MCL: usize> Component<MCL> {
19 pub fn new(s: &[u8]) -> Result<&Self, InvalidComponentError> {
31 if s.len() <= MCL {
32 Ok(unsafe { Self::new_unchecked(s) })
33 } else {
34 Err(InvalidComponentError)
35 }
36 }
37
38 pub unsafe fn new_unchecked(s: &[u8]) -> &Self {
55 debug_assert!(s.len() <= MCL);
56 unsafe { &*(s as *const [u8] as *const Self) }
57 }
58
59 pub fn new_mut(s: &mut [u8]) -> Result<&mut Self, InvalidComponentError> {
71 if s.len() <= MCL {
72 Ok(unsafe { Self::new_mut_unchecked(s) })
73 } else {
74 Err(InvalidComponentError)
75 }
76 }
77
78 pub unsafe fn new_mut_unchecked(s: &mut [u8]) -> &mut Self {
96 debug_assert!(s.len() <= MCL);
97 unsafe { &mut *(s as *mut [u8] as *mut Self) }
98 }
99
100 pub fn new_empty() -> &'static Self {
102 unsafe { Self::new_unchecked(&[]) }
103 }
104
105 pub fn as_bytes(&self) -> &[u8] {
112 &self.0
113 }
114
115 pub fn as_bytes_mut(&mut self) -> &mut [u8] {
125 &mut self.0
126 }
127}
128
129impl<const MCL: usize> Deref for Component<MCL> {
130 type Target = [u8];
131
132 fn deref(&self) -> &Self::Target {
133 &self.0
134 }
135}
136
137impl<const MCL: usize> AsRef<[u8]> for Component<MCL> {
138 fn as_ref(&self) -> &[u8] {
139 &self.0
140 }
141}
142
143impl<const MCL: usize> Borrow<[u8]> for Component<MCL> {
144 fn borrow(&self) -> &[u8] {
145 &self.0
146 }
147}
148
149impl<const MCL: usize> fmt::Debug for Component<MCL> {
150 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151 f.debug_tuple("Component")
152 .field(&ComponentFmtHelper::new(&self, true))
153 .finish()
154 }
155}
156
157impl<const MCL: usize> fmt::Display for Component<MCL> {
158 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159 ComponentFmtHelper::new(&self, true).fmt(f)
160 }
161}
162
163impl<const MCL: usize> IndexMut<usize> for Component<MCL> {
164 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
165 &mut self.0[index]
166 }
167}
168
169impl<const MCL: usize> Index<usize> for Component<MCL> {
170 type Output = u8;
171
172 fn index(&self, index: usize) -> &Self::Output {
173 &self.0[index]
174 }
175}
176
177impl<const MCL: usize> Index<Range<usize>> for Component<MCL> {
178 type Output = Component<MCL>;
179
180 fn index(&self, index: Range<usize>) -> &Self::Output {
181 unsafe { Component::new_unchecked(&self.0[index]) }
185 }
186}
187
188impl<const MCL: usize> Index<RangeFrom<usize>> for Component<MCL> {
189 type Output = Component<MCL>;
190
191 fn index(&self, index: RangeFrom<usize>) -> &Self::Output {
192 unsafe { Component::new_unchecked(&self.0[index]) }
196 }
197}
198
199impl<const MCL: usize> Index<RangeTo<usize>> for Component<MCL> {
200 type Output = Component<MCL>;
201
202 fn index(&self, index: RangeTo<usize>) -> &Self::Output {
203 unsafe { Component::new_unchecked(&self.0[index]) }
207 }
208}
209
210impl<const MCL: usize> Index<RangeFull> for Component<MCL> {
211 type Output = Component<MCL>;
212
213 fn index(&self, index: RangeFull) -> &Self::Output {
214 unsafe { Component::new_unchecked(&self.0[index]) }
218 }
219}
220
221impl<const MCL: usize> Index<RangeInclusive<usize>> for Component<MCL> {
222 type Output = Component<MCL>;
223
224 fn index(&self, index: RangeInclusive<usize>) -> &Self::Output {
225 unsafe { Component::new_unchecked(&self.0[index]) }
229 }
230}
231
232impl<const MCL: usize> Index<RangeToInclusive<usize>> for Component<MCL> {
233 type Output = Component<MCL>;
234
235 fn index(&self, index: RangeToInclusive<usize>) -> &Self::Output {
236 unsafe { Component::new_unchecked(&self.0[index]) }
240 }
241}
242
243#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
247pub struct OwnedComponent<const MCL: usize>(pub(crate) Bytes);
248
249impl<const MCL: usize> OwnedComponent<MCL> {
250 pub fn new(data: &[u8]) -> Result<Self, InvalidComponentError> {
264 if data.len() <= MCL {
265 Ok(unsafe { Self::new_unchecked(data) }) } else {
267 Err(InvalidComponentError)
268 }
269 }
270
271 pub unsafe fn new_unchecked(data: &[u8]) -> Self {
290 debug_assert!(data.len() <= MCL);
291 Self(Bytes::copy_from_slice(data))
292 }
293
294 pub fn new_empty() -> Self {
309 Self(Bytes::new())
310 }
311
312 pub fn as_bytes(&self) -> &[u8] {
319 self.0.as_ref()
320 }
321}
322
323impl<const MCL: usize> Deref for OwnedComponent<MCL> {
324 type Target = [u8];
325
326 fn deref(&self) -> &Self::Target {
327 self.0.deref()
328 }
329}
330
331impl<const MCL: usize> AsRef<[u8]> for OwnedComponent<MCL> {
332 fn as_ref(&self) -> &[u8] {
333 self.0.as_ref()
334 }
335}
336
337impl<const MCL: usize> Borrow<[u8]> for OwnedComponent<MCL> {
338 fn borrow(&self) -> &[u8] {
339 self.0.borrow()
340 }
341}
342
343impl<const MCL: usize> fmt::Debug for OwnedComponent<MCL> {
344 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
345 f.debug_tuple("OwnedComponent")
346 .field(&ComponentFmtHelper::new(self, true))
347 .finish()
348 }
349}
350
351impl<const MCL: usize> fmt::Display for OwnedComponent<MCL> {
352 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
353 ComponentFmtHelper::new(self, true).fmt(f)
354 }
355}
356
357impl<const MCL: usize> Index<usize> for OwnedComponent<MCL> {
358 type Output = u8;
359
360 fn index(&self, index: usize) -> &Self::Output {
361 &self.0[index]
362 }
363}
364
365impl<const MCL: usize> Index<Range<usize>> for OwnedComponent<MCL> {
366 type Output = Component<MCL>;
367
368 fn index(&self, index: Range<usize>) -> &Self::Output {
369 unsafe { Component::new_unchecked(&self.0[index]) }
373 }
374}
375
376impl<const MCL: usize> Index<RangeFrom<usize>> for OwnedComponent<MCL> {
377 type Output = Component<MCL>;
378
379 fn index(&self, index: RangeFrom<usize>) -> &Self::Output {
380 unsafe { Component::new_unchecked(&self.0[index]) }
384 }
385}
386
387impl<const MCL: usize> Index<RangeTo<usize>> for OwnedComponent<MCL> {
388 type Output = Component<MCL>;
389
390 fn index(&self, index: RangeTo<usize>) -> &Self::Output {
391 unsafe { Component::new_unchecked(&self.0[index]) }
395 }
396}
397
398impl<const MCL: usize> Index<RangeFull> for OwnedComponent<MCL> {
399 type Output = Component<MCL>;
400
401 fn index(&self, index: RangeFull) -> &Self::Output {
402 unsafe { Component::new_unchecked(&self.0[index]) }
406 }
407}
408
409impl<const MCL: usize> Index<RangeInclusive<usize>> for OwnedComponent<MCL> {
410 type Output = Component<MCL>;
411
412 fn index(&self, index: RangeInclusive<usize>) -> &Self::Output {
413 unsafe { Component::new_unchecked(&self.0[index]) }
417 }
418}
419
420impl<const MCL: usize> Index<RangeToInclusive<usize>> for OwnedComponent<MCL> {
421 type Output = Component<MCL>;
422
423 fn index(&self, index: RangeToInclusive<usize>) -> &Self::Output {
424 unsafe { Component::new_unchecked(&self.0[index]) }
428 }
429}
430
431#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
432pub struct InvalidComponentError;
441
442impl core::fmt::Display for InvalidComponentError {
443 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
444 write!(
445 f,
446 "Length of a component exceeded the maximum component length"
447 )
448 }
449}
450
451impl core::error::Error for InvalidComponentError {}
452
453pub(crate) struct ComponentFmtHelper<'a, T> {
454 component: &'a T,
455 render_empty: bool,
457}
458
459impl<'a, T> ComponentFmtHelper<'a, T> {
460 pub(crate) fn new(component: &'a T, render_empty: bool) -> Self {
461 Self {
462 component,
463 render_empty,
464 }
465 }
466}
467
468impl<'a, T> fmt::Debug for ComponentFmtHelper<'a, T>
469where
470 T: AsRef<[u8]>,
471{
472 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
473 if self.component.as_ref().is_empty() {
474 if self.render_empty {
475 write!(f, "<empty>")
476 } else {
477 Ok(())
478 }
479 } else {
480 for byte in self.component.as_ref().iter() {
481 percent_encode_fmt(f, *byte)?;
482 }
483 Ok(())
484 }
485 }
486}
487
488fn byte_is_unreserved(byte: u8) -> bool {
489 byte.is_ascii_alphanumeric() || byte == b'-' || byte == b'.' || byte == b'_' || byte == b'~'
490}
491
492fn percent_encode_fmt(f: &mut fmt::Formatter<'_>, byte: u8) -> fmt::Result {
493 if byte_is_unreserved(byte) {
494 f.write_char(unsafe { char::from_u32_unchecked(byte as u32) })
495 } else {
496 f.write_char('%')?;
497 let low = byte & 0b0000_1111;
498 let high = byte >> 4;
499 f.write_char(char::from_digit(high as u32, 16).unwrap())?;
500 f.write_char(char::from_digit(low as u32, 16).unwrap())
501 }
502}
503
504#[test]
505#[cfg(feature = "std")]
506fn test_fmt() {
507 assert_eq!(
508 &format!("{}", Component::<17>::new(b"").unwrap()),
509 "<empty>"
510 );
511 assert_eq!(&format!("{}", Component::<17>::new(b" ").unwrap()), "%20");
512 assert_eq!(
513 &format!("{}", Component::<17>::new(b".- ~_ab190%/").unwrap()),
514 ".-%20~_ab190%25%2f"
515 );
516}
517
518#[cfg(test)]
519mod tests {
520 use super::*;
521
522 #[test]
525 fn index_mut() {
526 let base = &mut [b'y', b'a', b'y'];
527 let slice = &mut [b'y', b'a', b'y'][..];
528 let compo = Component::<8>::new_mut(base).unwrap();
529
530 compo[0] = b'b';
531 slice[0] = b'b';
532
533 assert_eq!(&b"bay"[..], slice);
534 assert_eq!(&b"bay"[..], compo.as_bytes());
535 }
536
537 #[test]
538 fn index() {
539 let slice = &b"sunshine"[..];
540 let compo = Component::<8>::new(b"sunshine").unwrap();
541 assert_eq!(slice[1], compo[1]);
542 assert_eq!(slice[5], compo[5]);
543 }
544
545 #[test]
546 #[should_panic]
547 fn index_out_of_bound() {
548 let compo = Component::<8>::new(b"sunshine").unwrap();
549 let _ = &compo[8];
550 }
551
552 #[test]
553 fn range() {
554 let slice = &b"sunshine"[..];
555 let compo = Component::<8>::new(b"sunshine").unwrap();
556 assert_eq!(&slice[1..3], compo[1..3].as_bytes());
557 }
558
559 #[test]
560 fn range_0_0() {
561 let slice = &b"sunshine"[..];
562 let compo = Component::<8>::new(b"sunshine").unwrap();
563 assert_eq!(&slice[0..0], compo[0..0].as_bytes());
564 }
565
566 #[test]
567 fn rangefull() {
568 let slice = &b"sunshine"[..];
569 let compo = Component::<8>::new(b"sunshine").unwrap();
570 assert_eq!(slice, compo[..].as_bytes());
571 }
572
573 #[test]
574 fn rangefrom() {
575 let slice = &b"sunshine"[..];
576 let compo = Component::<8>::new(b"sunshine").unwrap();
577 assert_eq!(&slice[1..], compo[1..].as_bytes());
578 }
579
580 #[test]
581 fn rangeto() {
582 let slice = &b"sunshine"[..];
583 let compo = Component::<8>::new(b"sunshine").unwrap();
584 assert_eq!(&slice[..3], compo[..3].as_bytes());
585 }
586
587 #[test]
588 #[should_panic]
589 #[allow(clippy::reversed_empty_ranges)]
590 fn range_start_bigger_than_end() {
591 let compo = Component::<8>::new(b"sunshine").unwrap();
592 let _ = &compo[2..1];
593 }
594
595 #[test]
596 fn rangefrom_start_equal_component() {
597 let slice = &b"sunshine"[..];
598 let compo = Component::<8>::new(b"sunshine").unwrap();
599 assert_eq!(&slice[8..], compo[8..].as_bytes());
600 }
601
602 #[test]
603 #[should_panic]
604 fn rangefrom_start_bigger_than_component() {
605 let compo = Component::<8>::new(b"sunshine").unwrap();
606 let _ = &compo[9..];
607 }
608
609 #[test]
610 #[should_panic]
611 fn rangeto_end_bigger_than_component() {
612 let compo = Component::<8>::new(b"sunshine").unwrap();
613 let _ = &compo[..9];
614 }
615
616 #[test]
619 fn owned_index() {
620 let slice = &b"sunshine"[..];
621 let compo = OwnedComponent::<8>::new(b"sunshine").unwrap();
622 assert_eq!(slice[1], compo[1]);
623 assert_eq!(slice[5], compo[5]);
624 }
625
626 #[test]
627 #[should_panic]
628 fn owned_index_out_of_bound() {
629 let compo = OwnedComponent::<8>::new(b"sunshine").unwrap();
630 let _ = &compo[8];
631 }
632
633 #[test]
634 fn owned_range() {
635 let slice = &b"sunshine"[..];
636 let compo = OwnedComponent::<8>::new(b"sunshine").unwrap();
637 assert_eq!(&slice[1..3], compo[1..3].as_bytes());
638 }
639
640 #[test]
641 fn owned_range_0_0() {
642 let slice = &b"sunshine"[..];
643 let compo = OwnedComponent::<8>::new(b"sunshine").unwrap();
644 assert_eq!(&slice[0..0], compo[0..0].as_bytes());
645 }
646
647 #[test]
648 fn owned_rangefull() {
649 let slice = &b"sunshine"[..];
650 let compo = OwnedComponent::<8>::new(b"sunshine").unwrap();
651 assert_eq!(slice, compo[..].as_bytes());
652 }
653
654 #[test]
655 fn owned_rangefrom() {
656 let slice = &b"sunshine"[..];
657 let compo = OwnedComponent::<8>::new(b"sunshine").unwrap();
658 assert_eq!(&slice[1..], compo[1..].as_bytes());
659 }
660
661 #[test]
662 fn owned_rangeto() {
663 let slice = &b"sunshine"[..];
664 let compo = OwnedComponent::<8>::new(b"sunshine").unwrap();
665 assert_eq!(&slice[..3], compo[..3].as_bytes());
666 }
667
668 #[test]
669 #[should_panic]
670 #[allow(clippy::reversed_empty_ranges)]
671 fn owned_range_start_bigger_than_end() {
672 let compo = OwnedComponent::<8>::new(b"sunshine").unwrap();
673 let _ = &compo[2..1];
674 }
675
676 #[test]
677 fn owned_rangefrom_start_equal_component() {
678 let slice = &b"sunshine"[..];
679 let compo = OwnedComponent::<8>::new(b"sunshine").unwrap();
680 assert_eq!(&slice[8..], compo[8..].as_bytes());
681 }
682
683 #[test]
684 #[should_panic]
685 fn owned_rangefrom_start_bigger_than_component() {
686 let compo = OwnedComponent::<8>::new(b"sunshine").unwrap();
687 let _ = &compo[9..];
688 }
689
690 #[test]
691 #[should_panic]
692 fn owned_rangeto_end_bigger_than_component() {
693 let compo = OwnedComponent::<8>::new(b"sunshine").unwrap();
694 let _ = &compo[..9];
695 }
696}