This commit is contained in:
Maksymilian Mozolewski 2025-07-14 17:42:20 +01:00 committed by GitHub
commit f7c17a143d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 210 additions and 89 deletions

View File

@ -6,7 +6,7 @@ use core::fmt;
use super::error::AccessErrorKind;
use crate::{AccessError, PartialReflect, ReflectKind, ReflectMut, ReflectRef, VariantType};
type InnerResult<T> = Result<T, AccessErrorKind>;
type InnerResult<'a, T> = Result<T, AccessErrorKind<'a>>;
/// A singular element access within a path.
/// Multiple accesses can be combined into a [`ParsedPath`](super::ParsedPath).
@ -60,49 +60,94 @@ impl<'a> Access<'a> {
.map_err(|err| err.with_access(self.clone(), offset))
}
#[inline]
fn try_parse_field_as_index(
base: impl Fn() -> ReflectKind,
field: Cow<'a, str>,
) -> Result<usize, AccessErrorKind<'a>> {
field
.parse()
.map_err(|_| AccessErrorKind::UnsupportedAccess {
base: base(),
access: Access::Field(field),
})
}
fn element_inner<'r>(
&self,
base: &'r dyn PartialReflect,
) -> InnerResult<Option<&'r dyn PartialReflect>> {
) -> InnerResult<'a, Option<&'r dyn PartialReflect>> {
use ReflectRef::*;
let invalid_variant =
|expected, actual| AccessErrorKind::IncompatibleEnumVariantTypes { expected, actual };
match (self, base.reflect_ref()) {
(Self::Field(field), Struct(struct_ref)) => Ok(struct_ref.field(field.as_ref())),
(Self::Field(field), Enum(enum_ref)) => match enum_ref.variant_type() {
match (base.reflect_ref(), self) {
// Struct
(Struct(struct_ref), Access::Field(field)) => Ok(struct_ref.field(field.as_ref())),
(Struct(struct_ref), &Access::FieldIndex(index)) => Ok(struct_ref.field_at(index)),
// Tuple Struct
(TupleStruct(struct_ref), Access::Field(field)) => Ok(struct_ref.field(
Self::try_parse_field_as_index(|| struct_ref.reflect_kind(), field.clone())?,
)),
(
TupleStruct(struct_ref),
&Access::FieldIndex(index) | &Access::TupleIndex(index) | &Access::ListIndex(index),
) => Ok(struct_ref.field(index)),
// Tuple
(Tuple(tuple_ref), Access::Field(field)) => Ok(tuple_ref.field(
Self::try_parse_field_as_index(|| tuple_ref.reflect_kind(), field.clone())?,
)),
(
Tuple(tuple_ref),
&Access::FieldIndex(index) | &Access::TupleIndex(index) | &Access::ListIndex(index),
) => Ok(tuple_ref.field(index)),
// List
(List(list_ref), Access::Field(field)) => Ok(list_ref.get(
Self::try_parse_field_as_index(|| list_ref.reflect_kind(), field.clone())?,
)),
(
List(list_ref),
&Access::FieldIndex(index) | &Access::TupleIndex(index) | &Access::ListIndex(index),
) => Ok(list_ref.get(index)),
// Array
(Array(array_ref), Access::Field(field)) => Ok(array_ref.get(
Self::try_parse_field_as_index(|| array_ref.reflect_kind(), field.clone())?,
)),
(
Array(array_ref),
&Access::FieldIndex(index) | &Access::TupleIndex(index) | &Access::ListIndex(index),
) => Ok(array_ref.get(index)),
// Map
(Map(map_ref), Access::Field(field)) => Ok(map_ref.get(&field.clone().into_owned())),
(
Map(map_ref),
&Access::FieldIndex(index) | &Access::TupleIndex(index) | &Access::ListIndex(index),
) => Ok(map_ref.get(&index)),
// Set
(Set(set_ref), Access::Field(field)) => Ok(set_ref.get(&field.clone().into_owned())),
(
Set(set_ref),
&Access::FieldIndex(index) | &Access::TupleIndex(index) | &Access::ListIndex(index),
) => Ok(set_ref.get(&index)),
// Enum
(Enum(enum_ref), Access::Field(field)) => match enum_ref.variant_type() {
VariantType::Struct => Ok(enum_ref.field(field.as_ref())),
actual => Err(invalid_variant(VariantType::Struct, actual)),
},
(&Self::FieldIndex(index), Struct(struct_ref)) => Ok(struct_ref.field_at(index)),
(&Self::FieldIndex(index), Enum(enum_ref)) => match enum_ref.variant_type() {
(Enum(enum_ref), &Access::FieldIndex(index)) => match enum_ref.variant_type() {
VariantType::Struct => Ok(enum_ref.field_at(index)),
actual => Err(invalid_variant(VariantType::Struct, actual)),
},
(Self::Field(_) | Self::FieldIndex(_), actual) => {
Err(AccessErrorKind::IncompatibleTypes {
expected: ReflectKind::Struct,
actual: actual.into(),
})
(Enum(enum_ref), &Access::TupleIndex(index) | &Access::ListIndex(index)) => {
match enum_ref.variant_type() {
VariantType::Tuple => Ok(enum_ref.field_at(index)),
actual => Err(invalid_variant(VariantType::Tuple, actual)),
}
}
(&Self::TupleIndex(index), TupleStruct(tuple)) => Ok(tuple.field(index)),
(&Self::TupleIndex(index), Tuple(tuple)) => Ok(tuple.field(index)),
(&Self::TupleIndex(index), Enum(enum_ref)) => match enum_ref.variant_type() {
VariantType::Tuple => Ok(enum_ref.field_at(index)),
actual => Err(invalid_variant(VariantType::Tuple, actual)),
},
(Self::TupleIndex(_), actual) => Err(AccessErrorKind::IncompatibleTypes {
expected: ReflectKind::Tuple,
actual: actual.into(),
}),
(&Self::ListIndex(index), List(list)) => Ok(list.get(index)),
(&Self::ListIndex(index), Array(list)) => Ok(list.get(index)),
(Self::ListIndex(_), actual) => Err(AccessErrorKind::IncompatibleTypes {
expected: ReflectKind::List,
actual: actual.into(),
(other, access) => Err(AccessErrorKind::UnsupportedAccess {
base: other.kind(),
access: access.clone().into_owned(),
}),
}
}
@ -116,52 +161,90 @@ impl<'a> Access<'a> {
self.element_inner_mut(base)
.and_then(|maybe| maybe.ok_or(AccessErrorKind::MissingField(kind)))
.map_err(|err| err.with_access(self.clone(), offset))
.map_err(move |err| err.with_access(self.clone(), offset))
}
fn element_inner_mut<'r>(
&self,
base: &'r mut dyn PartialReflect,
) -> InnerResult<Option<&'r mut dyn PartialReflect>> {
) -> InnerResult<'a, Option<&'r mut dyn PartialReflect>> {
use ReflectMut::*;
let invalid_variant =
|expected, actual| AccessErrorKind::IncompatibleEnumVariantTypes { expected, actual };
match (self, base.reflect_mut()) {
(Self::Field(field), Struct(struct_mut)) => Ok(struct_mut.field_mut(field.as_ref())),
(Self::Field(field), Enum(enum_mut)) => match enum_mut.variant_type() {
VariantType::Struct => Ok(enum_mut.field_mut(field.as_ref())),
actual => Err(invalid_variant(VariantType::Struct, actual)),
},
(&Self::FieldIndex(index), Struct(struct_mut)) => Ok(struct_mut.field_at_mut(index)),
(&Self::FieldIndex(index), Enum(enum_mut)) => match enum_mut.variant_type() {
VariantType::Struct => Ok(enum_mut.field_at_mut(index)),
actual => Err(invalid_variant(VariantType::Struct, actual)),
},
(Self::Field(_) | Self::FieldIndex(_), actual) => {
Err(AccessErrorKind::IncompatibleTypes {
expected: ReflectKind::Struct,
actual: actual.into(),
})
match (base.reflect_mut(), self) {
// Struct
(Struct(struct_ref), Access::Field(field)) => Ok(struct_ref.field_mut(field.as_ref())),
(
Struct(struct_ref),
&Access::FieldIndex(index) | &Access::TupleIndex(index) | &Access::ListIndex(index),
) => Ok(struct_ref.field_at_mut(index)),
// Tuple Struct
(TupleStruct(struct_ref), Access::Field(field)) => Ok(struct_ref.field_mut(
Self::try_parse_field_as_index(|| struct_ref.reflect_kind(), field.clone())?,
)),
(
TupleStruct(struct_ref),
&Access::FieldIndex(index) | &Access::TupleIndex(index) | &Access::ListIndex(index),
) => Ok(struct_ref.field_mut(index)),
// Tuple
(Tuple(tuple_ref), Access::Field(field)) => Ok(tuple_ref.field_mut(
Self::try_parse_field_as_index(|| tuple_ref.reflect_kind(), field.clone())?,
)),
(
Tuple(tuple_ref),
&Access::FieldIndex(index) | &Access::TupleIndex(index) | &Access::ListIndex(index),
) => Ok(tuple_ref.field_mut(index)),
// List
(List(list_ref), Access::Field(field)) => Ok(list_ref.get_mut(
Self::try_parse_field_as_index(|| list_ref.reflect_kind(), field.clone())?,
)),
(
List(list_ref),
&Access::FieldIndex(index) | &Access::TupleIndex(index) | &Access::ListIndex(index),
) => Ok(list_ref.get_mut(index)),
// Array
(Array(array_ref), Access::Field(field)) => Ok(array_ref.get_mut(
Self::try_parse_field_as_index(|| array_ref.reflect_kind(), field.clone())?,
)),
(
Array(array_ref),
&Access::FieldIndex(index) | &Access::TupleIndex(index) | &Access::ListIndex(index),
) => Ok(array_ref.get_mut(index)),
// Map
(Map(map_ref), Access::Field(field)) => {
Ok(map_ref.get_mut(&field.clone().into_owned()))
}
(&Self::TupleIndex(index), TupleStruct(tuple)) => Ok(tuple.field_mut(index)),
(&Self::TupleIndex(index), Tuple(tuple)) => Ok(tuple.field_mut(index)),
(&Self::TupleIndex(index), Enum(enum_mut)) => match enum_mut.variant_type() {
VariantType::Tuple => Ok(enum_mut.field_at_mut(index)),
actual => Err(invalid_variant(VariantType::Tuple, actual)),
(
Map(map_ref),
&Access::FieldIndex(index) | &Access::TupleIndex(index) | &Access::ListIndex(index),
) => Ok(map_ref.get_mut(&index)),
// Set - no get_mut
// (Set(set_ref), Access::Field(field)) => Ok(set_ref.get(&field.clone().into_owned())),
// (
// Set(set_ref),
// &Access::FieldIndex(index) | &Access::TupleIndex(index) | &Access::ListIndex(index),
// ) => Ok(set_ref.get(&index)),
// Enum
// Enum
(Enum(enum_ref), Access::Field(field)) => match enum_ref.variant_type() {
VariantType::Struct => Ok(enum_ref.field_mut(field.as_ref())),
actual => Err(invalid_variant(VariantType::Struct, actual)),
},
(Self::TupleIndex(_), actual) => Err(AccessErrorKind::IncompatibleTypes {
expected: ReflectKind::Tuple,
actual: actual.into(),
}),
(&Self::ListIndex(index), List(list)) => Ok(list.get_mut(index)),
(&Self::ListIndex(index), Array(list)) => Ok(list.get_mut(index)),
(Self::ListIndex(_), actual) => Err(AccessErrorKind::IncompatibleTypes {
expected: ReflectKind::List,
actual: actual.into(),
(Enum(enum_ref), &Access::FieldIndex(index)) => match enum_ref.variant_type() {
VariantType::Struct => Ok(enum_ref.field_at_mut(index)),
actual => Err(invalid_variant(VariantType::Struct, actual)),
},
(Enum(enum_ref), &Access::TupleIndex(index) | &Access::ListIndex(index)) => {
match enum_ref.variant_type() {
VariantType::Tuple => Ok(enum_ref.field_at_mut(index)),
actual => Err(invalid_variant(VariantType::Tuple, actual)),
}
}
(other, access) => Err(AccessErrorKind::UnsupportedAccess {
base: other.kind(),
access: access.clone().into_owned(),
}),
}
}

View File

@ -5,20 +5,11 @@ use crate::{ReflectKind, VariantType};
/// The kind of [`AccessError`], along with some kind-specific information.
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum AccessErrorKind {
pub enum AccessErrorKind<'a> {
/// An error that occurs when a certain type doesn't
/// contain the value referenced by the [`Access`].
MissingField(ReflectKind),
/// An error that occurs when using an [`Access`] on the wrong type.
/// (i.e. a [`ListIndex`](Access::ListIndex) on a struct, or a [`TupleIndex`](Access::TupleIndex) on a list)
IncompatibleTypes {
/// The [`ReflectKind`] that was expected based on the [`Access`].
expected: ReflectKind,
/// The actual [`ReflectKind`] that was found.
actual: ReflectKind,
},
/// An error that occurs when using an [`Access`] on the wrong enum variant.
/// (i.e. a [`ListIndex`](Access::ListIndex) on a struct variant, or a [`TupleIndex`](Access::TupleIndex) on a unit variant)
IncompatibleEnumVariantTypes {
@ -27,10 +18,25 @@ pub enum AccessErrorKind {
/// The actual [`VariantType`] that was found.
actual: VariantType,
},
/// Occurs when an [`Access`] is attempted on a type that does not support it.
UnsupportedAccess {
/// The [`ReflectKind`] that was attempted to be accessed.
base: ReflectKind,
/// The [`Access`] that was attempted.
access: Access<'a>,
},
}
impl AccessErrorKind {
pub(super) fn with_access(self, access: Access, offset: Option<usize>) -> AccessError {
impl AccessErrorKind<'_> {
pub(super) fn with_access<'a>(
self,
access: Access<'a>,
offset: Option<usize>,
) -> AccessError<'a>
where
Self: 'a,
{
AccessError {
kind: self,
access,
@ -52,7 +58,7 @@ impl AccessErrorKind {
/// ```
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AccessError<'a> {
pub(super) kind: AccessErrorKind,
pub(super) kind: AccessErrorKind<'a>,
pub(super) access: Access<'a>,
pub(super) offset: Option<usize>,
}
@ -113,11 +119,11 @@ impl fmt::Display for AccessError<'_> {
access.display_value()
)
}
}
AccessErrorKind::IncompatibleTypes { expected, actual } => write!(
},
AccessErrorKind::UnsupportedAccess { base, access } => write!(
f,
"Expected {} access to access a {expected}, found a {actual} instead.",
access.kind()
"Tried to access {base} with {} access: '{access}'",
access.kind(),
),
AccessErrorKind::IncompatibleEnumVariantTypes { expected, actual } => write!(
f,

View File

@ -520,6 +520,7 @@ impl core::ops::IndexMut<usize> for ParsedPath {
mod tests {
use super::*;
use crate::*;
use alloc::string::String;
use alloc::vec;
#[derive(Reflect, PartialEq, Debug)]
@ -533,6 +534,8 @@ mod tests {
struct_variant: F,
array: [i32; 3],
tuple: (bool, f32),
string_map: std::collections::HashMap<String, String>,
usize_map: std::collections::HashMap<usize, usize>,
}
#[derive(Reflect, PartialEq, Debug)]
@ -573,6 +576,10 @@ mod tests {
struct_variant: F::Şķràźÿ { : 'm' },
array: [86, 75, 309],
tuple: (true, 1.23),
string_map: vec![("foo".into(), "bar".into()), ("baz".into(), "qux".into())]
.into_iter()
.collect(),
usize_map: vec![(0, 1), (1, 2), (2, 3)].into_iter().collect(),
}
}
@ -589,14 +596,19 @@ mod tests {
type StaticError = ReflectPathError<'static>;
fn invalid_access(
offset: usize,
actual: ReflectKind,
expected: ReflectKind,
access: &'static str,
) -> StaticError {
fn invalid_access(offset: usize, actual: ReflectKind, access: &'static str) -> StaticError {
ReflectPathError::InvalidAccess(AccessError {
kind: AccessErrorKind::IncompatibleTypes { actual, expected },
kind: AccessErrorKind::UnsupportedAccess {
base: actual,
access: ParsedPath::parse_static(access)
.unwrap()
.0
.into_iter()
.last()
.unwrap()
.access
.clone(),
},
access: ParsedPath::parse_static(access).unwrap()[1].access.clone(),
offset: Some(offset),
})
@ -755,6 +767,8 @@ mod tests {
assert_eq!(*a.path::<f32>("x.łørđ.mосква").unwrap(), 3.14);
assert_eq!(*a.path::<f32>("y[1].mосква").unwrap(), 2.0);
assert_eq!(*a.path::<usize>("z.0.1").unwrap(), 42);
assert_eq!(*a.path::<usize>("z.0[1]").unwrap(), 42);
assert_eq!(*a.path::<usize>("z[0][1]").unwrap(), 42);
assert_eq!(*a.path::<usize>("x#0").unwrap(), 10);
assert_eq!(*a.path::<f32>("x#1#0").unwrap(), 3.14);
@ -764,6 +778,7 @@ mod tests {
assert_eq!(*a.path::<char>("struct_variant#0").unwrap(), 'm');
assert_eq!(*a.path::<i32>("array[2]").unwrap(), 309);
assert_eq!(*a.path::<i32>("array.2").unwrap(), 309);
assert_eq!(*a.path::<f32>("tuple.1").unwrap(), 1.23);
*a.path_mut::<f32>("tuple.1").unwrap() = 3.21;
@ -775,6 +790,23 @@ mod tests {
*a.path_mut::<u32>("tuple_variant.0").unwrap() = 1337;
assert_eq!(a.tuple_variant, F::Tuple(1337, 321));
assert_eq!(
*a.path::<String>("string_map.foo").unwrap(),
String::from("bar")
);
assert_eq!(
*a.path::<String>("string_map.baz").unwrap(),
String::from("qux")
);
assert_eq!(*a.path::<usize>("usize_map[0]").unwrap(), 1);
assert_eq!(*a.path::<usize>("usize_map[1]").unwrap(), 2);
assert_eq!(*a.path::<usize>("usize_map[2]").unwrap(), 3);
assert_eq!(*a.path::<usize>("usize_map.0").unwrap(), 1);
assert_eq!(*a.path::<usize>("usize_map.1").unwrap(), 2);
assert_eq!(*a.path::<usize>("usize_map.2").unwrap(), 3);
assert_eq!(
a.reflect_path("x.notreal").err().unwrap(),
ReflectPathError::InvalidAccess(AccessError {
@ -799,11 +831,11 @@ mod tests {
);
assert_eq!(
a.reflect_path("x[0]").err().unwrap(),
invalid_access(2, ReflectKind::Struct, ReflectKind::List, "x[0]")
invalid_access(2, ReflectKind::Struct, "x[0]")
);
assert_eq!(
a.reflect_path("y.x").err().unwrap(),
invalid_access(2, ReflectKind::List, ReflectKind::Struct, "y.x")
invalid_access(2, ReflectKind::List, "y.x")
);
}