pub use bevy_ecs_macros::FromTemplate;
use core::{hash::Hash, ops::Deref};
use crate::{
component::Mutable,
entity::Entity,
error::{BevyError, Result},
resource::Resource,
world::{EntityWorldMut, Mut, World},
};
use alloc::vec::Vec;
use bevy_platform::{collections::hash_map::RawEntryMut, hash::Hashed};
use bevy_utils::PreHashMap;
use indexmap::Equivalent;
use variadics_please::all_tuples;
pub trait Template {
type Output;
fn build_template(&self, context: &mut TemplateContext) -> Result<Self::Output>;
fn clone_template(&self) -> Self;
}
pub struct TemplateContext<'a, 'w> {
pub entity: &'a mut EntityWorldMut<'w>,
pub entity_references: &'a mut SceneEntityReferences,
}
impl<'a, 'w> TemplateContext<'a, 'w> {
pub fn new(
entity: &'a mut EntityWorldMut<'w>,
entity_references: &'a mut SceneEntityReferences,
) -> Self {
Self {
entity,
entity_references,
}
}
pub fn get_entity(&mut self, reference: SceneEntityReference) -> Entity {
self.entity_references.get(
reference,
unsafe { self.entity.world_mut() },
)
}
#[inline]
pub fn resource<R: Resource>(&self) -> &R {
self.entity.resource()
}
#[inline]
pub fn resource_mut<R: Resource<Mutability = Mutable>>(&mut self) -> Mut<'_, R> {
self.entity.resource_mut()
}
#[inline]
pub fn resource_entity<R: Resource>(&self) -> Option<Entity> {
self.entity.resource_entity::<R>()
}
}
#[derive(Default)]
pub struct SceneEntityReferences(PreHashMap<InnerSceneEntityReference, Entity>);
impl SceneEntityReferences {
pub fn get(&mut self, reference: SceneEntityReference, world: &mut World) -> Entity {
let inner = reference.0;
let entry = self
.0
.raw_entry_mut()
.from_key_hashed_nocheck(inner.hash(), &inner);
match entry {
RawEntryMut::Occupied(entry) => *entry.get(),
RawEntryMut::Vacant(view) => {
let entity = world.spawn_empty().id();
view.insert_hashed_nocheck(inner.hash(), inner, entity);
entity
}
}
}
pub fn set(&mut self, reference: SceneEntityReference, entity: Entity) {
let inner = reference.0;
match self
.0
.raw_entry_mut()
.from_key_hashed_nocheck(inner.hash(), &inner)
{
RawEntryMut::Occupied(_) => {}
RawEntryMut::Vacant(view) => {
view.insert_hashed_nocheck(inner.hash(), inner, entity);
}
};
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct SceneEntityReference(Hashed<InnerSceneEntityReference>);
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct InnerSceneEntityReference {
file: &'static str,
line: usize,
column: usize,
name_id: usize,
runtime: u64,
}
impl SceneEntityReference {
pub fn new(
(file, line, column): (&'static str, usize, usize),
name_id: usize,
runtime: u64,
) -> Self {
Self(Hashed::new(InnerSceneEntityReference {
file,
line,
column,
name_id,
runtime,
}))
}
}
impl core::fmt::Display for SceneEntityReference {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_fmt(format_args!(
"global={}:{}:{} name_id={} runtime={:?}",
self.file, self.line, self.column, self.name_id, self.runtime
))
}
}
impl Deref for SceneEntityReference {
type Target = Hashed<InnerSceneEntityReference>;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Equivalent<Hashed<InnerSceneEntityReference>> for SceneEntityReference {
#[inline]
fn equivalent(&self, key: &Hashed<InnerSceneEntityReference>) -> bool {
&self.0 == key
}
}
pub trait FromTemplate: Sized {
type Template: Template<Output = Self>;
}
macro_rules! template_impl {
($($template: ident),*) => {
#[expect(
clippy::allow_attributes,
reason = "This is a tuple-related macro; as such, the lints below may not always apply."
)]
impl<$($template: Template),*> Template for TemplateTuple<($($template,)*)> {
type Output = ($($template::Output,)*);
fn build_template(&self, _context: &mut TemplateContext) -> Result<Self::Output> {
#[allow(
non_snake_case,
reason = "The names of these variables are provided by the caller, not by us."
)]
let ($($template,)*) = &self.0;
Ok(($($template.build_template(_context)?,)*))
}
fn clone_template(&self) -> Self {
#[allow(
non_snake_case,
reason = "The names of these variables are provided by the caller, not by us."
)]
let ($($template,)*) = &self.0;
TemplateTuple(($($template.clone_template(),)*))
}
}
}
}
pub struct TemplateTuple<T>(pub T);
all_tuples!(template_impl, 0, 12, T);
impl<T: Clone + Default + Unpin> Template for T {
type Output = T;
fn build_template(&self, _context: &mut TemplateContext) -> Result<Self::Output> {
Ok(self.clone())
}
fn clone_template(&self) -> Self {
self.clone()
}
}
impl<T: Clone + Default + Unpin> FromTemplate for T {
type Template = T;
}
#[diagnostic::on_unimplemented(
message = "This type does not manually implement FromTemplate, and it must. If you are deriving FromTemplate and you see this, it is likely because \
a field does not have a FromTemplate impl. This can usually be fixed by using a custom template for that field. \
Ex: for an Option<Handle<Image>> field, annotate the field with `#[template(OptionTemplate<HandleTemplate<Image>>)]`",
note = "FromTemplate currently uses pseudo-specialization to enable FromTemplate to override Default. This error message is a consequence of t."
)]
pub trait SpecializeFromTemplate: Sized {}
#[derive(Copy, Clone, Default, Debug)]
pub enum EntityTemplate {
Entity(Entity),
SceneEntityReference(SceneEntityReference),
#[default]
None,
}
impl Unpin for EntityTemplate where for<'a> [()]: SpecializeFromTemplate {}
impl EntityTemplate {
pub fn from_reference(
invocation: (&'static str, usize, usize),
name_id: usize,
runtime: u64,
) -> Self {
Self::SceneEntityReference(SceneEntityReference::new(invocation, name_id, runtime))
}
}
impl From<Entity> for EntityTemplate {
fn from(entity: Entity) -> Self {
Self::Entity(entity)
}
}
impl Template for EntityTemplate {
type Output = Entity;
fn build_template(&self, context: &mut TemplateContext) -> Result<Self::Output> {
Ok(match self {
Self::Entity(entity) => *entity,
Self::SceneEntityReference(reference) => context.get_entity(*reference),
Self::None => {
return Err(BevyError::error(
"Failed to specify an entity for this EntityTemplate",
))
}
})
}
fn clone_template(&self) -> Self {
match self {
Self::Entity(entity) => Self::Entity(*entity),
Self::SceneEntityReference(reference) => Self::SceneEntityReference(*reference),
Self::None => Self::None,
}
}
}
impl FromTemplate for Entity {
type Template = EntityTemplate;
}
pub struct FnTemplate<F: Fn(&mut TemplateContext) -> Result<O>, O>(pub F);
impl<F: Fn(&mut TemplateContext) -> Result<O> + Clone, O> Template for FnTemplate<F, O> {
type Output = O;
fn build_template(&self, context: &mut TemplateContext) -> Result<Self::Output> {
(self.0)(context)
}
fn clone_template(&self) -> Self {
Self(self.0.clone())
}
}
pub fn template<F: Fn(&mut TemplateContext) -> Result<O>, O>(func: F) -> FnTemplate<F, O> {
FnTemplate(func)
}
pub trait BuiltInTemplate: Sized {
type Template: Template;
}
impl<T: FromTemplate> BuiltInTemplate for Option<T> {
type Template = OptionTemplate<T::Template>;
}
impl<T: FromTemplate> BuiltInTemplate for Vec<T> {
type Template = VecTemplate<T::Template>;
}
#[derive(Default)]
pub enum OptionTemplate<T> {
Some(T),
#[default]
None,
}
impl<T> From<Option<T>> for OptionTemplate<T> {
fn from(value: Option<T>) -> Self {
match value {
Some(value) => OptionTemplate::Some(value),
None => OptionTemplate::None,
}
}
}
impl<T> From<T> for OptionTemplate<T> {
fn from(value: T) -> Self {
OptionTemplate::Some(value)
}
}
impl<T: Template> Template for OptionTemplate<T> {
type Output = Option<T::Output>;
fn build_template(&self, context: &mut TemplateContext) -> Result<Self::Output> {
Ok(match &self {
OptionTemplate::Some(template) => Some(template.build_template(context)?),
OptionTemplate::None => None,
})
}
fn clone_template(&self) -> Self {
match self {
OptionTemplate::Some(value) => OptionTemplate::Some(value.clone_template()),
OptionTemplate::None => OptionTemplate::None,
}
}
}
pub struct VecTemplate<T>(pub Vec<T>);
impl<T> Default for VecTemplate<T> {
fn default() -> Self {
Self(Vec::new())
}
}
impl<T: Template> Template for VecTemplate<T> {
type Output = Vec<T::Output>;
fn build_template(&self, context: &mut TemplateContext) -> Result<Self::Output> {
let mut output = Vec::with_capacity(self.0.len());
for value in &self.0 {
output.push(value.build_template(context)?);
}
Ok(output)
}
fn clone_template(&self) -> Self {
VecTemplate(self.0.iter().map(Template::clone_template).collect())
}
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
use alloc::string::{String, ToString};
#[test]
fn option_template() {
#[derive(FromTemplate)]
struct Handle(String);
#[derive(FromTemplate)]
struct Foo {
#[template(built_in)]
handle: Option<Handle>,
}
let mut world = World::new();
let foo_template = FooTemplate {
handle: Some(HandleTemplate("handle_path".to_string())).into(),
};
let foo = world.spawn_empty().build_template(&foo_template).unwrap();
assert_eq!(foo.handle.unwrap().0, "handle_path".to_string());
}
}