Repository: joseph-gio/bevy-trait-query (language: rust)

Issue:
Is `register_component_as` idempotent?
For my (a bit hacky) use-case of `bevy-trait-query`, I need to register a generic struct as a trait, for different generic variants that I can't know in advance.

Hence, I need to call `register_component_as` for certain user actions and cannot guarantee that the combination of generics is different every time.

Is `register_component_as` idempotent, i.e., is it fine to call it multiple times for the same combination of trait-component combinations? 


Discussion / hints:
It definitely should be fine to call multiple times. It returns early if you try to register an impl twice.

I should clarify this in the docs and add a test case, though.

Fix patch:
diff --git a/src/lib.rs b/src/lib.rs
index db22acd..0b5bbef 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -152,6 +152,12 @@ pub trait TraitQueryMarker<Trait: ?Sized + TraitQuery> {
 
 /// Extension methods for registering components with trait queries.
 pub trait RegisterExt {
+    /// Allows a component to be used in trait queries.
+    /// Calling this multiple times with the same arguments will do nothing on subsequent calls.
+    ///
+    /// # Panics
+    /// If this function is called after the simulation starts for a given [`World`].
+    /// Due to engine limitations, registering new trait impls after the game starts cannot be supported.
     fn register_component_as<Trait: ?Sized + TraitQuery, C: Component>(&mut self) -> &mut Self
     where
         (C,): TraitQueryMarker<Trait, Covered = C>;


Test patch:
diff --git a/src/tests.rs b/src/tests.rs
index dc25805..10f6b16 100644
--- a/src/tests.rs
+++ b/src/tests.rs
@@ -280,3 +280,41 @@ fn spawn_sparse(mut commands: Commands) {
         commands.spawn().insert(RecB(vec![format!("Sparse #{i}")]));
     }
 }
+
+// Make sure it works correctly when components are registered multiple times.
+#[test]
+fn multi_register() {
+    let mut world = World::new();
+    world.init_resource::<Output>();
+    // Register each trait impl multiple times. Nothing should happen for the extra registrations.
+    world
+        .register_component_as::<dyn Messages, RecA>()
+        .register_component_as::<dyn Messages, RecA>()
+        .register_component_as::<dyn Messages, RecA>()
+        .register_component_as::<dyn Messages, RecB>()
+        .register_component_as::<dyn Messages, RecB>();
+
+    world.spawn().insert(RecA(vec![]));
+    world.spawn().insert_bundle((RecA(vec![]), RecB(vec![])));
+    world.spawn().insert(RecB(vec![]));
+
+    let mut stage = SystemStage::parallel();
+    stage.add_system(count_impls);
+
+    fn count_impls(q: Query<&dyn Messages>, mut output: ResMut<Output>) {
+        for traits in &q {
+            // Make sure each impl gets yielded the correct number of times.
+            // We don't want any of them to get double-counted.
+            output
+                .0
+                .push(format!("{} Traits", traits.into_iter().count()));
+        }
+    }
+
+    stage.run(&mut world);
+
+    assert_eq!(
+        world.resource::<Output>().0,
+        &["1 Traits", "2 Traits", "1 Traits"]
+    );
+}