Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_reflect/src/path/mod.rs
6599 views
1
pub mod access;
2
pub use access::*;
3
4
mod error;
5
pub use error::*;
6
7
mod parse;
8
pub use parse::ParseError;
9
use parse::PathParser;
10
11
use crate::{PartialReflect, Reflect};
12
use alloc::vec::Vec;
13
use core::fmt;
14
use derive_more::derive::From;
15
use thiserror::Error;
16
17
type PathResult<'a, T> = Result<T, ReflectPathError<'a>>;
18
19
/// An error returned from a failed path string query.
20
#[derive(Error, Debug, PartialEq, Eq)]
21
pub 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
43
impl<'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`].
50
pub 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
86
impl<'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
)]
251
pub 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`
295
impl<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)]
299
pub 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
306
impl 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)]
367
pub struct ParsedPath(
368
/// This is a vector of pre-parsed [`OffsetAccess`]es.
369
pub Vec<OffsetAccess>,
370
);
371
372
impl 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
442
impl<'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
460
impl<const N: usize> From<[OffsetAccess; N]> for ParsedPath {
461
fn from(value: [OffsetAccess; N]) -> Self {
462
ParsedPath(value.to_vec())
463
}
464
}
465
466
impl 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
480
impl<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
486
impl<'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
493
impl 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
502
impl 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
509
impl 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
)]
520
mod 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
}
836
837