pub fn encode_path(value: &str) -> String {
let mut out = String::with_capacity(value.len());
for byte in value.as_bytes() {
match byte {
b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'.' | b'_' | b'~' => {
out.push(*byte as char)
}
other => out.push_str(&format!("%{other:02X}")),
}
}
out
}
pub fn encode_query_component(value: &str) -> String {
encode_path(value)
}
#[cfg(test)]
mod tests {
use super::encode_path;
#[test]
fn escapes_reserved_characters() {
assert_eq!(encode_path("plain-id_1.2~3"), "plain-id_1.2~3");
assert_eq!(encode_path("id with/slash"), "id%20with%2Fslash");
assert_eq!(encode_path("../etc"), "..%2Fetc");
}
#[test]
fn escapes_multibyte_characters() {
assert_eq!(encode_path("агент"), "%D0%B0%D0%B3%D0%B5%D0%BD%D1%82");
}
}