use super::PercentDecodeError;
use std::borrow::Cow;
pub fn percent_decode(input: &str) -> Result<Cow<'_, str>, PercentDecodeError> {
if !input.contains('%') {
return Ok(Cow::Borrowed(input));
}
let bytes = input.as_bytes();
let mut out = Vec::with_capacity(bytes.len());
let mut rest = bytes.iter().enumerate();
while let Some((index, &byte)) = rest.next() {
if byte == b'%' {
let high = rest.next().and_then(|(_, b)| hex_value(*b));
let low = rest.next().and_then(|(_, b)| hex_value(*b));
match (high, low) {
(Some(high), Some(low)) => out.push(high << 4 | low),
_ => return Err(PercentDecodeError::InvalidEscape { index }),
}
} else {
out.push(byte);
}
}
String::from_utf8(out)
.map(Cow::Owned)
.map_err(|_| PercentDecodeError::InvalidUtf8)
}
fn hex_value(byte: u8) -> Option<u8> {
char::from(byte)
.to_digit(16)
.and_then(|digit| u8::try_from(digit).ok())
}
#[cfg(test)]
#[path = "percent_decode.test.rs"]
mod tests;
#[cfg(test)]
#[path = "percent_decode.spec.rs"]
mod spec;