use http::HeaderValue;
pub(crate) fn is_json_media_type(value: &HeaderValue) -> bool {
read_essence(value).is_some_and(|essence| {
essence == "application/json"
|| essence
.split_once('/')
.and_then(|(_, subtype)| subtype.strip_suffix("+json"))
.is_some_and(|name| !name.is_empty())
})
}
#[doc(hidden)]
#[derive(Debug)]
pub enum FormMediaType {
UrlEncoded,
Multipart {
boundary: String,
},
}
pub(crate) enum FormMediaTypeError {
NotAForm,
NoBoundary,
}
impl FormMediaType {
pub(crate) fn parse(value: &HeaderValue) -> Result<Self, FormMediaTypeError> {
match read_essence(value).as_deref() {
Some("application/x-www-form-urlencoded") => Ok(Self::UrlEncoded),
Some("multipart/form-data") => value
.to_str()
.ok()
.and_then(|text| multer::parse_boundary(text).ok())
.map(|boundary| Self::Multipart { boundary })
.ok_or(FormMediaTypeError::NoBoundary),
_ => Err(FormMediaTypeError::NotAForm),
}
}
}
fn read_essence(value: &HeaderValue) -> Option<String> {
let text = value.to_str().ok()?;
let essence = text
.split(';')
.next()
.unwrap_or_default()
.trim()
.to_ascii_lowercase();
let (kind, subtype) = essence.split_once('/')?;
(is_token(kind) && is_token(subtype)).then_some(essence)
}
fn is_token(text: &str) -> bool {
!text.is_empty()
&& text
.bytes()
.all(|byte| byte.is_ascii_alphanumeric() || b"!#$%&'*+-.^_`|~".contains(&byte))
}