kern 1.8.3

General library for Rust
Documentation
use std::char;

use crate::Fail;

pub struct Url<'a> {
    pub secure: bool,
    pub addr: String,
    pub path: &'a str,
    pub server_name: &'a str,
}

impl<'a> TryFrom<&'a str> for Url<'a> {
    type Error = Box<Fail>;

    fn try_from(url: &'a str) -> std::result::Result<Self, Box<Fail>> {
        let (url, secure) = if let Some(secure_url) = url.strip_prefix("https://") {
            (secure_url, true)
        } else {
            (
                url.strip_prefix("http://")
                    .ok_or_else(|| Fail::new("invalid url: protocol missing"))?,
                false,
            )
        };

        let (raw_addr, path) = url.split_once('/').unwrap_or((url, ""));
        let mut addr = None;
        let (server_name, _) = raw_addr.split_once(':').unwrap_or_else(|| {
            addr = Some(format!("{}:{}", raw_addr, if secure { 443 } else { 80 }));
            (raw_addr, "")
        });

        Ok(Self {
            secure,
            addr: addr.unwrap_or(raw_addr.into()),
            path,
            server_name,
        })
    }
}

pub fn url_encode(url: impl AsRef<str>, skip_slash: bool) -> String {
    let url = url.as_ref();
    let mut encoded = String::with_capacity(url.len());
    let mut bytes = [0u8; 4];

    url.chars().for_each(|c| {
        if matches!(c, 'A'..='Z' | 'a'..='z' | '0'..='9' | '-' | '.' | '_' | '~')
            || (skip_slash && c == '/')
        {
            encoded.push(c);
        } else {
            c.encode_utf8(&mut bytes);
            for byte in bytes.iter().take(c.len_utf8()) {
                encoded.push('%');
                encoded.push(to_hex(byte >> 4));
                encoded.push(to_hex(byte & 15));
            }
        }
    });
    encoded
}

fn to_hex(byte: u8) -> char {
    if byte < 10 {
        (b'0' + byte) as char
    } else {
        (b'A' - 10 + byte) as char
    }
}

#[test]
fn test_url_encode() {
    assert_eq!(
        "%20%21%22%23%24%25%26%27%28%29%2A%2B%2C-.%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%7B%7C%7D",
        url_encode(" !\"#$%&'()*+,-./:;<=>?@[\\]{|}", false)
    );
    assert_eq!(
        "%20%21%22%23%24%25%26%27%28%29%2A%2B%2C-./%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%7B%7C%7D",
        url_encode(" !\"#$%&'()*+,-./:;<=>?@[\\]{|}", true)
    );
}