Skip to content

Feat/5764 map paths in bevy reflect #5812

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed

Conversation

themasch
Copy link
Contributor

@themasch themasch commented Aug 27, 2022

Objective

I aimed to solve #5764 here, implementing a way to access map entries using bevy_reflects get_path and get_path_mut.

Solution

The implementation is incomplete, only allowing to access entries in maps with Strings as key. I wasnt able to come up with a proper solution yet on how to get from having a &str ident to a K map_key for a Map<K, V>.

The current implementation uses the m['key'] syntax for specifiing the map key, but it should be easy to adapt it to a different syntax, if required.

I added a new SingleQuote Token variant, which is returned by next_token when it encounters a '.
Then extended the get_path and get_path_mut methods to support a SingleQuote token after a OpenBracket token instead of an Ident. If thats encountered, we expect the next token to be an Ident (the key) and then a closing SingleQuote, and we expect the current reflect node to be a Map.

Resolving the map_key from the read ident seems to only challenging part here and thus its the only part I was not able to solve (or I might have missed other stuff?). I opted for going for "String keys only for the first draft", but of course I do not expect this to be merged like this.

Everything after that is basically a mirror of the behaviour for lists.

Additionally I split/copied the test for get_path into a second one for get_path_mut which basically tests the exact same stuff but using the _mut variant to make sure both behave the same, since their code is roughly identical and seems prone to minor differences which may cause subtle bugs/inconsistencies.

Changelog

TBD

TODO

I plan on doing some experiments on how the &str ident => K map_key resolution could work, but if anyone got a good idea on how this can be done, I'd very happy to read that!

These methods use the same code (duplicated) and we should make sure they behave the same (modulo _mut).

This also found a minor "bug" where an error was reported different between the implementations.

We may want to derive the two tests with some kind of macro to make sure they stay the same?
This adds some parser code to be able to parse access to maps via the reflection get_path and get_path_mut API.
The current implementation only supports maps with String keys, dues to lack of type casting from the read ident to the map key. Needs some more logic there.
@Weibye Weibye added C-Usability A targeted quality-of-life change that makes Bevy easier to use A-Reflection Runtime information about types labels Aug 27, 2022
Comment on lines 155 to 161
if let Some(Token::SingleQuote) = next_token(path, &mut index) {
} else {
return Err(ReflectPathError::ExpectedToken {
index: current_index,
token: "'",
});
}
Copy link
Contributor

@NathanSWard NathanSWard Aug 27, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

an if/else branch with one of the branches being empty is a little odd. This could be condensed to

if !matches!(Some(Token::SingleQuote), next_token(path, &mut index)) {
    return Err(..);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are correct, that read better indeed. I just used the empty if-branch because it was already used further down in the function to check for the CloseBracked token. I changed all cases to if !matched! now to keep them consistent

Comment on lines 263 to 269
if let Some(Token::SingleQuote) = next_token(path, &mut index) {
} else {
return Err(ReflectPathError::ExpectedToken {
index: current_index,
token: "'",
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto here with the if !matches!(..) syntax.

@@ -136,12 +141,48 @@ impl GetPath for dyn Reflect {
index: current_index,
})
}
},
Some(Token::SingleQuote) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also notably, the two match branches in path() and path_mut() are exactly the same (minus the get/get_mut call on the map. It's probably worth DRY'ing this out into a similar function to reduce code duplication.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved the code to find the complete quoted identifier to its own function, which only leaves the match reflect_ref vs match reflect_mut part in those branches. Not sure if it possible/feasible to further reduce duplication here without creating some kind ob abstraction over mutability?

Some parts could easily be factored our to reduce code duplication. Also improved readability by cleaning up empty if-branches, as suggested by QA.
We want to report the correct error locations so users understand these errors correctly.
bors bot pushed a commit that referenced this pull request Sep 27, 2022
# Objective

Currently, arrays cannot indexed using the reflection path API. 
This change makes them behave like lists so `x.get_path("list[0]")` will behave the same way, whether x.list is a "List" (e.g. a Vec) or an array.

## Solution

When syntax is encounterd `[ <idx> ]` we check if the referenced type is either a `ReflectRef::List` or `ReflectRef::Array`   (or `ReflectMut` for the mutable case). Since both provide the identical API for accessing entries, we do the same for both, although it requires code duplication as far as I can tell. 


This was born from working on #5764, but since this seems to be an easier fix (and I am not sure if I can actually solve #5812) I figured it might be worth to split this out.
@themasch
Copy link
Contributor Author

Closing this as I cannot finish it right now.

@themasch themasch closed this Oct 15, 2022
james7132 pushed a commit to james7132/bevy that referenced this pull request Oct 19, 2022
# Objective

Currently, arrays cannot indexed using the reflection path API. 
This change makes them behave like lists so `x.get_path("list[0]")` will behave the same way, whether x.list is a "List" (e.g. a Vec) or an array.

## Solution

When syntax is encounterd `[ <idx> ]` we check if the referenced type is either a `ReflectRef::List` or `ReflectRef::Array`   (or `ReflectMut` for the mutable case). Since both provide the identical API for accessing entries, we do the same for both, although it requires code duplication as far as I can tell. 


This was born from working on bevyengine#5764, but since this seems to be an easier fix (and I am not sure if I can actually solve bevyengine#5812) I figured it might be worth to split this out.
james7132 pushed a commit to james7132/bevy that referenced this pull request Oct 28, 2022
# Objective

Currently, arrays cannot indexed using the reflection path API. 
This change makes them behave like lists so `x.get_path("list[0]")` will behave the same way, whether x.list is a "List" (e.g. a Vec) or an array.

## Solution

When syntax is encounterd `[ <idx> ]` we check if the referenced type is either a `ReflectRef::List` or `ReflectRef::Array`   (or `ReflectMut` for the mutable case). Since both provide the identical API for accessing entries, we do the same for both, although it requires code duplication as far as I can tell. 


This was born from working on bevyengine#5764, but since this seems to be an easier fix (and I am not sure if I can actually solve bevyengine#5812) I figured it might be worth to split this out.
ItsDoot pushed a commit to ItsDoot/bevy that referenced this pull request Feb 1, 2023
# Objective

Currently, arrays cannot indexed using the reflection path API. 
This change makes them behave like lists so `x.get_path("list[0]")` will behave the same way, whether x.list is a "List" (e.g. a Vec) or an array.

## Solution

When syntax is encounterd `[ <idx> ]` we check if the referenced type is either a `ReflectRef::List` or `ReflectRef::Array`   (or `ReflectMut` for the mutable case). Since both provide the identical API for accessing entries, we do the same for both, although it requires code duplication as far as I can tell. 


This was born from working on bevyengine#5764, but since this seems to be an easier fix (and I am not sure if I can actually solve bevyengine#5812) I figured it might be worth to split this out.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Reflection Runtime information about types C-Usability A targeted quality-of-life change that makes Bevy easier to use
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

3 participants