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 axum::response::Response;

use std::collections::HashSet;
use std::marker::PhantomData;
use std::sync::Arc;

use crate::HumusQuerySettings;
use crate::HumusView;
use crate::language::LanguageEngine;
use crate::middleware::TemplateSettingsLayer;
use crate::templating::{
	FormatChooser, HumusApiFormat, HumusFormatIdentifier, TemplatesManifest, TemplatingEngine,
};

/* The engine itself */

#[allow(clippy::doc_overindented_list_items)]
/// 🌱 An all in one datatype that takes care of frontend rendering.
///
/// You should construct one using a [crate::HumusEngineLoader] unless you have a very
/// good reason not to.
///
/// The `ApiFormat` type defines which API responses your implementation can produce.
/// For a good default you can use [JsonOnlyApiFormat][crate::templating::JsonOnlyApiFormat].
///
/// <b>Note:</b> The private field is a [PhantomData] because the engine only works
///              correctly when `ApiFormat` stays the same throuout its life, but
///              it doesn't have to store one of those.
///
/// The HumusEngine itself is a container for the [TemplatingEngine] which takes care of
/// rendering text templates and API responses and the [LanguageEngine] which can be used
/// by the TemplateEngine and your code to provide localized results.
///
/// Your main point of interaction will be the [render_view][Self::render_view] function
/// which you can use after fetching all the data your frontend needs to tun the data into
/// an HTTP response.
#[derive(Debug, Clone)]
pub struct HumusEngine<ApiFormat>
where
	ApiFormat: HumusApiFormat,
{
	/// The templating engine responsible for rendering text templates
	pub templating_engine: TemplatingEngine<ApiFormat>,

	/// The language engine that is responsible for localizing this template
	pub language_engine: Arc<LanguageEngine>,

	phantom_format: PhantomData<ApiFormat>,
}

impl<ApiFormat> HumusEngine<ApiFormat>
where
	ApiFormat: HumusApiFormat,
{
	/// Creates a new Templating Engine.
	///
	/// An alternative would be converting from a [HumusProtoEngine].
	///
	/// [HumusProtoEngine]: ./struct.HumusProtoEngine.html
	pub fn new(
		templating_engine: TemplatingEngine<ApiFormat>,
		language_engine: Arc<LanguageEngine>,
	) -> Self {
		Self {
			templating_engine,
			language_engine,
			phantom_format: PhantomData,
		}
	}

	/// Takes settings and a view, converting it to a serveable response.
	///
	/// Example:
	/// ```rust,ignore
	/// async fn hello_world_handler(
	/// 	State(arc_state): State<Arc<ServiceSharedState>>,
	/// 	Extension(settings): Extension<QuerySettings>,
	/// ) -> Response {
	/// 	let state = Arc::clone(&arc_state);
	///
	/// 	state.templating_engine.render_view(
	/// 		&settings,
	/// 		View::Message{
	/// 			title: "Hey There!".to_string(),
	/// 			message: "You are an awesome creature!".to_string()
	/// 		},
	/// 	)
	/// }
	///
	/// ```
	pub fn render_view<S: HumusQuerySettings>(
		&self,
		settings: &S,
		view: impl HumusView<S, ApiFormat>,
	) -> Response {
		self.templating_engine
			.render_view(settings, view, &self.language_engine)
	}

	/// Accessor for the localization engine inside this HumusEngine
	///
	/// You can use this to localize text elsewhere in your code.
	pub fn language_engine(&self) -> &LanguageEngine {
		&self.language_engine
	}

	/// Accessor for the templates manifest inside this HumusEngine
	#[inline]
	pub fn templates_manifest(&self) -> &TemplatesManifest {
		&self.templating_engine.templates_manifest
	}

	/// Returns a HashSet of all valid format identifiers this HumusEngine can render
	pub fn list_available_formats(&self) -> HashSet<HumusFormatIdentifier> {
		let mut set: HashSet<HumusFormatIdentifier> = ApiFormat::get_all()
			.into_iter()
			.map(|f| f.get_name())
			.collect();

		for identifier in self.templates_manifest().format.keys() {
			set.insert(*identifier);
		}

		set
	}

	/// Returns a [FormatChooser] matching this HumusEngine.
	pub fn get_format_chooser(&self) -> FormatChooser {
		FormatChooser::new::<ApiFormat>(self.templates_manifest())
	}

	/// Constructs a ready to use [TemplateSettingsLayer] that matches the configuration of this engine.
	pub fn get_template_settings_layer(&self) -> TemplateSettingsLayer {
		TemplateSettingsLayer::new(
			self.templates_manifest().clone(),
			self.language_engine.language_manifest().clone(),
			self.get_format_chooser(),
		)
	}
}