vorma 0.86.0-pre.7

Rust-first full-stack web framework: typed views and resources, generated TypeScript contracts, and a Vite-integrated frontend.
Documentation
//! Media types that Vorma accepts or keeps.

use http::HeaderValue;

/// Whether a `Content-Type` value is `application/json` or a media type with
/// a `+json` suffix, such as `application/merge-patch+json`. Parameters such as
/// `charset` are allowed, and the comparison ignores case.
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())
	})
}

/// The lowercase `type/subtype` of a `Content-Type` value, without its
/// parameters, or `None` when the value is not a media type.
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)
}

/// Whether `text` is an HTTP token: one or more of the characters that RFC 9110
/// allows in a media type's type and subtype.
fn is_token(text: &str) -> bool {
	!text.is_empty()
		&& text
			.bytes()
			.all(|byte| byte.is_ascii_alphanumeric() || b"!#$%&'*+-.^_`|~".contains(&byte))
}