From cbed21f5e6cfb449d49087cedc867c8e50721ba9 Mon Sep 17 00:00:00 2001 From: HampusM Date: Wed, 24 Sep 2025 22:16:50 +0200 Subject: refactor(ecs): replace Pair ctor functions with builder --- ecs/examples/component_relationship.rs | 15 ++++- ecs/examples/event_loop.rs | 37 +++++++++-- ecs/examples/relationship.rs | 5 +- ecs/examples/with_sole.rs | 8 ++- ecs/src/component.rs | 5 +- ecs/src/component/local.rs | 12 +++- ecs/src/lib.rs | 42 ++++++++++-- ecs/src/pair.rs | 114 +++++++++++++++++++++++++++------ ecs/src/phase.rs | 2 +- ecs/src/system/observer.rs | 5 +- ecs/src/util.rs | 61 ++++++++++++++++++ ecs/tests/query.rs | 66 ++++++++++++++----- 12 files changed, 316 insertions(+), 56 deletions(-) diff --git a/ecs/examples/component_relationship.rs b/ecs/examples/component_relationship.rs index 4453e3a..e07b214 100644 --- a/ecs/examples/component_relationship.rs +++ b/ecs/examples/component_relationship.rs @@ -39,17 +39,26 @@ fn main() world.create_entity(( Person { name: "Irving".to_string() }, - Pair::new_with_comp_target::(Dogs { large: true }), + Pair::builder() + .relation::() + .target_as_data(Dogs { large: true }) + .build(), )); world.create_entity(( Person { name: "Mark".to_string() }, - Pair::new_with_comp_target::(Cats), + Pair::builder() + .relation::() + .target_as_data(Cats) + .build(), )); world.create_entity(( Person { name: "Helena".to_string() }, - Pair::new_with_comp_target::(Dogs { large: false }), + Pair::builder() + .relation::() + .target_as_data(Dogs { large: false }) + .build(), )); world.step(); diff --git a/ecs/examples/event_loop.rs b/ecs/examples/event_loop.rs index cc2f7f4..bec2c00 100644 --- a/ecs/examples/event_loop.rs +++ b/ecs/examples/event_loop.rs @@ -65,11 +65,38 @@ fn age(query: Query<(&mut Health, &Name)>, mut actions: Actions) } } -declare_entity!(SHEER_PHASE, (Phase, Pair::new::(*UPDATE_PHASE))); - -declare_entity!(FEED_PHASE, (Phase, Pair::new::(*SHEER_PHASE))); - -declare_entity!(AGE_PHASE, (Phase, Pair::new::(*FEED_PHASE))); +declare_entity!( + SHEER_PHASE, + ( + Phase, + Pair::builder() + .relation::() + .target_id(*UPDATE_PHASE) + .build() + ) +); + +declare_entity!( + FEED_PHASE, + ( + Phase, + Pair::builder() + .relation::() + .target_id(*SHEER_PHASE) + .build() + ) +); + +declare_entity!( + AGE_PHASE, + ( + Phase, + Pair::builder() + .relation::() + .target_id(*FEED_PHASE) + .build() + ) +); fn main() { diff --git a/ecs/examples/relationship.rs b/ecs/examples/relationship.rs index dd2f77a..4e94151 100644 --- a/ecs/examples/relationship.rs +++ b/ecs/examples/relationship.rs @@ -46,7 +46,10 @@ fn main() world.create_entity(( Player, Health { health: 180 }, - Pair::new::(sword_uid), + Pair::builder() + .relation::() + .target_id(sword_uid) + .build(), )); world.step(); diff --git a/ecs/examples/with_sole.rs b/ecs/examples/with_sole.rs index a292f06..7e89b0a 100644 --- a/ecs/examples/with_sole.rs +++ b/ecs/examples/with_sole.rs @@ -33,7 +33,13 @@ fn print_total_ammo_count(ammo_counter: Single) declare_entity!( PRINT_AMMO_COUNT_PHASE, - (Phase, Pair::new::(*UPDATE_PHASE)) + ( + Phase, + Pair::builder() + .relation::() + .target_id(*UPDATE_PHASE) + .build() + ) ); fn main() diff --git a/ecs/src/component.rs b/ecs/src/component.rs index e4ecfce..17b279b 100644 --- a/ecs/src/component.rs +++ b/ecs/src/component.rs @@ -153,7 +153,10 @@ impl<'comp, DataT: 'static> HandleMut<'comp, DataT> pub fn set_changed(&self) { self.event_submitter.submit_event( - &Pair::new::(self.entity_component_ref.id()), + &Pair::builder() + .relation::() + .target_id(self.entity_component_ref.id()) + .build(), self.entity_component_ref.entity_id(), ); } diff --git a/ecs/src/component/local.rs b/ecs/src/component/local.rs index 6b2463f..b19a30b 100644 --- a/ecs/src/component/local.rs +++ b/ecs/src/component/local.rs @@ -37,7 +37,11 @@ where }; let Some(local_component) = system_ent.get_with_id_mut::( - Pair::new::(LocalComponent::id()).id(), + Pair::builder() + .relation::() + .target::() + .build() + .id(), ) else { panic!( "Local component {} of system with ID {} is uninitialized", @@ -60,7 +64,11 @@ where fn initialize(system: &mut SystemT, input: Self::Input) { system.add_local_component( - Pair::new_with_comp_target::(input).into_parts(), + Pair::builder() + .relation::() + .target_as_data(input) + .build() + .into_parts(), ); } } diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs index 8ce2e7b..fa5a352 100644 --- a/ecs/src/lib.rs +++ b/ecs/src/lib.rs @@ -168,7 +168,10 @@ impl World let system_ent_id = self.create_entity(( SystemComponent { system: type_erased_system }, - Pair::new::(phase_euid), + Pair::builder() + .relation::() + .target_id(phase_euid) + .build(), )); system_callbacks.on_created(self, SystemMetadata { ent_id: system_ent_id }); @@ -344,7 +347,11 @@ impl World QueryTerms::::builder() .with_required([ SystemComponent::id(), - Pair::new::(phase_euid).id(), + Pair::builder() + .relation::() + .target_id(phase_euid) + .build() + .id(), ]) .build(), ), @@ -366,7 +373,11 @@ impl World QueryTerms::<2>::builder() .with_required([ Phase::id(), - Pair::new::(parent_phase_euid).id(), + Pair::builder() + .relation::() + .target_id(parent_phase_euid) + .build() + .id(), ]) .build(), ); @@ -461,8 +472,13 @@ impl World continue; } - event_submitter - .submit_event(&Pair::new::(comp_id), entity_uid); + event_submitter.submit_event( + &Pair::builder() + .relation::() + .target_id(comp_id) + .build(), + entity_uid, + ); } } Action::AddComponents(entity_uid, components) => { @@ -514,7 +530,13 @@ impl World continue; } - event_submitter.submit_event(&Pair::new::(comp_id), entity_uid); + event_submitter.submit_event( + &Pair::builder() + .relation::() + .target_id(comp_id) + .build(), + entity_uid, + ); } } @@ -539,7 +561,13 @@ impl World continue; } - event_submitter.submit_event(&Pair::new::(component_id), entity_uid); + event_submitter.submit_event( + &Pair::builder() + .relation::() + .target_id(component_id) + .build(), + entity_uid, + ); } } diff --git a/ecs/src/pair.rs b/ecs/src/pair.rs index 85be9f6..985a2b3 100644 --- a/ecs/src/pair.rs +++ b/ecs/src/pair.rs @@ -19,8 +19,91 @@ use crate::query::{ TermsBuilderInterface, }; 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: Relation, + target: Target, +} + +impl Builder +{ + pub fn relation(self) -> Builder + { + Builder { + relation: NewRelation::id(), + target: self.target, + } + } + + pub fn relation_id(self, id: Uid) -> Builder + { + Builder { relation: id, target: self.target } + } + + pub fn target(self) -> Builder + { + Builder { + relation: self.relation, + target: NewTarget::id(), + } + } + + pub fn target_id(self, id: Uid) -> Builder + { + Builder { relation: self.relation, target: id } + } +} + +impl_multiple!( + Builder, + (impl _, impl _<(), Target>) + (ty_param_1, ty_param_2) => { + pub fn target_as_data( + self, + data: NewTarget, + ) -> Builder<$ty_param_1, NewTarget> + { + Builder { + relation: self.relation, + target: data, + } + } + } +); + +impl_multiple!( + Builder, + ( + impl _, + impl _, + impl _, + impl _ + ) + (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 + } + } + } +); + +impl Default for Builder<(), ()> +{ + fn default() -> Self + { + Self { relation: (), target: () } + } +} + #[derive(Debug)] pub struct Pair { @@ -28,14 +111,17 @@ pub struct Pair target: Target, } -impl Pair +impl Pair<(), ()> { #[must_use] - pub fn new(target: Uid) -> Self + pub fn builder() -> Builder<(), ()> { - Self { relation: Relation::id(), target } + Builder { relation: (), target: () } } +} +impl Pair +{ #[must_use] pub fn id(&self) -> Uid { @@ -46,20 +132,6 @@ impl Pair } } -impl Pair -where - Target: Component, -{ - /// Returns a new pair that contains the target component as data. - pub fn new_with_comp_target(target_component: Target) -> Self - { - Self { - relation: Relation::uid(), - target: target_component, - } - } -} - impl IntoComponentParts for Pair { fn into_parts(self) -> ComponentParts @@ -358,7 +430,13 @@ impl<'a, Relation: Component> MultipleWithWildcard<'a, Relation, Wildcard> world: self.world, component_ref: self .entity_handle - .get_matching_components(Pair::new::(target_id).id()) + .get_matching_components( + Pair::builder() + .relation::() + .target_id(target_id) + .build() + .id(), + ) .next()?, _pd: PhantomData, }) diff --git a/ecs/src/phase.rs b/ecs/src/phase.rs index c13c432..9e3be24 100644 --- a/ecs/src/phase.rs +++ b/ecs/src/phase.rs @@ -8,7 +8,7 @@ pub struct Phase; declare_entity!(pub START, (Phase,)); declare_entity!(pub PRE_UPDATE, (Phase,)); -declare_entity!(pub UPDATE, (Phase, Pair::new::(*PRE_UPDATE))); +declare_entity!(pub UPDATE, (Phase, Pair::builder().relation::().target_id(*PRE_UPDATE).build())); #[doc(hidden)] pub(crate) fn spawn_entities(world: &mut World) diff --git a/ecs/src/system/observer.rs b/ecs/src/system/observer.rs index 5455fd4..1496d40 100644 --- a/ecs/src/system/observer.rs +++ b/ecs/src/system/observer.rs @@ -37,7 +37,10 @@ where fn events() -> Self::Events { - [Pair::new::(Target::id())] + [Pair::builder() + .relation::() + .target::() + .build()] } } diff --git a/ecs/src/util.rs b/ecs/src/util.rs index 9ab4dc6..2ab78cd 100644 --- a/ecs/src/util.rs +++ b/ecs/src/util.rs @@ -300,6 +300,67 @@ macro_rules! gen_mask_64 { pub(crate) use gen_mask_64; +macro_rules! impl_multiple { + ( + $type: ident, + ($(impl$(<$($generic: tt$(: $bound: ident)?),*>)? _<$($type_param: ty),*>),*) + ($($ty_param_matcher: ident),*) => { + $($item_tt: tt)* + } + ) => { + const _: () = { + $crate::util::impl_multiple!( + @(make_gen_item_macro) + _gen_multiple_impl_item, + ($($ty_param_matcher),*), + ($($item_tt)*) + ); + + $( + impl $(<$($generic$(: $bound)?,)*>)? $type<$($type_param),*> + { + _gen_multiple_impl_item!($($type_param),*); + } + )* + }; + }; + + ( + @(make_gen_item_macro) + $name: ident, + ($($ty_param_matcher: ident),*), + ($($transcriber: tt)*) + ) => { + $crate::util::impl_multiple!( + @(make_gen_item_macro) + ($), $name, ($($ty_param_matcher),*), ($($transcriber)*) + ); + }; + + ( + @(make_gen_item_macro) + ($dollar: tt), + $name: ident, + ($($ty_param_matcher: ident),*), + ($($transcriber: tt)*) + ) => { + $crate::util::impl_multiple!( + @(make_gen_item_macro) + $name, ($($dollar$ty_param_matcher: ty),*) => { + $($transcriber)* + } + ); + }; + + (@(make_gen_item_macro) $name: ident, $($rule: tt)*) => { + macro_rules! $name { + $($rule)* + } + }; +} + +pub(crate) use impl_multiple; + mod sealed { pub trait Sealed {} diff --git a/ecs/tests/query.rs b/ecs/tests/query.rs index 8615e3a..7b218e3 100644 --- a/ecs/tests/query.rs +++ b/ecs/tests/query.rs @@ -335,14 +335,32 @@ fn query_with_wildcard_target_pair() world.create_entity((B,)); - let ent_2_id = world.create_entity((B, Pair::new::(ent_1_id))); - - world.create_entity((B, Pair::new::(ent_1_id))); - world.create_entity((B, A, C, Pair::new::(ent_1_id))); - - let ent_3_id = world.create_entity((B, Pair::new::(ent_2_id))); - - let ent_4_id = world.create_entity((B, E, Pair::new_with_comp_target::(D))); + let ent_2_id = world.create_entity(( + B, + Pair::builder().relation::().target_id(ent_1_id).build(), + )); + + world.create_entity(( + B, + Pair::builder().relation::().target_id(ent_1_id).build(), + )); + world.create_entity(( + B, + A, + C, + Pair::builder().relation::().target_id(ent_1_id).build(), + )); + + let ent_3_id = world.create_entity(( + B, + Pair::builder().relation::().target_id(ent_2_id).build(), + )); + + let ent_4_id = world.create_entity(( + B, + E, + Pair::builder().relation::().target_as_data(D).build(), + )); assert_query_finds_ents( world.query::<(&B, Pair), ()>(), @@ -363,14 +381,30 @@ fn query_with_component_target_pair() world.create_entity((B,)); - world.create_entity((B, Pair::new::(ent_1_id))); - - world.create_entity((B, Pair::new::(ent_1_id))); - world.create_entity((B, A, C, Pair::new::(ent_1_id))); - - let ent_2_id = world.create_entity((B, Pair::new_with_comp_target::(F))); - - let ent_3_id = world.create_entity((B, E, Pair::new_with_comp_target::(F))); + world.create_entity(( + B, + Pair::builder().relation::().target_id(ent_1_id).build(), + )); + + world.create_entity(( + B, + Pair::builder().relation::().target_id(ent_1_id).build(), + )); + world.create_entity(( + B, + A, + C, + Pair::builder().relation::().target_id(ent_1_id).build(), + )); + + let ent_2_id = world + .create_entity((B, Pair::builder().relation::().target_as_data(F).build())); + + let ent_3_id = world.create_entity(( + B, + E, + Pair::builder().relation::().target_as_data(F).build(), + )); assert_query_finds_ents( world.query::<(&B, Pair), ()>(), -- cgit v1.2.3-18-g5258