use std::{
cell::UnsafeCell,
collections::{HashMap, HashSet},
sync::Arc,
};
use anyhow::anyhow;
use bumpalo::Bump;
pub mod child_module;
pub mod dependencies;
pub mod function_handle;
pub mod handle;
pub mod module;
pub mod plugin;
pub use dependencies::Dependencies;
pub use function_handle::{FunctionHandle, RefFunctionHandle};
pub use handle::{Handle, UntypedHandle};
pub use module::{MainModule, Module, ModuleId};
pub use plugin::Plugin;
pub struct AllModules {
_inner: Arc<HashMap<ModuleId, UntypedHandle>>,
}
impl AllModules {
fn get<M: Module>(&self) -> Option<Handle<M>> {
self._inner
.get(&ModuleId::of::<M>())
.map(|e| e.typed::<M>())
}
}
pub struct App {
_modules: &'static bumpalo::Bump,
all_modules: AllModules,
main_module: ModuleId,
}
impl App {
pub fn build() -> AppBuilder {
AppBuilder::new()
}
pub fn all_modules(&self) {}
}
pub struct AppBuilder {
module_configs: bumpalo::Bump,
added_modules: HashMap<ModuleId, AddedModule>,
main_module: Option<AddedMainModule>,
}
impl Default for AppBuilder {
fn default() -> Self {
Self::new()
}
}
impl AppBuilder {
pub fn new() -> Self {
AppBuilder {
module_configs: bumpalo::Bump::new(),
added_modules: HashMap::new(),
main_module: None,
}
}
pub fn add_plugin(&mut self, plugin: impl Plugin) -> &mut Self {
plugin.add(self);
self
}
pub fn add_main_module<M: MainModule>(&mut self) -> &mut Self
where
M::Config: Default,
{
let config: M::Config = Default::default();
self.add_main_module_with_config::<M>(config)
}
pub fn add_main_module_with_config<M: MainModule>(&mut self, config: M::Config) -> &mut Self {
let main_module = AddedMainModule {
module_id: ModuleId::of::<M>(),
run_main_module_fn: run_main_module::<M>,
};
if let Some(main_before) = &self.main_module {
if main_before.module_id != main_module.module_id {
panic!("Main Module {} cannot be added, because Main Module {} was already registered before.", main_module.module_id, main_before.module_id);
}
let conf_ptr_before = self
.added_modules
.get(&main_module.module_id)
.unwrap()
.config_ptr;
let config_before: &M::Config = unsafe { &*(conf_ptr_before as *const M::Config) };
if *config_before != config {
panic!("Main Module {} cannot be added, because it was added with a different config before.\nBefore: {:?}\n Current: {:?}", main_module.module_id, config_before, &config );
}
}
self.main_module = Some(main_module);
self.add_with_config::<M>(config)
}
pub fn add<M: Module>(&mut self) -> &mut Self
where
M::Config: Default,
{
let config: M::Config = Default::default();
self.add_with_config::<M>(config)
}
pub fn add_with_config<M: Module>(&mut self, config: M::Config) -> &mut Self {
let config: &M::Config = self.module_configs.alloc(config);
let config_ptr: *const () = config as *const M::Config as *const ();
let dep_type_ids = M::Dependencies::type_ids();
let module_id = ModuleId::of::<M>();
let added_module = AddedModule {
dependencies: dep_type_ids,
module_id,
config_ptr,
instantiate_module_fn: instantiate_module::<M>,
};
if let Some(module_before) = self.added_modules.get(&module_id) {
let config_before = unsafe { &*(module_before.config_ptr as *const M::Config) };
if config_before != config {
panic!("Module {module_id} cannot be added twice, with different configs: {config_before:?} and {config:?}");
}
}
self.added_modules.insert(module_id, added_module);
self
}
pub fn run(self) -> anyhow::Result<()> {
let Some(added_main_module) = self.main_module else {
return Err(anyhow!("No Main Module registered in the AppBuilder!"));
};
let modules_bump: &'static Bump = Box::leak(Box::new(Bump::new()));
let mut instantiated_modules: HashMap<ModuleId, InstantiatedModule> = HashMap::new();
let order = instantiation_order(&self.added_modules)?;
for m_id in order.iter() {
let m = self.added_modules.get(m_id).unwrap();
m.instantiate(&mut instantiated_modules, modules_bump)?;
}
for m_id in order.iter() {
let m = instantiated_modules.get(m_id).unwrap();
m.initialize()?;
}
let all_modules: HashMap<ModuleId, UntypedHandle> = instantiated_modules
.into_iter()
.map(|(k, v)| (k, v.handle))
.collect();
let app = App {
_modules: modules_bump,
all_modules: AllModules {
_inner: Arc::new(all_modules),
},
main_module: added_main_module.module_id,
};
(added_main_module.run_main_module_fn)(&app)
}
}
fn instantiation_order(modules: &HashMap<ModuleId, AddedModule>) -> anyhow::Result<Vec<ModuleId>> {
fn dependency_chain_string(m_id: &ModuleId, dependency_chain: &Vec<ModuleId>) -> String {
let mut dep_chain_string = String::new();
for d in dependency_chain.iter() {
dep_chain_string.push_str(&d.to_string());
dep_chain_string.push_str(" -> ");
}
format!("{dep_chain_string}{m_id}")
}
fn depth_first_fill_order(
m_id: &ModuleId,
modules: &HashMap<ModuleId, AddedModule>,
visitied: &mut HashSet<ModuleId>,
visited_in_this_run: &mut HashSet<ModuleId>,
order: &mut Vec<ModuleId>,
dependency_chain: &Vec<ModuleId>,
) -> anyhow::Result<()> {
if !visitied.contains(m_id) {
visitied.insert(*m_id);
if visited_in_this_run.contains(m_id) {
dbg!(&visited_in_this_run);
let dep_chain_string = dependency_chain_string(m_id, dependency_chain);
return Err(anyhow!("Recursive dependency chain: {dep_chain_string}"));
}
visited_in_this_run.insert(*m_id);
if let Some(m) = modules.get(m_id) {
let mut chain = dependency_chain.clone();
chain.push(*m_id);
for d_id in m.dependencies.iter() {
depth_first_fill_order(
d_id,
modules,
visitied,
visited_in_this_run,
order,
&chain,
)?;
}
} else {
let dep_chain_string = dependency_chain_string(m_id, dependency_chain);
return Err(anyhow!(
"Module {m_id} not found. Needed in: {dep_chain_string}"
));
}
order.push(*m_id);
}
Ok(())
}
let mut visitied: HashSet<ModuleId> = HashSet::new();
let mut order: Vec<ModuleId> = vec![];
for m_id in modules.keys() {
depth_first_fill_order(
m_id,
modules,
&mut visitied,
&mut HashSet::new(),
&mut order,
&vec![],
)?;
}
Ok(order)
}
#[allow(dead_code)]
struct AddedModule {
dependencies: Vec<ModuleId>,
module_id: ModuleId,
config_ptr: *const (), instantiate_module_fn: fn(
&AddedModule,
instantiated_modules: &mut HashMap<ModuleId, InstantiatedModule>,
modules_bump: &'static Bump,
) -> anyhow::Result<()>,
}
impl AddedModule {
fn instantiate(
&self,
instantiated_modules: &mut HashMap<ModuleId, InstantiatedModule>,
modules_bump: &'static Bump,
) -> anyhow::Result<()> {
(self.instantiate_module_fn)(self, instantiated_modules, modules_bump)
}
}
struct AddedMainModule {
module_id: ModuleId,
run_main_module_fn: fn(&App) -> anyhow::Result<()>,
}
struct InstantiatedModule {
handle: UntypedHandle,
initialize_module_fn: fn(&InstantiatedModule) -> anyhow::Result<()>,
}
impl InstantiatedModule {
fn initialize(&self) -> anyhow::Result<()> {
(self.initialize_module_fn)(self)
}
}
fn instantiate_module<M: Module>(
added_module: &AddedModule,
instantiated_modules: &mut HashMap<ModuleId, InstantiatedModule>,
modules_bump: &'static Bump,
) -> anyhow::Result<()> {
let mut dep_handles: Vec<UntypedHandle> = vec![];
for ty_id in added_module.dependencies.iter() {
if let Some(m) = instantiated_modules.get(ty_id) {
dep_handles.push(m.handle)
} else {
panic!("Cannot instantiate module {} because dependency not in instantiated_module_handles", ModuleId::of::<M>());
}
}
let deps = M::Dependencies::from_untyped_handles(&dep_handles);
let config: &M::Config = unsafe { &*(added_module.config_ptr as *const M::Config) };
let module = M::new(config.clone(), deps)?;
let module_ref = modules_bump.alloc(UnsafeCell::new(module));
let handle = Handle::<M> { ptr: module_ref };
instantiated_modules.insert(
ModuleId::of::<M>(),
InstantiatedModule {
handle: handle.untyped(),
initialize_module_fn: initialize_module::<M>,
},
);
Ok(())
}
fn initialize_module<M: Module>(instantiated_module: &InstantiatedModule) -> anyhow::Result<()> {
let handle: Handle<M> = instantiated_module.handle.typed();
M::intialize(handle)
}
fn run_main_module<M: MainModule>(app: &App) -> anyhow::Result<()> {
assert_eq!(ModuleId::of::<M>(), app.main_module);
let mut main_module_handle = app
.all_modules
.get::<M>()
.ok_or_else(|| anyhow!("Main Module {} not found in App", app.main_module))?;
main_module_handle.main(app)
}
#[cfg(test)]
mod test {
use super::{instantiation_order, AppBuilder, Handle, MainModule, Module};
struct RendererSettings;
impl Module for RendererSettings {
type Config = ();
type Dependencies = ();
fn new(_config: Self::Config, _deps: Self::Dependencies) -> anyhow::Result<Self> {
println!("New RendererSettings created");
Ok(RendererSettings)
}
fn intialize(_handle: Handle<Self>) -> anyhow::Result<()> {
println!("RendererSettings Initialized");
Ok(())
}
}
struct GraphicsContext;
impl Module for GraphicsContext {
type Config = ();
type Dependencies = ();
fn new(_config: Self::Config, _deps: Self::Dependencies) -> anyhow::Result<Self> {
println!("New GraphicsContext created");
Ok(GraphicsContext)
}
fn intialize(_handle: Handle<Self>) -> anyhow::Result<()> {
println!("GraphicsContext Initialized");
Ok(())
}
}
#[allow(dead_code)]
struct Renderer {
ctx: Handle<GraphicsContext>,
settings: Handle<RendererSettings>,
}
impl Module for Renderer {
type Config = ();
type Dependencies = (Handle<RendererSettings>, Handle<GraphicsContext>);
fn new(_config: Self::Config, (settings, ctx): Self::Dependencies) -> anyhow::Result<Self> {
println!("New Renderer created");
Ok(Renderer { settings, ctx })
}
fn intialize(_handle: Handle<Self>) -> anyhow::Result<()> {
println!("Renderer Initialized");
Ok(())
}
}
struct C;
impl Module for C {
type Config = ();
type Dependencies = Handle<A>;
fn new(_config: Self::Config, _deps: Self::Dependencies) -> anyhow::Result<Self> {
Ok(C)
}
}
struct A;
impl Module for A {
type Config = ();
type Dependencies = Handle<B>;
fn new(_config: Self::Config, _deps: Self::Dependencies) -> anyhow::Result<Self> {
Ok(A)
}
}
struct B;
impl Module for B {
type Config = ();
type Dependencies = Handle<A>;
fn new(_config: Self::Config, _deps: Self::Dependencies) -> anyhow::Result<Self> {
Ok(B)
}
}
#[allow(dead_code)]
struct LineRenderer {
renderer: Handle<Renderer>,
}
impl Module for LineRenderer {
type Config = ();
type Dependencies = Handle<Renderer>;
fn new(_config: Self::Config, renderer: Self::Dependencies) -> anyhow::Result<Self> {
println!("New LineRenderer created");
Ok(LineRenderer { renderer })
}
fn intialize(_handle: Handle<Self>) -> anyhow::Result<()> {
println!("LineRenderer Initialized");
Ok(())
}
}
struct MainMod {}
impl Module for MainMod {
type Config = ();
type Dependencies = ();
fn new(_config: Self::Config, _deps: Self::Dependencies) -> anyhow::Result<Self> {
println!("New MainMod created.");
Ok(MainMod {})
}
}
impl MainModule for MainMod {
fn main(&mut self, _app: &super::App) -> anyhow::Result<()> {
println!("Running Main");
Ok(())
}
}
#[test]
fn dependency_order() {
let mut app1 = AppBuilder::new();
app1.add::<LineRenderer>()
.add::<GraphicsContext>()
.add::<Renderer>()
.add::<RendererSettings>();
let mut app2 = AppBuilder::new();
app2.add::<GraphicsContext>()
.add::<Renderer>()
.add::<RendererSettings>();
let mut app3 = AppBuilder::new();
app3.add::<LineRenderer>()
.add::<GraphicsContext>()
.add::<RendererSettings>();
let mut app4 = AppBuilder::new();
app4.add::<LineRenderer>()
.add::<Renderer>()
.add::<RendererSettings>();
let mut apprec = AppBuilder::new();
apprec.add::<A>().add::<B>();
let mut apprec2 = AppBuilder::new();
apprec2.add::<C>().add::<A>().add::<B>();
assert!(instantiation_order(&app1.added_modules).is_ok());
assert!(instantiation_order(&app2.added_modules).is_ok());
assert!(instantiation_order(&app3.added_modules).is_err());
assert!(instantiation_order(&app4.added_modules).is_err());
assert!(instantiation_order(&apprec.added_modules).is_err());
assert!(instantiation_order(&apprec2.added_modules).is_err());
}
#[test]
fn instantiation() {
let mut app1 = AppBuilder::new();
app1.add::<LineRenderer>()
.add::<GraphicsContext>()
.add::<Renderer>()
.add::<RendererSettings>()
.add_main_module::<MainMod>();
app1.run().unwrap();
}
}