use async_trait::async_trait;
use std::collections::HashMap;
use std::sync::Arc;
use crate::controller::base::ElifController;
use crate::errors::HttpError;
use crate::request::ElifRequest;
use elif_core::container::{IocContainer, ScopeId};
#[async_trait]
pub trait ControllerFactory: Send + Sync {
async fn create_controller(
&self,
container: &IocContainer,
scope: Option<&ScopeId>,
) -> Result<Arc<dyn ElifController>, HttpError>;
}
pub struct IocControllerFactory<T> {
_phantom: std::marker::PhantomData<T>,
}
impl<T> IocControllerFactory<T> {
pub fn new() -> Self {
Self {
_phantom: std::marker::PhantomData,
}
}
}
impl<T> Default for IocControllerFactory<T> {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl<T> ControllerFactory for IocControllerFactory<T>
where
T: ElifController + IocControllable + 'static,
{
async fn create_controller(
&self,
container: &IocContainer,
scope: Option<&ScopeId>,
) -> Result<Arc<dyn ElifController>, HttpError> {
let controller =
T::from_ioc_container(container, scope).map_err(|e| HttpError::InternalError {
message: format!("Failed to create controller: {}", e),
})?;
Ok(Arc::new(controller))
}
}
pub trait IocControllable {
fn from_ioc_container(
container: &IocContainer,
scope: Option<&ScopeId>,
) -> Result<Self, String>
where
Self: Sized;
}
pub struct ControllerRegistry {
factories: HashMap<String, Box<dyn ControllerFactory>>,
container: Arc<IocContainer>,
}
impl ControllerRegistry {
pub fn new(container: Arc<IocContainer>) -> Self {
Self {
factories: HashMap::new(),
container,
}
}
pub fn register<T>(&mut self, name: &str) -> Result<(), HttpError>
where
T: ElifController + IocControllable + 'static,
{
let factory = Box::new(IocControllerFactory::<T>::new());
self.factories.insert(name.to_string(), factory);
Ok(())
}
pub fn register_factory(&mut self, name: &str, factory: Box<dyn ControllerFactory>) {
self.factories.insert(name.to_string(), factory);
}
pub async fn create_controller(
&self,
name: &str,
scope: Option<&ScopeId>,
) -> Result<Arc<dyn ElifController>, HttpError> {
let factory = self
.factories
.get(name)
.ok_or_else(|| HttpError::InternalError {
message: format!("Controller '{}' not registered", name),
})?;
factory.create_controller(&self.container, scope).await
}
pub async fn create_scoped_registry(
&self,
request: &ElifRequest,
) -> Result<ScopedControllerRegistry<'_>, HttpError> {
let scope_id = self
.container
.create_scope()
.map_err(|e| HttpError::InternalError {
message: format!("Failed to create request scope: {}", e),
})?;
Ok(ScopedControllerRegistry {
registry: self,
scope_id,
request_context: RequestContext::from_request(request),
})
}
pub fn registered_controllers(&self) -> Vec<String> {
self.factories.keys().cloned().collect()
}
}
pub struct ScopedControllerRegistry<'a> {
registry: &'a ControllerRegistry,
scope_id: ScopeId,
#[allow(dead_code)]
request_context: RequestContext,
}
impl<'a> ScopedControllerRegistry<'a> {
pub async fn create_controller(
&self,
name: &str,
) -> Result<Arc<dyn ElifController>, HttpError> {
self.registry
.create_controller(name, Some(&self.scope_id))
.await
}
pub fn scope_id(&self) -> &ScopeId {
&self.scope_id
}
pub async fn dispose(self) -> Result<(), HttpError> {
self.registry
.container
.dispose_scope(&self.scope_id)
.await
.map_err(|e| HttpError::InternalError {
message: format!("Failed to dispose scope: {}", e),
})
}
}
#[derive(Clone, Debug)]
pub struct RequestContext {
pub method: String,
pub path: String,
pub query_params: HashMap<String, String>,
pub headers: HashMap<String, String>,
}
impl RequestContext {
pub fn from_request(request: &ElifRequest) -> Self {
Self {
method: request.method.to_string(),
path: request.path().to_string(),
query_params: HashMap::new(), headers: HashMap::new(), }
}
}
pub struct ControllerRegistryBuilder {
container: Option<Arc<IocContainer>>,
controllers: Vec<(String, Box<dyn ControllerFactory>)>,
}
impl ControllerRegistryBuilder {
pub fn new() -> Self {
Self {
container: None,
controllers: Vec::new(),
}
}
pub fn container(mut self, container: Arc<IocContainer>) -> Self {
self.container = Some(container);
self
}
pub fn register<T>(mut self, name: &str) -> Self
where
T: ElifController + IocControllable + 'static,
{
let factory = Box::new(IocControllerFactory::<T>::new());
self.controllers.push((name.to_string(), factory));
self
}
pub fn register_factory(mut self, name: &str, factory: Box<dyn ControllerFactory>) -> Self {
self.controllers.push((name.to_string(), factory));
self
}
pub fn build(self) -> Result<ControllerRegistry, HttpError> {
let container = self.container.ok_or_else(|| HttpError::InternalError {
message: "IoC container is required for controller registry".to_string(),
})?;
let mut registry = ControllerRegistry::new(container);
for (name, factory) in self.controllers {
registry.register_factory(&name, factory);
}
Ok(registry)
}
}
impl Default for ControllerRegistryBuilder {
fn default() -> Self {
Self::new()
}
}
pub struct ControllerScanner;
impl ControllerScanner {
pub async fn scan_and_register(
_registry: &mut ControllerRegistry,
_module_path: &str,
) -> Result<usize, HttpError> {
Ok(0)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::controller::base::{ControllerRoute, ElifController};
use async_trait::async_trait;
use elif_core::ServiceBinder;
pub struct TestController {
#[allow(dead_code)]
pub service: Arc<TestService>,
}
#[async_trait]
impl ElifController for TestController {
fn name(&self) -> &str {
"TestController"
}
fn base_path(&self) -> &str {
"/test"
}
fn routes(&self) -> Vec<ControllerRoute> {
vec![]
}
async fn handle_request(
self: Arc<Self>,
_method_name: String,
_request: ElifRequest,
) -> crate::errors::HttpResult<crate::response::ElifResponse> {
Ok(crate::response::ElifResponse::ok().text("test"))
}
}
impl IocControllable for TestController {
fn from_ioc_container(
container: &IocContainer,
_scope: Option<&ScopeId>,
) -> Result<Self, String> {
let service = container
.resolve::<TestService>()
.map_err(|e| format!("Failed to resolve TestService: {}", e))?;
Ok(Self { service })
}
}
#[derive(Default)]
pub struct TestService {
#[allow(dead_code)]
pub name: String,
}
unsafe impl Send for TestService {}
unsafe impl Sync for TestService {}
#[tokio::test]
async fn test_controller_factory_creation() {
let mut container = IocContainer::new();
container.bind::<TestService, TestService>();
container.build().expect("Container build failed");
let container_arc = Arc::new(container);
let mut registry = ControllerRegistry::new(container_arc);
registry
.register::<TestController>("test")
.expect("Failed to register controller");
let controller = registry
.create_controller("test", None)
.await
.expect("Failed to create controller");
assert_eq!(controller.name(), "TestController");
}
}