use crate::Error;
const fn is_bchar(b: u8) -> bool {
#[rustfmt::skip]
let special = matches!(b, b'\'' | b'(' | b')' | b'+' | b'_' | b',' | b'-' | b'.' | b'/' | b':' | b'=' | b'?' | b' ');
b.is_ascii_alphanumeric() || special
}
const fn is_tchar(b: u8) -> bool {
#[rustfmt::skip]
let special = matches!(b, b'!' | b'#' | b'$' | b'%' | b'&' | b'\'' | b'*' | b'+' | b'-' | b'.' | b'^' | b'_' | b'`' | b'|' | b'~');
b.is_ascii_alphanumeric() || special
}
pub(crate) const fn is_ows(b: u8) -> bool {
b == b' ' || b == b'\t'
}
pub(crate) fn is_valid_boundary(b: &[u8]) -> bool {
b.len() <= 70 && b.iter().all(|&c| is_bchar(c)) && !matches!(b.last(), None | Some(b' '))
}
pub(crate) fn is_token(s: &[u8]) -> bool {
!s.is_empty() && s.iter().all(|&b| is_tchar(b))
}
pub(crate) fn ows_trimmed_range(s: &[u8]) -> (usize, usize) {
let start = s.iter().take_while(|&&b| is_ows(b)).count();
let trailing = s.iter().rev().take_while(|&&b| is_ows(b)).count();
(start, s.len().saturating_sub(trailing).max(start))
}
pub(crate) fn trim_ows(mut s: &[u8]) -> &[u8] {
while let [b' ' | b'\t', rest @ ..] = s {
s = rest;
}
while let [rest @ .., b' ' | b'\t'] = s {
s = rest;
}
s
}
type Param<'a> = (&'a [u8], &'a [u8], bool, &'a [u8]);
pub(crate) fn param(s: &[u8]) -> Result<Option<Param<'_>>, Error> {
let s = trim_ows(s);
if s.is_empty() {
return Ok(None);
}
let eq = s
.iter()
.position(|&b| b == b'=')
.ok_or(Error::Disposition)?;
let (attr, v) = s.split_at_checked(eq).ok_or(Error::Disposition)?;
let attr = trim_ows(attr);
if !is_token(attr) {
return Err(Error::Disposition);
}
let v = trim_ows(v.get(1..).unwrap_or(&[]));
if let Some(inner) = v.strip_prefix(b"\"") {
let close = quoted_string_end(inner).ok_or(Error::Disposition)?;
let (value, tail) = inner.split_at_checked(close).ok_or(Error::Disposition)?;
let tail = trim_ows(tail.get(1..).unwrap_or(&[]));
if !tail.is_empty() && !tail.starts_with(b";") {
return Err(Error::Disposition);
}
return Ok(Some((attr, value, true, tail)));
}
let end = v.iter().position(|&b| b == b';').unwrap_or(v.len());
let (value, tail) = v.split_at_checked(end).ok_or(Error::Disposition)?;
Ok(Some((attr, trim_ows(value), false, tail)))
}
fn quoted_string_end(s: &[u8]) -> Option<usize> {
let mut escaped = false;
s.iter().position(|&b| {
let close = !escaped && b == b'"';
escaped = !escaped && b == b'\\';
close
})
}