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