reinhardt-conf 0.3.2

Configuration management framework for Reinhardt - Django-inspired settings with encryption and secrets management
Documentation
//! # Settings Module
//!
//! Django-inspired settings system for Reinhardt projects.
//! This module provides configuration management for Reinhardt applications.

// The `From<&FragmentTemplateConfig>` bridge below names the deprecated
// `TemplateConfig` during the 0.2 compatibility window.
#![allow(deprecated)]

pub mod builder;
pub mod cache;
/// Trait for composed settings structs generated by the `#[settings(...)]` macro.
pub mod composed;
pub mod contacts;
pub mod core_settings;
pub mod cors;
pub mod email;
pub mod env;
pub mod env_loader;
pub mod env_parser;
pub mod fragment;
pub mod i18n;
pub mod interpolation;
pub mod logging;
pub mod media;
pub(crate) mod merge;
/// OpenAPI documentation endpoint configuration.
pub mod openapi;
/// Field-level policy types for settings fragments.
pub mod policy;
pub mod prelude;
pub mod profile;
/// Typed settings schema references and recursive settings metadata.
pub mod schema;
pub mod secret_types;
pub mod security;
pub mod session;
pub mod sources;
pub mod static_files;
pub mod template_settings;
pub mod typed_deserializer;
pub mod validation;

// Dynamic settings (async feature required)
#[cfg(feature = "async")]
pub mod dynamic;

#[cfg(feature = "async")]
pub mod backends;

/// Secret management with provider-based storage and rotation support.
#[cfg(feature = "async")]
pub mod secrets;

#[cfg(feature = "encryption")]
pub mod encryption;

#[cfg(feature = "async")]
pub mod audit;

#[cfg(feature = "hot-reload")]
pub mod hot_reload;

/// Additional application-level configuration types.
pub mod config;
/// Database connection configuration types and helpers.
pub mod database_config;
/// Settings documentation and introspection utilities.
pub mod docs;
/// Test utilities for settings configuration.
pub mod testing;

use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;

/// Contact information for administrators and managers
///
/// Used for error notifications, broken link notifications, etc.
///
/// # Examples
///
/// ```
/// use reinhardt_conf::settings::Contact;
///
/// let admin = Contact::new("John Doe", "john@example.com");
/// assert_eq!(admin.name, "John Doe");
/// assert_eq!(admin.email, "john@example.com");
/// ```
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct Contact {
	/// Person's name
	pub name: String,
	/// Email address
	pub email: String,
}

impl Contact {
	/// Create a new contact
	///
	/// # Examples
	///
	/// ```
	/// use reinhardt_conf::settings::Contact;
	///
	/// let contact = Contact::new("Alice Smith", "alice@example.com");
	/// assert_eq!(contact.name, "Alice Smith");
	/// assert_eq!(contact.email, "alice@example.com");
	/// ```
	pub fn new(name: impl Into<String>, email: impl Into<String>) -> Self {
		Self {
			name: name.into(),
			email: email.into(),
		}
	}
}

// Re-export DatabaseConfig from database_config module
pub use database_config::DatabaseConfig;

// Re-export policy types
pub use policy::{FieldPolicy, FieldRequirement};

// Re-export ComposedSettings trait
pub use composed::ComposedSettings;

// Re-export the merge strategy selector for SettingsBuilder. See issue #4260.
pub use builder::MergeStrategy;

/// Template engine configuration
///
/// Deprecated in favor of the [`TemplateSettings`](template_settings::TemplateSettings)
/// fragment (and its nested [`FragmentTemplateConfig`](template_settings::FragmentTemplateConfig)
/// value object), which compose with the `#[settings]` macro. A
/// [`From<&FragmentTemplateConfig>`] bridge is provided for migration.
#[deprecated(
	since = "0.2.0",
	note = "Use `TemplateSettings` with the `#[settings]` macro instead."
)]
#[non_exhaustive]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct TemplateConfig {
	/// Template backend/engine
	pub backend: String,

	/// Directories to search for templates
	pub dirs: Vec<PathBuf>,

	/// Search for templates in app directories
	pub app_dirs: bool,

	/// Template engine options
	pub options: HashMap<String, serde_json::Value>,
}

impl TemplateConfig {
	/// Create a new template configuration
	///
	/// # Examples
	///
	/// ```
	/// use reinhardt_conf::settings::TemplateConfig;
	///
	/// let config = TemplateConfig::new("reinhardt.template.backends.jinja2.Jinja2");
	///
	/// assert_eq!(config.backend, "reinhardt.template.backends.jinja2.Jinja2");
	/// assert!(config.app_dirs);
	/// assert_eq!(config.dirs.len(), 0);
	/// ```
	pub fn new(backend: impl Into<String>) -> Self {
		Self {
			backend: backend.into(),
			dirs: vec![],
			app_dirs: true,
			options: HashMap::new(),
		}
	}
	/// Add a template directory
	///
	/// # Examples
	///
	/// ```
	/// use reinhardt_conf::settings::TemplateConfig;
	/// use std::path::PathBuf;
	///
	/// let config = TemplateConfig::new("reinhardt.template.backends.jinja2.Jinja2")
	///     .add_dir("/app/templates");
	///
	/// assert_eq!(config.dirs.len(), 1);
	/// assert_eq!(config.dirs[0], PathBuf::from("/app/templates"));
	/// ```
	pub fn add_dir(mut self, dir: impl Into<PathBuf>) -> Self {
		self.dirs.push(dir.into());
		self
	}
}

impl Default for TemplateConfig {
	fn default() -> Self {
		let mut options = HashMap::new();
		options.insert(
			"context_processors".to_string(),
			serde_json::json!([
				"reinhardt.template.context_processors.request",
				"reinhardt.contrib.auth.context_processors.auth",
				"reinhardt.contrib.messages.context_processors.messages",
			]),
		);

		Self {
			backend: "reinhardt.template.backends.jinja2.Jinja2".to_string(),
			dirs: vec![],
			app_dirs: true,
			options,
		}
	}
}

impl From<&template_settings::FragmentTemplateConfig> for TemplateConfig {
	/// Bridge a settings-fragment template config into the deprecated
	/// compatibility [`TemplateConfig`] during the 0.2 migration window.
	fn from(settings: &template_settings::FragmentTemplateConfig) -> Self {
		Self {
			backend: settings.backend.clone(),
			dirs: settings.dirs.clone(),
			app_dirs: settings.app_dirs,
			options: settings.options.clone(),
		}
	}
}

/// Build the deprecated [`TemplateConfig`] list from a
/// [`TemplateSettings`](template_settings::TemplateSettings) fragment.
///
/// Prefer the [`TemplateSettings`](template_settings::TemplateSettings) fragment
/// directly in new code; this helper exists to ease migration off the legacy
/// `TemplateConfig` type.
pub fn create_template_configs_from_settings(
	settings: &template_settings::TemplateSettings,
) -> Vec<TemplateConfig> {
	settings.configs.iter().map(TemplateConfig::from).collect()
}

/// Middleware configuration
#[non_exhaustive]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct MiddlewareConfig {
	/// Full path to the middleware class
	pub path: String,

	/// Middleware options
	pub options: HashMap<String, serde_json::Value>,
}

impl MiddlewareConfig {
	/// Create a new middleware configuration
	///
	/// # Examples
	///
	/// ```
	/// use reinhardt_conf::settings::MiddlewareConfig;
	///
	/// let middleware = MiddlewareConfig::new("myapp.middleware.CustomMiddleware");
	///
	/// assert_eq!(middleware.path, "myapp.middleware.CustomMiddleware");
	/// assert_eq!(middleware.options.len(), 0);
	/// ```
	pub fn new(path: impl Into<String>) -> Self {
		Self {
			path: path.into(),
			options: HashMap::new(),
		}
	}
	/// Add an option to the middleware
	///
	/// # Examples
	///
	/// ```
	/// use reinhardt_conf::settings::MiddlewareConfig;
	///
	/// let middleware = MiddlewareConfig::new("myapp.middleware.CustomMiddleware")
	///     .with_option("timeout", serde_json::json!(30));
	///
	/// assert_eq!(middleware.options.get("timeout"), Some(&serde_json::json!(30)));
	/// ```
	pub fn with_option(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
		self.options.insert(key.into(), value);
		self
	}
}