Rust percent-encode and parse URL query strings

Rust
use std::collections::HashMap;

fn encode(s: &str) -> String {
    s.bytes().map(|b| if b.is_ascii_alphanumeric() || "-_.~".contains(b as char) {
        (b as char).to_string()
    } else if b == b' ' { "+".to_string() } else { format!("%{b:02X}") }).collect()
}

fn decode(s: &str) -> String {
    s.split('+').map(|chunk| chunk.split('%').enumerate().map(|(i, part)| {
        if i == 0 { return part.to_string(); }
        let cut = part.char_indices().nth(2).map(|(i, _)| i).unwrap_or(part.len());
        let (hex, rest) = part.split_at(cut);
        match u8::from_str_radix(hex, 16) { Ok(byte) => format!("{}{rest}", byte as char), Err(_) => format!("%{part}") }
    }).collect::<String>()).collect::<Vec<_>>().join(" ")
}

fn parse(query: &str) -> HashMap<String, String> {
    query.trim_start_matches('?').split('&').filter_map(|p| p.split_once('='))
        .map(|(k, v)| (decode(k), decode(v))).collect()
}