lib-humus 0.6.0

Helps creating configurable frontends for humans and computers using axum, Tera and toml.
Documentation
// SPDX-FileCopyrightText: 2026 Slatian <baschdel@disroot.org>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

use std::str::FromStr;

use axum::extract::OptionalFromRequestParts;

use crate::headers::parser::HeaderParser;

/// Represents the two kinds of language identifies in an [AcceptLanguageHeader].
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AcceptLanguageHeaderLanguage {
	/// Parsed from a `*`
	Wildcard,
	/// Parsed from everything else
	Code(String),
}

/// A parsed [`Accept-Language` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Accept-Language) that can also be used as an optional axum extractor
#[derive(Debug, Clone, PartialEq)]
pub struct AcceptLanguageHeader {
	/// Languages in decending order of preference
	pub languages: Vec<(AcceptLanguageHeaderLanguage, f32)>,
}

impl AcceptLanguageHeader {
	/// Creates a new Parsed header from header text. The parser will stop when it encounterrs syntax it doesn't understand.
	pub fn new(header_text: &str) -> Self {
		HeaderParser::new(header_text).parse_accept_language()
	}
}

impl FromStr for AcceptLanguageHeader {
	type Err = ();

	fn from_str(s: &str) -> Result<Self, Self::Err> {
		Ok(HeaderParser::new(s).parse_accept_language())
	}
}

impl<S> OptionalFromRequestParts<S> for AcceptLanguageHeader
where
	S: Sync,
{
	type Rejection = ();

	async fn from_request_parts(
		parts: &mut axum::http::request::Parts,
		_state: &S,
	) -> Result<Option<Self>, Self::Rejection> {
		Ok(parts
			.headers
			.get("accept-language")
			.and_then(|v| Some(AcceptLanguageHeader::new(v.to_str().ok()?))))
	}
}