use crate::helpers::iter_ext::IterExt;
use std::{ str, fmt::Debug, hash::Hasher };
pub trait Encoding: Copy + Clone + Debug + Default {
fn is_valid(bytes: &[u8]) -> bool;
fn is_eq(a: &[u8], b: &[u8]) -> bool {
a == b
}
fn hash(bytes: &[u8], hasher: &mut dyn Hasher) {
hasher.write(bytes);
}
}
pub trait SubsetOf<T: Encoding>: Encoding {}
#[derive(Copy, Clone, Debug, Default)]
pub struct Binary;
impl Encoding for Binary {
fn is_valid(_bytes: &[u8]) -> bool {
true
}
}
#[derive(Copy, Clone, Debug, Default)]
pub struct Utf8;
impl Encoding for Utf8 {
fn is_valid(bytes: &[u8]) -> bool {
str::from_utf8(bytes).is_ok()
}
}
#[derive(Copy, Clone, Debug, Default)]
pub struct Ascii;
impl Encoding for Ascii {
fn is_valid(bytes: &[u8]) -> bool {
fn is_printable(b: &u8) -> bool {
b.is_ascii_alphanumeric()
| b.is_ascii_whitespace()
| b.is_ascii_punctuation()
}
bytes.iter().all(is_printable)
}
}
impl SubsetOf<Binary> for Ascii {}
impl SubsetOf<Utf8> for Ascii {}
#[derive(Copy, Clone, Debug, Default)]
pub struct HeaderFieldKey;
impl Encoding for HeaderFieldKey {
fn is_valid(bytes: &[u8]) -> bool {
match bytes.len() {
0 => false,
_ => bytes.iter().all(|b| match *b {
b if b > 127 => false,
b if b < 32 => false,
b if b"()<>@,;:/[]?={} \t\"\\".contains(&b) => false,
_ => true
})
}
}
fn is_eq(a: &[u8], b: &[u8]) -> bool {
let a= str::from_utf8(a).unwrap().to_ascii_lowercase();
let b= str::from_utf8(b).unwrap().to_ascii_lowercase();
a == b
}
fn hash(bytes: &[u8], hasher: &mut dyn Hasher) {
let s = str::from_utf8(bytes).unwrap().to_ascii_lowercase();
hasher.write(s.as_bytes());
}
}
impl SubsetOf<Binary> for HeaderFieldKey {}
impl SubsetOf<Utf8> for HeaderFieldKey {}
impl SubsetOf<Ascii> for HeaderFieldKey {}
#[derive(Copy, Clone, Debug, Default)]
pub struct Uri;
impl Uri {
fn percent_encoding(b: u8, next: &mut dyn Iterator<Item = &u8>) -> bool {
match b {
b'%' => {
let ab = match next.take(2).collect_min(2) {
Some(ab) => ab,
None => return false
};
ab.iter().all(|b| b.is_ascii_hexdigit())
},
_ => return false
}
}
fn unreserved(b: u8) -> bool {
b.is_ascii_alphanumeric() || b"-._~".contains(&b)
}
fn gen_delims(b: u8) -> bool {
b":/?#[]@".contains(&b)
}
fn sub_delims(b: u8) -> bool {
b"!$&'()*+,;=".contains(&b)
}
fn reserved(b: u8) -> bool {
Self::gen_delims(b) || Self::sub_delims(b)
}
}
impl Encoding for Uri {
fn is_valid(bytes: &[u8]) -> bool {
let mut bytes = bytes.iter();
while let Some(b) = bytes.next() {
match *b {
b if Self::unreserved(b) => continue,
b if Self::reserved(b) => continue,
b => match Self::percent_encoding(b, &mut bytes) {
true => continue,
false => return false
}
}
}
true
}
}
impl SubsetOf<Binary> for Uri {}
impl SubsetOf<Utf8> for Uri {}
impl SubsetOf<Ascii> for Uri {}
#[derive(Copy, Clone, Debug, Default)]
pub struct UriQuery;
impl Encoding for UriQuery {
fn is_valid(bytes: &[u8]) -> bool {
let mut bytes = bytes.iter();
while let Some(b) = bytes.next() {
match *b {
b if Uri::unreserved(b) => continue,
b if Uri::sub_delims(b) => continue,
b':' | b'@' => continue,
b => match Uri::percent_encoding(b, &mut bytes) {
true => continue,
false => return false
}
}
}
true
}
}
impl SubsetOf<Binary> for UriQuery {}
impl SubsetOf<Utf8> for UriQuery {}
impl SubsetOf<Ascii> for UriQuery {}
#[derive(Copy, Clone, Debug, Default)]
pub struct Integer;
impl Encoding for Integer {
fn is_valid(bytes: &[u8]) -> bool {
bytes.iter().all(u8::is_ascii_digit)
}
}
impl SubsetOf<Binary> for Integer {}
impl SubsetOf<Utf8> for Integer {}
impl SubsetOf<Ascii> for Integer {}