use crate::ParamError;
pub(super) fn decode_component(input: &str) -> Result<String, ParamError> {
let bytes = input.as_bytes();
let mut output = Vec::with_capacity(bytes.len());
let mut index = 0usize;
while index < bytes.len() {
match bytes[index] {
b'+' => {
output.push(b' ');
index += 1;
}
b'%' => {
if index + 2 >= bytes.len() {
return Err(ParamError::InvalidEncoding);
}
let high = hex(bytes[index + 1]).ok_or(ParamError::InvalidEncoding)?;
let low = hex(bytes[index + 2]).ok_or(ParamError::InvalidEncoding)?;
output.push((high << 4) | low);
index += 3;
}
byte => {
output.push(byte);
index += 1;
}
}
}
String::from_utf8(output).map_err(|_| ParamError::InvalidEncoding)
}
pub(super) fn encoded_len(input: &str) -> usize {
input
.bytes()
.map(|byte| {
if byte.is_ascii_alphanumeric() || matches!(byte, b'-' | b'_' | b'.' | b'~') {
1
} else {
3
}
})
.sum()
}
pub(super) fn encode_component(input: &str, output: &mut String) {
for byte in input.bytes() {
if byte.is_ascii_alphanumeric() || matches!(byte, b'-' | b'_' | b'.' | b'~') {
output.push(char::from(byte));
} else {
output.push('%');
output.push(char::from(b"0123456789ABCDEF"[(byte >> 4) as usize]));
output.push(char::from(b"0123456789ABCDEF"[(byte & 0x0f) as usize]));
}
}
}
const fn hex(byte: u8) -> Option<u8> {
match byte {
b'0'..=b'9' => Some(byte - b'0'),
b'a'..=b'f' => Some(byte - b'a' + 10),
b'A'..=b'F' => Some(byte - b'A' + 10),
_ => None,
}
}