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