Skip to main content

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