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}
85impl<'a> ReflectPath<'a> for &'a str {
86    fn reflect_element(self, mut root: &dyn PartialReflect) -> PathResult<'a, &dyn PartialReflect> {
87        for (access, offset) in PathParser::new(self) {
88            let a = access?;
89            root = a.element(root, Some(offset))?;
90        }
91        Ok(root)
92    }
93    fn reflect_element_mut(
94        self,
95        mut root: &mut dyn PartialReflect,
96    ) -> PathResult<'a, &mut dyn PartialReflect> {
97        for (access, offset) in PathParser::new(self) {
98            root = access?.element_mut(root, Some(offset))?;
99        }
100        Ok(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/// # Syntax
109///
110/// ## Structs
111///
112/// Field paths for [`Struct`] elements use the standard Rust field access syntax of
113/// 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 removed
119/// need for string matching.
120/// And while this can be more performant, it's best to keep in mind the tradeoffs when
121/// utilizing such optimizations.
122/// For example, this can result in fairly fragile code as the string paths will need to be
123/// 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/// ### Example
133/// ```
134/// # use bevy_reflect::{GetPath, Reflect};
135/// #[derive(Reflect, PartialEq, Debug)]
136/// struct MyStruct {
137///   value: u32
138/// }
139///
140/// let my_struct = MyStruct { value: 123 };
141/// // Access via field name
142/// assert_eq!(my_struct.path::<u32>(".value").unwrap(), &123);
143/// // Access via field index
144/// assert_eq!(my_struct.path::<u32>("#0").unwrap(), &123);
145/// // Access self
146/// assert_eq!(*my_struct.path::<MyStruct>("").unwrap(), my_struct);
147/// ```
148///
149/// ## Tuples and Tuple Structs
150///
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/// ### Example
158/// ```
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 Arrays
168///
169/// [`List`] and [`Array`] elements are accessed with brackets: `[0]`.
170///
171/// ### Example
172/// ```
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/// ## Enums
179///
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 access
189///
190/// If the variant cannot be known ahead of time, the path will need to be split up
191/// and proper enum pattern matching will need to be handled manually.
192///
193/// ### Example
194/// ```
195/// # use bevy_reflect::{GetPath, Reflect};
196/// #[derive(Reflect)]
197/// enum MyEnum {
198///   Unit,
199///   Tuple(bool),
200///   Struct {
201///     value: u32
202///   }
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 name
210/// assert_eq!(struct_variant.path::<u32>(".value").unwrap(), &123);
211/// // Access via field index
212/// assert_eq!(struct_variant.path::<u32>("#0").unwrap(), &123);
213///
214/// // Error: Expected struct variant
215/// assert!(matches!(tuple_variant.path::<u32>(".value"), Err(_)));
216/// ```
217///
218/// # Chaining
219///
220/// Using the aforementioned syntax, path items may be chained one after another
221/// to create a full path to a nested element.
222///
223/// ## Example
224/// ```
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::Struct
241/// [`Tuple`]: crate::Tuple
242/// [`TupleStruct`]: crate::TupleStruct
243/// [`List`]: crate::List
244/// [`Array`]: crate::Array
245/// [`Enum`]: crate::Enum
246#[diagnostic::on_unimplemented(
247    message = "`{Self}` does not implement `GetPath` so cannot be accessed by reflection path",
248    note = "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, use
254    /// [`path`][GetPath::path].
255    fn reflect_path<'p>(&self, path: impl ReflectPath<'p>) -> PathResult<'p, &dyn PartialReflect> {
256        path.reflect_element(self.as_partial_reflect())
257    }
258
259    /// Returns a mutable reference to the value specified by `path`.
260    ///
261    /// To retrieve a statically typed mutable reference, use
262    /// [`path_mut`][GetPath::path_mut].
263    fn reflect_path_mut<'p>(
264        &mut self,
265        path: impl ReflectPath<'p>,
266    ) -> PathResult<'p, &mut dyn PartialReflect> {
267        path.reflect_element_mut(self.as_partial_reflect_mut())
268    }
269
270    /// 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::DynamicStruct
277    fn path<'p, T: Reflect>(&self, path: impl ReflectPath<'p>) -> PathResult<'p, &T> {
278        path.element(self.as_partial_reflect())
279    }
280
281    /// 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::DynamicStruct
288    fn path_mut<'p, T: Reflect>(&mut self, path: impl ReflectPath<'p>) -> PathResult<'p, &mut T> {
289        path.element_mut(self.as_partial_reflect_mut())
290    }
291}
292
293// Implement `GetPath` for `dyn Reflect`
294impl<T: Reflect + ?Sized> GetPath for T {}
295
296/// 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.
300    pub access: Access<'static>,
301    /// A character offset in the string the path was parsed from.
302    pub offset: Option<usize>,
303}
304
305impl From<Access<'static>> for OffsetAccess {
306    fn from(access: Access<'static>) -> Self {
307        OffsetAccess {
308            access,
309            offset: None,
310        }
311    }
312}
313
314/// A pre-parsed path to an element within a type.
315///
316/// This struct can be constructed manually from its [`Access`]es or with
317/// the [parse](ParsedPath::parse) method.
318///
319/// This struct may be used like [`GetPath`] but removes the cost of parsing the path
320/// string at each element access.
321///
322/// It's recommended to use this in place of [`GetPath`] when the path string is
323/// unlikely to be changed and will be accessed repeatedly.
324///
325/// ## Examples
326///
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 0
334/// //   ".1" - Access tuple struct field at index 1
335/// //   "[2]" - Access list element at index 2
336/// //   ".0" - Access tuple variant field at index 0
337/// 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 0
346/// //   ".1" - Access tuple struct field at index 1
347/// //   "[2]" - Access list element at index 2
348/// //   ".0" - Access tuple variant field at index 0
349/// 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.
368    pub Vec<OffsetAccess>,
369);
370
371impl 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    /// # Example
385    /// ```
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 0
410    /// //   ".1" - Access tuple struct field at index 1
411    /// //   "[2]" - Access list element at index 2
412    /// //   ".0" - Access tuple variant field at index 0
413    ///
414    /// assert_eq!(parsed_path.element::<u32>(&foo).unwrap(), &123);
415    /// ```
416    pub fn parse(string: &str) -> PathResult<Self> {
417        let mut parts = Vec::new();
418        for (access, offset) in PathParser::new(string) {
419            parts.push(OffsetAccess {
420                access: access?.into_owned(),
421                offset: Some(offset),
422            });
423        }
424        Ok(Self(parts))
425    }
426
427    /// Similar to [`Self::parse`] but only works on `&'static str`
428    /// and does not allocate per named field.
429    pub fn parse_static(string: &'static str) -> PathResult<'static, Self> {
430        let mut parts = Vec::new();
431        for (access, offset) in PathParser::new(string) {
432            parts.push(OffsetAccess {
433                access: access?,
434                offset: Some(offset),
435            });
436        }
437        Ok(Self(parts))
438    }
439}
440impl<'a> ReflectPath<'a> for &'a ParsedPath {
441    fn reflect_element(self, mut root: &dyn PartialReflect) -> PathResult<'a, &dyn PartialReflect> {
442        for OffsetAccess { access, offset } in &self.0 {
443            root = access.element(root, *offset)?;
444        }
445        Ok(root)
446    }
447    fn reflect_element_mut(
448        self,
449        mut root: &mut dyn PartialReflect,
450    ) -> PathResult<'a, &mut dyn PartialReflect> {
451        for OffsetAccess { access, offset } in &self.0 {
452            root = access.element_mut(root, *offset)?;
453        }
454        Ok(root)
455    }
456}
457impl<const N: usize> From<[OffsetAccess; N]> for ParsedPath {
458    fn from(value: [OffsetAccess; N]) -> Self {
459        ParsedPath(value.to_vec())
460    }
461}
462impl From<Vec<Access<'static>>> for ParsedPath {
463    fn from(value: Vec<Access<'static>>) -> Self {
464        ParsedPath(
465            value
466                .into_iter()
467                .map(|access| OffsetAccess {
468                    access,
469                    offset: None,
470                })
471                .collect(),
472        )
473    }
474}
475impl<const N: usize> From<[Access<'static>; N]> for ParsedPath {
476    fn from(value: [Access<'static>; N]) -> Self {
477        value.to_vec().into()
478    }
479}
480
481impl<'a> TryFrom<&'a str> for ParsedPath {
482    type Error = ReflectPathError<'a>;
483    fn try_from(value: &'a str) -> Result<Self, Self::Error> {
484        ParsedPath::parse(value)
485    }
486}
487
488impl fmt::Display for ParsedPath {
489    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
490        for OffsetAccess { access, .. } in &self.0 {
491            write!(f, "{access}")?;
492        }
493        Ok(())
494    }
495}
496impl core::ops::Index<usize> for ParsedPath {
497    type Output = OffsetAccess;
498    fn index(&self, index: usize) -> &Self::Output {
499        &self.0[index]
500    }
501}
502impl core::ops::IndexMut<usize> for ParsedPath {
503    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
504        &mut self.0[index]
505    }
506}
507
508#[cfg(test)]
509#[expect(
510    clippy::approx_constant,
511    reason = "We don't need the exact value of Pi here."
512)]
513mod tests {
514    use super::*;
515    use crate::*;
516    use alloc::vec;
517
518    #[derive(Reflect, PartialEq, Debug)]
519    struct A {
520        w: usize,
521        x: B,
522        y: Vec<C>,
523        z: D,
524        unit_variant: F,
525        tuple_variant: F,
526        struct_variant: F,
527        array: [i32; 3],
528        tuple: (bool, f32),
529    }
530
531    #[derive(Reflect, PartialEq, Debug)]
532    struct B {
533        foo: usize,
534        łørđ: C,
535    }
536
537    #[derive(Reflect, PartialEq, Debug)]
538    struct C {
539        mосква: f32,
540    }
541
542    #[derive(Reflect, PartialEq, Debug)]
543    struct D(E);
544
545    #[derive(Reflect, PartialEq, Debug)]
546    struct E(f32, usize);
547
548    #[derive(Reflect, PartialEq, Debug)]
549    enum F {
550        Unit,
551        Tuple(u32, u32),
552        Şķràźÿ { 東京: char },
553    }
554
555    fn a_sample() -> A {
556        A {
557            w: 1,
558            x: B {
559                foo: 10,
560                łørđ: C { mосква: 3.14 },
561            },
562            y: vec![C { mосква: 1.0 }, C { mосква: 2.0 }],
563            z: D(E(10.0, 42)),
564            unit_variant: F::Unit,
565            tuple_variant: F::Tuple(123, 321),
566            struct_variant: F::Şķràźÿ { 東京: 'm' },
567            array: [86, 75, 309],
568            tuple: (true, 1.23),
569        }
570    }
571
572    fn offset(access: Access<'static>, offset: usize) -> OffsetAccess {
573        OffsetAccess {
574            access,
575            offset: Some(offset),
576        }
577    }
578
579    fn access_field(field: &'static str) -> Access<'static> {
580        Access::Field(field.into())
581    }
582
583    type StaticError = ReflectPathError<'static>;
584
585    fn invalid_access(
586        offset: usize,
587        actual: ReflectKind,
588        expected: ReflectKind,
589        access: &'static str,
590    ) -> StaticError {
591        ReflectPathError::InvalidAccess(AccessError {
592            kind: AccessErrorKind::IncompatibleTypes { actual, expected },
593            access: ParsedPath::parse_static(access).unwrap()[1].access.clone(),
594            offset: Some(offset),
595        })
596    }
597
598    #[test]
599    fn try_from() {
600        assert_eq!(
601            ParsedPath::try_from("w").unwrap().0,
602            &[offset(access_field("w"), 1)]
603        );
604
605        let r = ParsedPath::try_from("w[");
606        let matches = matches!(r, Err(ReflectPathError::ParseError { .. }));
607        assert!(
608            matches,
609            "ParsedPath::try_from did not return a ParseError for \"w[\""
610        );
611    }
612
613    #[test]
614    fn parsed_path_parse() {
615        assert_eq!(
616            ParsedPath::parse("w").unwrap().0,
617            &[offset(access_field("w"), 1)]
618        );
619        assert_eq!(
620            ParsedPath::parse("x.foo").unwrap().0,
621            &[offset(access_field("x"), 1), offset(access_field("foo"), 2)]
622        );
623        assert_eq!(
624            ParsedPath::parse("x.łørđ.mосква").unwrap().0,
625            &[
626                offset(access_field("x"), 1),
627                offset(access_field("łørđ"), 2),
628                offset(access_field("mосква"), 10)
629            ]
630        );
631        assert_eq!(
632            ParsedPath::parse("y[1].mосква").unwrap().0,
633            &[
634                offset(access_field("y"), 1),
635                offset(Access::ListIndex(1), 2),
636                offset(access_field("mосква"), 5)
637            ]
638        );
639        assert_eq!(
640            ParsedPath::parse("z.0.1").unwrap().0,
641            &[
642                offset(access_field("z"), 1),
643                offset(Access::TupleIndex(0), 2),
644                offset(Access::TupleIndex(1), 4),
645            ]
646        );
647        assert_eq!(
648            ParsedPath::parse("x#0").unwrap().0,
649            &[
650                offset(access_field("x"), 1),
651                offset(Access::FieldIndex(0), 2)
652            ]
653        );
654        assert_eq!(
655            ParsedPath::parse("x#0#1").unwrap().0,
656            &[
657                offset(access_field("x"), 1),
658                offset(Access::FieldIndex(0), 2),
659                offset(Access::FieldIndex(1), 4)
660            ]
661        );
662    }
663
664    #[test]
665    fn parsed_path_get_field() {
666        let a = a_sample();
667
668        let b = ParsedPath::parse("w").unwrap();
669        let c = ParsedPath::parse("x.foo").unwrap();
670        let d = ParsedPath::parse("x.łørđ.mосква").unwrap();
671        let e = ParsedPath::parse("y[1].mосква").unwrap();
672        let f = ParsedPath::parse("z.0.1").unwrap();
673        let g = ParsedPath::parse("x#0").unwrap();
674        let h = ParsedPath::parse("x#1#0").unwrap();
675        let i = ParsedPath::parse("unit_variant").unwrap();
676        let j = ParsedPath::parse("tuple_variant.1").unwrap();
677        let k = ParsedPath::parse("struct_variant.東京").unwrap();
678        let l = ParsedPath::parse("struct_variant#0").unwrap();
679        let m = ParsedPath::parse("array[2]").unwrap();
680        let n = ParsedPath::parse("tuple.1").unwrap();
681
682        for _ in 0..30 {
683            assert_eq!(*b.element::<usize>(&a).unwrap(), 1);
684            assert_eq!(*c.element::<usize>(&a).unwrap(), 10);
685            assert_eq!(*d.element::<f32>(&a).unwrap(), 3.14);
686            assert_eq!(*e.element::<f32>(&a).unwrap(), 2.0);
687            assert_eq!(*f.element::<usize>(&a).unwrap(), 42);
688            assert_eq!(*g.element::<usize>(&a).unwrap(), 10);
689            assert_eq!(*h.element::<f32>(&a).unwrap(), 3.14);
690            assert_eq!(*i.element::<F>(&a).unwrap(), F::Unit);
691            assert_eq!(*j.element::<u32>(&a).unwrap(), 321);
692            assert_eq!(*k.element::<char>(&a).unwrap(), 'm');
693            assert_eq!(*l.element::<char>(&a).unwrap(), 'm');
694            assert_eq!(*m.element::<i32>(&a).unwrap(), 309);
695            assert_eq!(*n.element::<f32>(&a).unwrap(), 1.23);
696        }
697    }
698
699    #[test]
700    fn reflect_array_behaves_like_list() {
701        #[derive(Reflect)]
702        struct A {
703            list: Vec<u8>,
704            array: [u8; 10],
705        }
706
707        let a = A {
708            list: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
709            array: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
710        };
711
712        assert_eq!(*a.path::<u8>("list[5]").unwrap(), 5);
713        assert_eq!(*a.path::<u8>("array[5]").unwrap(), 5);
714        assert_eq!(*a.path::<u8>("list[0]").unwrap(), 0);
715        assert_eq!(*a.path::<u8>("array[0]").unwrap(), 0);
716    }
717
718    #[test]
719    fn reflect_array_behaves_like_list_mut() {
720        #[derive(Reflect)]
721        struct A {
722            list: Vec<u8>,
723            array: [u8; 10],
724        }
725
726        let mut a = A {
727            list: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
728            array: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
729        };
730
731        assert_eq!(*a.path_mut::<u8>("list[5]").unwrap(), 5);
732        assert_eq!(*a.path_mut::<u8>("array[5]").unwrap(), 5);
733
734        *a.path_mut::<u8>("list[5]").unwrap() = 10;
735        *a.path_mut::<u8>("array[5]").unwrap() = 10;
736
737        assert_eq!(*a.path_mut::<u8>("list[5]").unwrap(), 10);
738        assert_eq!(*a.path_mut::<u8>("array[5]").unwrap(), 10);
739    }
740
741    #[test]
742    fn reflect_path() {
743        let mut a = a_sample();
744
745        assert_eq!(*a.path::<A>("").unwrap(), a);
746        assert_eq!(*a.path::<usize>("w").unwrap(), 1);
747        assert_eq!(*a.path::<usize>("x.foo").unwrap(), 10);
748        assert_eq!(*a.path::<f32>("x.łørđ.mосква").unwrap(), 3.14);
749        assert_eq!(*a.path::<f32>("y[1].mосква").unwrap(), 2.0);
750        assert_eq!(*a.path::<usize>("z.0.1").unwrap(), 42);
751        assert_eq!(*a.path::<usize>("x#0").unwrap(), 10);
752        assert_eq!(*a.path::<f32>("x#1#0").unwrap(), 3.14);
753
754        assert_eq!(*a.path::<F>("unit_variant").unwrap(), F::Unit);
755        assert_eq!(*a.path::<u32>("tuple_variant.1").unwrap(), 321);
756        assert_eq!(*a.path::<char>("struct_variant.東京").unwrap(), 'm');
757        assert_eq!(*a.path::<char>("struct_variant#0").unwrap(), 'm');
758
759        assert_eq!(*a.path::<i32>("array[2]").unwrap(), 309);
760
761        assert_eq!(*a.path::<f32>("tuple.1").unwrap(), 1.23);
762        *a.path_mut::<f32>("tuple.1").unwrap() = 3.21;
763        assert_eq!(*a.path::<f32>("tuple.1").unwrap(), 3.21);
764
765        *a.path_mut::<f32>("y[1].mосква").unwrap() = 3.0;
766        assert_eq!(a.y[1].mосква, 3.0);
767
768        *a.path_mut::<u32>("tuple_variant.0").unwrap() = 1337;
769        assert_eq!(a.tuple_variant, F::Tuple(1337, 321));
770
771        assert_eq!(
772            a.reflect_path("x.notreal").err().unwrap(),
773            ReflectPathError::InvalidAccess(AccessError {
774                kind: AccessErrorKind::MissingField(ReflectKind::Struct),
775                access: access_field("notreal"),
776                offset: Some(2),
777            })
778        );
779
780        assert_eq!(
781            a.reflect_path("unit_variant.0").err().unwrap(),
782            ReflectPathError::InvalidAccess(AccessError {
783                kind: AccessErrorKind::IncompatibleEnumVariantTypes {
784                    actual: VariantType::Unit,
785                    expected: VariantType::Tuple,
786                },
787                access: ParsedPath::parse_static("unit_variant.0").unwrap()[1]
788                    .access
789                    .clone(),
790                offset: Some(13),
791            })
792        );
793        assert_eq!(
794            a.reflect_path("x[0]").err().unwrap(),
795            invalid_access(2, ReflectKind::Struct, ReflectKind::List, "x[0]")
796        );
797        assert_eq!(
798            a.reflect_path("y.x").err().unwrap(),
799            invalid_access(2, ReflectKind::List, ReflectKind::Struct, "y.x")
800        );
801    }
802
803    #[test]
804    fn accept_leading_tokens() {
805        assert_eq!(
806            ParsedPath::parse(".w").unwrap().0,
807            &[offset(access_field("w"), 1)]
808        );
809        assert_eq!(
810            ParsedPath::parse("#0.foo").unwrap().0,
811            &[
812                offset(Access::FieldIndex(0), 1),
813                offset(access_field("foo"), 3)
814            ]
815        );
816        assert_eq!(
817            ParsedPath::parse(".5").unwrap().0,
818            &[offset(Access::TupleIndex(5), 1)]
819        );
820        assert_eq!(
821            ParsedPath::parse("[0].łørđ").unwrap().0,
822            &[
823                offset(Access::ListIndex(0), 1),
824                offset(access_field("łørđ"), 4)
825            ]
826        );
827    }
828}