Skip to content

Commit ccb7069

Browse files
BleachfuelTim OverbeekTim OverbeekTim OverbeekTim Overbeek
authored
Change ChildOf to Childof { parent: Entity} and support deriving Relationship and RelationshipTarget with named structs (#17905)
# Objective fixes #17896 ## Solution Change ChildOf ( Entity ) to ChildOf { parent: Entity } by doing this we also allow users to use named structs for relationship derives, When you have more than 1 field in a struct with named fields the macro will look for a field with the attribute #[relationship] and all of the other fields should implement the Default trait. Unnamed fields are still supported. When u have a unnamed struct with more than one field the macro will fail. Do we want to support something like this ? ```rust #[derive(Component)] #[relationship_target(relationship = ChildOf)] pub struct Children (#[relationship] Entity, u8); ``` I could add this, it but doesn't seem nice. ## Testing crates/bevy_ecs - cargo test ## Showcase ```rust use bevy_ecs::component::Component; use bevy_ecs::entity::Entity; #[derive(Component)] #[relationship(relationship_target = Children)] pub struct ChildOf { #[relationship] pub parent: Entity, internal: u8, }; #[derive(Component)] #[relationship_target(relationship = ChildOf)] pub struct Children { children: Vec<Entity> }; ``` --------- Co-authored-by: Tim Overbeek <[email protected]> Co-authored-by: Tim Overbeek <[email protected]> Co-authored-by: Tim Overbeek <[email protected]> Co-authored-by: Tim Overbeek <[email protected]> Co-authored-by: Tim Overbeek <[email protected]>
1 parent 67146bd commit ccb7069

File tree

17 files changed

+170
-89
lines changed

17 files changed

+170
-89
lines changed

benches/benches/bevy_ecs/entity_cloning.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,9 @@ fn bench_clone_hierarchy<B: Bundle + Default + GetTypeRegistration>(
153153

154154
hierarchy_level.clear();
155155

156-
for parent_id in current_hierarchy_level {
156+
for parent in current_hierarchy_level {
157157
for _ in 0..children {
158-
let child_id = world.spawn((B::default(), ChildOf(parent_id))).id();
158+
let child_id = world.spawn((B::default(), ChildOf { parent })).id();
159159
hierarchy_level.push(child_id);
160160
}
161161
}

crates/bevy_ecs/macros/src/component.rs

Lines changed: 91 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ use syn::{
99
punctuated::Punctuated,
1010
spanned::Spanned,
1111
token::{Comma, Paren},
12-
Data, DataStruct, DeriveInput, ExprClosure, ExprPath, Fields, Ident, Index, LitStr, Member,
13-
Path, Result, Token, Visibility,
12+
Data, DataStruct, DeriveInput, ExprClosure, ExprPath, Field, Fields, Ident, Index, LitStr,
13+
Member, Path, Result, Token, Visibility,
1414
};
1515

1616
pub fn derive_event(input: TokenStream) -> TokenStream {
@@ -260,6 +260,18 @@ fn visit_entities(data: &Data, bevy_ecs_path: &Path, is_relationship: bool) -> T
260260
Data::Struct(DataStruct { fields, .. }) => {
261261
let mut visited_fields = Vec::new();
262262
let mut visited_indices = Vec::new();
263+
264+
if is_relationship {
265+
let field = match relationship_field(fields, "VisitEntities", fields.span()) {
266+
Ok(f) => f,
267+
Err(e) => return e.to_compile_error(),
268+
};
269+
270+
match field.ident {
271+
Some(ref ident) => visited_fields.push(ident.clone()),
272+
None => visited_indices.push(Index::from(0)),
273+
}
274+
}
263275
match fields {
264276
Fields::Named(fields) => {
265277
for field in &fields.named {
@@ -276,9 +288,7 @@ fn visit_entities(data: &Data, bevy_ecs_path: &Path, is_relationship: bool) -> T
276288
}
277289
Fields::Unnamed(fields) => {
278290
for (index, field) in fields.unnamed.iter().enumerate() {
279-
if index == 0 && is_relationship {
280-
visited_indices.push(Index::from(0));
281-
} else if field
291+
if field
282292
.attrs
283293
.iter()
284294
.any(|a| a.meta.path().is_ident(ENTITIES_ATTR))
@@ -289,7 +299,6 @@ fn visit_entities(data: &Data, bevy_ecs_path: &Path, is_relationship: bool) -> T
289299
}
290300
Fields::Unit => {}
291301
}
292-
293302
if visited_fields.is_empty() && visited_indices.is_empty() {
294303
TokenStream2::new()
295304
} else {
@@ -651,25 +660,24 @@ fn derive_relationship(
651660
let Some(relationship) = &attrs.relationship else {
652661
return Ok(None);
653662
};
654-
const RELATIONSHIP_FORMAT_MESSAGE: &str = "Relationship derives must be a tuple struct with the only element being an EntityTargets type (ex: ChildOf(Entity))";
655-
if let Data::Struct(DataStruct {
656-
fields: Fields::Unnamed(unnamed_fields),
663+
let Data::Struct(DataStruct {
664+
fields,
657665
struct_token,
658666
..
659667
}) = &ast.data
660-
{
661-
if unnamed_fields.unnamed.len() != 1 {
662-
return Err(syn::Error::new(ast.span(), RELATIONSHIP_FORMAT_MESSAGE));
663-
}
664-
if unnamed_fields.unnamed.first().is_none() {
665-
return Err(syn::Error::new(
666-
struct_token.span(),
667-
RELATIONSHIP_FORMAT_MESSAGE,
668-
));
669-
}
670-
} else {
671-
return Err(syn::Error::new(ast.span(), RELATIONSHIP_FORMAT_MESSAGE));
668+
else {
669+
return Err(syn::Error::new(
670+
ast.span(),
671+
"Relationship can only be derived for structs.",
672+
));
672673
};
674+
let field = relationship_field(fields, "Relationship", struct_token.span())?;
675+
676+
let relationship_member: Member = field.ident.clone().map_or(Member::from(0), Member::Named);
677+
678+
let members = fields
679+
.members()
680+
.filter(|member| member != &relationship_member);
673681

674682
let struct_name = &ast.ident;
675683
let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
@@ -682,12 +690,15 @@ fn derive_relationship(
682690

683691
#[inline(always)]
684692
fn get(&self) -> #bevy_ecs_path::entity::Entity {
685-
self.0
693+
self.#relationship_member
686694
}
687695

688696
#[inline]
689697
fn from(entity: #bevy_ecs_path::entity::Entity) -> Self {
690-
Self(entity)
698+
Self {
699+
#(#members: core::default::Default::default(),),*
700+
#relationship_member: entity
701+
}
691702
}
692703
}
693704
}))
@@ -702,30 +713,29 @@ fn derive_relationship_target(
702713
return Ok(None);
703714
};
704715

705-
const RELATIONSHIP_TARGET_FORMAT_MESSAGE: &str = "RelationshipTarget derives must be a tuple struct with the first element being a private RelationshipSourceCollection (ex: Children(Vec<Entity>))";
706-
let collection = if let Data::Struct(DataStruct {
707-
fields: Fields::Unnamed(unnamed_fields),
716+
let Data::Struct(DataStruct {
717+
fields,
708718
struct_token,
709719
..
710720
}) = &ast.data
711-
{
712-
if let Some(first) = unnamed_fields.unnamed.first() {
713-
if first.vis != Visibility::Inherited {
714-
return Err(syn::Error::new(first.span(), "The collection in RelationshipTarget must be private to prevent users from directly mutating it, which could invalidate the correctness of relationships."));
715-
}
716-
first.ty.clone()
717-
} else {
718-
return Err(syn::Error::new(
719-
struct_token.span(),
720-
RELATIONSHIP_TARGET_FORMAT_MESSAGE,
721-
));
722-
}
723-
} else {
721+
else {
724722
return Err(syn::Error::new(
725723
ast.span(),
726-
RELATIONSHIP_TARGET_FORMAT_MESSAGE,
724+
"RelationshipTarget can only be derived for structs.",
727725
));
728726
};
727+
let field = relationship_field(fields, "RelationshipTarget", struct_token.span())?;
728+
729+
if field.vis != Visibility::Inherited {
730+
return Err(syn::Error::new(field.span(), "The collection in RelationshipTarget must be private to prevent users from directly mutating it, which could invalidate the correctness of relationships."));
731+
}
732+
let collection = &field.ty;
733+
734+
let relationship_member = field.ident.clone().map_or(Member::from(0), Member::Named);
735+
736+
let members = fields
737+
.members()
738+
.filter(|member| member != &relationship_member);
729739

730740
let relationship = &relationship_target.relationship;
731741
let struct_name = &ast.ident;
@@ -739,18 +749,56 @@ fn derive_relationship_target(
739749

740750
#[inline]
741751
fn collection(&self) -> &Self::Collection {
742-
&self.0
752+
&self.#relationship_member
743753
}
744754

745755
#[inline]
746756
fn collection_mut_risky(&mut self) -> &mut Self::Collection {
747-
&mut self.0
757+
&mut self.#relationship_member
748758
}
749759

750760
#[inline]
751761
fn from_collection_risky(collection: Self::Collection) -> Self {
752-
Self(collection)
762+
Self {
763+
#(#members: core::default::Default::default(),),*
764+
#relationship_member: collection
765+
}
753766
}
754767
}
755768
}))
756769
}
770+
771+
/// Returns the field with the `#[relationship]` attribute, the only field if unnamed,
772+
/// or the only field in a [`Fields::Named`] with one field, otherwise `Err`.
773+
fn relationship_field<'a>(
774+
fields: &'a Fields,
775+
derive: &'static str,
776+
span: Span,
777+
) -> Result<&'a Field> {
778+
match fields {
779+
Fields::Named(fields) if fields.named.len() == 1 => Ok(fields.named.first().unwrap()),
780+
Fields::Named(fields) => fields.named.iter().find(|field| {
781+
field
782+
.attrs
783+
.iter()
784+
.any(|attr| attr.path().is_ident("relationship"))
785+
}).ok_or(syn::Error::new(
786+
span,
787+
format!("{derive} derive expected named structs with a single field or with a field annotated with #[relationship].")
788+
)),
789+
Fields::Unnamed(fields) => fields
790+
.unnamed
791+
.len()
792+
.eq(&1)
793+
.then(|| fields.unnamed.first())
794+
.flatten()
795+
.ok_or(syn::Error::new(
796+
span,
797+
format!("{derive} derive expected unnamed structs with one field."),
798+
)),
799+
Fields::Unit => Err(syn::Error::new(
800+
span,
801+
format!("{derive} derive expected named or unnamed struct, found unit struct."),
802+
)),
803+
}
804+
}

crates/bevy_ecs/src/entity/clone_entities.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1291,9 +1291,9 @@ mod tests {
12911291
fn recursive_clone() {
12921292
let mut world = World::new();
12931293
let root = world.spawn_empty().id();
1294-
let child1 = world.spawn(ChildOf(root)).id();
1295-
let grandchild = world.spawn(ChildOf(child1)).id();
1296-
let child2 = world.spawn(ChildOf(root)).id();
1294+
let child1 = world.spawn(ChildOf { parent: root }).id();
1295+
let grandchild = world.spawn(ChildOf { parent: child1 }).id();
1296+
let child2 = world.spawn(ChildOf { parent: root }).id();
12971297

12981298
let clone_root = world.spawn_empty().id();
12991299
EntityCloner::build(&mut world)

crates/bevy_ecs/src/hierarchy.rs

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ use log::warn;
5252
/// # use bevy_ecs::prelude::*;
5353
/// # let mut world = World::new();
5454
/// let root = world.spawn_empty().id();
55-
/// let child1 = world.spawn(ChildOf(root)).id();
56-
/// let child2 = world.spawn(ChildOf(root)).id();
57-
/// let grandchild = world.spawn(ChildOf(child1)).id();
55+
/// let child1 = world.spawn(ChildOf {parent: root}).id();
56+
/// let child2 = world.spawn(ChildOf {parent: root}).id();
57+
/// let grandchild = world.spawn(ChildOf {parent: child1}).id();
5858
///
5959
/// assert_eq!(&**world.entity(root).get::<Children>().unwrap(), &[child1, child2]);
6060
/// assert_eq!(&**world.entity(child1).get::<Children>().unwrap(), &[grandchild]);
@@ -94,12 +94,15 @@ use log::warn;
9494
)]
9595
#[relationship(relationship_target = Children)]
9696
#[doc(alias = "IsChild", alias = "Parent")]
97-
pub struct ChildOf(pub Entity);
97+
pub struct ChildOf {
98+
/// The parent entity of this child entity.
99+
pub parent: Entity,
100+
}
98101

99102
impl ChildOf {
100103
/// Returns the parent entity, which is the "target" of this relationship.
101104
pub fn get(&self) -> Entity {
102-
self.0
105+
self.parent
103106
}
104107
}
105108

@@ -108,7 +111,7 @@ impl Deref for ChildOf {
108111

109112
#[inline]
110113
fn deref(&self) -> &Self::Target {
111-
&self.0
114+
&self.parent
112115
}
113116
}
114117

@@ -119,7 +122,9 @@ impl Deref for ChildOf {
119122
impl FromWorld for ChildOf {
120123
#[inline(always)]
121124
fn from_world(_world: &mut World) -> Self {
122-
ChildOf(Entity::PLACEHOLDER)
125+
ChildOf {
126+
parent: Entity::PLACEHOLDER,
127+
}
123128
}
124129
}
125130

@@ -196,9 +201,9 @@ impl<'w> EntityWorldMut<'w> {
196201
///
197202
/// [`with_children`]: EntityWorldMut::with_children
198203
pub fn with_child(&mut self, bundle: impl Bundle) -> &mut Self {
199-
let id = self.id();
204+
let parent = self.id();
200205
self.world_scope(|world| {
201-
world.spawn((bundle, ChildOf(id)));
206+
world.spawn((bundle, ChildOf { parent }));
202207
});
203208
self
204209
}
@@ -213,7 +218,7 @@ impl<'w> EntityWorldMut<'w> {
213218
/// Inserts the [`ChildOf`] component with the given `parent` entity, if it exists.
214219
#[deprecated(since = "0.16.0", note = "Use entity_mut.insert(ChildOf(entity))")]
215220
pub fn set_parent(&mut self, parent: Entity) -> &mut Self {
216-
self.insert(ChildOf(parent));
221+
self.insert(ChildOf { parent });
217222
self
218223
}
219224
}
@@ -244,8 +249,8 @@ impl<'a> EntityCommands<'a> {
244249
///
245250
/// [`with_children`]: EntityCommands::with_children
246251
pub fn with_child(&mut self, bundle: impl Bundle) -> &mut Self {
247-
let id = self.id();
248-
self.commands.spawn((bundle, ChildOf(id)));
252+
let parent = self.id();
253+
self.commands.spawn((bundle, ChildOf { parent }));
249254
self
250255
}
251256

@@ -259,7 +264,7 @@ impl<'a> EntityCommands<'a> {
259264
/// Inserts the [`ChildOf`] component with the given `parent` entity, if it exists.
260265
#[deprecated(since = "0.16.0", note = "Use entity_commands.insert(ChildOf(entity))")]
261266
pub fn set_parent(&mut self, parent: Entity) -> &mut Self {
262-
self.insert(ChildOf(parent));
267+
self.insert(ChildOf { parent });
263268
self
264269
}
265270
}
@@ -375,9 +380,9 @@ mod tests {
375380
fn hierarchy() {
376381
let mut world = World::new();
377382
let root = world.spawn_empty().id();
378-
let child1 = world.spawn(ChildOf(root)).id();
379-
let grandchild = world.spawn(ChildOf(child1)).id();
380-
let child2 = world.spawn(ChildOf(root)).id();
383+
let child1 = world.spawn(ChildOf { parent: root }).id();
384+
let grandchild = world.spawn(ChildOf { parent: child1 }).id();
385+
let child2 = world.spawn(ChildOf { parent: root }).id();
381386

382387
// Spawn
383388
let hierarchy = get_hierarchy(&world, root);
@@ -398,7 +403,7 @@ mod tests {
398403
assert_eq!(hierarchy, Node::new_with(root, vec![Node::new(child2)]));
399404

400405
// Insert
401-
world.entity_mut(child1).insert(ChildOf(root));
406+
world.entity_mut(child1).insert(ChildOf { parent: root });
402407
let hierarchy = get_hierarchy(&world, root);
403408
assert_eq!(
404409
hierarchy,
@@ -457,7 +462,7 @@ mod tests {
457462
fn self_parenting_invalid() {
458463
let mut world = World::new();
459464
let id = world.spawn_empty().id();
460-
world.entity_mut(id).insert(ChildOf(id));
465+
world.entity_mut(id).insert(ChildOf { parent: id });
461466
assert!(
462467
world.entity(id).get::<ChildOf>().is_none(),
463468
"invalid ChildOf relationships should self-remove"
@@ -469,7 +474,7 @@ mod tests {
469474
let mut world = World::new();
470475
let parent = world.spawn_empty().id();
471476
world.entity_mut(parent).despawn();
472-
let id = world.spawn(ChildOf(parent)).id();
477+
let id = world.spawn(ChildOf { parent }).id();
473478
assert!(
474479
world.entity(id).get::<ChildOf>().is_none(),
475480
"invalid ChildOf relationships should self-remove"
@@ -480,10 +485,10 @@ mod tests {
480485
fn reinsert_same_parent() {
481486
let mut world = World::new();
482487
let parent = world.spawn_empty().id();
483-
let id = world.spawn(ChildOf(parent)).id();
484-
world.entity_mut(id).insert(ChildOf(parent));
488+
let id = world.spawn(ChildOf { parent }).id();
489+
world.entity_mut(id).insert(ChildOf { parent });
485490
assert_eq!(
486-
Some(&ChildOf(parent)),
491+
Some(&ChildOf { parent }),
487492
world.entity(id).get::<ChildOf>(),
488493
"ChildOf should still be there"
489494
);

0 commit comments

Comments
 (0)