sark 0.12.0

Simple Asynchronous Rust webKit - Server
Documentation
use std::ops::Range;

use o3::buffer::{Bytes, Retained};
use sark_core::{
    error::{Error, Result},
    utils::bytes::Ascii,
};

pub trait HeaderValue {
    fn eq_bytes(&self, expected: &[u8]) -> bool;

    fn eq_ignore_ascii_case(&self, expected: &[u8]) -> bool;

    fn as_range(&self) -> Range<usize>;

    fn copy_frame(&self) -> Bytes<Retained>;

    fn parse_usize(&self) -> Result<usize>;

    fn parse_u64(&self) -> Result<u64>;
}

impl HeaderValue for Bytes<Retained> {
    fn eq_bytes(&self, expected: &[u8]) -> bool {
        self.as_slice() == expected
    }

    fn eq_ignore_ascii_case(&self, expected: &[u8]) -> bool {
        self.as_slice().eq_ignore_ascii_case(expected)
    }

    fn as_range(&self) -> Range<usize> {
        0..self.len()
    }

    fn copy_frame(&self) -> Bytes<Retained> {
        self.clone()
    }

    fn parse_usize(&self) -> Result<usize> {
        Ascii::parse_usize(self.as_slice()).ok_or_else(Error::invalid_integer_header)
    }

    fn parse_u64(&self) -> Result<u64> {
        Ascii::parse_u64(self.as_slice()).ok_or_else(Error::invalid_integer_header)
    }
}

pub struct SliceValue<'a> {
    raw: &'a [u8],
    start: usize,
    end: usize,
}

impl<'a> SliceValue<'a> {
    pub const fn new(raw: &'a [u8], range: Range<usize>) -> Self {
        Self {
            raw,
            start: range.start,
            end: range.end,
        }
    }

    fn bytes(&self) -> &[u8] {
        self.raw.get(self.start..self.end).unwrap_or(&[])
    }
}

impl HeaderValue for SliceValue<'_> {
    fn eq_bytes(&self, expected: &[u8]) -> bool {
        self.bytes() == expected
    }

    fn eq_ignore_ascii_case(&self, expected: &[u8]) -> bool {
        self.bytes().eq_ignore_ascii_case(expected)
    }

    fn as_range(&self) -> Range<usize> {
        self.start..self.end
    }

    fn copy_frame(&self) -> Bytes<Retained> {
        Bytes::<Retained>::copy_from_slice(self.bytes())
    }

    fn parse_usize(&self) -> Result<usize> {
        Ascii::parse_usize(self.bytes()).ok_or_else(Error::invalid_integer_header)
    }

    fn parse_u64(&self) -> Result<u64> {
        Ascii::parse_u64(self.bytes()).ok_or_else(Error::invalid_integer_header)
    }
}

pub trait FieldValue: Sized {
    fn parse_value<V: HeaderValue>(value: &V) -> Result<Self>;

    fn parse_path<P: PathProbe>(_path: &P, _start: usize, _end: usize) -> Option<Self> {
        None
    }
}

impl FieldValue for Range<usize> {
    fn parse_value<V: HeaderValue>(value: &V) -> Result<Self> {
        Ok(value.as_range())
    }

    fn parse_path<P: PathProbe>(_path: &P, start: usize, end: usize) -> Option<Self> {
        Some(start..end)
    }
}

impl FieldValue for Bytes<Retained> {
    fn parse_value<V: HeaderValue>(value: &V) -> Result<Self> {
        Ok(value.copy_frame())
    }

    fn parse_path<P: PathProbe>(path: &P, start: usize, end: usize) -> Option<Self> {
        path.copy_range_frame(start, end)
    }
}

impl FieldValue for usize {
    fn parse_value<V: HeaderValue>(value: &V) -> Result<Self> {
        value.parse_usize()
    }

    fn parse_path<P: PathProbe>(path: &P, start: usize, end: usize) -> Option<Self> {
        path.parse_range_usize(start, end)
    }
}

impl FieldValue for u64 {
    fn parse_value<V: HeaderValue>(value: &V) -> Result<Self> {
        value.parse_u64()
    }

    fn parse_path<P: PathProbe>(path: &P, start: usize, end: usize) -> Option<Self> {
        path.parse_range_u64(start, end)
    }
}

impl FieldValue for bool {
    fn parse_value<V: HeaderValue>(value: &V) -> Result<Self> {
        if value.eq_ignore_ascii_case(b"true") || value.eq_bytes(b"1") {
            return Ok(true);
        }
        if value.eq_ignore_ascii_case(b"false") || value.eq_bytes(b"0") {
            return Ok(false);
        }
        Err(Error::BadRequest("Invalid boolean field".into()))
    }

    fn parse_path<P: PathProbe>(path: &P, start: usize, end: usize) -> Option<Self> {
        if path.eq_range_ignore_ascii_case(start, end, b"true") || path.eq_range(start, end, b"1") {
            return Some(true);
        }
        if path.eq_range_ignore_ascii_case(start, end, b"false") || path.eq_range(start, end, b"0")
        {
            return Some(false);
        }
        None
    }
}

pub trait PathProbe {
    fn is_end(&self, idx: usize) -> bool;

    fn eq_bytes(&self, expected: &[u8]) -> bool;

    fn eq_range(&self, start: usize, end: usize, expected: &[u8]) -> bool;

    fn eq_range_ignore_ascii_case(&self, start: usize, end: usize, expected: &[u8]) -> bool;

    fn parse_range_usize(&self, start: usize, end: usize) -> Option<usize>;

    fn parse_range_u64(&self, start: usize, end: usize) -> Option<u64>;

    fn copy_range_frame(&self, start: usize, end: usize) -> Option<Bytes<Retained>>;

    fn next_seg(&self, idx: usize) -> Option<(usize, usize, usize)>;

    fn probe_literal(&self, cur: usize, lit: &[u8]) -> Option<usize> {
        let (start, end, nx) = self.next_seg(cur)?;
        if self.eq_range(start, end, lit) {
            Some(nx)
        } else {
            None
        }
    }
}

pub struct TargetPath<'a> {
    raw: &'a [u8],
}

impl<'a> TargetPath<'a> {
    pub const fn new(raw: &'a [u8]) -> Self {
        Self { raw }
    }
}

impl PathProbe for TargetPath<'_> {
    fn is_end(&self, idx: usize) -> bool {
        idx >= self.raw.len() || self.raw[idx] == b'?'
    }

    fn eq_bytes(&self, expected: &[u8]) -> bool {
        self.raw.starts_with(expected) && self.is_end(expected.len())
    }

    fn eq_range(&self, start: usize, end: usize, expected: &[u8]) -> bool {
        if end < start || end > self.raw.len() || expected.len() != end - start {
            return false;
        }
        self.raw[start..end] == *expected
    }

    fn eq_range_ignore_ascii_case(&self, start: usize, end: usize, expected: &[u8]) -> bool {
        if end < start || end > self.raw.len() || expected.len() != end - start {
            return false;
        }
        self.raw[start..end].eq_ignore_ascii_case(expected)
    }

    fn parse_range_usize(&self, start: usize, end: usize) -> Option<usize> {
        if end < start || end > self.raw.len() {
            return None;
        }
        Ascii::parse_usize(&self.raw[start..end])
    }

    fn parse_range_u64(&self, start: usize, end: usize) -> Option<u64> {
        if end < start || end > self.raw.len() {
            return None;
        }
        Ascii::parse_u64(&self.raw[start..end])
    }

    fn copy_range_frame(&self, start: usize, end: usize) -> Option<Bytes<Retained>> {
        if end < start || end > self.raw.len() {
            return None;
        }
        Some(Bytes::<Retained>::copy_from_slice(&self.raw[start..end]))
    }

    fn next_seg(&self, idx: usize) -> Option<(usize, usize, usize)> {
        if self.is_end(idx) || self.raw[idx] != b'/' {
            return None;
        }
        let start = idx + 1;
        let mut end = start;
        while end < self.raw.len() && self.raw[end] != b'/' && self.raw[end] != b'?' {
            end += 1;
        }
        Some((start, end, end))
    }

    fn probe_literal(&self, cur: usize, lit: &[u8]) -> Option<usize> {
        let start = cur.checked_add(1)?;
        let end = start.checked_add(lit.len())?;
        if end > self.raw.len() || self.raw[start..end] != *lit {
            return None;
        }
        if end < self.raw.len() && self.raw[end] != b'/' && self.raw[end] != b'?' {
            return None;
        }
        Some(end)
    }
}