url_utils/
decode.rs

1use std::borrow::Cow;
2use crate::Result;
3
4/// UrlDecode the given string
5pub fn decode(url: &str) -> Result<Cow<str>> {
6    if !url.contains(['%','+']) {
7        return Ok(url.into())
8    }
9    let mut result:Vec<u8> = Vec::new();
10    let mut it = url.as_bytes().iter();
11    while let Some(b) = it.next() {
12        if *b == b'+' {
13            result.push(b' ');
14            continue;
15        }
16        if *b != b'%' {
17            result.push(*b);
18            continue;
19        }
20        let first = it.next().ok_or("Missing byte after '%'")?;
21        let second = it.next().ok_or("Missing byte after '%'")?;
22        let first = from_hex_digit(*first)?;
23        let second = from_hex_digit(*second)?;
24        let c = first << 4 | second;
25        result.push(c);
26    };
27    let result = String::from_utf8(result).or_else(|err| Err(err.to_string()))?;
28    Ok(result.into())
29}
30
31#[inline(always)]
32fn from_hex_digit(digit: u8) -> Result<u8> {
33    match digit {
34        b'0'..=b'9' => Ok(digit - b'0'),
35        b'A'..=b'F' => Ok(digit - b'A' + 10),
36        b'a'..=b'f' => Ok(digit - b'a' + 10),
37        _ => Err(format!("{digit} is not a valid hex digit").into()),
38    }
39}
40