mod error;
pub(crate) mod query_or_fragment;
pub(crate) use self::error::{InvalidByte, InvalidComponent, InvalidPercentEncodedByte};
use crate::percent_encoded::PercentEncoded;
use shared_bytes::SharedStr;
pub(crate) const fn is_unreserved(b: u8) -> bool {
b.is_ascii_alphanumeric() || matches!(b, b'-' | b'.' | b'_' | b'~')
}
pub(crate) const fn is_sub_delimiter(b: u8) -> bool {
matches!(
b,
b'!' | b'$' | b'&' | b'\'' | b'(' | b')' | b'*' | b'+' | b',' | b';' | b'='
)
}
pub(crate) fn with_normalized_percent_encoding(
string: &str,
is_valid_byte: fn(u8) -> bool,
) -> Result<Option<SharedStr>, InvalidComponent> {
let mut normalized = String::new();
let mut index = 0;
while index < string.len() {
let byte = string.as_bytes()[index];
if byte == b'%' {
let encoded = PercentEncoded::from_bytes_at_index(string.as_bytes(), index)
.map_err(InvalidComponent::PercentEncoded)?;
if is_valid_byte(encoded.byte) {
if normalized.is_empty() {
normalized.reserve(string.len());
normalized.push_str(&string[..index]);
}
normalized.push(char::from(encoded.byte))
}
index += PercentEncoded::BYTES_LENGTH;
} else if is_valid_byte(byte) {
if !normalized.is_empty() {
normalized.push(char::from(byte))
}
index += 1;
} else {
return Err(InvalidComponent::Byte(InvalidByte { byte }));
}
}
Ok(if normalized.is_empty() {
None
} else {
Some(normalized.into())
})
}