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#[derive(Error, Debug, PartialEq, Eq)]
21pub enum ReflectPathError<'a> {
22 #[error(transparent)]
25 InvalidAccess(AccessError<'a>),
26
27 #[error("Can't downcast result of access to the given type")]
29 InvalidDowncast,
30
31 #[error("Encountered an error at offset {offset} while parsing `{path}`: {error}")]
33 ParseError {
34 offset: usize,
36 path: &'a str,
38 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
49pub trait ReflectPath<'a>: Sized {
51 fn reflect_element(self, root: &dyn PartialReflect) -> PathResult<'a, &dyn PartialReflect>;
56
57 fn reflect_element_mut(
61 self,
62 root: &mut dyn PartialReflect,
63 ) -> PathResult<'a, &mut dyn PartialReflect>;
64
65 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 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#[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 fn reflect_path<'p>(&self, path: impl ReflectPath<'p>) -> PathResult<'p, &dyn PartialReflect> {
257 path.reflect_element(self.as_partial_reflect())
258 }
259
260 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 fn path<'p, T: Reflect>(&self, path: impl ReflectPath<'p>) -> PathResult<'p, &T> {
279 path.element(self.as_partial_reflect())
280 }
281
282 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
294impl<T: Reflect + ?Sized> GetPath for T {}
296
297#[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash, Reflect)]
299#[reflect(Clone, Debug, PartialEq, PartialOrd, Hash)]
300pub struct OffsetAccess {
301 pub access: Access<'static>,
303 pub offset: Option<usize>,
305}
306
307impl From<Access<'static>> for OffsetAccess {
308 fn from(access: Access<'static>) -> Self {
309 OffsetAccess {
310 access,
311 offset: None,
312 }
313 }
314}
315
316#[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash, From, Reflect)]
368#[reflect(Clone, Debug, PartialEq, PartialOrd, Hash)]
369pub struct ParsedPath(
370 pub Vec<OffsetAccess>,
372);
373
374impl ParsedPath {
375 pub fn parse(string: &str) -> PathResult<'_, Self> {
420 let mut parts = Vec::new();
421 for (access, offset) in PathParser::new(string) {
422 parts.push(OffsetAccess {
423 access: access?.into_owned(),
424 offset: Some(offset),
425 });
426 }
427 Ok(Self(parts))
428 }
429
430 pub fn parse_static(string: &'static str) -> PathResult<'static, Self> {
433 let mut parts = Vec::new();
434 for (access, offset) in PathParser::new(string) {
435 parts.push(OffsetAccess {
436 access: access?,
437 offset: Some(offset),
438 });
439 }
440 Ok(Self(parts))
441 }
442}
443
444impl<'a> ReflectPath<'a> for &'a ParsedPath {
445 fn reflect_element(self, mut root: &dyn PartialReflect) -> PathResult<'a, &dyn PartialReflect> {
446 for OffsetAccess { access, offset } in &self.0 {
447 root = access.element(root, *offset)?;
448 }
449 Ok(root)
450 }
451 fn reflect_element_mut(
452 self,
453 mut root: &mut dyn PartialReflect,
454 ) -> PathResult<'a, &mut dyn PartialReflect> {
455 for OffsetAccess { access, offset } in &self.0 {
456 root = access.element_mut(root, *offset)?;
457 }
458 Ok(root)
459 }
460}
461
462impl<const N: usize> From<[OffsetAccess; N]> for ParsedPath {
463 fn from(value: [OffsetAccess; N]) -> Self {
464 ParsedPath(value.to_vec())
465 }
466}
467
468impl From<Vec<Access<'static>>> for ParsedPath {
469 fn from(value: Vec<Access<'static>>) -> Self {
470 ParsedPath(
471 value
472 .into_iter()
473 .map(|access| OffsetAccess {
474 access,
475 offset: None,
476 })
477 .collect(),
478 )
479 }
480}
481
482impl<const N: usize> From<[Access<'static>; N]> for ParsedPath {
483 fn from(value: [Access<'static>; N]) -> Self {
484 value.to_vec().into()
485 }
486}
487
488impl<'a> TryFrom<&'a str> for ParsedPath {
489 type Error = ReflectPathError<'a>;
490 fn try_from(value: &'a str) -> Result<Self, Self::Error> {
491 ParsedPath::parse(value)
492 }
493}
494
495impl fmt::Display for ParsedPath {
496 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
497 for OffsetAccess { access, .. } in &self.0 {
498 write!(f, "{access}")?;
499 }
500 Ok(())
501 }
502}
503
504impl core::ops::Index<usize> for ParsedPath {
505 type Output = OffsetAccess;
506 fn index(&self, index: usize) -> &Self::Output {
507 &self.0[index]
508 }
509}
510
511impl core::ops::IndexMut<usize> for ParsedPath {
512 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
513 &mut self.0[index]
514 }
515}
516
517#[cfg(test)]
518#[expect(
519 clippy::approx_constant,
520 reason = "We don't need the exact value of Pi here."
521)]
522mod tests {
523 use super::*;
524 use crate::{enums::VariantType, *};
525 use alloc::vec;
526
527 #[derive(Reflect, PartialEq, Debug)]
528 struct A {
529 w: usize,
530 x: B,
531 y: Vec<C>,
532 z: D,
533 unit_variant: F,
534 tuple_variant: F,
535 struct_variant: F,
536 array: [i32; 3],
537 tuple: (bool, f32),
538 }
539
540 #[derive(Reflect, PartialEq, Debug)]
541 struct B {
542 foo: usize,
543 łørđ: C,
544 }
545
546 #[derive(Reflect, PartialEq, Debug)]
547 struct C {
548 mосква: f32,
549 }
550
551 #[derive(Reflect, PartialEq, Debug)]
552 struct D(E);
553
554 #[derive(Reflect, PartialEq, Debug)]
555 struct E(f32, usize);
556
557 #[derive(Reflect, PartialEq, Debug)]
558 enum F {
559 Unit,
560 Tuple(u32, u32),
561 Şķràźÿ { 東京: char },
562 }
563
564 fn a_sample() -> A {
565 A {
566 w: 1,
567 x: B {
568 foo: 10,
569 łørđ: C { mосква: 3.14 },
570 },
571 y: vec![C { mосква: 1.0 }, C { mосква: 2.0 }],
572 z: D(E(10.0, 42)),
573 unit_variant: F::Unit,
574 tuple_variant: F::Tuple(123, 321),
575 struct_variant: F::Şķràźÿ { 東京: 'm' },
576 array: [86, 75, 309],
577 tuple: (true, 1.23),
578 }
579 }
580
581 fn offset(access: Access<'static>, offset: usize) -> OffsetAccess {
582 OffsetAccess {
583 access,
584 offset: Some(offset),
585 }
586 }
587
588 fn access_field(field: &'static str) -> Access<'static> {
589 Access::Field(field.into())
590 }
591
592 type StaticError = ReflectPathError<'static>;
593
594 fn invalid_access(
595 offset: usize,
596 actual: ReflectKind,
597 expected: ReflectKind,
598 access: &'static str,
599 ) -> StaticError {
600 ReflectPathError::InvalidAccess(AccessError {
601 kind: AccessErrorKind::IncompatibleTypes { actual, expected },
602 access: ParsedPath::parse_static(access).unwrap()[1].access.clone(),
603 offset: Some(offset),
604 })
605 }
606
607 #[test]
608 fn try_from() {
609 assert_eq!(
610 ParsedPath::try_from("w").unwrap().0,
611 &[offset(access_field("w"), 1)]
612 );
613
614 let r = ParsedPath::try_from("w[");
615 let matches = matches!(r, Err(ReflectPathError::ParseError { .. }));
616 assert!(
617 matches,
618 "ParsedPath::try_from did not return a ParseError for \"w[\""
619 );
620 }
621
622 #[test]
623 fn parsed_path_parse() {
624 assert_eq!(
625 ParsedPath::parse("w").unwrap().0,
626 &[offset(access_field("w"), 1)]
627 );
628 assert_eq!(
629 ParsedPath::parse("x.foo").unwrap().0,
630 &[offset(access_field("x"), 1), offset(access_field("foo"), 2)]
631 );
632 assert_eq!(
633 ParsedPath::parse("x.łørđ.mосква").unwrap().0,
634 &[
635 offset(access_field("x"), 1),
636 offset(access_field("łørđ"), 2),
637 offset(access_field("mосква"), 10)
638 ]
639 );
640 assert_eq!(
641 ParsedPath::parse("y[1].mосква").unwrap().0,
642 &[
643 offset(access_field("y"), 1),
644 offset(Access::ListIndex(1), 2),
645 offset(access_field("mосква"), 5)
646 ]
647 );
648 assert_eq!(
649 ParsedPath::parse("z.0.1").unwrap().0,
650 &[
651 offset(access_field("z"), 1),
652 offset(Access::TupleIndex(0), 2),
653 offset(Access::TupleIndex(1), 4),
654 ]
655 );
656 assert_eq!(
657 ParsedPath::parse("x#0").unwrap().0,
658 &[
659 offset(access_field("x"), 1),
660 offset(Access::FieldIndex(0), 2)
661 ]
662 );
663 assert_eq!(
664 ParsedPath::parse("x#0#1").unwrap().0,
665 &[
666 offset(access_field("x"), 1),
667 offset(Access::FieldIndex(0), 2),
668 offset(Access::FieldIndex(1), 4)
669 ]
670 );
671 }
672
673 #[test]
674 fn parsed_path_get_field() {
675 let a = a_sample();
676
677 let b = ParsedPath::parse("w").unwrap();
678 let c = ParsedPath::parse("x.foo").unwrap();
679 let d = ParsedPath::parse("x.łørđ.mосква").unwrap();
680 let e = ParsedPath::parse("y[1].mосква").unwrap();
681 let f = ParsedPath::parse("z.0.1").unwrap();
682 let g = ParsedPath::parse("x#0").unwrap();
683 let h = ParsedPath::parse("x#1#0").unwrap();
684 let i = ParsedPath::parse("unit_variant").unwrap();
685 let j = ParsedPath::parse("tuple_variant.1").unwrap();
686 let k = ParsedPath::parse("struct_variant.東京").unwrap();
687 let l = ParsedPath::parse("struct_variant#0").unwrap();
688 let m = ParsedPath::parse("array[2]").unwrap();
689 let n = ParsedPath::parse("tuple.1").unwrap();
690
691 for _ in 0..30 {
692 assert_eq!(*b.element::<usize>(&a).unwrap(), 1);
693 assert_eq!(*c.element::<usize>(&a).unwrap(), 10);
694 assert_eq!(*d.element::<f32>(&a).unwrap(), 3.14);
695 assert_eq!(*e.element::<f32>(&a).unwrap(), 2.0);
696 assert_eq!(*f.element::<usize>(&a).unwrap(), 42);
697 assert_eq!(*g.element::<usize>(&a).unwrap(), 10);
698 assert_eq!(*h.element::<f32>(&a).unwrap(), 3.14);
699 assert_eq!(*i.element::<F>(&a).unwrap(), F::Unit);
700 assert_eq!(*j.element::<u32>(&a).unwrap(), 321);
701 assert_eq!(*k.element::<char>(&a).unwrap(), 'm');
702 assert_eq!(*l.element::<char>(&a).unwrap(), 'm');
703 assert_eq!(*m.element::<i32>(&a).unwrap(), 309);
704 assert_eq!(*n.element::<f32>(&a).unwrap(), 1.23);
705 }
706 }
707
708 #[test]
709 fn reflect_array_behaves_like_list() {
710 #[derive(Reflect)]
711 struct A {
712 list: Vec<u8>,
713 array: [u8; 10],
714 }
715
716 let a = A {
717 list: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
718 array: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
719 };
720
721 assert_eq!(*a.path::<u8>("list[5]").unwrap(), 5);
722 assert_eq!(*a.path::<u8>("array[5]").unwrap(), 5);
723 assert_eq!(*a.path::<u8>("list[0]").unwrap(), 0);
724 assert_eq!(*a.path::<u8>("array[0]").unwrap(), 0);
725 }
726
727 #[test]
728 fn reflect_array_behaves_like_list_mut() {
729 #[derive(Reflect)]
730 struct A {
731 list: Vec<u8>,
732 array: [u8; 10],
733 }
734
735 let mut a = A {
736 list: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
737 array: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
738 };
739
740 assert_eq!(*a.path_mut::<u8>("list[5]").unwrap(), 5);
741 assert_eq!(*a.path_mut::<u8>("array[5]").unwrap(), 5);
742
743 *a.path_mut::<u8>("list[5]").unwrap() = 10;
744 *a.path_mut::<u8>("array[5]").unwrap() = 10;
745
746 assert_eq!(*a.path_mut::<u8>("list[5]").unwrap(), 10);
747 assert_eq!(*a.path_mut::<u8>("array[5]").unwrap(), 10);
748 }
749
750 #[test]
751 fn reflect_path() {
752 let mut a = a_sample();
753
754 assert_eq!(*a.path::<A>("").unwrap(), a);
755 assert_eq!(*a.path::<usize>("w").unwrap(), 1);
756 assert_eq!(*a.path::<usize>("x.foo").unwrap(), 10);
757 assert_eq!(*a.path::<f32>("x.łørđ.mосква").unwrap(), 3.14);
758 assert_eq!(*a.path::<f32>("y[1].mосква").unwrap(), 2.0);
759 assert_eq!(*a.path::<usize>("z.0.1").unwrap(), 42);
760 assert_eq!(*a.path::<usize>("x#0").unwrap(), 10);
761 assert_eq!(*a.path::<f32>("x#1#0").unwrap(), 3.14);
762
763 assert_eq!(*a.path::<F>("unit_variant").unwrap(), F::Unit);
764 assert_eq!(*a.path::<u32>("tuple_variant.1").unwrap(), 321);
765 assert_eq!(*a.path::<char>("struct_variant.東京").unwrap(), 'm');
766 assert_eq!(*a.path::<char>("struct_variant#0").unwrap(), 'm');
767
768 assert_eq!(*a.path::<i32>("array[2]").unwrap(), 309);
769
770 assert_eq!(*a.path::<f32>("tuple.1").unwrap(), 1.23);
771 *a.path_mut::<f32>("tuple.1").unwrap() = 3.21;
772 assert_eq!(*a.path::<f32>("tuple.1").unwrap(), 3.21);
773
774 *a.path_mut::<f32>("y[1].mосква").unwrap() = 3.0;
775 assert_eq!(a.y[1].mосква, 3.0);
776
777 *a.path_mut::<u32>("tuple_variant.0").unwrap() = 1337;
778 assert_eq!(a.tuple_variant, F::Tuple(1337, 321));
779
780 assert_eq!(
781 a.reflect_path("x.notreal").err().unwrap(),
782 ReflectPathError::InvalidAccess(AccessError {
783 kind: AccessErrorKind::MissingField(ReflectKind::Struct),
784 access: access_field("notreal"),
785 offset: Some(2),
786 })
787 );
788
789 assert_eq!(
790 a.reflect_path("unit_variant.0").err().unwrap(),
791 ReflectPathError::InvalidAccess(AccessError {
792 kind: AccessErrorKind::IncompatibleEnumVariantTypes {
793 actual: VariantType::Unit,
794 expected: VariantType::Tuple,
795 },
796 access: ParsedPath::parse_static("unit_variant.0").unwrap()[1]
797 .access
798 .clone(),
799 offset: Some(13),
800 })
801 );
802 assert_eq!(
803 a.reflect_path("x[0]").err().unwrap(),
804 invalid_access(2, ReflectKind::Struct, ReflectKind::List, "x[0]")
805 );
806 assert_eq!(
807 a.reflect_path("y.x").err().unwrap(),
808 invalid_access(2, ReflectKind::List, ReflectKind::Struct, "y.x")
809 );
810 }
811
812 #[test]
813 fn accept_leading_tokens() {
814 assert_eq!(
815 ParsedPath::parse(".w").unwrap().0,
816 &[offset(access_field("w"), 1)]
817 );
818 assert_eq!(
819 ParsedPath::parse("#0.foo").unwrap().0,
820 &[
821 offset(Access::FieldIndex(0), 1),
822 offset(access_field("foo"), 3)
823 ]
824 );
825 assert_eq!(
826 ParsedPath::parse(".5").unwrap().0,
827 &[offset(Access::TupleIndex(5), 1)]
828 );
829 assert_eq!(
830 ParsedPath::parse("[0].łørđ").unwrap().0,
831 &[
832 offset(Access::ListIndex(0), 1),
833 offset(access_field("łørđ"), 4)
834 ]
835 );
836 }
837}