From 7ee6ef0a08ff00fc54318619f1f9e063c62262c2 Mon Sep 17 00:00:00 2001
From: hodasemi <michaelh.95@t-online.de>
Date: Wed, 9 Apr 2025 09:00:42 +0200
Subject: [PATCH] Start implementing proc macro for updates

---
 ecs/Cargo.toml              |   3 -
 ecs/src/updates.rs          |  44 +-----
 update_macros/src/update.rs | 289 ++++++++++++++++++++++++++++++++++--
 3 files changed, 284 insertions(+), 52 deletions(-)

diff --git a/ecs/Cargo.toml b/ecs/Cargo.toml
index 2901052..9ea5df7 100644
--- a/ecs/Cargo.toml
+++ b/ecs/Cargo.toml
@@ -13,6 +13,3 @@ ron.workspace = true
 utilities.workspace = true
 
 update_macros = { path = "../update_macros" }
-
-[features]
-timings = []
diff --git a/ecs/src/updates.rs b/ecs/src/updates.rs
index fd9ae4b..cf827b6 100644
--- a/ecs/src/updates.rs
+++ b/ecs/src/updates.rs
@@ -4,15 +4,10 @@ use std::ops::{Deref, DerefMut};
 use std::{any::TypeId, marker::PhantomData};
 
 use std::collections::HashMap;
-#[cfg(feature = "timings")]
-use std::time::Instant;
 
 use anyhow::Result;
 use indexmap::IndexMap;
 
-#[cfg(feature = "timings")]
-use super::super::timings::Timings;
-
 use crate::resources::Resource as ResourceTrait;
 use crate::*;
 use update_macros::{implement_pair_update, implement_single_update};
@@ -1152,18 +1147,12 @@ impl From<ArchetypePair> for Update {
 }
 
 pub struct Updates {
-    #[cfg(feature = "timings")]
-    timings: Timings,
-
-    updates: Vec<(String, u32, Update)>,
+    updates: Vec<(u32, Update)>,
 }
 
 impl Default for Updates {
     fn default() -> Self {
         Self {
-            #[cfg(feature = "timings")]
-            timings: Timings::default,
-
             updates: Vec::new(),
         }
     }
@@ -1171,22 +1160,9 @@ impl Default for Updates {
 
 impl Updates {
     pub(crate) fn update(&mut self, world: &mut World) -> Result<()> {
-        #[cfg(feature = "timings")]
-        if let Some(timings) = self.timings.check_timing(world.now(), None) {
-            if !timings.is_empty() {
-                println!("timings: {:#?}", timings);
-            }
-        }
-
-        #[cfg(feature = "timings")]
-        let timings = &mut self.timings;
-
         self.updates
             .iter()
-            .try_for_each(|(_name, _, update)| -> Result<()> {
-                #[cfg(feature = "timings")]
-                let before = Instant::now();
-
+            .try_for_each(|(_, update)| -> Result<()> {
                 match update {
                     Update::Single(archetype) => {
                         archetype.execute(world)?;
@@ -1196,9 +1172,6 @@ impl Updates {
                     }
                 }
 
-                #[cfg(feature = "timings")]
-                timings.add(_name, Instant::now().duration_since(before));
-
                 Ok(())
             })?;
 
@@ -1210,7 +1183,7 @@ impl Updates {
         entity_object: &EntityObject,
         entities: &IndexMap<Entity, EntityObject>,
     ) -> Result<()> {
-        for (_, _, update) in self.updates.iter_mut() {
+        for (_, update) in self.updates.iter_mut() {
             match update {
                 Update::Single(archetype) => {
                     archetype.add_entity(entity_object)?;
@@ -1225,7 +1198,7 @@ impl Updates {
     }
 
     pub(crate) fn remove_entity(&mut self, entity: Entity) {
-        for (_, _, update) in self.updates.iter_mut() {
+        for (_, update) in self.updates.iter_mut() {
             match update {
                 Update::Single(archetype) => {
                     archetype.remove_entity(entity);
@@ -1244,13 +1217,10 @@ impl Updates {
     //     self.timings.clear();
     // }
 
-    pub(crate) fn add(&mut self, name: &str, priority: u32, update: Update) -> Result<()> {
-        #[cfg(feature = "timings")]
-        self.timings.add_timing_afterwards(name);
-
-        self.updates.push((name.to_string(), priority, update));
+    pub(crate) fn add(&mut self, priority: u32, update: Update) -> Result<()> {
+        self.updates.push((priority, update));
         self.updates
-            .sort_by(|(_, lhs_prio, _), (_, rhs_prio, _)| lhs_prio.cmp(rhs_prio));
+            .sort_by(|(lhs_prio, _), (rhs_prio, _)| lhs_prio.cmp(rhs_prio));
 
         Ok(())
     }
diff --git a/update_macros/src/update.rs b/update_macros/src/update.rs
index 23cd829..950dce5 100644
--- a/update_macros/src/update.rs
+++ b/update_macros/src/update.rs
@@ -16,46 +16,113 @@ impl Query {
         }
     }
 
-    pub fn types(&self) -> proc_macro2::TokenStream {
+    pub fn types(&self) -> TokenStream2 {
         let component_list = self
             .components
             .iter()
-            .map(|c| quote! { #c, })
+            .map(|c| quote! { #c })
             .collect::<Vec<_>>();
 
         let components = if component_list.len() == 1 {
             let component = &component_list[0];
             quote! { #component }
         } else {
-            quote! { ( #(#component_list)* ) }
+            quote! { ( #( #component_list, )* ) }
         };
 
         components
     }
 
+    pub fn dedup_checks(&self) -> TokenStream2 {
+        let components = &self.components;
+        let filter = &self.filter;
+
+        quote! {
+            #( #filter::verify_dedup::<#components>(); )*
+        }
+    }
+
+    pub fn check_entity(&self, index: usize) -> TokenStream2 {
+        let components = &self.components;
+        let filter = &self.filter;
+
+        quote! {
+            (
+                let entity = &entites[#index];
+
+                #(
+                    if !entity.components.contains::<#components>() {
+                        return false;
+                    }
+                )*
+
+                if !#filter::check(entity) {
+                    return false;
+                }
+            )
+        }
+    }
+
+    pub fn component_store_and_query(&self, index: usize) -> (TokenStream2, TokenStream2, Ident) {
+        let components = &self.components;
+        let entity_name = format_ident!("entity_{index}");
+        let component_name = self
+            .components
+            .iter()
+            .map(|component| format_ident!("q_{index}_{component}"))
+            .collect::<Vec<_>>();
+        let query_ident = format_ident!("query_{index}");
+        let entity_ident = format_ident!("entity_{index}");
+
+        (
+            quote! {
+                let #entity_name = &entities[#index];
+                let #entity_ident = entity_name.as_entity();
+
+                #(
+                    let #component_name = UnsafeComponentStore::from(
+                        entity.get_component::<#components>()?
+                    );
+                )*
+            },
+            quote! {
+                let #query_ident = Query {
+                    entity: #entity_ident,
+                    c: unsafe { ( #( #component_name.as_mut() )* ) },
+                    filter: PhantomData,
+                };
+            },
+            query_ident,
+        )
+    }
+
+    pub fn components(&self) -> &Vec<Ident> {
+        &self.components
+    }
+
     pub fn filter(&self) -> &Ident {
         &self.filter
     }
 }
 
 impl ToTokens for Query {
-    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
+    fn to_tokens(&self, tokens: &mut TokenStream2) {
         let component_list = self
             .components
             .iter()
-            .map(|c| quote! { &mut #c, })
+            .map(|c| quote! { &mut #c })
             .collect::<Vec<_>>();
 
         let components = if component_list.len() == 1 {
             let component = &component_list[0];
             quote! { #component }
         } else {
-            quote! { ( #(#component_list)* ) }
+            quote! { ( #( #component_list, )* ) }
         };
 
         let filter = self.filter.clone();
 
-        proc_macro2::TokenStream::from(quote! {
+        TokenStream2::from(quote! {
             Query<#components, #filter>
         })
         .to_tokens(tokens);
@@ -68,6 +135,10 @@ struct Update {
 }
 
 impl Update {
+    const MIN_RESOURCE: usize = 0;
+    const MIN_COMPONENTS: usize = 1;
+    const MIN_QUERIES: usize = 1;
+
     pub fn new(queries: impl Iterator<Item = usize>, resources: usize) -> Self {
         Self {
             queries: queries
@@ -85,14 +156,208 @@ impl Update {
         }
     }
 
-    pub fn query_types(&self)
+    pub fn filter_types(&self) -> TokenStream2 {
+        let filter_types = self
+            .queries
+            .iter()
+            .map(|query| query.filter())
+            .collect::<Vec<_>>();
+
+        if filter_types.len() == 1 {
+            let filter = &filter_types[0];
+
+            quote! {
+                #filter
+            }
+        } else {
+            quote! {
+               ( #( #filter_types, )* )
+            }
+        }
+    }
+
+    pub fn filter_requirements(&self) -> TokenStream2 {
+        let filter_requirements = self
+            .queries
+            .iter()
+            .map(|query| query.filter())
+            .collect::<Vec<_>>();
+
+        quote! {
+           #( #filter_requirements: CheckFilter + 'static, )*
+        }
+    }
+
+    pub fn query_types(&self) -> TokenStream2 {
+        let query_types = self
+            .queries
+            .iter()
+            .map(|query| query.types())
+            .collect::<Vec<_>>();
+
+        quote! {
+            ( #( #query_types, )* )
+        }
+    }
+
+    pub fn component_requirements(&self) -> TokenStream2 {
+        let component_requirements = self
+            .queries
+            .iter()
+            .map(|query| {
+                query.components.iter().map(|component| {
+                    quote! {
+                        #component: EntityComponent + ComponentDebug,
+                    }
+                })
+            })
+            .flatten()
+            .collect::<Vec<_>>();
+
+        quote! {
+            #( #component_requirements, )*
+        }
+    }
+
+    pub fn resource_types(&self) -> TokenStream2 {
+        let resource_types = &self.resources;
+
+        quote! {
+            ( #( #resource_types, )* )
+        }
+    }
+
+    pub fn reource_requirement(&self) -> TokenStream2 {
+        let resource_types = &self.resources;
+
+        quote! {
+            #( #resource_types: ResourceTrait, )*
+        }
+    }
+}
+
+impl ToTokens for Update {
+    fn to_tokens(&self, tokens: &mut TokenStream2) {
+        // generics to specify trait implementations
+        let filter_types = self.filter_types();
+        let query_types = self.query_types();
+        let resource_types = self.resource_types();
+
+        // function parameter
+        let queries = &self.queries;
+        let resources = if self.resources.is_empty() {
+            quote! {}
+        } else {
+            let resources = &self.resources;
+
+            quote! {
+                #( &mut #resources, )*
+            }
+        };
+
+        // trait requirements
+        let filter_requirements = self.filter_requirements();
+        let component_requirements = self.component_requirements();
+        let reource_requirement = self.reource_requirement();
+
+        let verify_dedup = self
+            .queries
+            .iter()
+            .map(|query| query.dedup_checks())
+            .collect::<Vec<_>>();
+
+        let check_entities = self
+            .queries
+            .iter()
+            .enumerate()
+            .map(|(index, query)| query.check_entity(index))
+            .collect::<Vec<_>>();
+
+        let component_stores = self
+            .queries
+            .iter()
+            .enumerate()
+            .map(|(index, query)| query.component_store_and_query(index))
+            .collect::<Vec<_>>();
+
+        TokenStream2::from(quote! {
+            impl<Func, #filter_types, #query_types, #resource_types> CreateArchetype<#query_types, #resource_types, Func, #filter_types> for Archetype
+            where
+                Func: Fn(&mut Commands, #( #queries, )* #resources ) -> Result<()> + Send + Sync + Clone + 'static,
+                #filter_requirements
+                #component_requirements
+                #reource_requirement
+            {
+                fn create(f: Func) -> Self {
+                    #( #verify_dedup )*
+
+                    Self {
+                        check_entity: Box::new(move |entities| {
+                            #( #check_entities )*
+
+                            true
+                        }),
+
+                        create_callback: Box::new(move |entities| {
+                            let e = entity.as_entity();
+                            let f = f.clone();
+
+                            Ok(Box::new(move |commands, world| {
+
+
+                                f(commands )
+                            }))
+                        }),
+
+                        entities: IndexMap::new(),
+                    }
+                }
+            }
+
+            impl<Func, #filter_types, #query_types, #resource_types> AddUpdates<#query_types, #resource_types, Func, #filter_types> for Updates
+            where
+                Func: Fn(&mut Commands, #( #queries, )* #resources ) -> Result<()> + Send + Sync + Clone + 'static,
+                #filter_requirements
+                #component_requirements
+                #reource_requirement
+            {
+                fn add_update(
+                    &mut self,
+                    priority: u32,
+                    func: Func,
+                ) -> Result<()> {
+                    self.add(priority, Archetype::create(func))
+                }
+            }
+
+            impl<Func, #filter_types, #query_types, #resource_types> AddUpdates<#query_types, #resource_types, Func, #filter_types> for WorldBuilder
+            where
+                Func: Fn(&mut Commands, #( #queries, )* #resources ) -> Result<()> + Send + Sync + Clone + 'static,
+                #filter_requirements
+                #component_requirements
+                #reource_requirement
+            {
+                fn add_update(
+                    &mut self,
+                    priority: u32,
+                    func: Func,
+                ) -> Result<()> {
+                    self.updates.add_update(priority, func)
+                }
+            }
+        })
+        .to_tokens(tokens);
+    }
 }
 
 pub fn update(max_components: usize, max_queries: usize) -> TokenStream {
+    let updates = (Update::MIN_QUERIES..max_queries)
+        .map(|query_count| {
+            //
+        })
+        .collect::<Vec<_>>();
+
     TokenStream::from(quote! {
-        impl<Func, Filter, >
-
-
-
+        #( #updates )
     })
 }