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::{
	fmt::Display,
	ops::{Deref, DerefMut},
	str::FromStr,
};

use mime::Mime;
use serde::{Deserialize, Serialize};

/// Represents a parsed and valid media type, uses the [mime] crate under the hood.
#[derive(Debug, Clone)]
pub struct MediaType(Mime);

impl From<Mime> for MediaType {
	fn from(value: Mime) -> Self {
		Self(value)
	}
}

impl From<MediaType> for Mime {
	fn from(value: MediaType) -> Self {
		value.0
	}
}

impl FromStr for MediaType {
	type Err = mime::FromStrError;

	fn from_str(s: &str) -> Result<Self, Self::Err> {
		Ok(Self(Mime::from_str(s)?))
	}
}

impl Display for MediaType {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		self.0.fmt(f)
	}
}

impl Deref for MediaType {
	type Target = Mime;

	fn deref(&self) -> &Self::Target {
		&self.0
	}
}

impl DerefMut for MediaType {
	fn deref_mut(&mut self) -> &mut Self::Target {
		&mut self.0
	}
}

impl Serialize for MediaType {
	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
	where
		S: serde::Serializer,
	{
		serializer.serialize_str(self.0.as_ref())
	}
}

struct MediaTypeVisitor;

impl<'de> serde::de::Visitor<'de> for MediaTypeVisitor {
	type Value = MediaType;

	fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
		write!(formatter, "A well formed media type/mimetype")
	}

	fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
	where
		E: serde::de::Error,
	{
		Mime::from_str(v)
			.map(MediaType)
			.map_err(|e| E::custom(format!("Problem parsing media type: {e}")))
	}
}

impl<'de> Deserialize<'de> for MediaType {
	fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
	where
		D: serde::Deserializer<'de>,
	{
		deserializer.deserialize_string(MediaTypeVisitor)
	}
}