pub mod access;1pub use access::*;23mod error;4pub use error::*;56mod parse;7pub use parse::ParseError;8use parse::PathParser;910use crate::{PartialReflect, Reflect};11use alloc::vec::Vec;12use core::fmt;13use derive_more::derive::From;14use thiserror::Error;1516type PathResult<'a, T> = Result<T, ReflectPathError<'a>>;1718/// An error returned from a failed path string query.19#[derive(Error, Debug, PartialEq, Eq)]20pub enum ReflectPathError<'a> {21/// An error caused by trying to access a path that's not able to be accessed,22/// see [`AccessError`] for details.23#[error(transparent)]24InvalidAccess(AccessError<'a>),2526/// An error that occurs when a type cannot downcast to a given type.27#[error("Can't downcast result of access to the given type")]28InvalidDowncast,2930/// An error caused by an invalid path string that couldn't be parsed.31#[error("Encountered an error at offset {offset} while parsing `{path}`: {error}")]32ParseError {33/// Position in `path`.34offset: usize,35/// The path that the error occurred in.36path: &'a str,37/// The underlying error.38error: ParseError<'a>,39},40}4142impl<'a> From<AccessError<'a>> for ReflectPathError<'a> {43fn from(value: AccessError<'a>) -> Self {44ReflectPathError::InvalidAccess(value)45}46}4748/// Something that can be interpreted as a reflection path in [`GetPath`].49pub trait ReflectPath<'a>: Sized {50/// Gets a reference to the specified element on the given [`Reflect`] object.51///52/// See [`GetPath::reflect_path`] for more details,53/// see [`element`](Self::element) if you want a typed return value.54fn reflect_element(self, root: &dyn PartialReflect) -> PathResult<'a, &dyn PartialReflect>;5556/// Gets a mutable reference to the specified element on the given [`Reflect`] object.57///58/// See [`GetPath::reflect_path_mut`] for more details.59fn reflect_element_mut(60self,61root: &mut dyn PartialReflect,62) -> PathResult<'a, &mut dyn PartialReflect>;6364/// Gets a `&T` to the specified element on the given [`Reflect`] object.65///66/// See [`GetPath::path`] for more details.67fn element<T: Reflect>(self, root: &dyn PartialReflect) -> PathResult<'a, &T> {68self.reflect_element(root).and_then(|p| {69p.try_downcast_ref::<T>()70.ok_or(ReflectPathError::InvalidDowncast)71})72}7374/// Gets a `&mut T` to the specified element on the given [`Reflect`] object.75///76/// See [`GetPath::path_mut`] for more details.77fn element_mut<T: Reflect>(self, root: &mut dyn PartialReflect) -> PathResult<'a, &mut T> {78self.reflect_element_mut(root).and_then(|p| {79p.try_downcast_mut::<T>()80.ok_or(ReflectPathError::InvalidDowncast)81})82}83}8485impl<'a> ReflectPath<'a> for &'a str {86fn reflect_element(self, mut root: &dyn PartialReflect) -> PathResult<'a, &dyn PartialReflect> {87for (access, offset) in PathParser::new(self) {88let a = access?;89root = a.element(root, Some(offset))?;90}91Ok(root)92}93fn reflect_element_mut(94self,95mut root: &mut dyn PartialReflect,96) -> PathResult<'a, &mut dyn PartialReflect> {97for (access, offset) in PathParser::new(self) {98root = access?.element_mut(root, Some(offset))?;99}100Ok(root)101}102}103/// A trait which allows nested [`Reflect`] values to be retrieved with path strings.104///105/// Using these functions repeatedly with the same string requires parsing the string every time.106/// To avoid this cost, it's recommended to construct a [`ParsedPath`] instead.107///108/// # Syntax109///110/// ## Structs111///112/// Field paths for [`Struct`] elements use the standard Rust field access syntax of113/// dot and field name: `.field_name`.114///115/// Additionally, struct fields may be accessed by their index within the struct's definition.116/// This is accomplished by using the hash symbol (`#`) in place of the standard dot: `#0`.117///118/// Accessing a struct's field by index can speed up fetches at runtime due to the removed119/// need for string matching.120/// And while this can be more performant, it's best to keep in mind the tradeoffs when121/// utilizing such optimizations.122/// For example, this can result in fairly fragile code as the string paths will need to be123/// kept in sync with the struct definitions since the order of fields could be easily changed.124/// Because of this, storing these kinds of paths in persistent storage (i.e. game assets)125/// is strongly discouraged.126///127/// Note that a leading dot (`.`) or hash (`#`) token is implied for the first item in a path,128/// and may therefore be omitted.129///130/// Additionally, an empty path may be used to get the struct itself.131///132/// ### Example133/// ```134/// # use bevy_reflect::{GetPath, Reflect};135/// #[derive(Reflect, PartialEq, Debug)]136/// struct MyStruct {137/// value: u32138/// }139///140/// let my_struct = MyStruct { value: 123 };141/// // Access via field name142/// assert_eq!(my_struct.path::<u32>(".value").unwrap(), &123);143/// // Access via field index144/// assert_eq!(my_struct.path::<u32>("#0").unwrap(), &123);145/// // Access self146/// assert_eq!(*my_struct.path::<MyStruct>("").unwrap(), my_struct);147/// ```148///149/// ## Tuples and Tuple Structs150///151/// [`Tuple`] and [`TupleStruct`] elements also follow a conventional Rust syntax.152/// Fields are accessed with a dot and the field index: `.0`.153///154/// Note that a leading dot (`.`) token is implied for the first item in a path,155/// and may therefore be omitted.156///157/// ### Example158/// ```159/// # use bevy_reflect::{GetPath, Reflect};160/// #[derive(Reflect)]161/// struct MyTupleStruct(u32);162///163/// let my_tuple_struct = MyTupleStruct(123);164/// assert_eq!(my_tuple_struct.path::<u32>(".0").unwrap(), &123);165/// ```166///167/// ## Lists and Arrays168///169/// [`List`] and [`Array`] elements are accessed with brackets: `[0]`.170///171/// ### Example172/// ```173/// # use bevy_reflect::{GetPath};174/// let my_list: Vec<u32> = vec![1, 2, 3];175/// assert_eq!(my_list.path::<u32>("[2]").unwrap(), &3);176/// ```177///178/// ## Enums179///180/// Pathing for [`Enum`] elements works a bit differently than in normal Rust.181/// Usually, you would need to pattern match an enum, branching off on the desired variants.182/// Paths used by this trait do not have any pattern matching capabilities;183/// instead, they assume the variant is already known ahead of time.184///185/// The syntax used, therefore, depends on the variant being accessed:186/// - Struct variants use the struct syntax (outlined above)187/// - Tuple variants use the tuple syntax (outlined above)188/// - Unit variants have no fields to access189///190/// If the variant cannot be known ahead of time, the path will need to be split up191/// and proper enum pattern matching will need to be handled manually.192///193/// ### Example194/// ```195/// # use bevy_reflect::{GetPath, Reflect};196/// #[derive(Reflect)]197/// enum MyEnum {198/// Unit,199/// Tuple(bool),200/// Struct {201/// value: u32202/// }203/// }204///205/// let tuple_variant = MyEnum::Tuple(true);206/// assert_eq!(tuple_variant.path::<bool>(".0").unwrap(), &true);207///208/// let struct_variant = MyEnum::Struct { value: 123 };209/// // Access via field name210/// assert_eq!(struct_variant.path::<u32>(".value").unwrap(), &123);211/// // Access via field index212/// assert_eq!(struct_variant.path::<u32>("#0").unwrap(), &123);213///214/// // Error: Expected struct variant215/// assert!(matches!(tuple_variant.path::<u32>(".value"), Err(_)));216/// ```217///218/// # Chaining219///220/// Using the aforementioned syntax, path items may be chained one after another221/// to create a full path to a nested element.222///223/// ## Example224/// ```225/// # use bevy_reflect::{GetPath, Reflect};226/// #[derive(Reflect)]227/// struct MyStruct {228/// value: Vec<Option<u32>>229/// }230///231/// let my_struct = MyStruct {232/// value: vec![None, None, Some(123)],233/// };234/// assert_eq!(235/// my_struct.path::<u32>(".value[2].0").unwrap(),236/// &123,237/// );238/// ```239///240/// [`Struct`]: crate::Struct241/// [`Tuple`]: crate::Tuple242/// [`TupleStruct`]: crate::TupleStruct243/// [`List`]: crate::List244/// [`Array`]: crate::Array245/// [`Enum`]: crate::Enum246#[diagnostic::on_unimplemented(247message = "`{Self}` does not implement `GetPath` so cannot be accessed by reflection path",248note = "consider annotating `{Self}` with `#[derive(Reflect)]`"249)]250pub trait GetPath: PartialReflect {251/// Returns a reference to the value specified by `path`.252///253/// To retrieve a statically typed reference, use254/// [`path`][GetPath::path].255fn reflect_path<'p>(&self, path: impl ReflectPath<'p>) -> PathResult<'p, &dyn PartialReflect> {256path.reflect_element(self.as_partial_reflect())257}258259/// Returns a mutable reference to the value specified by `path`.260///261/// To retrieve a statically typed mutable reference, use262/// [`path_mut`][GetPath::path_mut].263fn reflect_path_mut<'p>(264&mut self,265path: impl ReflectPath<'p>,266) -> PathResult<'p, &mut dyn PartialReflect> {267path.reflect_element_mut(self.as_partial_reflect_mut())268}269270/// Returns a statically typed reference to the value specified by `path`.271///272/// This will automatically handle downcasting to type `T`.273/// The downcast will fail if this value is not of type `T`274/// (which may be the case when using dynamic types like [`DynamicStruct`]).275///276/// [`DynamicStruct`]: crate::DynamicStruct277fn path<'p, T: Reflect>(&self, path: impl ReflectPath<'p>) -> PathResult<'p, &T> {278path.element(self.as_partial_reflect())279}280281/// Returns a statically typed mutable reference to the value specified by `path`.282///283/// This will automatically handle downcasting to type `T`.284/// The downcast will fail if this value is not of type `T`285/// (which may be the case when using dynamic types like [`DynamicStruct`]).286///287/// [`DynamicStruct`]: crate::DynamicStruct288fn path_mut<'p, T: Reflect>(&mut self, path: impl ReflectPath<'p>) -> PathResult<'p, &mut T> {289path.element_mut(self.as_partial_reflect_mut())290}291}292293// Implement `GetPath` for `dyn Reflect`294impl<T: Reflect + ?Sized> GetPath for T {}295296/// An [`Access`] combined with an `offset` for more helpful error reporting.297#[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)]298pub struct OffsetAccess {299/// The [`Access`] itself.300pub access: Access<'static>,301/// A character offset in the string the path was parsed from.302pub offset: Option<usize>,303}304305impl From<Access<'static>> for OffsetAccess {306fn from(access: Access<'static>) -> Self {307OffsetAccess {308access,309offset: None,310}311}312}313314/// A pre-parsed path to an element within a type.315///316/// This struct can be constructed manually from its [`Access`]es or with317/// the [parse](ParsedPath::parse) method.318///319/// This struct may be used like [`GetPath`] but removes the cost of parsing the path320/// string at each element access.321///322/// It's recommended to use this in place of [`GetPath`] when the path string is323/// unlikely to be changed and will be accessed repeatedly.324///325/// ## Examples326///327/// Parsing a [`&'static str`](str):328/// ```329/// # use bevy_reflect::ParsedPath;330/// let my_static_string: &'static str = "bar#0.1[2].0";331/// // Breakdown:332/// // "bar" - Access struct field named "bar"333/// // "#0" - Access struct field at index 0334/// // ".1" - Access tuple struct field at index 1335/// // "[2]" - Access list element at index 2336/// // ".0" - Access tuple variant field at index 0337/// let my_path = ParsedPath::parse_static(my_static_string);338/// ```339/// Parsing a non-static [`&str`](str):340/// ```341/// # use bevy_reflect::ParsedPath;342/// let my_string = String::from("bar#0.1[2].0");343/// // Breakdown:344/// // "bar" - Access struct field named "bar"345/// // "#0" - Access struct field at index 0346/// // ".1" - Access tuple struct field at index 1347/// // "[2]" - Access list element at index 2348/// // ".0" - Access tuple variant field at index 0349/// let my_path = ParsedPath::parse(&my_string);350/// ```351/// Manually constructing a [`ParsedPath`]:352/// ```353/// # use std::borrow::Cow;354/// # use bevy_reflect::access::Access;355/// # use bevy_reflect::ParsedPath;356/// let path_elements = [357/// Access::Field(Cow::Borrowed("bar")),358/// Access::FieldIndex(0),359/// Access::TupleIndex(1),360/// Access::ListIndex(2),361/// Access::TupleIndex(1),362/// ];363/// let my_path = ParsedPath::from(path_elements);364/// ```365#[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash, From)]366pub struct ParsedPath(367/// This is a vector of pre-parsed [`OffsetAccess`]es.368pub Vec<OffsetAccess>,369);370371impl ParsedPath {372/// Parses a [`ParsedPath`] from a string.373///374/// Returns an error if the string does not represent a valid path to an element.375///376/// The exact format for path strings can be found in the documentation for [`GetPath`].377/// In short, though, a path consists of one or more chained accessor strings.378/// These are:379/// - Named field access (`.field`)380/// - Unnamed field access (`.1`)381/// - Field index access (`#0`)382/// - Sequence access (`[2]`)383///384/// # Example385/// ```386/// # use bevy_reflect::{ParsedPath, Reflect, ReflectPath};387/// #[derive(Reflect)]388/// struct Foo {389/// bar: Bar,390/// }391///392/// #[derive(Reflect)]393/// struct Bar {394/// baz: Baz,395/// }396///397/// #[derive(Reflect)]398/// struct Baz(f32, Vec<Option<u32>>);399///400/// let foo = Foo {401/// bar: Bar {402/// baz: Baz(3.14, vec![None, None, Some(123)])403/// },404/// };405///406/// let parsed_path = ParsedPath::parse("bar#0.1[2].0").unwrap();407/// // Breakdown:408/// // "bar" - Access struct field named "bar"409/// // "#0" - Access struct field at index 0410/// // ".1" - Access tuple struct field at index 1411/// // "[2]" - Access list element at index 2412/// // ".0" - Access tuple variant field at index 0413///414/// assert_eq!(parsed_path.element::<u32>(&foo).unwrap(), &123);415/// ```416pub fn parse(string: &str) -> PathResult<'_, Self> {417let mut parts = Vec::new();418for (access, offset) in PathParser::new(string) {419parts.push(OffsetAccess {420access: access?.into_owned(),421offset: Some(offset),422});423}424Ok(Self(parts))425}426427/// Similar to [`Self::parse`] but only works on `&'static str`428/// and does not allocate per named field.429pub fn parse_static(string: &'static str) -> PathResult<'static, Self> {430let mut parts = Vec::new();431for (access, offset) in PathParser::new(string) {432parts.push(OffsetAccess {433access: access?,434offset: Some(offset),435});436}437Ok(Self(parts))438}439}440441impl<'a> ReflectPath<'a> for &'a ParsedPath {442fn reflect_element(self, mut root: &dyn PartialReflect) -> PathResult<'a, &dyn PartialReflect> {443for OffsetAccess { access, offset } in &self.0 {444root = access.element(root, *offset)?;445}446Ok(root)447}448fn reflect_element_mut(449self,450mut root: &mut dyn PartialReflect,451) -> PathResult<'a, &mut dyn PartialReflect> {452for OffsetAccess { access, offset } in &self.0 {453root = access.element_mut(root, *offset)?;454}455Ok(root)456}457}458459impl<const N: usize> From<[OffsetAccess; N]> for ParsedPath {460fn from(value: [OffsetAccess; N]) -> Self {461ParsedPath(value.to_vec())462}463}464465impl From<Vec<Access<'static>>> for ParsedPath {466fn from(value: Vec<Access<'static>>) -> Self {467ParsedPath(468value469.into_iter()470.map(|access| OffsetAccess {471access,472offset: None,473})474.collect(),475)476}477}478479impl<const N: usize> From<[Access<'static>; N]> for ParsedPath {480fn from(value: [Access<'static>; N]) -> Self {481value.to_vec().into()482}483}484485impl<'a> TryFrom<&'a str> for ParsedPath {486type Error = ReflectPathError<'a>;487fn try_from(value: &'a str) -> Result<Self, Self::Error> {488ParsedPath::parse(value)489}490}491492impl fmt::Display for ParsedPath {493fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {494for OffsetAccess { access, .. } in &self.0 {495write!(f, "{access}")?;496}497Ok(())498}499}500501impl core::ops::Index<usize> for ParsedPath {502type Output = OffsetAccess;503fn index(&self, index: usize) -> &Self::Output {504&self.0[index]505}506}507508impl core::ops::IndexMut<usize> for ParsedPath {509fn index_mut(&mut self, index: usize) -> &mut Self::Output {510&mut self.0[index]511}512}513514#[cfg(test)]515#[expect(516clippy::approx_constant,517reason = "We don't need the exact value of Pi here."518)]519mod tests {520use super::*;521use crate::*;522use alloc::vec;523524#[derive(Reflect, PartialEq, Debug)]525struct A {526w: usize,527x: B,528y: Vec<C>,529z: D,530unit_variant: F,531tuple_variant: F,532struct_variant: F,533array: [i32; 3],534tuple: (bool, f32),535}536537#[derive(Reflect, PartialEq, Debug)]538struct B {539foo: usize,540łørđ: C,541}542543#[derive(Reflect, PartialEq, Debug)]544struct C {545mосква: f32,546}547548#[derive(Reflect, PartialEq, Debug)]549struct D(E);550551#[derive(Reflect, PartialEq, Debug)]552struct E(f32, usize);553554#[derive(Reflect, PartialEq, Debug)]555enum F {556Unit,557Tuple(u32, u32),558Şķràźÿ { 東京: char },559}560561fn a_sample() -> A {562A {563w: 1,564x: B {565foo: 10,566łørđ: C { mосква: 3.14 },567},568y: vec![C { mосква: 1.0 }, C { mосква: 2.0 }],569z: D(E(10.0, 42)),570unit_variant: F::Unit,571tuple_variant: F::Tuple(123, 321),572struct_variant: F::Şķràźÿ { 東京: 'm' },573array: [86, 75, 309],574tuple: (true, 1.23),575}576}577578fn offset(access: Access<'static>, offset: usize) -> OffsetAccess {579OffsetAccess {580access,581offset: Some(offset),582}583}584585fn access_field(field: &'static str) -> Access<'static> {586Access::Field(field.into())587}588589type StaticError = ReflectPathError<'static>;590591fn invalid_access(592offset: usize,593actual: ReflectKind,594expected: ReflectKind,595access: &'static str,596) -> StaticError {597ReflectPathError::InvalidAccess(AccessError {598kind: AccessErrorKind::IncompatibleTypes { actual, expected },599access: ParsedPath::parse_static(access).unwrap()[1].access.clone(),600offset: Some(offset),601})602}603604#[test]605fn try_from() {606assert_eq!(607ParsedPath::try_from("w").unwrap().0,608&[offset(access_field("w"), 1)]609);610611let r = ParsedPath::try_from("w[");612let matches = matches!(r, Err(ReflectPathError::ParseError { .. }));613assert!(614matches,615"ParsedPath::try_from did not return a ParseError for \"w[\""616);617}618619#[test]620fn parsed_path_parse() {621assert_eq!(622ParsedPath::parse("w").unwrap().0,623&[offset(access_field("w"), 1)]624);625assert_eq!(626ParsedPath::parse("x.foo").unwrap().0,627&[offset(access_field("x"), 1), offset(access_field("foo"), 2)]628);629assert_eq!(630ParsedPath::parse("x.łørđ.mосква").unwrap().0,631&[632offset(access_field("x"), 1),633offset(access_field("łørđ"), 2),634offset(access_field("mосква"), 10)635]636);637assert_eq!(638ParsedPath::parse("y[1].mосква").unwrap().0,639&[640offset(access_field("y"), 1),641offset(Access::ListIndex(1), 2),642offset(access_field("mосква"), 5)643]644);645assert_eq!(646ParsedPath::parse("z.0.1").unwrap().0,647&[648offset(access_field("z"), 1),649offset(Access::TupleIndex(0), 2),650offset(Access::TupleIndex(1), 4),651]652);653assert_eq!(654ParsedPath::parse("x#0").unwrap().0,655&[656offset(access_field("x"), 1),657offset(Access::FieldIndex(0), 2)658]659);660assert_eq!(661ParsedPath::parse("x#0#1").unwrap().0,662&[663offset(access_field("x"), 1),664offset(Access::FieldIndex(0), 2),665offset(Access::FieldIndex(1), 4)666]667);668}669670#[test]671fn parsed_path_get_field() {672let a = a_sample();673674let b = ParsedPath::parse("w").unwrap();675let c = ParsedPath::parse("x.foo").unwrap();676let d = ParsedPath::parse("x.łørđ.mосква").unwrap();677let e = ParsedPath::parse("y[1].mосква").unwrap();678let f = ParsedPath::parse("z.0.1").unwrap();679let g = ParsedPath::parse("x#0").unwrap();680let h = ParsedPath::parse("x#1#0").unwrap();681let i = ParsedPath::parse("unit_variant").unwrap();682let j = ParsedPath::parse("tuple_variant.1").unwrap();683let k = ParsedPath::parse("struct_variant.東京").unwrap();684let l = ParsedPath::parse("struct_variant#0").unwrap();685let m = ParsedPath::parse("array[2]").unwrap();686let n = ParsedPath::parse("tuple.1").unwrap();687688for _ in 0..30 {689assert_eq!(*b.element::<usize>(&a).unwrap(), 1);690assert_eq!(*c.element::<usize>(&a).unwrap(), 10);691assert_eq!(*d.element::<f32>(&a).unwrap(), 3.14);692assert_eq!(*e.element::<f32>(&a).unwrap(), 2.0);693assert_eq!(*f.element::<usize>(&a).unwrap(), 42);694assert_eq!(*g.element::<usize>(&a).unwrap(), 10);695assert_eq!(*h.element::<f32>(&a).unwrap(), 3.14);696assert_eq!(*i.element::<F>(&a).unwrap(), F::Unit);697assert_eq!(*j.element::<u32>(&a).unwrap(), 321);698assert_eq!(*k.element::<char>(&a).unwrap(), 'm');699assert_eq!(*l.element::<char>(&a).unwrap(), 'm');700assert_eq!(*m.element::<i32>(&a).unwrap(), 309);701assert_eq!(*n.element::<f32>(&a).unwrap(), 1.23);702}703}704705#[test]706fn reflect_array_behaves_like_list() {707#[derive(Reflect)]708struct A {709list: Vec<u8>,710array: [u8; 10],711}712713let a = A {714list: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9],715array: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],716};717718assert_eq!(*a.path::<u8>("list[5]").unwrap(), 5);719assert_eq!(*a.path::<u8>("array[5]").unwrap(), 5);720assert_eq!(*a.path::<u8>("list[0]").unwrap(), 0);721assert_eq!(*a.path::<u8>("array[0]").unwrap(), 0);722}723724#[test]725fn reflect_array_behaves_like_list_mut() {726#[derive(Reflect)]727struct A {728list: Vec<u8>,729array: [u8; 10],730}731732let mut a = A {733list: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9],734array: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],735};736737assert_eq!(*a.path_mut::<u8>("list[5]").unwrap(), 5);738assert_eq!(*a.path_mut::<u8>("array[5]").unwrap(), 5);739740*a.path_mut::<u8>("list[5]").unwrap() = 10;741*a.path_mut::<u8>("array[5]").unwrap() = 10;742743assert_eq!(*a.path_mut::<u8>("list[5]").unwrap(), 10);744assert_eq!(*a.path_mut::<u8>("array[5]").unwrap(), 10);745}746747#[test]748fn reflect_path() {749let mut a = a_sample();750751assert_eq!(*a.path::<A>("").unwrap(), a);752assert_eq!(*a.path::<usize>("w").unwrap(), 1);753assert_eq!(*a.path::<usize>("x.foo").unwrap(), 10);754assert_eq!(*a.path::<f32>("x.łørđ.mосква").unwrap(), 3.14);755assert_eq!(*a.path::<f32>("y[1].mосква").unwrap(), 2.0);756assert_eq!(*a.path::<usize>("z.0.1").unwrap(), 42);757assert_eq!(*a.path::<usize>("x#0").unwrap(), 10);758assert_eq!(*a.path::<f32>("x#1#0").unwrap(), 3.14);759760assert_eq!(*a.path::<F>("unit_variant").unwrap(), F::Unit);761assert_eq!(*a.path::<u32>("tuple_variant.1").unwrap(), 321);762assert_eq!(*a.path::<char>("struct_variant.東京").unwrap(), 'm');763assert_eq!(*a.path::<char>("struct_variant#0").unwrap(), 'm');764765assert_eq!(*a.path::<i32>("array[2]").unwrap(), 309);766767assert_eq!(*a.path::<f32>("tuple.1").unwrap(), 1.23);768*a.path_mut::<f32>("tuple.1").unwrap() = 3.21;769assert_eq!(*a.path::<f32>("tuple.1").unwrap(), 3.21);770771*a.path_mut::<f32>("y[1].mосква").unwrap() = 3.0;772assert_eq!(a.y[1].mосква, 3.0);773774*a.path_mut::<u32>("tuple_variant.0").unwrap() = 1337;775assert_eq!(a.tuple_variant, F::Tuple(1337, 321));776777assert_eq!(778a.reflect_path("x.notreal").err().unwrap(),779ReflectPathError::InvalidAccess(AccessError {780kind: AccessErrorKind::MissingField(ReflectKind::Struct),781access: access_field("notreal"),782offset: Some(2),783})784);785786assert_eq!(787a.reflect_path("unit_variant.0").err().unwrap(),788ReflectPathError::InvalidAccess(AccessError {789kind: AccessErrorKind::IncompatibleEnumVariantTypes {790actual: VariantType::Unit,791expected: VariantType::Tuple,792},793access: ParsedPath::parse_static("unit_variant.0").unwrap()[1]794.access795.clone(),796offset: Some(13),797})798);799assert_eq!(800a.reflect_path("x[0]").err().unwrap(),801invalid_access(2, ReflectKind::Struct, ReflectKind::List, "x[0]")802);803assert_eq!(804a.reflect_path("y.x").err().unwrap(),805invalid_access(2, ReflectKind::List, ReflectKind::Struct, "y.x")806);807}808809#[test]810fn accept_leading_tokens() {811assert_eq!(812ParsedPath::parse(".w").unwrap().0,813&[offset(access_field("w"), 1)]814);815assert_eq!(816ParsedPath::parse("#0.foo").unwrap().0,817&[818offset(Access::FieldIndex(0), 1),819offset(access_field("foo"), 3)820]821);822assert_eq!(823ParsedPath::parse(".5").unwrap().0,824&[offset(Access::TupleIndex(5), 1)]825);826assert_eq!(827ParsedPath::parse("[0].łørđ").unwrap().0,828&[829offset(Access::ListIndex(0), 1),830offset(access_field("łørđ"), 4)831]832);833}834}835836837