use std::fmt::Write as _;
pub fn encode_unreserved_into(out: &mut String, input: &str) {
out.reserve(input.len());
for b in input.bytes() {
let safe = b.is_ascii_alphanumeric() || matches!(b, b'-' | b'_' | b'.' | b'~');
if safe {
out.push(b as char);
} else {
let _ = write!(out, "%{:02X}", b);
}
}
}
pub fn encode_unreserved(input: &str) -> String {
let unsafe_count = input
.bytes()
.filter(|b| !b.is_ascii_alphanumeric() && !matches!(*b, b'-' | b'_' | b'.' | b'~'))
.count();
let mut out = String::with_capacity(input.len() + 2 * unsafe_count);
encode_unreserved_into(&mut out, input);
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn preserves_unreserved() {
assert_eq!(encode_unreserved("abc-XYZ_0.9~"), "abc-XYZ_0.9~");
}
#[test]
fn escapes_space_and_brackets() {
assert_eq!(
encode_unreserved("[Tool]: foo bar"),
"%5BTool%5D%3A%20foo%20bar"
);
}
#[test]
fn escapes_multibyte_utf8_byte_by_byte() {
assert_eq!(encode_unreserved("日"), "%E6%97%A5");
}
#[test]
fn escapes_crlf() {
assert_eq!(encode_unreserved("\r\n"), "%0D%0A");
}
}