diff options
Diffstat (limited to 'ecs-macros/src')
| -rw-r--r-- | ecs-macros/src/lib.rs | 437 |
1 files changed, 0 insertions, 437 deletions
diff --git a/ecs-macros/src/lib.rs b/ecs-macros/src/lib.rs deleted file mode 100644 index 862b0b1..0000000 --- a/ecs-macros/src/lib.rs +++ /dev/null @@ -1,437 +0,0 @@ -use std::path::PathBuf as FsPathBuf; - -use proc_macro::TokenStream; -use quote::{format_ident, quote, ToTokens}; -use syn::spanned::Spanned; -use syn::{ - parse, - Attribute, - GenericParam, - Generics, - Ident, - Item, - ItemEnum, - ItemStruct, - ItemUnion, - Path, - Type, -}; -use toml::value::{Table as TomlTable, Value as TomlValue}; - -macro_rules! syn_path { - ($first_segment: ident $(::$segment: ident)*) => { - ::syn::Path { - leading_colon: None, - segments: ::syn::punctuated::Punctuated::from_iter([ - syn_path_segment!($first_segment), - $(syn_path_segment!($segment),)* - ]) - } - }; -} - -macro_rules! syn_path_segment { - ($segment: ident) => { - ::syn::PathSegment { - ident: ::proc_macro2::Ident::new( - stringify!($segment), - ::proc_macro2::Span::call_site(), - ), - arguments: ::syn::PathArguments::None, - } - }; -} - -#[proc_macro_derive(Component, attributes(component))] -pub fn component_derive(input: TokenStream) -> TokenStream -{ - let item: TypeItem = parse::<Item>(input).unwrap().try_into().unwrap(); - - let ComponentAttribute { handle_type, handle_mut_type } = item - .attribute::<ComponentAttribute>("component") - .unwrap_or_default(); - - let item_ident = item.ident(); - - let (impl_generics, type_generics, where_clause) = item.generics().split_for_impl(); - - let ecs_path = find_engine_ecs_crate_path().unwrap_or_else(|| syn_path!(ecs)); - - let (id_or_ids, get_id) = if !item.generics().params.is_empty() { - let id_lut_ident = - format_ident!("{}_ID_LUT", item_ident.to_string().to_uppercase()); - - let id_lut = quote! { - static #id_lut_ident: LazyLock<Mutex<HashMap<TypeId, Uid>>> = - LazyLock::new(|| Mutex::new(HashMap::new())); - }; - - let generics = item.generics().params.iter().map(|param| match param { - GenericParam::Type(type_param) => type_param.ident.clone(), - GenericParam::Lifetime(_) => panic!("Lifetime generics are not supported"), - GenericParam::Const(_) => panic!("Const generics are not supported"), - }); - - let get_id = quote! { - *#id_lut_ident - .try_lock() - .unwrap() - .entry(TypeId::of::<(#(#generics,)*)>()) - .or_insert_with(|| Uid::new_unique(UidKind::Component)) - }; - - (id_lut, get_id) - } else { - let id_lazylock_ident = - format_ident!("{}_ID", item_ident.to_string().to_uppercase()); - - let id_lazylock = quote! { - static #id_lazylock_ident: LazyLock<Uid> = LazyLock::new(|| { - Uid::new_unique(UidKind::Component) - }); - }; - - let get_id = quote! { - *#id_lazylock_ident - }; - - (id_lazylock, get_id) - }; - - let mod_ident = format_ident!( - "__ecs_priv_component_impl_{}", - item_ident.to_string().to_lowercase() - ); - - quote! { - mod #mod_ident { - use ::std::any::{Any, TypeId}; - use ::std::sync::{LazyLock, Mutex}; - use ::std::collections::HashMap; - - use #ecs_path::component::Component; - use #ecs_path::event::component::{ - Removed as ComponentRemovedEvent, - Kind as ComponentEventKind, - }; - use #ecs_path::component::{ - Handle as ComponentHandle, - HandleMut as ComponentHandleMut - }; - use #ecs_path::uid::{Uid, Kind as UidKind}; - use #ecs_path::system::Input as SystemInput; - use #ecs_path::type_name::TypeName; - - use super::*; - - #id_or_ids - - impl #impl_generics Component for #item_ident #type_generics - #where_clause - { - type Component = Self; - - type HandleMut<'component> = #handle_mut_type; - type Handle<'component> = #handle_type; - - fn id() -> Uid - { - #get_id - } - - fn get_event_uid(&self, event_kind: ComponentEventKind) -> Uid - { - match event_kind { - ComponentEventKind::Removed => ComponentRemovedEvent::<Self>::id(), - _ => { - panic!( - "Support for event kind {event_kind:?} not implemented!" - ); - } - } - } - } - - impl #impl_generics SystemInput for #item_ident #type_generics - #where_clause - { - } - - impl #impl_generics TypeName for #item_ident #type_generics - #where_clause - { - fn type_name(&self) -> &'static str - { - std::any::type_name::<Self>() - } - } - } - } - .into() -} - -#[proc_macro_derive(Sole, attributes(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 drop_last = sole_attr.drop_last; - - let (impl_generics, type_generics, where_clause) = item.generics().split_for_impl(); - - let ecs_path = find_engine_ecs_crate_path().unwrap_or_else(|| syn_path!(ecs)); - - quote! { - impl #impl_generics #ecs_path::sole::Sole for #item_ident #type_generics - #where_clause - { - fn drop_last(&self) -> bool - { - #drop_last - } - - fn as_any_mut(&mut self) -> &mut dyn std::any::Any - { - self - } - - fn as_any(&self) -> &dyn std::any::Any - { - self - } - } - - impl #impl_generics #ecs_path::type_name::TypeName for #item_ident #type_generics - #where_clause - { - fn type_name(&self) -> &'static str - { - std::any::type_name::<Self>() - } - } - } - .into() -} - -trait FromAttribute: Sized -{ - fn from_attribute(attribute: &Attribute) -> Result<Self, syn::Error>; -} - -enum TypeItem -{ - Struct(ItemStruct), - Enum(ItemEnum), - Union(ItemUnion), -} - -impl TypeItem -{ - fn ident(&self) -> &Ident - { - match self { - Self::Struct(struct_item) => &struct_item.ident, - Self::Enum(enum_item) => &enum_item.ident, - Self::Union(union_item) => &union_item.ident, - } - } - - 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 { - if attr.is_some() { - panic!("Expected only one {} attribute", attr_ident); - } - - if item_attr.path().get_ident()? == attr_ident { - attr = Some(item_attr); - } - } - - Some(Attr::from_attribute(attr?).unwrap()) - } - - fn generics(&self) -> &Generics - { - match self { - Self::Struct(struct_item) => &struct_item.generics, - Self::Enum(enum_item) => &enum_item.generics, - Self::Union(union_item) => &union_item.generics, - } - } -} - -impl TryFrom<Item> for TypeItem -{ - type Error = syn::Error; - - fn try_from(item: Item) -> Result<Self, Self::Error> - { - match item { - Item::Struct(struct_item) => Ok(Self::Struct(struct_item)), - Item::Enum(enum_item) => Ok(Self::Enum(enum_item)), - Item::Union(union_item) => Ok(Self::Union(union_item)), - _ => Err(syn::Error::new( - item.span(), - "Expected a struct, a enum or a union", - )), - } - } -} - -impl ToTokens for TypeItem -{ - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) - { - match self { - Self::Struct(struct_item) => struct_item.to_tokens(tokens), - Self::Enum(enum_item) => enum_item.to_tokens(tokens), - Self::Union(union_item) => union_item.to_tokens(tokens), - } - } -} - -#[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()?); - - let cargo_crate_name = std::env::var("CARGO_CRATE_NAME").ok()?; - let cargo_pkg_name = std::env::var("CARGO_PKG_NAME").ok()?; - - if cargo_pkg_name == "ecs" && cargo_crate_name != "ecs" { - // Macro is used by a ecs crate example/test/benchmark - return Some(syn_path!(ecs)); - } - - let crate_manifest = std::fs::read_to_string(cargo_manifest_dir.join("Cargo.toml")) - .ok()? - .parse::<TomlTable>() - .expect("Failed to parse crate manifest file"); - - let package = match crate_manifest.get("package")? { - TomlValue::Table(package) => Some(package), - _ => None, - }?; - - let package_name = match package.get("name")? { - TomlValue::String(package_name) => Some(package_name), - _ => None, - }?; - - if package_name == "ecs" { - return Some(syn_path!(crate)); - } - - let crate_dependencies = match crate_manifest.get("dependencies")? { - TomlValue::Table(dependencies) => Some(dependencies), - _ => None, - }?; - - crate_dependencies.iter().find_map(|(crate_dep_name, _)| { - if crate_dep_name == "engine" { - return Some(syn_path!(engine::ecs)); - } - - None - }) -} - -#[derive(Debug)] -struct ComponentAttribute -{ - handle_type: proc_macro2::TokenStream, - handle_mut_type: proc_macro2::TokenStream, -} - -impl FromAttribute for ComponentAttribute -{ - fn from_attribute(attribute: &Attribute) -> Result<Self, syn::Error> - { - let mut handle_type: Option<Type> = None; - let mut handle_mut_type: Option<Type> = None; - - attribute.parse_nested_meta(|meta| { - let Some(flag) = meta.path.get_ident() else { - return Err(meta.error("Not a single identifier")); - }; - - if flag == "handle_type" { - let value = meta.value()?; - - handle_type = Some(value.parse::<Type>()?); - - return Ok(()); - } else if flag == "handle_mut_type" { - let value = meta.value()?; - - handle_mut_type = Some(value.parse::<Type>()?); - - return Ok(()); - } - - Err(meta.error("Unrecognized token")) - })?; - - Ok(Self { - handle_type: handle_type - .map(|handle_type| handle_type.into_token_stream()) - .unwrap_or_else(|| Self::default().handle_type), - handle_mut_type: handle_mut_type - .map(|handle_mut_type| handle_mut_type.into_token_stream()) - .unwrap_or_else(|| Self::default().handle_mut_type), - }) - } -} - -impl Default for ComponentAttribute -{ - fn default() -> Self - { - Self { - handle_type: quote! { ComponentHandle<'component, Self> }, - handle_mut_type: quote! { ComponentHandleMut<'component, Self> }, - } - } -} |
