summaryrefslogtreecommitdiff
path: root/engine-ecs-macros/src/lib.rs
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2026-06-20 16:01:00 +0200
committerHampusM <hampus@hampusmat.com>2026-06-23 00:25:26 +0200
commit7b3374ad9585f78c60e1b158126ab54384a83f32 (patch)
treeee14cd0bebd6554fab43685da89c459310f0e97d /engine-ecs-macros/src/lib.rs
parentd6cf708a4fd1caf0f2e193ceb7c23fa8e77cc1db (diff)
refactor(engine-ecs): store soles as components
Diffstat (limited to 'engine-ecs-macros/src/lib.rs')
-rw-r--r--engine-ecs-macros/src/lib.rs175
1 files changed, 101 insertions, 74 deletions
diff --git a/engine-ecs-macros/src/lib.rs b/engine-ecs-macros/src/lib.rs
index a304706..106d350 100644
--- a/engine-ecs-macros/src/lib.rs
+++ b/engine-ecs-macros/src/lib.rs
@@ -6,7 +6,6 @@ use quote::{format_ident, quote, ToTokens};
use syn::spanned::Spanned;
use syn::{
parse,
- Attribute,
Generics,
Ident,
Item,
@@ -148,6 +147,17 @@ pub fn component_derive(input: TokenStream) -> TokenStream
#where_clause
{
}
+
+ impl #ecs_path::component::IntoParts for #item_ident
+ {
+ fn into_parts(self) -> #ecs_path::component::Parts
+ {
+ #ecs_path::component::Parts::builder()
+ .name(self.name())
+ .type_reflection(<Self as Component>::type_reflection())
+ .build(Self::id(), self)
+ }
+ }
}
}
.into()
@@ -156,50 +166,118 @@ pub fn component_derive(input: TokenStream) -> TokenStream
/// Generates a `Sole` implementation.
///
/// # Panics
-/// Will panic if not attributed to a type item or if parsing the user crate's
-/// `Cargo.toml` file fails.
-#[proc_macro_derive(Sole, attributes(sole))]
+/// Will panic if:
+/// - Not attributed to a type item
+/// - The attributed-to type item is generic
+/// - If parsing the user crate's `Cargo.toml` file fails.
+#[proc_macro_derive(Sole)]
pub fn sole_derive(input: TokenStream) -> TokenStream
{
let item: TypeItem = parse::<Item>(input).unwrap().try_into().unwrap();
let item_ident = item.ident();
- let sole_attr = item.attribute::<SoleAttribute>("sole").unwrap_or_default();
+ let ecs_path = find_engine_ecs_crate_path().unwrap_or_else(|| syn_path!(ecs));
- let drop_last = sole_attr.drop_last;
+ assert!(
+ item.generics().params.is_empty(),
+ "Generic types are not supported as sole components"
+ );
- let (impl_generics, type_generics, where_clause) = item.generics().split_for_impl();
+ let id_var_ident = format_ident!("{}_ID", item_ident.to_string().to_uppercase());
- let ecs_path = find_engine_ecs_crate_path().unwrap_or_else(|| syn_path!(ecs));
+ let id_var = quote! {
+ static #id_var_ident: LazyLock<Uid> = LazyLock::new(|| {
+ Uid::new_unique()
+ });
+ };
+
+ let mod_ident = format_ident!(
+ "__ecs_priv_sole_impl_{}",
+ item_ident.to_string().to_lowercase()
+ );
quote! {
- impl #impl_generics #ecs_path::sole::Sole for #item_ident #type_generics
- #where_clause
- {
- fn drop_last(&self) -> bool
- {
- #drop_last
- }
+ mod #mod_ident {
+ use ::std::any::{Any, TypeId};
+ use ::std::sync::{LazyLock, Mutex};
+
+ use #ecs_path::sole::Sole;
+ use #ecs_path::uid::Uid;
+ use #ecs_path::system::Input as SystemInput;
+
+ use super::*;
+
+ #id_var
- fn as_any_mut(&mut self) -> &mut dyn std::any::Any
+ impl Sole for #item_ident
{
- self
+ fn id() -> Uid
+ {
+ *#id_var_ident
+ }
+
+ fn type_reflection() -> Option<&'static #ecs_path::reflection::Type>
+ {
+ struct SpecializationTarget<T>(std::marker::PhantomData<T>);
+
+ trait HasReflection
+ {
+ fn type_reflection(&self)
+ -> Option<&'static #ecs_path::reflection::Type>;
+ }
+
+ trait NoReflection
+ {
+ fn type_reflection(&self)
+ -> Option<&'static #ecs_path::reflection::Type>;
+ }
+
+ impl<T> NoReflection for &SpecializationTarget<T>
+ {
+ fn type_reflection(&self)
+ -> Option<&'static #ecs_path::reflection::Type>
+ {
+ None
+ }
+ }
+
+ impl<T> HasReflection for SpecializationTarget<T>
+ where
+ T: #ecs_path::reflection::Reflection
+ {
+ fn type_reflection(&self)
+ -> Option<&'static #ecs_path::reflection::Type>
+ {
+ Some(T::type_reflection())
+ }
+ }
+
+ (&SpecializationTarget::<Self>(std::marker::PhantomData))
+ .type_reflection()
+ }
+
+ fn name(&self) -> &'static str
+ {
+ std::any::type_name::<Self>()
+ }
}
- fn as_any(&self) -> &dyn std::any::Any
+ impl #ecs_path::component::IntoParts for #item_ident
{
- self
+ fn into_parts(self) -> #ecs_path::component::Parts
+ {
+ #ecs_path::component::Parts::builder()
+ .name(self.name())
+ .type_reflection(<Self as Sole>::type_reflection())
+ .build(Self::id(), self)
+ }
}
}
}
.into()
}
-trait FromAttribute: Sized
-{
- fn from_attribute(attribute: &Attribute) -> Result<Self, syn::Error>;
-}
enum TypeItem
{
@@ -219,27 +297,6 @@ impl TypeItem
}
}
- fn attribute<Attr: FromAttribute>(&self, attr_ident: &str) -> Option<Attr>
- {
- let item_attrs = match &self {
- Self::Struct(struct_item) => &struct_item.attrs,
- &Self::Enum(enum_item) => &enum_item.attrs,
- &Self::Union(union_item) => &union_item.attrs,
- };
-
- let mut attr: Option<&Attribute> = None;
-
- for item_attr in item_attrs {
- assert!(attr.is_none(), "Expected only one {attr_ident} attribute");
-
- if item_attr.path().get_ident()? == attr_ident {
- attr = Some(item_attr);
- }
- }
-
- Some(Attr::from_attribute(attr?).unwrap())
- }
-
fn generics(&self) -> &Generics
{
match self {
@@ -280,36 +337,6 @@ impl ToTokens for TypeItem
}
}
-#[derive(Debug, Default)]
-struct SoleAttribute
-{
- drop_last: bool,
-}
-
-impl FromAttribute for SoleAttribute
-{
- fn from_attribute(attribute: &Attribute) -> Result<Self, syn::Error>
- {
- let mut drop_last = false;
-
- attribute.parse_nested_meta(|meta| {
- if meta
- .path
- .get_ident()
- .is_some_and(|flag| flag == "drop_last")
- {
- drop_last = true;
-
- return Ok(());
- }
-
- Err(meta.error("Unrecognized token"))
- })?;
-
- Ok(Self { drop_last })
- }
-}
-
fn find_engine_ecs_crate_path() -> Option<Path>
{
let cargo_manifest_dir = FsPathBuf::from(std::env::var("CARGO_MANIFEST_DIR").ok()?);