bevy_reflect/path/
mod.rs

1pub mod access;
2pub use access::*;
3
4mod error;
5pub use error::*;
6
7mod parse;
8pub use parse::ParseError;
9use parse::PathParser;
10
11use crate::Reflect;
12use std::fmt;
13use thiserror::Error;
14
15type PathResult<'a, T> = Result<T, ReflectPathError<'a>>;
16
17/// An error returned from a failed path string query.
18#[derive(Debug, PartialEq, Eq, Error)]
19pub enum ReflectPathError<'a> {
20    /// An error caused by trying to access a path that's not able to be accessed,
21    /// see [`AccessError`] for details.
22    #[error(transparent)]
23    InvalidAccess(AccessError<'a>),
24
25    /// An error that occurs when a type cannot downcast to a given type.
26    #[error("Can't downcast result of access to the given type")]
27    InvalidDowncast,
28
29    /// An error caused by an invalid path string that couldn't be parsed.
30    #[error("Encountered an error at offset {offset} while parsing `{path}`: {error}")]
31    ParseError {
32        /// Position in `path`.
33        offset: usize,
34        /// The path that the error occurred in.
35        path: &'a str,
36        /// The underlying error.
37        error: ParseError<'a>,
38    },
39}
40impl<'a> From<AccessError<'a>> for ReflectPathError<'a> {
41    fn from(value: AccessError<'a>) -> Self {
42        Self::InvalidAccess(value)
43    }
44}
45
46/// Something that can be interpreted as a reflection path in [`GetPath`].
47pub trait ReflectPath<'a>: Sized {
48    /// Gets a reference to the specified element on the given [`Reflect`] object.
49    ///
50    /// See [`GetPath::reflect_path`] for more details,
51    /// see [`element`](Self::element) if you want a typed return value.
52    fn reflect_element(self, root: &dyn Reflect) -> PathResult<'a, &dyn Reflect>;
53
54    /// Gets a mutable reference to the specified element on the given [`Reflect`] object.
55    ///
56    /// See [`GetPath::reflect_path_mut`] for more details.
57    fn reflect_element_mut(self, root: &mut dyn Reflect) -> PathResult<'a, &mut dyn Reflect>;
58
59    /// Gets a `&T` to the specified element on the given [`Reflect`] object.
60    ///
61    /// See [`GetPath::path`] for more details.
62    fn element<T: Reflect>(self, root: &dyn Reflect) -> PathResult<'a, &T> {
63        self.reflect_element(root).and_then(|p| {
64            p.downcast_ref::<T>()
65                .ok_or(ReflectPathError::InvalidDowncast)
66        })
67    }
68
69    /// Gets a `&mut T` to the specified element on the given [`Reflect`] object.
70    ///
71    /// See [`GetPath::path_mut`] for more details.
72    fn element_mut<T: Reflect>(self, root: &mut dyn Reflect) -> PathResult<'a, &mut T> {
73        self.reflect_element_mut(root).and_then(|p| {
74            p.downcast_mut::<T>()
75                .ok_or(ReflectPathError::InvalidDowncast)
76        })
77    }
78}
79impl<'a> ReflectPath<'a> for &'a str {
80    fn reflect_element(self, mut root: &dyn Reflect) -> PathResult<'a, &dyn Reflect> {
81        for (access, offset) in PathParser::new(self) {
82            let a = access?;
83            root = a.element(root, Some(offset))?;
84        }
85        Ok(root)
86    }
87    fn reflect_element_mut(self, mut root: &mut dyn Reflect) -> PathResult<'a, &mut dyn Reflect> {
88        for (access, offset) in PathParser::new(self) {
89            root = access?.element_mut(root, Some(offset))?;
90        }
91        Ok(root)
92    }
93}
94/// A trait which allows nested [`Reflect`] values to be retrieved with path strings.
95///
96/// Using these functions repeatedly with the same string requires parsing the string every time.
97/// To avoid this cost, it's recommended to construct a [`ParsedPath`] instead.
98///
99/// # Syntax
100///
101/// ## Structs
102///
103/// Field paths for [`Struct`] elements use the standard Rust field access syntax of
104/// dot and field name: `.field_name`.
105///
106/// Additionally, struct fields may be accessed by their index within the struct's definition.
107/// This is accomplished by using the hash symbol (`#`) in place of the standard dot: `#0`.
108///
109/// Accessing a struct's field by index can speed up fetches at runtime due to the removed
110/// need for string matching.
111/// And while this can be more performant, it's best to keep in mind the tradeoffs when
112/// utilizing such optimizations.
113/// For example, this can result in fairly fragile code as the string paths will need to be
114/// kept in sync with the struct definitions since the order of fields could be easily changed.
115/// Because of this, storing these kinds of paths in persistent storage (i.e. game assets)
116/// is strongly discouraged.
117///
118/// Note that a leading dot (`.`) or hash (`#`) token is implied for the first item in a path,
119/// and may therefore be omitted.
120///
121/// ### Example
122/// ```
123/// # use bevy_reflect::{GetPath, Reflect};
124/// #[derive(Reflect)]
125/// struct MyStruct {
126///   value: u32
127/// }
128///
129/// let my_struct = MyStruct { value: 123 };
130/// // Access via field name
131/// assert_eq!(my_struct.path::<u32>(".value").unwrap(), &123);
132/// // Access via field index
133/// assert_eq!(my_struct.path::<u32>("#0").unwrap(), &123);
134/// ```
135///
136/// ## Tuples and Tuple Structs
137///
138/// [`Tuple`] and [`TupleStruct`] elements also follow a conventional Rust syntax.
139/// Fields are accessed with a dot and the field index: `.0`.
140///
141/// Note that a leading dot (`.`) token is implied for the first item in a path,
142/// and may therefore be omitted.
143///
144/// ### Example
145/// ```
146/// # use bevy_reflect::{GetPath, Reflect};
147/// #[derive(Reflect)]
148/// struct MyTupleStruct(u32);
149///
150/// let my_tuple_struct = MyTupleStruct(123);
151/// assert_eq!(my_tuple_struct.path::<u32>(".0").unwrap(), &123);
152/// ```
153///
154/// ## Lists and Arrays
155///
156/// [`List`] and [`Array`] elements are accessed with brackets: `[0]`.
157///
158/// ### Example
159/// ```
160/// # use bevy_reflect::{GetPath};
161/// let my_list: Vec<u32> = vec![1, 2, 3];
162/// assert_eq!(my_list.path::<u32>("[2]").unwrap(), &3);
163/// ```
164///
165/// ## Enums
166///
167/// Pathing for [`Enum`] elements works a bit differently than in normal Rust.
168/// Usually, you would need to pattern match an enum, branching off on the desired variants.
169/// Paths used by this trait do not have any pattern matching capabilities;
170/// instead, they assume the variant is already known ahead of time.
171///
172/// The syntax used, therefore, depends on the variant being accessed:
173/// - Struct variants use the struct syntax (outlined above)
174/// - Tuple variants use the tuple syntax (outlined above)
175/// - Unit variants have no fields to access
176///
177/// If the variant cannot be known ahead of time, the path will need to be split up
178/// and proper enum pattern matching will need to be handled manually.
179///
180/// ### Example
181/// ```
182/// # use bevy_reflect::{GetPath, Reflect};
183/// #[derive(Reflect)]
184/// enum MyEnum {
185///   Unit,
186///   Tuple(bool),
187///   Struct {
188///     value: u32
189///   }
190/// }
191///
192/// let tuple_variant = MyEnum::Tuple(true);
193/// assert_eq!(tuple_variant.path::<bool>(".0").unwrap(), &true);
194///
195/// let struct_variant = MyEnum::Struct { value: 123 };
196/// // Access via field name
197/// assert_eq!(struct_variant.path::<u32>(".value").unwrap(), &123);
198/// // Access via field index
199/// assert_eq!(struct_variant.path::<u32>("#0").unwrap(), &123);
200///
201/// // Error: Expected struct variant
202/// assert!(matches!(tuple_variant.path::<u32>(".value"), Err(_)));
203/// ```
204///
205/// # Chaining
206///
207/// Using the aforementioned syntax, path items may be chained one after another
208/// to create a full path to a nested element.
209///
210/// ## Example
211/// ```
212/// # use bevy_reflect::{GetPath, Reflect};
213/// #[derive(Reflect)]
214/// struct MyStruct {
215///   value: Vec<Option<u32>>
216/// }
217///
218/// let my_struct = MyStruct {
219///   value: vec![None, None, Some(123)],
220/// };
221/// assert_eq!(
222///   my_struct.path::<u32>(".value[2].0").unwrap(),
223///   &123,
224/// );
225/// ```
226///
227/// [`Struct`]: crate::Struct
228/// [`Tuple`]: crate::Tuple
229/// [`TupleStruct`]: crate::TupleStruct
230/// [`List`]: crate::List
231/// [`Array`]: crate::Array
232/// [`Enum`]: crate::Enum
233#[diagnostic::on_unimplemented(
234    message = "`{Self}` does not provide a reflection path",
235    note = "consider annotating `{Self}` with `#[derive(Reflect)]`"
236)]
237pub trait GetPath: Reflect {
238    /// Returns a reference to the value specified by `path`.
239    ///
240    /// To retrieve a statically typed reference, use
241    /// [`path`][GetPath::path].
242    fn reflect_path<'p>(&self, path: impl ReflectPath<'p>) -> PathResult<'p, &dyn Reflect> {
243        path.reflect_element(self.as_reflect())
244    }
245
246    /// Returns a mutable reference to the value specified by `path`.
247    ///
248    /// To retrieve a statically typed mutable reference, use
249    /// [`path_mut`][GetPath::path_mut].
250    fn reflect_path_mut<'p>(
251        &mut self,
252        path: impl ReflectPath<'p>,
253    ) -> PathResult<'p, &mut dyn Reflect> {
254        path.reflect_element_mut(self.as_reflect_mut())
255    }
256
257    /// Returns a statically typed reference to the value specified by `path`.
258    ///
259    /// This will automatically handle downcasting to type `T`.
260    /// The downcast will fail if this value is not of type `T`
261    /// (which may be the case when using dynamic types like [`DynamicStruct`]).
262    ///
263    /// [`DynamicStruct`]: crate::DynamicStruct
264    fn path<'p, T: Reflect>(&self, path: impl ReflectPath<'p>) -> PathResult<'p, &T> {
265        path.element(self.as_reflect())
266    }
267
268    /// Returns a statically typed mutable reference to the value specified by `path`.
269    ///
270    /// This will automatically handle downcasting to type `T`.
271    /// The downcast will fail if this value is not of type `T`
272    /// (which may be the case when using dynamic types like [`DynamicStruct`]).
273    ///
274    /// [`DynamicStruct`]: crate::DynamicStruct
275    fn path_mut<'p, T: Reflect>(&mut self, path: impl ReflectPath<'p>) -> PathResult<'p, &mut T> {
276        path.element_mut(self.as_reflect_mut())
277    }
278}
279
280// Implement `GetPath` for `dyn Reflect`
281impl<T: Reflect + ?Sized> GetPath for T {}
282
283/// An [`Access`] combined with an `offset` for more helpful error reporting.
284#[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)]
285pub struct OffsetAccess {
286    /// The [`Access`] itself.
287    pub access: Access<'static>,
288    /// A character offset in the string the path was parsed from.
289    pub offset: Option<usize>,
290}
291
292impl From<Access<'static>> for OffsetAccess {
293    fn from(access: Access<'static>) -> Self {
294        OffsetAccess {
295            access,
296            offset: None,
297        }
298    }
299}
300
301/// A pre-parsed path to an element within a type.
302///
303/// This struct can be constructed manually from its [`Access`]es or with
304/// the [parse](ParsedPath::parse) method.
305///
306/// This struct may be used like [`GetPath`] but removes the cost of parsing the path
307/// string at each element access.
308///
309/// It's recommended to use this in place of [`GetPath`] when the path string is
310/// unlikely to be changed and will be accessed repeatedly.
311///
312/// ## Examples
313///
314/// Parsing a [`&'static str`](str):
315/// ```
316/// # use bevy_reflect::ParsedPath;
317/// let my_static_string: &'static str = "bar#0.1[2].0";
318/// // Breakdown:
319/// //   "bar" - Access struct field named "bar"
320/// //   "#0" - Access struct field at index 0
321/// //   ".1" - Access tuple struct field at index 1
322/// //   "[2]" - Access list element at index 2
323/// //   ".0" - Access tuple variant field at index 0
324/// let my_path = ParsedPath::parse_static(my_static_string);
325/// ```
326/// Parsing a non-static [`&str`](str):
327/// ```
328/// # use bevy_reflect::ParsedPath;
329/// let my_string = String::from("bar#0.1[2].0");
330/// // Breakdown:
331/// //   "bar" - Access struct field named "bar"
332/// //   "#0" - Access struct field at index 0
333/// //   ".1" - Access tuple struct field at index 1
334/// //   "[2]" - Access list element at index 2
335/// //   ".0" - Access tuple variant field at index 0
336/// let my_path = ParsedPath::parse(&my_string);
337/// ```
338/// Manually constructing a [`ParsedPath`]:
339/// ```
340/// # use std::borrow::Cow;
341/// # use bevy_reflect::access::Access;
342/// # use bevy_reflect::ParsedPath;
343/// let path_elements = [
344///     Access::Field(Cow::Borrowed("bar")),
345///     Access::FieldIndex(0),
346///     Access::TupleIndex(1),
347///     Access::ListIndex(2),
348///     Access::TupleIndex(1),
349/// ];
350/// let my_path = ParsedPath::from(path_elements);
351/// ```
352///
353#[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)]
354pub struct ParsedPath(
355    /// This is a vector of pre-parsed [`OffsetAccess`]es.
356    pub Vec<OffsetAccess>,
357);
358
359impl ParsedPath {
360    /// Parses a [`ParsedPath`] from a string.
361    ///
362    /// Returns an error if the string does not represent a valid path to an element.
363    ///
364    /// The exact format for path strings can be found in the documentation for [`GetPath`].
365    /// In short, though, a path consists of one or more chained accessor strings.
366    /// These are:
367    /// - Named field access (`.field`)
368    /// - Unnamed field access (`.1`)
369    /// - Field index access (`#0`)
370    /// - Sequence access (`[2]`)
371    ///
372    /// # Example
373    /// ```
374    /// # use bevy_reflect::{ParsedPath, Reflect, ReflectPath};
375    /// #[derive(Reflect)]
376    /// struct Foo {
377    ///   bar: Bar,
378    /// }
379    ///
380    /// #[derive(Reflect)]
381    /// struct Bar {
382    ///   baz: Baz,
383    /// }
384    ///
385    /// #[derive(Reflect)]
386    /// struct Baz(f32, Vec<Option<u32>>);
387    ///
388    /// let foo = Foo {
389    ///   bar: Bar {
390    ///     baz: Baz(3.14, vec![None, None, Some(123)])
391    ///   },
392    /// };
393    ///
394    /// let parsed_path = ParsedPath::parse("bar#0.1[2].0").unwrap();
395    /// // Breakdown:
396    /// //   "bar" - Access struct field named "bar"
397    /// //   "#0" - Access struct field at index 0
398    /// //   ".1" - Access tuple struct field at index 1
399    /// //   "[2]" - Access list element at index 2
400    /// //   ".0" - Access tuple variant field at index 0
401    ///
402    /// assert_eq!(parsed_path.element::<u32>(&foo).unwrap(), &123);
403    /// ```
404    ///
405    pub fn parse(string: &str) -> PathResult<Self> {
406        let mut parts = Vec::new();
407        for (access, offset) in PathParser::new(string) {
408            parts.push(OffsetAccess {
409                access: access?.into_owned(),
410                offset: Some(offset),
411            });
412        }
413        Ok(Self(parts))
414    }
415
416    /// Similar to [`Self::parse`] but only works on `&'static str`
417    /// and does not allocate per named field.
418    pub fn parse_static(string: &'static str) -> PathResult<Self> {
419        let mut parts = Vec::new();
420        for (access, offset) in PathParser::new(string) {
421            parts.push(OffsetAccess {
422                access: access?,
423                offset: Some(offset),
424            });
425        }
426        Ok(Self(parts))
427    }
428}
429impl<'a> ReflectPath<'a> for &'a ParsedPath {
430    fn reflect_element(self, mut root: &dyn Reflect) -> PathResult<'a, &dyn Reflect> {
431        for OffsetAccess { access, offset } in &self.0 {
432            root = access.element(root, *offset)?;
433        }
434        Ok(root)
435    }
436    fn reflect_element_mut(self, mut root: &mut dyn Reflect) -> PathResult<'a, &mut dyn Reflect> {
437        for OffsetAccess { access, offset } in &self.0 {
438            root = access.element_mut(root, *offset)?;
439        }
440        Ok(root)
441    }
442}
443impl From<Vec<OffsetAccess>> for ParsedPath {
444    fn from(value: Vec<OffsetAccess>) -> Self {
445        ParsedPath(value)
446    }
447}
448impl<const N: usize> From<[OffsetAccess; N]> for ParsedPath {
449    fn from(value: [OffsetAccess; N]) -> Self {
450        ParsedPath(value.to_vec())
451    }
452}
453impl From<Vec<Access<'static>>> for ParsedPath {
454    fn from(value: Vec<Access<'static>>) -> Self {
455        ParsedPath(
456            value
457                .into_iter()
458                .map(|access| OffsetAccess {
459                    access,
460                    offset: None,
461                })
462                .collect(),
463        )
464    }
465}
466impl<const N: usize> From<[Access<'static>; N]> for ParsedPath {
467    fn from(value: [Access<'static>; N]) -> Self {
468        value.to_vec().into()
469    }
470}
471
472impl fmt::Display for ParsedPath {
473    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
474        for OffsetAccess { access, .. } in &self.0 {
475            write!(f, "{access}")?;
476        }
477        Ok(())
478    }
479}
480impl std::ops::Index<usize> for ParsedPath {
481    type Output = OffsetAccess;
482    fn index(&self, index: usize) -> &Self::Output {
483        &self.0[index]
484    }
485}
486impl std::ops::IndexMut<usize> for ParsedPath {
487    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
488        &mut self.0[index]
489    }
490}
491
492#[cfg(test)]
493#[allow(clippy::float_cmp, clippy::approx_constant)]
494mod tests {
495    use super::*;
496    use crate as bevy_reflect;
497    use crate::*;
498
499    #[derive(Reflect)]
500    struct A {
501        w: usize,
502        x: B,
503        y: Vec<C>,
504        z: D,
505        unit_variant: F,
506        tuple_variant: F,
507        struct_variant: F,
508        array: [i32; 3],
509        tuple: (bool, f32),
510    }
511
512    #[derive(Reflect)]
513    struct B {
514        foo: usize,
515        łørđ: C,
516    }
517
518    #[derive(Reflect)]
519    struct C {
520        mосква: f32,
521    }
522
523    #[derive(Reflect)]
524    struct D(E);
525
526    #[derive(Reflect)]
527    struct E(f32, usize);
528
529    #[derive(Reflect, PartialEq, Debug)]
530    enum F {
531        Unit,
532        Tuple(u32, u32),
533        Şķràźÿ { 東京: char },
534    }
535
536    fn a_sample() -> A {
537        A {
538            w: 1,
539            x: B {
540                foo: 10,
541                łørđ: C { mосква: 3.14 },
542            },
543            y: vec![C { mосква: 1.0 }, C { mосква: 2.0 }],
544            z: D(E(10.0, 42)),
545            unit_variant: F::Unit,
546            tuple_variant: F::Tuple(123, 321),
547            struct_variant: F::Şķràźÿ { 東京: 'm' },
548            array: [86, 75, 309],
549            tuple: (true, 1.23),
550        }
551    }
552
553    fn offset(access: Access<'static>, offset: usize) -> OffsetAccess {
554        OffsetAccess {
555            access,
556            offset: Some(offset),
557        }
558    }
559
560    fn access_field(field: &'static str) -> Access {
561        Access::Field(field.into())
562    }
563
564    type StaticError = ReflectPathError<'static>;
565
566    fn invalid_access(
567        offset: usize,
568        actual: ReflectKind,
569        expected: ReflectKind,
570        access: &'static str,
571    ) -> StaticError {
572        ReflectPathError::InvalidAccess(AccessError {
573            kind: AccessErrorKind::IncompatibleTypes { actual, expected },
574            access: ParsedPath::parse_static(access).unwrap()[1].access.clone(),
575            offset: Some(offset),
576        })
577    }
578
579    #[test]
580    fn parsed_path_parse() {
581        assert_eq!(
582            ParsedPath::parse("w").unwrap().0,
583            &[offset(access_field("w"), 1)]
584        );
585        assert_eq!(
586            ParsedPath::parse("x.foo").unwrap().0,
587            &[offset(access_field("x"), 1), offset(access_field("foo"), 2)]
588        );
589        assert_eq!(
590            ParsedPath::parse("x.łørđ.mосква").unwrap().0,
591            &[
592                offset(access_field("x"), 1),
593                offset(access_field("łørđ"), 2),
594                offset(access_field("mосква"), 10)
595            ]
596        );
597        assert_eq!(
598            ParsedPath::parse("y[1].mосква").unwrap().0,
599            &[
600                offset(access_field("y"), 1),
601                offset(Access::ListIndex(1), 2),
602                offset(access_field("mосква"), 5)
603            ]
604        );
605        assert_eq!(
606            ParsedPath::parse("z.0.1").unwrap().0,
607            &[
608                offset(access_field("z"), 1),
609                offset(Access::TupleIndex(0), 2),
610                offset(Access::TupleIndex(1), 4),
611            ]
612        );
613        assert_eq!(
614            ParsedPath::parse("x#0").unwrap().0,
615            &[
616                offset(access_field("x"), 1),
617                offset(Access::FieldIndex(0), 2)
618            ]
619        );
620        assert_eq!(
621            ParsedPath::parse("x#0#1").unwrap().0,
622            &[
623                offset(access_field("x"), 1),
624                offset(Access::FieldIndex(0), 2),
625                offset(Access::FieldIndex(1), 4)
626            ]
627        );
628    }
629
630    #[test]
631    fn parsed_path_get_field() {
632        let a = a_sample();
633
634        let b = ParsedPath::parse("w").unwrap();
635        let c = ParsedPath::parse("x.foo").unwrap();
636        let d = ParsedPath::parse("x.łørđ.mосква").unwrap();
637        let e = ParsedPath::parse("y[1].mосква").unwrap();
638        let f = ParsedPath::parse("z.0.1").unwrap();
639        let g = ParsedPath::parse("x#0").unwrap();
640        let h = ParsedPath::parse("x#1#0").unwrap();
641        let i = ParsedPath::parse("unit_variant").unwrap();
642        let j = ParsedPath::parse("tuple_variant.1").unwrap();
643        let k = ParsedPath::parse("struct_variant.東京").unwrap();
644        let l = ParsedPath::parse("struct_variant#0").unwrap();
645        let m = ParsedPath::parse("array[2]").unwrap();
646        let n = ParsedPath::parse("tuple.1").unwrap();
647
648        for _ in 0..30 {
649            assert_eq!(*b.element::<usize>(&a).unwrap(), 1);
650            assert_eq!(*c.element::<usize>(&a).unwrap(), 10);
651            assert_eq!(*d.element::<f32>(&a).unwrap(), 3.14);
652            assert_eq!(*e.element::<f32>(&a).unwrap(), 2.0);
653            assert_eq!(*f.element::<usize>(&a).unwrap(), 42);
654            assert_eq!(*g.element::<usize>(&a).unwrap(), 10);
655            assert_eq!(*h.element::<f32>(&a).unwrap(), 3.14);
656            assert_eq!(*i.element::<F>(&a).unwrap(), F::Unit);
657            assert_eq!(*j.element::<u32>(&a).unwrap(), 321);
658            assert_eq!(*k.element::<char>(&a).unwrap(), 'm');
659            assert_eq!(*l.element::<char>(&a).unwrap(), 'm');
660            assert_eq!(*m.element::<i32>(&a).unwrap(), 309);
661            assert_eq!(*n.element::<f32>(&a).unwrap(), 1.23);
662        }
663    }
664
665    #[test]
666    fn reflect_array_behaves_like_list() {
667        #[derive(Reflect)]
668        struct A {
669            list: Vec<u8>,
670            array: [u8; 10],
671        }
672
673        let a = A {
674            list: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
675            array: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
676        };
677
678        assert_eq!(*a.path::<u8>("list[5]").unwrap(), 5);
679        assert_eq!(*a.path::<u8>("array[5]").unwrap(), 5);
680        assert_eq!(*a.path::<u8>("list[0]").unwrap(), 0);
681        assert_eq!(*a.path::<u8>("array[0]").unwrap(), 0);
682    }
683
684    #[test]
685    fn reflect_array_behaves_like_list_mut() {
686        #[derive(Reflect)]
687        struct A {
688            list: Vec<u8>,
689            array: [u8; 10],
690        }
691
692        let mut a = A {
693            list: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
694            array: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
695        };
696
697        assert_eq!(*a.path_mut::<u8>("list[5]").unwrap(), 5);
698        assert_eq!(*a.path_mut::<u8>("array[5]").unwrap(), 5);
699
700        *a.path_mut::<u8>("list[5]").unwrap() = 10;
701        *a.path_mut::<u8>("array[5]").unwrap() = 10;
702
703        assert_eq!(*a.path_mut::<u8>("list[5]").unwrap(), 10);
704        assert_eq!(*a.path_mut::<u8>("array[5]").unwrap(), 10);
705    }
706
707    #[test]
708    fn reflect_path() {
709        let mut a = a_sample();
710
711        assert_eq!(*a.path::<usize>("w").unwrap(), 1);
712        assert_eq!(*a.path::<usize>("x.foo").unwrap(), 10);
713        assert_eq!(*a.path::<f32>("x.łørđ.mосква").unwrap(), 3.14);
714        assert_eq!(*a.path::<f32>("y[1].mосква").unwrap(), 2.0);
715        assert_eq!(*a.path::<usize>("z.0.1").unwrap(), 42);
716        assert_eq!(*a.path::<usize>("x#0").unwrap(), 10);
717        assert_eq!(*a.path::<f32>("x#1#0").unwrap(), 3.14);
718
719        assert_eq!(*a.path::<F>("unit_variant").unwrap(), F::Unit);
720        assert_eq!(*a.path::<u32>("tuple_variant.1").unwrap(), 321);
721        assert_eq!(*a.path::<char>("struct_variant.東京").unwrap(), 'm');
722        assert_eq!(*a.path::<char>("struct_variant#0").unwrap(), 'm');
723
724        assert_eq!(*a.path::<i32>("array[2]").unwrap(), 309);
725
726        assert_eq!(*a.path::<f32>("tuple.1").unwrap(), 1.23);
727        *a.path_mut::<f32>("tuple.1").unwrap() = 3.21;
728        assert_eq!(*a.path::<f32>("tuple.1").unwrap(), 3.21);
729
730        *a.path_mut::<f32>("y[1].mосква").unwrap() = 3.0;
731        assert_eq!(a.y[1].mосква, 3.0);
732
733        *a.path_mut::<u32>("tuple_variant.0").unwrap() = 1337;
734        assert_eq!(a.tuple_variant, F::Tuple(1337, 321));
735
736        assert_eq!(
737            a.reflect_path("x.notreal").err().unwrap(),
738            ReflectPathError::InvalidAccess(AccessError {
739                kind: AccessErrorKind::MissingField(ReflectKind::Struct),
740                access: access_field("notreal"),
741                offset: Some(2),
742            })
743        );
744
745        assert_eq!(
746            a.reflect_path("unit_variant.0").err().unwrap(),
747            ReflectPathError::InvalidAccess(AccessError {
748                kind: AccessErrorKind::IncompatibleEnumVariantTypes {
749                    actual: VariantType::Unit,
750                    expected: VariantType::Tuple,
751                },
752                access: ParsedPath::parse_static("unit_variant.0").unwrap()[1]
753                    .access
754                    .clone(),
755                offset: Some(13),
756            })
757        );
758        assert_eq!(
759            a.reflect_path("x[0]").err().unwrap(),
760            invalid_access(2, ReflectKind::Struct, ReflectKind::List, "x[0]")
761        );
762        assert_eq!(
763            a.reflect_path("y.x").err().unwrap(),
764            invalid_access(2, ReflectKind::List, ReflectKind::Struct, "y.x")
765        );
766    }
767
768    #[test]
769    fn accept_leading_tokens() {
770        assert_eq!(
771            ParsedPath::parse(".w").unwrap().0,
772            &[offset(access_field("w"), 1)]
773        );
774        assert_eq!(
775            ParsedPath::parse("#0.foo").unwrap().0,
776            &[
777                offset(Access::FieldIndex(0), 1),
778                offset(access_field("foo"), 3)
779            ]
780        );
781        assert_eq!(
782            ParsedPath::parse(".5").unwrap().0,
783            &[offset(Access::TupleIndex(5), 1)]
784        );
785        assert_eq!(
786            ParsedPath::parse("[0].łørđ").unwrap().0,
787            &[
788                offset(Access::ListIndex(0), 1),
789                offset(access_field("łørđ"), 4)
790            ]
791        );
792    }
793}