Merge df4b1c6ed4
into 9edf538643
This commit is contained in:
commit
f7c17a143d
@ -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(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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")
|
||||
);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user