toni 0.1.1

Fast and modular web framework for scalable applications
Documentation
use std::{collections::hash_map::Drain, sync::Arc};

use anyhow::{Result, anyhow};
use rustc_hash::{FxHashMap, FxHashSet};

use crate::traits_helpers::{Controller, ControllerTrait, ModuleMetadata, Provider, ProviderTrait};

use super::module::Module;

pub struct ToniContainer {
    modules: FxHashMap<String, Module>,
}

impl Default for ToniContainer {
    fn default() -> Self {
        Self::new()
    }
}

impl ToniContainer {
    pub fn new() -> Self {
        Self {
            modules: FxHashMap::default(),
        }
    }

    pub fn add_module(&mut self, module_metadata: Box<dyn ModuleMetadata>) {
        let token: String = module_metadata.get_id();
        let name: String = module_metadata.get_name();
        let module = Module::new(&token, &name, module_metadata);
        self.modules.insert(token, module);
    }

    pub fn add_import(
        &mut self,
        module_ref_token: &String,
        imported_module_token: String,
    ) -> Result<()> {
        let module_ref = self
            .modules
            .get_mut(module_ref_token)
            .ok_or_else(|| anyhow!("Module not found"))?;
        module_ref.add_import(imported_module_token);
        Ok(())
    }

    pub fn add_controller(
        &mut self,
        module_ref_token: &String,
        controller: Box<dyn Controller>,
    ) -> Result<()> {
        let module_ref = self
            .modules
            .get_mut(module_ref_token)
            .ok_or_else(|| anyhow!("Module not found"))?;
        module_ref.add_controller(controller);
        Ok(())
    }

    pub fn add_provider(
        &mut self,
        module_ref_token: &String,
        provider: Box<dyn Provider>,
    ) -> Result<()> {
        let module_ref = self
            .modules
            .get_mut(module_ref_token)
            .ok_or_else(|| anyhow!("Module not found"))?;
        module_ref.add_provider(provider);
        Ok(())
    }

    pub fn add_provider_instance(
        &mut self,
        module_ref_token: &String,
        provider_instance: Arc<Box<dyn ProviderTrait>>,
    ) -> Result<()> {
        let module_ref = self
            .modules
            .get_mut(module_ref_token)
            .ok_or_else(|| anyhow!("Module not found"))?;
        module_ref.add_provider_instance(provider_instance);
        Ok(())
    }

    pub fn add_controller_instance(
        &mut self,
        module_ref_token: &String,
        controller_instance: Arc<Box<dyn ControllerTrait>>,
    ) -> Result<()> {
        let module_ref = self
            .modules
            .get_mut(module_ref_token)
            .ok_or_else(|| anyhow!("Module not found"))?;
        module_ref.add_controller_instance(controller_instance);
        Ok(())
    }

    pub fn add_export(&mut self, module_ref_token: &String, provider_token: String) -> Result<()> {
        let module_ref = self
            .modules
            .get_mut(module_ref_token)
            .ok_or_else(|| anyhow!("Module not found"))?;
        module_ref.add_export(provider_token);
        Ok(())
    }

    pub fn add_export_instance(
        &mut self,
        module_ref_token: &String,
        provider_token: String,
    ) -> Result<()> {
        let module_ref = self
            .modules
            .get_mut(module_ref_token)
            .ok_or_else(|| anyhow!("Module not found"))?;
        module_ref.add_export_instance(provider_token);
        Ok(())
    }

    pub fn get_providers_manager(
        &self,
        module_ref_token: &String,
    ) -> Result<&FxHashMap<String, Box<dyn Provider>>> {
        let module_ref = self
            .modules
            .get(module_ref_token)
            .ok_or_else(|| anyhow!("Module not found"))?;
        Ok(module_ref.get_providers_manager())
    }

    pub fn get_controllers_manager(
        &self,
        module_ref_token: &String,
    ) -> Result<&FxHashMap<String, Box<dyn Controller>>> {
        let module_ref = self
            .modules
            .get(module_ref_token)
            .ok_or_else(|| anyhow!("Module not found"))?;
        Ok(module_ref.get_controllers_manager())
    }

    pub fn get_providers_instance(
        &self,
        module_ref_token: &String,
    ) -> Result<&FxHashMap<String, Arc<Box<dyn ProviderTrait>>>> {
        let module_ref = self
            .modules
            .get(module_ref_token)
            .ok_or_else(|| anyhow!("Module not found"))?;
        Ok(module_ref.get_providers_instances())
    }

    pub fn get_provider_instance_by_token(
        &self,
        module_ref_token: &String,
        provider_token: &String,
    ) -> Result<Option<&Arc<Box<dyn ProviderTrait>>>> {
        let module_ref = self
            .modules
            .get(module_ref_token)
            .ok_or_else(|| anyhow!("Module not found"))?;
        Ok(module_ref.get_provider_instance_by_token(provider_token))
    }

    pub fn get_provider_by_token(
        &self,
        module_ref_token: &String,
        provider_token: &String,
    ) -> Result<Option<&dyn Provider>> {
        let module_ref = self
            .modules
            .get(module_ref_token)
            .ok_or_else(|| anyhow!("Module not found"))?;
        Ok(module_ref
            .get_provider_by_token(provider_token))
    }

    pub fn get_controllers_instance(
        &mut self,
        module_ref_token: &String,
    ) -> Result<Drain<'_, String, Arc<Box<dyn ControllerTrait>>>> {
        let module_ref = self
            .modules
            .get_mut(module_ref_token)
            .ok_or_else(|| anyhow!("Module not found"))?;
        Ok(module_ref.drain_controllers_instances())
    }

    pub fn get_imported_modules(&self, module_ref_token: &String) -> Result<&FxHashSet<String>> {
        let module_ref = self
            .modules
            .get(module_ref_token)
            .ok_or_else(|| anyhow!("Module not found"))?;
        Ok(module_ref.get_imported_modules())
    }

    pub fn get_exports_instances_tokens(
        &self,
        module_ref_token: &String,
    ) -> Result<&FxHashSet<String>> {
        let module_ref = self
            .modules
            .get(module_ref_token)
            .ok_or_else(|| anyhow!("Module not found: {:?}", module_ref_token))?;
        Ok(module_ref.get_exports_instances_tokens())
    }

    pub fn get_exports_tokens_vec(&self, module_ref_token: &String) -> Result<Vec<String>> {
        let module_ref = self
            .modules
            .get(module_ref_token)
            .ok_or_else(|| anyhow!("Module not found: {:?}", module_ref_token))?;
        Ok(module_ref.get_exports_tokens().iter().cloned().collect())
    }

    pub fn get_modules_token(&self) -> Vec<String> {
        self.modules.keys().cloned().collect::<Vec<String>>()
    }

    pub fn get_ordered_modules_token(&self) -> Vec<String> {
        let mut ordered_modules: Vec<String> = Vec::new();
        let mut visited: FxHashMap<String, bool> = FxHashMap::default();

        for (token, module) in self.modules.iter() {
            if module.get_imported_modules().is_empty() {
                ordered_modules.push(token.clone());
                visited.insert(token.clone(), true);
            }
        }
        while ordered_modules.len() < self.modules.len() {
            for (token, module) in self.modules.iter() {
                if visited.contains_key(token) {
                    continue;
                }

                let imported_modules = module.get_imported_modules();
                let all_imports_processed = imported_modules
                    .iter()
                    .all(|import_token| visited.contains_key(import_token));

                if all_imports_processed {
                    ordered_modules.push(token.clone());
                    visited.insert(token.clone(), true);
                }
            }
        }

        ordered_modules
    }

    pub fn get_module_by_token(&self, module_ref_token: &String) -> Option<&Module> {
        self.modules.get(module_ref_token)
    }
}