diff options
Diffstat (limited to 'engine-ecs/src/pair.rs')
| -rw-r--r-- | engine-ecs/src/pair.rs | 946 |
1 files changed, 946 insertions, 0 deletions
diff --git a/engine-ecs/src/pair.rs b/engine-ecs/src/pair.rs new file mode 100644 index 0000000..834077c --- /dev/null +++ b/engine-ecs/src/pair.rs @@ -0,0 +1,946 @@ +use std::any::{type_name, TypeId}; +use std::convert::Infallible; +use std::marker::PhantomData; + +use crate::component::{ + Handle as ComponentHandle, + HandleError as ComponentHandleError, + HandleMut as ComponentHandleMut, + IntoParts as IntoComponentParts, + Parts as ComponentParts, +}; +use crate::entity::{ + Handle as EntityHandle, + MatchingComponentIter as EntityMatchingComponentIter, +}; +use crate::query::{ + SearchResult, + Term as QueryTerm, + TermMetadata as QueryTermMetadata, + TermsBuilder as QueryTermsBuilder, + TermsBuilderInterface, +}; +use crate::reflection::Type as TypeReflection; +use crate::uid::{PairParams as UidPairParams, Uid, With as WithUid}; +use crate::util::impl_multiple; +use crate::{Component, EntityComponentRef, World}; + +/// Pair builder. +#[derive(Debug)] +pub struct Builder<Relation, Target> +{ + relation: Relation, + target: Target, + relation_metadata: Option<MemberMetadata>, + target_metadata: Option<MemberMetadata>, +} + +impl<Relation, Target> Builder<Relation, Target> +{ + pub fn relation<NewRelation: Component>(self) -> Builder<Uid, Target> + { + Builder { + relation: NewRelation::id(), + target: self.target, + relation_metadata: Some(MemberMetadata { + ty: NewRelation::type_reflection(), + ty_id: TypeId::of::<NewRelation>(), + name: type_name::<NewRelation>(), + is_sole: false, + }), + target_metadata: self.target_metadata, + } + } + + pub fn relation_id(self, id: Uid) -> Builder<Uid, Target> + { + Builder { + relation: id, + target: self.target, + relation_metadata: None, + target_metadata: self.target_metadata, + } + } + + pub fn target<NewTarget: Component>(self) -> Builder<Relation, Uid> + { + Builder { + relation: self.relation, + target: NewTarget::id(), + relation_metadata: self.relation_metadata, + target_metadata: Some(MemberMetadata { + ty: NewTarget::type_reflection(), + ty_id: TypeId::of::<NewTarget>(), + name: type_name::<NewTarget>(), + is_sole: false, + }), + } + } + + pub fn target_id(self, id: Uid) -> Builder<Relation, Uid> + { + Builder { + relation: self.relation, + target: id, + relation_metadata: self.relation_metadata, + target_metadata: None, + } + } +} + +impl_multiple!( + Builder, + (impl<Target> _<><Uid, Target>, impl<Target> _<><(), Target>) + cb=(type_params=(ty_param_1, ty_param_2)) => { + pub fn target_as_data<NewTarget: Component>( + self, + data: NewTarget, + ) -> Builder<$ty_param_1, NewTarget> + { + Builder { + relation: self.relation, + target: data, + relation_metadata: self.relation_metadata, + target_metadata: Some(MemberMetadata { + ty: NewTarget::type_reflection(), + ty_id: TypeId::of::<NewTarget>(), + name: type_name::<NewTarget>(), + is_sole: false, + }), + } + } + } +); + +impl_multiple!( + Builder, + (impl<Relation> _<><Relation, Uid>, impl<Relation> _<><Relation, ()>) + cb=(type_params=(ty_param_1, ty_param_2)) => { + pub fn relation_as_data<NewRelation: Component>( + self, + data: NewRelation, + ) -> Builder<NewRelation, $ty_param_2> + { + Builder { + relation: data, + target: self.target, + relation_metadata: Some(MemberMetadata { + ty: NewRelation::type_reflection(), + ty_id: TypeId::of::<NewRelation>(), + name: type_name::<NewRelation>(), + is_sole: false, + }), + target_metadata: self.target_metadata, + } + } + } +); + +impl_multiple!( + Builder, + ( + impl _<><Uid, Uid>, + impl<Relation: Component> _<><Relation, Uid>, + impl<Target: Component> _<><Uid, Target>, + impl<Relation: Component, Target: Component> _<><Relation, Target> + ) + cb=(type_params=(ty_param_1, ty_param_2)) => { + #[must_use] + pub fn build(self) -> Pair<$ty_param_1, $ty_param_2> + { + Pair { + relation: self.relation, + target: self.target, + relation_metadata: self.relation_metadata, + target_metadata: self.target_metadata, + } + } + } +); + +impl Default for Builder<(), ()> +{ + fn default() -> Self + { + Self { + relation: (), + target: (), + relation_metadata: None, + target_metadata: None, + } + } +} + +#[derive(Debug)] +pub struct Pair<Relation, Target> +{ + relation: Relation, + target: Target, + relation_metadata: Option<MemberMetadata>, + target_metadata: Option<MemberMetadata>, +} + +impl Pair<(), ()> +{ + #[must_use] + pub fn builder() -> Builder<(), ()> + { + Builder::default() + } +} + +impl Pair<Uid, Uid> +{ + #[must_use] + pub fn id(&self) -> Uid + { + Uid::new_pair(&UidPairParams { + relation: self.relation, + target: self.target, + }) + } +} + +impl IntoComponentParts for Pair<Uid, Uid> +{ + fn into_parts(self) -> ComponentParts + { + let id = self.id(); + + ComponentParts::builder() + .name("Pair") + .pair_relation_metadata(self.relation_metadata) + .pair_target_metadata(self.target_metadata) + .build(id, ()) + } +} + +impl<Target> IntoComponentParts for Pair<Uid, Target> +where + Target: Component, +{ + fn into_parts(self) -> ComponentParts + { + let id = Uid::new_pair(&UidPairParams { + relation: self.relation, + target: Target::id(), + }); + + ComponentParts::builder() + .name("Pair") + .pair_relation_metadata(self.relation_metadata) + .pair_target_metadata(self.target_metadata) + .build(id, self.target) + } +} + +impl<Relation> IntoComponentParts for Pair<Relation, Uid> +where + Relation: Component, +{ + fn into_parts(self) -> ComponentParts + { + let id = Uid::new_pair(&UidPairParams { + relation: Relation::id(), + target: self.target, + }); + + ComponentParts::builder() + .name("Pair") + .pair_relation_metadata(self.relation_metadata) + .pair_target_metadata(self.target_metadata) + .build(id, self.relation) + } +} + +impl<'query, Relation, Target> QueryTerm<'query> for Pair<Relation, &Target> +where + Relation: Component, + Target: Component, +{ + type Fields = (ComponentHandle<'query, Target>,); + + fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( + terms_builder: &mut QueryTermsBuilder<MAX_TERM_CNT>, + _term_metadata: QueryTermMetadata, + ) + { + terms_builder.present([Pair::<Relation, Target>::uid()]); + } + + fn fields( + _world: &'query World, + search_result: &SearchResult<'query, '_>, + _term_metadata: QueryTermMetadata, + ) -> Self::Fields + { + let target_component = search_result + .entity_handle + .get_matching_components(Pair::<Relation, Target>::uid()) + .next() + .expect("Not possible"); + + let target_component = + match ComponentHandle::<Target>::from_entity_component_ref(&target_component) + { + Ok(target_component) => target_component, + Err(err) => { + panic!( + "Creating handle to target component {} failed: {err}", + type_name::<Target>() + ); + } + }; + + (target_component,) + } +} + +impl<'query, Relation, Target> QueryTerm<'query> for Pair<Relation, &mut Target> +where + Relation: Component, + Target: Component, +{ + type Fields = (ComponentHandleMut<'query, Target>,); + + fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( + terms_builder: &mut QueryTermsBuilder<MAX_TERM_CNT>, + _term_metadata: QueryTermMetadata, + ) + { + terms_builder.present([Pair::<Relation, Target>::uid()]); + } + + fn fields( + world: &'query World, + search_result: &SearchResult<'query, '_>, + _term_metadata: QueryTermMetadata, + ) -> Self::Fields + { + let target_component = search_result + .entity_handle + .get_matching_components(Pair::<Relation, Target>::uid()) + .next() + .expect("Not possible"); + + let target_component = + match ComponentHandleMut::<Target>::from_entity_component_ref( + &target_component, + world, + ) { + Ok(target_component) => target_component, + Err(err) => { + panic!( + "Creating mut handle to target component {} failed: {err}", + type_name::<Target>() + ); + } + }; + + (target_component,) + } +} + +// TODO: implement QueryTerm for Pair<&Relation, Target> (or equivalent) +// TODO: implement QueryTerm for Pair<&mut Relation, Target> (or equivalent) + +impl<'query, Relation> QueryTerm<'query> for Pair<Relation, Wildcard> +where + Relation: Component, +{ + type Fields = (WithWildcard<'query, Relation, Wildcard>,); + + fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( + terms_builder: &mut QueryTermsBuilder<MAX_TERM_CNT>, + _term_metadata: QueryTermMetadata, + ) + { + terms_builder.present([Self::uid()]); + } + + fn fields( + world: &'query World, + search_result: &SearchResult<'query, '_>, + _term_metadata: QueryTermMetadata, + ) -> Self::Fields + { + let first_matching_comp = search_result + .entity_handle + .get_matching_components(Self::uid()) + .next() + .expect("Not possible"); + + (WithWildcard { + world, + component_ref: first_matching_comp, + _pd: PhantomData, + },) + } +} + +impl<Relation, Target> WithUid for Pair<Relation, Target> +where + Relation: Component, + Target: Component, +{ + fn uid() -> Uid + { + Uid::new_pair(&UidPairParams { + relation: Relation::id(), + target: Target::id(), + }) + } +} + +impl<Relation> WithUid for Pair<Relation, Wildcard> +where + Relation: Component, +{ + fn uid() -> Uid + { + Uid::new_pair(&UidPairParams { + relation: Relation::id(), + target: Wildcard::uid(), + }) + } +} + +impl<'query, Relation> QueryTerm<'query> for &[Pair<Relation, Wildcard>] +where + Relation: Component, +{ + type Fields = (MultipleWithWildcard<'query, Relation, Wildcard>,); + + fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( + _terms_builder: &mut QueryTermsBuilder<MAX_TERM_CNT>, + _term_metadata: QueryTermMetadata, + ) + { + } + + fn fields( + world: &'query World, + search_result: &SearchResult<'query, '_>, + _term_metadata: QueryTermMetadata, + ) -> Self::Fields + { + (MultipleWithWildcard { + entity_handle: search_result.entity_handle.clone(), + world, + _pd: PhantomData, + },) + } +} + +/// Reference to a pair with a wildcard relation/target. +#[derive(Debug)] +pub struct WithWildcard<'query, Relation, Target> +{ + world: &'query World, + component_ref: EntityComponentRef<'query>, + _pd: PhantomData<(Relation, Target)>, +} + +impl<'query, Relation, Target> WithWildcard<'query, Relation, Target> +{ + /// Returns a new `WithWildcard`. + /// + /// # Panics + /// This function will panic if: + /// - The given component's ID is not a pair ID. + /// - `Relation::uid()` is not wildcard and does not equal to the relation of the + /// given component's ID + /// - `Target::uid()` is not wildcard and does not equal to the target of the given + /// component's ID + /// - Both `Relation::uid()` and `Target::uid()` are wildcards + /// - Neither `Relation::uid()` or `Target::uid()` are wildcards + pub fn new(world: &'query World, component_ref: EntityComponentRef<'query>) -> Self + where + Relation: ComponentOrWildcard, + Target: ComponentOrWildcard, + { + let component_id = component_ref.id(); + + assert!(component_id.is_pair()); + + assert!( + Relation::uid() == Wildcard::uid() + || component_id.relation() == Relation::uid() + ); + + assert!( + Target::uid() == Wildcard::uid() || component_id.target() == Target::uid() + ); + + assert!(Relation::uid() == Wildcard::uid() || Target::uid() == Wildcard::uid()); + + assert!( + !(Relation::uid() == Wildcard::uid() && Target::uid() == Wildcard::uid()) + ); + + WithWildcard { + world, + component_ref, + _pd: PhantomData, + } + } + + /// Returns the [`Uid`] of the pair. + #[must_use] + pub fn id(&self) -> Uid + { + self.component_ref.id() + } + + /// Attempts to get the component data of this pair, returning `None` if the `Data` + /// type is incorrect. + /// + /// # Panics + /// Will panic if the component data is mutably borrowed elsewhere. + #[must_use] + pub fn get_data<Data>(&self) -> Option<ComponentHandle<'_, Data>> + where + Data: 'static, + { + ComponentHandle::<Data>::from_entity_component_ref(&self.component_ref) + .map_or_else( + |err| match err { + ComponentHandleError::IncorrectType => None, + err @ ComponentHandleError::AcquireLockFailed(_) => { + panic!( + "Creating handle to pair data as component {} failed: {err}", + type_name::<Data>() + ); + } + }, + Some, + ) + } + + /// Attempts to get the component data of this pair, returning `None` if the `Data` + /// type is incorrect. + /// + /// # Panics + /// Will panic if the component data is borrowed elsewhere. + #[must_use] + pub fn get_data_mut<Data>(&self) -> Option<ComponentHandleMut<'_, Data>> + where + Data: 'static, + { + ComponentHandleMut::<Data>::from_entity_component_ref( + &self.component_ref, + self.world, + ) + .map_or_else( + |err| match err { + ComponentHandleError::IncorrectType => None, + err @ ComponentHandleError::AcquireLockFailed(_) => { + panic!( + "Creating handle to pair data as component {} failed: {err}", + type_name::<Data>() + ); + } + }, + Some, + ) + } +} + +impl<'query, Relation> WithWildcard<'query, Relation, Wildcard> +where + Relation: Component, +{ + /// Attempts to retrieve the target as a entity, returning `None` if not found. + #[must_use] + pub fn get_target_ent(&self) -> Option<EntityHandle<'query>> + { + let archetype = self + .world + .data + .component_storage + .get_entity_archetype(self.component_ref.id().target())?; + + let Some(archetype_entity) = + archetype.get_entity_by_id(self.component_ref.id().target()) + else { + unreachable!(); + }; + + Some(EntityHandle::new(archetype, archetype_entity, self.world)) + } + + /// Attempts to get the component data of this pair, returning `None` if the + /// `Relation` type is incorrect. + /// + /// # Panics + /// Will panic if the component data is mutably borrowed elsewhere. + #[must_use] + pub fn get_data_as_relation(&self) -> Option<ComponentHandle<'_, Relation>> + { + ComponentHandle::<Relation>::from_entity_component_ref(&self.component_ref) + .map_or_else( + |err| match err { + ComponentHandleError::IncorrectType => None, + err @ ComponentHandleError::AcquireLockFailed(_) => { + panic!( + "Creating handle to pair data as component {} failed: {err}", + type_name::<Relation>() + ); + } + }, + Some, + ) + } + + /// Attempts to get the component data of this pair, returning `None` if the + /// `Relation` type is incorrect. + /// + /// # Panics + /// Will panic if the component data is borrowed elsewhere. + #[must_use] + pub fn get_data_as_relation_mut(&self) -> Option<ComponentHandleMut<'_, Relation>> + { + ComponentHandleMut::<Relation>::from_entity_component_ref( + &self.component_ref, + self.world, + ) + .map_or_else( + |err| match err { + ComponentHandleError::IncorrectType => None, + err @ ComponentHandleError::AcquireLockFailed(_) => { + panic!( + "Creating handle to pair data as component {} failed: {err}", + type_name::<Relation>() + ); + } + }, + Some, + ) + } +} + +/// Used to access matching pairs in a entity containing zero or more matching pairs. +#[derive(Debug)] +pub struct MultipleWithWildcard<'a, Relation, Target> +{ + entity_handle: EntityHandle<'a>, + world: &'a World, + _pd: PhantomData<(Relation, Target)>, +} + +impl<'query, Relation, Target> MultipleWithWildcard<'query, Relation, Target> +{ + /// Returns a new `MultipleWithWildcard`. + /// + /// # Panics + /// This function will panic if: + /// - Both `Relation::uid()` and `Target::uid()` are wildcards + /// - Neither `Relation::uid()` or `Target::uid()` are wildcards + pub fn new(world: &'query World, entity_handle: EntityHandle<'query>) -> Self + where + Relation: ComponentOrWildcard, + Target: ComponentOrWildcard, + { + assert!(Relation::uid() == Wildcard::uid() || Target::uid() == Wildcard::uid()); + + assert!( + !(Relation::uid() == Wildcard::uid() && Target::uid() == Wildcard::uid()) + ); + + MultipleWithWildcard { + entity_handle, + world, + _pd: PhantomData, + } + } +} + +impl<'a, Relation: Component> MultipleWithWildcard<'a, Relation, Wildcard> +{ + #[must_use] + pub fn get_with_target_id( + &self, + target_id: Uid, + ) -> Option<WithWildcard<'a, Relation, Wildcard>> + { + Some(WithWildcard { + world: self.world, + component_ref: self + .entity_handle + .get_matching_components( + Pair::builder() + .relation::<Relation>() + .target_id(target_id) + .build() + .id(), + ) + .next()?, + _pd: PhantomData, + }) + } +} + +impl<'a, Relation: Component> IntoIterator + for MultipleWithWildcard<'a, Relation, Wildcard> +{ + type IntoIter = WithWildcardIter<'a, Relation, Wildcard>; + type Item = <Self::IntoIter as Iterator>::Item; + + fn into_iter(self) -> Self::IntoIter + { + WithWildcardIter { + inner: self + .entity_handle + .get_matching_components(Pair::<Relation, Wildcard>::uid()), + world: self.world, + _pd: PhantomData, + } + } +} + +/// Iterator of matching pairs in a entity. +pub struct WithWildcardIter<'a, Relation, Target> +{ + inner: EntityMatchingComponentIter<'a>, + world: &'a World, + _pd: PhantomData<(Relation, Target)>, +} + +impl<'a, Relation, Target> Iterator for WithWildcardIter<'a, Relation, Target> +{ + type Item = WithWildcard<'a, Relation, Target>; + + fn next(&mut self) -> Option<Self::Item> + { + let matching_comp = self.inner.next()?; + + Some(WithWildcard { + world: self.world, + component_ref: matching_comp, + _pd: PhantomData, + }) + } +} + +/// Relation denoting a dependency to another entity +#[derive(Debug, Default, Clone, Copy, Component)] +pub struct DependsOn; + +/// Relation denoting being the child of another entity. +#[derive(Debug, Default, Clone, Copy, Component)] +pub struct ChildOf; + +#[derive(Debug)] +pub struct Wildcard(Infallible); + +impl Wildcard +{ + #[must_use] + pub fn uid() -> Uid + { + Uid::wildcard() + } +} + +pub trait ComponentOrWildcard: sealed::Sealed +{ + fn uid() -> Uid; +} + +impl<ComponentT: Component> ComponentOrWildcard for ComponentT +{ + fn uid() -> Uid + { + ComponentT::id() + } +} + +impl<ComponentT: Component> sealed::Sealed for ComponentT {} + +impl ComponentOrWildcard for Wildcard +{ + fn uid() -> Uid + { + Wildcard::uid() + } +} + +impl sealed::Sealed for Wildcard {} + +#[derive(Debug, Clone)] +#[non_exhaustive] +pub struct MemberMetadata +{ + pub ty: Option<&'static TypeReflection>, + pub ty_id: TypeId, + pub name: &'static str, + pub is_sole: bool, +} + +#[macro_export] +macro_rules! pair { + + (Wildcard, $target: tt) => { + { + let pair: $crate::pair::Pair<(), ()> = std::compile_error!( + "Use shorthand * instead of Wildcard as relation" + ); + + pair + } + }; + + ($relation: tt, Wildcard) => { + { + let pair: $crate::pair::Pair<(), ()> = std::compile_error!( + "Use shorthand * instead of Wildcard as target" + ); + + pair + } + }; + + (={ $relation: tt }, ={ $target: tt }) => { + { + let pair: $crate::pair::Pair<(), ()> = std::compile_error!( + "Cannot use both relation and target as pair data" + ); + + pair + } + }; + + (={ $relation: expr }, $target: tt) => { + $crate::pair!(@inner (=, { $relation }), (, $target)) + }; + + ($relation: tt, ={ $target: expr }) => { + $crate::pair!(@inner (,$relation), (=, { $target })) + }; + + ($relation: tt, $target: tt) => { + $crate::pair!(@inner (,$relation), (, $target)) + }; + + ( + @inner + ( + $(=$($relation_eq: literal)?)?, + $relation: tt + ), + ( + $(=$($target_eq: literal)?)?, + $target: tt + ) + ) => { + { + #[allow(dead_code)] + fn get_type_uid<T: $crate::pair::ComponentOrWildcard>() + -> $crate::uid::Uid + { + T::uid() + } + + let builder = $crate::pair::Pair::builder(); + + $crate::pair!( + @set_member_in_builder, builder, relation, $(=$($relation_eq)?)? $relation + ); + + $crate::pair!( + @set_member_in_builder, builder, target, $(=$($target_eq)?)? $target + ); + + builder.build() + } + }; + + (@set_member_in_builder, $builder: ident, $member: ident, *) => { + $crate::pair::__private::paste! { + let $builder = $builder.[<$member _id>]($crate::uid::Uid::wildcard()); + } + }; + + (@set_member_in_builder, $builder: ident, $member: ident, $src: ty) => { + let $builder = $builder.$member::<$src>(); + }; + + (@set_member_in_builder, $builder: ident, $member: ident, { $src: expr }) => { + $crate::pair::__private::paste! { + let $builder = $builder.[<$member _id>]($src); + } + }; + + (@set_member_in_builder, $builder: ident, $member: ident, ={ $src: expr }) => { + $crate::pair::__private::paste! { + let $builder = $builder.[<$member _as_data>]($src); + } + }; +} + +#[doc(hidden)] +pub mod __private +{ + pub use paste::paste; +} + +mod sealed +{ + pub trait Sealed {} +} + +#[cfg(test)] +mod tests +{ + use crate::pair::ChildOf; + use crate::uid::Uid; + use crate::Component; + + #[derive(Debug, Component)] + struct Foo; + + #[derive(Debug, Component, PartialEq, Eq)] + struct Bar + { + number: u32, + } + + #[test] + fn macro_works() + { + let pair_a = pair!(ChildOf, Foo); + + assert_eq!(pair_a.relation, ChildOf::id()); + assert_eq!(pair_a.target, Foo::id()); + + let pair_b = pair!(ChildOf, *); + + assert_eq!(pair_b.relation, ChildOf::id()); + assert_eq!(pair_b.target, Uid::wildcard()); + + let pair_c = pair!(*, Foo); + + assert_eq!(pair_c.relation, Uid::wildcard()); + assert_eq!(pair_c.target, Foo::id()); + + let pair_d = pair!(Foo, ={ Bar { number: 1234 } }); + + assert_eq!(pair_d.relation, Foo::id()); + assert_eq!(pair_d.target, Bar { number: 1234 }); + + let pair_e = pair!(={ Bar { number: 789} }, Foo); + + assert_eq!(pair_e.relation, Bar { number: 789 }); + assert_eq!(pair_e.target, Foo::id()); + + let pair_f = pair!(ChildOf, { Foo::id() }); + + assert_eq!(pair_f.relation, ChildOf::id()); + assert_eq!(pair_f.target, Foo::id()); + + let pair_g = pair!({ Bar::id() }, Foo); + + assert_eq!(pair_g.relation, Bar::id()); + assert_eq!(pair_g.target, Foo::id()); + } +} |
