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