use core::fmt::{Debug, Write};
use core::{borrow::Borrow, fmt, ops::Deref};
use bytes::Bytes;
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct Component<const MCL: usize>([u8]);
impl<const MCL: usize> Component<MCL> {
pub fn new(s: &[u8]) -> Result<&Self, InvalidComponentError> {
if s.len() <= MCL {
Ok(unsafe { Self::new_unchecked(s) })
} else {
Err(InvalidComponentError)
}
}
pub unsafe fn new_unchecked(s: &[u8]) -> &Self {
debug_assert!(s.len() <= MCL);
unsafe { &*(s as *const [u8] as *const Self) }
}
pub fn new_mut(s: &mut [u8]) -> Result<&mut Self, InvalidComponentError> {
if s.len() <= MCL {
Ok(unsafe { Self::new_mut_unchecked(s) })
} else {
Err(InvalidComponentError)
}
}
pub unsafe fn new_mut_unchecked(s: &mut [u8]) -> &mut Self {
debug_assert!(s.len() <= MCL);
unsafe { &mut *(s as *mut [u8] as *mut Self) }
}
pub fn new_empty() -> &'static Self {
unsafe { Self::new_unchecked(&[]) }
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
pub fn as_bytes_mut(&mut self) -> &mut [u8] {
&mut self.0
}
}
impl<const MCL: usize> Deref for Component<MCL> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<const MCL: usize> AsRef<[u8]> for Component<MCL> {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl<const MCL: usize> Borrow<[u8]> for Component<MCL> {
fn borrow(&self) -> &[u8] {
&self.0
}
}
impl<const MCL: usize> fmt::Debug for Component<MCL> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Component")
.field(&ComponentFmtHelper::new(&self, true))
.finish()
}
}
impl<const MCL: usize> fmt::Display for Component<MCL> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
ComponentFmtHelper::new(&self, true).fmt(f)
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct OwnedComponent<const MCL: usize>(pub(crate) Bytes);
impl<const MCL: usize> OwnedComponent<MCL> {
pub fn new(data: &[u8]) -> Result<Self, InvalidComponentError> {
if data.len() <= MCL {
Ok(unsafe { Self::new_unchecked(data) }) } else {
Err(InvalidComponentError)
}
}
pub unsafe fn new_unchecked(data: &[u8]) -> Self {
debug_assert!(data.len() <= MCL);
Self(Bytes::copy_from_slice(data))
}
pub fn new_empty() -> Self {
Self(Bytes::new())
}
pub fn as_bytes(&self) -> &[u8] {
self.0.as_ref()
}
}
impl<const MCL: usize> Deref for OwnedComponent<MCL> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.0.deref()
}
}
impl<const MCL: usize> AsRef<[u8]> for OwnedComponent<MCL> {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
impl<const MCL: usize> Borrow<[u8]> for OwnedComponent<MCL> {
fn borrow(&self) -> &[u8] {
self.0.borrow()
}
}
impl<const MCL: usize> fmt::Debug for OwnedComponent<MCL> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("OwnedComponent")
.field(&ComponentFmtHelper::new(self, true))
.finish()
}
}
impl<const MCL: usize> fmt::Display for OwnedComponent<MCL> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
ComponentFmtHelper::new(self, true).fmt(f)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct InvalidComponentError;
impl core::fmt::Display for InvalidComponentError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"Length of a component exceeded the maximum component length"
)
}
}
impl core::error::Error for InvalidComponentError {}
pub(crate) struct ComponentFmtHelper<'a, T> {
component: &'a T,
render_empty: bool,
}
impl<'a, T> ComponentFmtHelper<'a, T> {
pub(crate) fn new(component: &'a T, render_empty: bool) -> Self {
Self {
component,
render_empty,
}
}
}
impl<'a, T> fmt::Debug for ComponentFmtHelper<'a, T>
where
T: AsRef<[u8]>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.component.as_ref().is_empty() {
if self.render_empty {
write!(f, "<empty>")
} else {
Ok(())
}
} else {
for byte in self.component.as_ref().iter() {
percent_encode_fmt(f, *byte)?;
}
Ok(())
}
}
}
fn byte_is_unreserved(byte: u8) -> bool {
byte.is_ascii_alphanumeric() || byte == b'-' || byte == b'.' || byte == b'_' || byte == b'~'
}
fn percent_encode_fmt(f: &mut fmt::Formatter<'_>, byte: u8) -> fmt::Result {
if byte_is_unreserved(byte) {
f.write_char(unsafe { char::from_u32_unchecked(byte as u32) })
} else {
f.write_char('%')?;
let low = byte & 0b0000_1111;
let high = byte >> 4;
f.write_char(char::from_digit(high as u32, 16).unwrap())?;
f.write_char(char::from_digit(low as u32, 16).unwrap())
}
}
#[test]
#[cfg(feature = "std")]
fn test_fmt() {
assert_eq!(
&format!("{}", Component::<17>::new(b"").unwrap()),
"<empty>"
);
assert_eq!(&format!("{}", Component::<17>::new(b" ").unwrap()), "%20");
assert_eq!(
&format!("{}", Component::<17>::new(b".- ~_ab190%/").unwrap()),
".-%20~_ab190%25%2f"
);
}