#![doc = include_str!("../README.md")]
pub mod blueprint;
pub mod factory;
use std::collections::BTreeMap;
use blueprint::{BlueprintLibrary, BlueprintLookupError, BlueprintParseError};
use factory::{ComponentFactory, SerdeComponentFactory};
use palkia::prelude::*;
use serde::de::DeserializeOwned;
use smol_str::SmolStr;
use thiserror::Error;
pub struct EntityFabricator<Ctx> {
blueprints: BlueprintLibrary,
assemblers: BTreeMap<SmolStr, Box<dyn ComponentFactory<Ctx>>>,
}
impl<Ctx> EntityFabricator<Ctx>
where
Ctx: 'static,
{
pub fn new() -> Self {
Self {
blueprints: BlueprintLibrary::new(),
assemblers: BTreeMap::new(),
}
}
pub fn register<CA: ComponentFactory<Ctx>>(
&mut self,
name: &str,
factory: CA,
) {
if let Some(_) = self
.assemblers
.insert(SmolStr::from(name), Box::new(factory))
{
panic!("already registered something under the name {:?}", name);
}
}
pub fn register_serde<C: DeserializeOwned + Component>(
&mut self,
name: &str,
) {
self.register(name, SerdeComponentFactory::<C, Ctx>::new())
}
pub fn load_str(
&mut self,
src: &str,
filepath: &str,
) -> Result<(), BlueprintParseError> {
self.blueprints.load_str(src, filepath)
}
pub fn instantiate_to_builder<'a, 'w>(
&self,
name: &str,
mut builder: EntityBuilder<'a, 'w>,
ctx: &Ctx,
) -> Result<EntityBuilder<'a, 'w>, InstantiationError> {
let print = self.blueprints.lookup(name)?;
for node in print.components {
let name = node.name().value();
let factory = self
.assemblers
.get(name)
.ok_or_else(|| InstantiationError::NoAssembler(name.into()))?;
builder = factory.assemble(builder, &node, ctx).map_err(|err| {
InstantiationError::AssemblerError(name.into(), err)
})?
}
Ok(builder)
}
pub fn instantiate<'a, 'w>(
&self,
name: &str,
builder: EntityBuilder<'a, 'w>,
ctx: &Ctx,
) -> Result<Entity, InstantiationError> {
Ok(self.instantiate_to_builder(name, builder, ctx)?.build())
}
}
#[derive(Debug, Error)]
pub enum InstantiationError {
#[error("while looking up the blueprint: {0}")]
BlueprintLookupError(#[from] BlueprintLookupError),
#[error("there was no assembler registered for a component named {0:?}")]
NoAssembler(SmolStr),
#[error("the assembler for {0:?} gave an error: {1}")]
AssemblerError(SmolStr, eyre::Error),
}