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}