safe_http 0.1.0-beta.4

Simple and safe HTTP types.
Documentation
pub(crate) struct Invalid {
    pub byte: u8,
}

/// Validate this is a token as specified in RFC 7230
pub(crate) const fn validate(bytes: &[u8]) -> Result<(), Invalid> {
    let mut index = 0;
    while index < bytes.len() {
        let byte = bytes[index];
        if !is_rfc_token_char(byte) {
            return Err(Invalid { byte });
        }
        index += 1;
    }
    Ok(())
}

const fn is_rfc_token_char(b: u8) -> bool {
    matches!(b, b'!'
        | b'#'
        | b'$'
        | b'%'
        | b'&'
        | b'\''
        | b'*'
        | b'+'
        | b'-'
        | b'.'
        | b'^'
        | b'_'
        | b'`'
        | b'|'
        | b'~'
        | b'0'..=b'9'
        | b'A'..=b'Z'
        | b'a'..=b'z'
    )
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::collections::BTreeSet;

    const SPECIAL_TOKEN_CHARS: &[u8] = &[
        b'!', b'#', b'$', b'%', b'&', b'\'', b'*', b'+', b'-', b'.', b'^', b'_', b'`', b'|', b'~',
    ];

    fn rfc_token_chars() -> BTreeSet<u8> {
        let digit_chars = b'0'..=b'9';
        let uppercase = b'A'..=b'Z';
        let lowercase = b'a'..=b'z';
        SPECIAL_TOKEN_CHARS
            .iter()
            .copied()
            .chain(digit_chars)
            .chain(uppercase)
            .chain(lowercase)
            .collect()
    }

    fn not_rfc_token_chars() -> BTreeSet<u8> {
        let t_chars = rfc_token_chars();
        (u8::MIN..=u8::MAX)
            .filter(|c| !t_chars.contains(c))
            .collect()
    }

    #[test]
    fn test_all_rfc_token_chars() {
        for c in rfc_token_chars() {
            print_char(c);
            assert!(is_rfc_token_char(c));
        }
    }

    #[test]
    fn test_all_not_rfc_token_chars() {
        for c in not_rfc_token_chars() {
            print_char(c);
            assert!(!is_rfc_token_char(c));
        }
    }

    fn print_char(b: u8) {
        println!("{}/'{}'", b, char::from(b));
    }
}