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