sark-http 0.26.1

HTTP wire types, semantics, codecs, and ownership invariants for Sark
Documentation
use crate::field::{self, scan};

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Error {
    Empty,
    InvalidByte,
    Overflow,
    InvalidOrdering,
}

#[derive(Clone, Copy, PartialEq, Eq)]
pub(super) enum Match {
    Taken,
    Other,
}

pub(super) struct Parser<'a> {
    value: &'a [u8],
}

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

    pub(super) fn skip_ows(&mut self) {
        while let Some((b' ' | b'\t', tail)) = self.value.split_first() {
            self.value = tail;
        }
    }

    pub(super) fn is_done(&self) -> bool {
        self.value.is_empty()
    }

    pub(super) fn take(&mut self, byte: u8) -> Match {
        let Some((&first, tail)) = self.value.split_first() else {
            return Match::Other;
        };
        if first != byte {
            return Match::Other;
        }
        self.value = tail;
        Match::Taken
    }

    pub(super) fn token(&mut self) -> Result<&'a [u8], Error> {
        let (token, tail) =
            match scan::Match::find_by(self.value, |byte| !field::Error::is_name_byte(byte)) {
                Some(end) => (end.prefix(), end.tail()),
                None => (self.value, &self.value[self.value.len()..]),
            };
        if token.is_empty() {
            return Err(if self.is_done() {
                Error::Empty
            } else {
                Error::InvalidByte
            });
        }
        self.value = tail;
        Ok(token)
    }

    pub(super) fn item(&mut self) -> Result<(), Error> {
        let Some((b'"', tail)) = self.value.split_first() else {
            self.token()?;
            return Ok(());
        };
        let mut bytes = tail.iter();
        while let Some(&byte) = bytes.next() {
            match byte {
                b'"' => {
                    self.value = bytes.as_slice();
                    return Ok(());
                }
                b'\\' => {
                    let escaped = bytes.next().ok_or(Error::InvalidByte)?;
                    if !matches!(escaped, b'\t' | b' ' | 0x21..=0x7e | 0x80..=0xff) {
                        return Err(Error::InvalidByte);
                    }
                }
                b'\t' | b' ' | b'!' | 0x23..=0x5b | 0x5d..=0x7e | 0x80..=0xff => {}
                0..=8 | 10..=31 | 0x7f => return Err(Error::InvalidByte),
            }
        }
        Err(Error::InvalidByte)
    }
}