use std::collections::HashMap;
use std::sync::RwLock;
use once_cell::sync::Lazy;
use crate::controller::ElifController;
use crate::bootstrap::BootstrapError;
pub type ControllerFactory = fn() -> Box<dyn ElifController>;
#[derive(Debug)]
pub struct ControllerTypeRegistry {
factories: RwLock<HashMap<String, ControllerFactory>>,
}
impl ControllerTypeRegistry {
fn new() -> Self {
Self {
factories: RwLock::new(HashMap::new()),
}
}
pub fn register(&self, name: &str, factory: ControllerFactory) {
let mut factories = self.factories.write()
.expect("Controller type registry lock poisoned");
if factories.contains_key(name) {
panic!(
"Controller type '{}' is already registered. Each controller type must have a unique name.",
name
);
}
factories.insert(name.to_string(), factory);
tracing::debug!("Registered controller type: {}", name);
}
pub fn create_controller(&self, name: &str) -> Result<Box<dyn ElifController>, BootstrapError> {
let factories = self.factories.read()
.expect("Controller type registry lock poisoned");
let factory = factories.get(name)
.ok_or_else(|| BootstrapError::ControllerNotFound {
controller_name: name.to_string(),
available_controllers: factories.keys().cloned().collect(),
})?;
let controller = factory();
tracing::debug!("Created controller instance: {}", name);
Ok(controller)
}
pub fn is_registered(&self, name: &str) -> bool {
let factories = self.factories.read()
.expect("Controller type registry lock poisoned");
factories.contains_key(name)
}
pub fn get_registered_types(&self) -> Vec<String> {
let factories = self.factories.read()
.expect("Controller type registry lock poisoned");
factories.keys().cloned().collect()
}
pub fn count(&self) -> usize {
let factories = self.factories.read()
.expect("Controller type registry lock poisoned");
factories.len()
}
#[cfg(test)]
pub fn clear(&self) {
let mut factories = self.factories.write()
.expect("Controller type registry lock poisoned");
factories.clear();
tracing::debug!("Cleared all registered controller types");
}
}
pub static CONTROLLER_TYPE_REGISTRY: Lazy<ControllerTypeRegistry> = Lazy::new(ControllerTypeRegistry::new);
pub fn register_controller_type(name: &str, factory: ControllerFactory) {
CONTROLLER_TYPE_REGISTRY.register(name, factory);
}
pub fn create_controller(name: &str) -> Result<Box<dyn ElifController>, BootstrapError> {
CONTROLLER_TYPE_REGISTRY.create_controller(name)
}
pub fn create_controller_instance<T>() -> Box<dyn ElifController>
where
T: ElifController + Default + 'static,
{
Box::new(T::default()) as Box<dyn ElifController>
}
pub fn create_ioc_controller_instance<T>() -> Box<dyn ElifController>
where
T: ElifController + crate::controller::factory::IocControllable + 'static,
{
let container = elif_core::container::IocContainer::new();
match T::from_ioc_container(&container, None) {
Ok(controller) => Box::new(controller) as Box<dyn ElifController>,
Err(e) => {
tracing::error!("Failed to create IoC controller: {}", e);
panic!(
"Auto-registration failed for controller {}. Error: {}\n\
Consider:\n\
1. Implementing Default for all dependencies\n\
2. Using router.controller_from_container::<T>() with proper IoC setup\n\
3. Registering dependencies in IoC container before controller registration",
std::any::type_name::<T>(),
e
);
}
}
}
#[macro_export]
macro_rules! __controller_auto_register {
($name:expr, $type:ty) => {
const _: () = {
#[::ctor::ctor]
fn __register_controller() {
$crate::bootstrap::controller_registry::register_controller_type(
$name,
|| {
$crate::bootstrap::controller_registry::create_controller_instance::<$type>()
}
);
}
};
};
}
#[macro_export]
macro_rules! __controller_auto_register_ioc {
($name:expr, $type:ty) => {
const _: () = {
#[::ctor::ctor]
fn __register_controller() {
$crate::bootstrap::controller_registry::register_controller_type(
$name,
|| {
$crate::bootstrap::controller_registry::create_ioc_controller_instance::<$type>()
}
);
}
};
};
}
#[cfg(test)]
mod tests {
use super::*;
use crate::controller::{ElifController, ControllerRoute};
use crate::{ElifRequest, ElifResponse, HttpResult};
use crate::routing::HttpMethod;
use async_trait::async_trait;
use std::sync::Arc;
#[derive(Debug)]
struct TestController {
name: String,
}
impl TestController {
fn new() -> Self {
Self {
name: "TestController".to_string(),
}
}
}
#[async_trait]
impl ElifController for TestController {
fn name(&self) -> &str {
&self.name
}
fn base_path(&self) -> &str {
"/test"
}
fn routes(&self) -> Vec<ControllerRoute> {
vec![
ControllerRoute {
method: HttpMethod::GET,
path: "".to_string(),
handler_name: "index".to_string(),
middleware: vec![],
params: vec![],
}
]
}
fn dependencies(&self) -> Vec<String> {
vec![]
}
async fn handle_request(
self: Arc<Self>,
_method_name: String,
_request: ElifRequest,
) -> HttpResult<ElifResponse> {
Ok(ElifResponse::ok())
}
}
#[test]
fn test_controller_type_registry_creation() {
let registry = ControllerTypeRegistry::new();
assert_eq!(registry.count(), 0);
assert!(registry.get_registered_types().is_empty());
}
#[test]
fn test_controller_registration() {
let registry = ControllerTypeRegistry::new();
registry.register("TestController", || Box::new(TestController::new()));
assert_eq!(registry.count(), 1);
assert!(registry.is_registered("TestController"));
assert!(!registry.is_registered("NonExistentController"));
let types = registry.get_registered_types();
assert_eq!(types.len(), 1);
assert!(types.contains(&"TestController".to_string()));
}
#[test]
fn test_controller_creation() {
let registry = ControllerTypeRegistry::new();
registry.register("TestController", || Box::new(TestController::new()));
let result = registry.create_controller("TestController");
assert!(result.is_ok());
let controller = result.unwrap();
assert_eq!(controller.name(), "TestController");
assert_eq!(controller.base_path(), "/test");
assert_eq!(controller.routes().len(), 1);
}
#[test]
fn test_controller_not_found() {
let registry = ControllerTypeRegistry::new();
let result = registry.create_controller("NonExistentController");
assert!(result.is_err());
if let Err(BootstrapError::ControllerNotFound { controller_name, available_controllers }) = result {
assert_eq!(controller_name, "NonExistentController");
assert_eq!(available_controllers.len(), 0);
} else {
panic!("Expected ControllerNotFound error");
}
}
#[test]
#[should_panic(expected = "Controller type 'TestController' is already registered")]
fn test_duplicate_registration() {
let registry = ControllerTypeRegistry::new();
registry.register("TestController", || Box::new(TestController::new()));
registry.register("TestController", || Box::new(TestController::new()));
}
#[test]
fn test_global_registry_functions() {
#[cfg(test)]
CONTROLLER_TYPE_REGISTRY.clear();
register_controller_type("GlobalTestController", || Box::new(TestController::new()));
assert!(CONTROLLER_TYPE_REGISTRY.is_registered("GlobalTestController"));
assert_eq!(CONTROLLER_TYPE_REGISTRY.count(), 1);
let result = create_controller("GlobalTestController");
assert!(result.is_ok());
let controller = result.unwrap();
assert_eq!(controller.name(), "TestController"); }
}