cri-ref 0.0.2

Embedded-friendly equivalents of URIs
Documentation
/// A set of ASCII characters
///
/// It's just a type alias rather than a new type as it is used as a generic parameter.
///
/// Consequently, consider all functions in this module methods of this function; it will likely be
/// converted to a newtype once such types are available for const generics.
pub type AsciiSet = u128;

pub const UNRESERVED: AsciiSet = alphanumerics() | chars_to_ascii_set(b"-._~");
pub const SUBDELIMS: AsciiSet = chars_to_ascii_set(b"!$&'()*+,;=");
pub const GENDELIMS: AsciiSet = chars_to_ascii_set(b":/?#[]@");
pub const RESERVED: AsciiSet = GENDELIMS | SUBDELIMS;

pub const HOST_UE: AsciiSet = UNRESERVED | SUBDELIMS;
pub const PATH_UE: AsciiSet = UNRESERVED | SUBDELIMS | chars_to_ascii_set(b":@");
pub const QUERY_UE: AsciiSet = (UNRESERVED | SUBDELIMS | chars_to_ascii_set(b":@/?")) & !chars_to_ascii_set(b"&");
pub const FRAGMENT_UE: AsciiSet = UNRESERVED | SUBDELIMS | chars_to_ascii_set(b":@/?");

// preferably `s: &str`, but .chars() isn't const yet
const fn chars_to_ascii_set(s: &[u8]) -> AsciiSet {
    let mut result = 0;
    let mut i = 0;
    while i < s.len() {
        let c = s[i];
        if c < 128 {
            result |= 1u128 << c;
        } else {
            panic!("High characters can not be part in an ASCII set");
        }
        i += 1;
    }
    result
}

const fn alphanumerics() -> AsciiSet {
    let mut result = 0;
    let mut i = 0u8;
    while i < 128 {
        if i.is_ascii_alphanumeric() {
            result |= 1u128 << i;
        }
        i += 1;
    }
    result
}

/// Test if the character is in the set. Non-ASCII characters can not be in any set.
pub const fn contains(s: AsciiSet, c: char) -> bool {
    let c = c as u32;
    if c < 128 {
        s & (1 << c) != 0
    } else {
        false
    }
}

/// Test if the byte is in the set. High bytes can not be in any set.
pub const fn contains_byte(s: AsciiSet, c: u8) -> bool {
    if c < 128 {
        s & (1 << c) != 0
    } else {
        false
    }
}

#[test]
fn asciiset_examples() {
    assert!(contains(UNRESERVED, 'a'));
    assert!(contains(UNRESERVED, 'A'));
    assert!(contains(UNRESERVED, '9'));
    assert!(contains(UNRESERVED, '~'));
    assert!(!contains(UNRESERVED, '@'));

    assert!(contains(RESERVED, '['));
    assert!(!contains(RESERVED, 'A'));

    assert!(!contains_byte(RESERVED, b'A'));

    assert!(!contains_byte(QUERY_UE, b'&'));
}