Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the ability to manually create ParsedPaths (+ cleanup) #11029

Merged
merged 10 commits into from
Feb 1, 2024
251 changes: 174 additions & 77 deletions crates/bevy_reflect/src/path/access.rs
Original file line number Diff line number Diff line change
@@ -1,56 +1,74 @@
//! Representation for individual element accesses within a path.

use std::{borrow::Cow, fmt};

use super::{AccessError, ReflectPathError};
use super::ReflectPathError;
use crate::{Reflect, ReflectMut, ReflectRef, VariantType};
use thiserror::Error;

type InnerResult<T> = Result<Option<T>, Error<'static>>;
type InnerResult<'a, T> = Result<Option<T>, Error<'a>>;

/// An error originating from an [`Access`] of an element within a type.
#[derive(Debug, PartialEq, Eq, Error)]
pub(super) enum Error<'a> {
pub enum Error<'a> {
doonv marked this conversation as resolved.
Show resolved Hide resolved
/// An error that occurs when a certain type doesn't
/// contain the value contained in the [`Access`].
#[error(
"the current {ty} doesn't have the {} {}",
"the current {kind} doesn't have the {} {}",
access.kind(),
access.display_value(),
)]
Access { ty: TypeShape, access: Access<'a> },
MissingAccess {
/// The kind of the type being accessed.
kind: TypeKind,
/// The [`Access`] used on the type.
access: Access<'a>,
},

#[error("invalid type shape: expected {expected} but found a reflect {actual}")]
Type {
expected: TypeShape,
actual: TypeShape,
/// 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)
#[error(
"invalid type: expected {} access to use a type of {expected} but found a reflect {actual}",
access.kind()
)]
InvalidType {
/// The [`TypeKind`] that was expected based on the [`Access`].
expected: TypeKind,
/// The actual [`TypeKind`] that was found.
actual: TypeKind,
/// The [`Access`] used.
access: Access<'a>,
},

#[error("invalid enum access: expected {expected} variant but found {actual} variant")]
Enum {
expected: TypeShape,
actual: TypeShape,
/// 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)
#[error(
"invalid enum variant: expected {} access to use a type of {expected:?} variant but found {actual:?} variant",
access.kind()
)]
InvalidEnumVariant {
/// The [`VariantType`] that was expected based on the [`Access`].
expected: VariantType,
/// The actual [`VariantType`] that was found.
actual: VariantType,
/// The [`Access`] used.
access: Access<'a>,
},
}

impl<'a> Error<'a> {
fn with_offset(self, offset: usize) -> ReflectPathError<'a> {
let error = AccessError(self);
ReflectPathError::InvalidAccess { offset, error }
}

fn access(ty: TypeShape, access: Access<'a>) -> Self {
Self::Access { ty, access }
}
}
impl Error<'static> {
fn bad_enum_variant(expected: TypeShape, actual: impl Into<TypeShape>) -> Self {
let actual = actual.into();
Error::Enum { expected, actual }
}
fn bad_type(expected: TypeShape, actual: impl Into<TypeShape>) -> Self {
let actual = actual.into();
Error::Type { expected, actual }
fn with_offset(self, offset: Option<usize>) -> ReflectPathError<'a> {
ReflectPathError::InvalidAccess {
offset,
error: self,
}
}
}

/// The kind of the type trying to be accessed.
#[allow(missing_docs /* Variants are self-explanatory */)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub(super) enum TypeShape {
pub enum TypeKind {
Struct,
TupleStruct,
Tuple,
Expand All @@ -62,54 +80,73 @@ pub(super) enum TypeShape {
Unit,
}

impl fmt::Display for TypeShape {
impl fmt::Display for TypeKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match self {
TypeShape::Struct => "struct",
TypeShape::TupleStruct => "tuple struct",
TypeShape::Tuple => "tuple",
TypeShape::List => "list",
TypeShape::Array => "array",
TypeShape::Map => "map",
TypeShape::Enum => "enum",
TypeShape::Value => "value",
TypeShape::Unit => "unit",
TypeKind::Struct => "struct",
TypeKind::TupleStruct => "tuple struct",
TypeKind::Tuple => "tuple",
TypeKind::List => "list",
TypeKind::Array => "array",
TypeKind::Map => "map",
TypeKind::Enum => "enum",
TypeKind::Value => "value",
TypeKind::Unit => "unit",
};
write!(f, "{name}")
}
}
impl<'a> From<ReflectRef<'a>> for TypeShape {
impl<'a> From<ReflectRef<'a>> for TypeKind {
fn from(value: ReflectRef<'a>) -> Self {
match value {
ReflectRef::Struct(_) => TypeShape::Struct,
ReflectRef::TupleStruct(_) => TypeShape::TupleStruct,
ReflectRef::Tuple(_) => TypeShape::Tuple,
ReflectRef::List(_) => TypeShape::List,
ReflectRef::Array(_) => TypeShape::Array,
ReflectRef::Map(_) => TypeShape::Map,
ReflectRef::Enum(_) => TypeShape::Enum,
ReflectRef::Value(_) => TypeShape::Value,
ReflectRef::Struct(_) => TypeKind::Struct,
ReflectRef::TupleStruct(_) => TypeKind::TupleStruct,
ReflectRef::Tuple(_) => TypeKind::Tuple,
ReflectRef::List(_) => TypeKind::List,
ReflectRef::Array(_) => TypeKind::Array,
ReflectRef::Map(_) => TypeKind::Map,
ReflectRef::Enum(_) => TypeKind::Enum,
ReflectRef::Value(_) => TypeKind::Value,
}
}
}
impl<'a> From<ReflectMut<'a>> for TypeKind {
fn from(value: ReflectMut<'a>) -> Self {
match value {
ReflectMut::Struct(_) => TypeKind::Struct,
ReflectMut::TupleStruct(_) => TypeKind::TupleStruct,
ReflectMut::Tuple(_) => TypeKind::Tuple,
ReflectMut::List(_) => TypeKind::List,
ReflectMut::Array(_) => TypeKind::Array,
ReflectMut::Map(_) => TypeKind::Map,
ReflectMut::Enum(_) => TypeKind::Enum,
ReflectMut::Value(_) => TypeKind::Value,
}
}
}
impl From<VariantType> for TypeShape {
impl From<VariantType> for TypeKind {
fn from(value: VariantType) -> Self {
match value {
VariantType::Struct => TypeShape::Struct,
VariantType::Tuple => TypeShape::Tuple,
VariantType::Unit => TypeShape::Unit,
VariantType::Struct => TypeKind::Struct,
VariantType::Tuple => TypeKind::Tuple,
VariantType::Unit => TypeKind::Unit,
}
}
}

/// A singular element access within a path.
/// Multiple accesses can be combined into a [`ParsedPath`](super::ParsedPath).
///
/// Can be applied to a `dyn Reflect` to get a reference to the targeted element.
/// Can be applied to a [`dyn Reflect`](Reflect) to get a reference to the targeted element.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(super) enum Access<'a> {
pub enum Access<'a> {
/// A name-based field access on a struct.
Field(Cow<'a, str>),
/// A index-based field access on a struct.
FieldIndex(usize),
/// An index-based access on a tuple.
TupleIndex(usize),
/// An index-based access on a list.
ListIndex(usize),
}

Expand All @@ -125,9 +162,14 @@ impl fmt::Display for Access<'_> {
}

impl<'a> Access<'a> {
pub(super) fn into_owned(self) -> Access<'static> {
/// Converts this into an "owned" value.
///
/// If the [`Access`] is of variant [`Field`](Access::Field),
/// the field's [`Cow<str>`] will be converted to it's owned
/// counterpart, which doesn't require a reference.
pub fn into_owned(self) -> Access<'static> {
match self {
Self::Field(value) => Access::Field(value.to_string().into()),
Self::Field(value) => Access::Field(Cow::Owned(value.into_owned())),
Self::FieldIndex(value) => Access::FieldIndex(value),
Self::TupleIndex(value) => Access::TupleIndex(value),
Self::ListIndex(value) => Access::ListIndex(value),
Expand All @@ -140,6 +182,7 @@ impl<'a> Access<'a> {
Self::FieldIndex(value) | Self::TupleIndex(value) | Self::ListIndex(value) => value,
}
}

fn kind(&self) -> &'static str {
match self {
Self::Field(_) => "field",
Expand All @@ -151,75 +194,129 @@ impl<'a> Access<'a> {
pub(super) fn element<'r>(
&self,
base: &'r dyn Reflect,
offset: usize,
offset: Option<usize>,
) -> Result<&'r dyn Reflect, ReflectPathError<'a>> {
let ty = base.reflect_ref().into();
let kind = base.reflect_ref().into();
self.element_inner(base)
.and_then(|maybe| maybe.ok_or(Error::access(ty, self.clone())))
.and_then(|maybe| {
maybe.ok_or(Error::MissingAccess {
kind,
access: self.clone(),
})
})
.map_err(|err| err.with_offset(offset))
}

fn element_inner<'r>(&self, base: &'r dyn Reflect) -> InnerResult<&'r dyn Reflect> {
fn element_inner<'r>(&self, base: &'r dyn Reflect) -> InnerResult<'a, &'r dyn Reflect> {
use ReflectRef::*;

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() {
VariantType::Struct => Ok(enum_ref.field(field.as_ref())),
actual => Err(Error::bad_enum_variant(TypeShape::Struct, actual)),
actual => Err(Error::InvalidEnumVariant {
expected: VariantType::Struct,
actual,
access: self.clone(),
}),
},
(&Self::FieldIndex(index), Struct(struct_ref)) => Ok(struct_ref.field_at(index)),
(&Self::FieldIndex(index), Enum(enum_ref)) => match enum_ref.variant_type() {
VariantType::Struct => Ok(enum_ref.field_at(index)),
actual => Err(Error::bad_enum_variant(TypeShape::Struct, actual)),
actual => Err(Error::InvalidEnumVariant {
expected: VariantType::Struct,
actual,
access: self.clone(),
}),
},
(&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(Error::bad_enum_variant(TypeShape::Tuple, actual)),
actual => Err(Error::InvalidEnumVariant {
expected: VariantType::Tuple,
actual,
access: self.clone(),
}),
},
(&Self::ListIndex(index), List(list)) => Ok(list.get(index)),
(&Self::ListIndex(index), Array(list)) => Ok(list.get(index)),
(&Self::ListIndex(_), actual) => Err(Error::bad_type(TypeShape::List, actual)),
(_, actual) => Err(Error::bad_type(TypeShape::Struct, actual)),
(&Self::ListIndex(_), actual) => Err(Error::InvalidType {
expected: TypeKind::List,
actual: actual.into(),
access: self.clone(),
}),
(_, actual) => Err(Error::InvalidType {
expected: TypeKind::Struct,
actual: actual.into(),
access: self.clone(),
}),
}
}

pub(super) fn element_mut<'r>(
&self,
base: &'r mut dyn Reflect,
offset: usize,
offset: Option<usize>,
) -> Result<&'r mut dyn Reflect, ReflectPathError<'a>> {
let ty = base.reflect_ref().into();
let kind = base.reflect_ref().into();
self.element_inner_mut(base)
.and_then(|maybe| maybe.ok_or(Error::access(ty, self.clone())))
.and_then(|maybe| {
maybe.ok_or(Error::MissingAccess {
kind,
access: self.clone(),
})
})
.map_err(|err| err.with_offset(offset))
}

fn element_inner_mut<'r>(&self, base: &'r mut dyn Reflect) -> InnerResult<&'r mut dyn Reflect> {
fn element_inner_mut<'r>(
&self,
base: &'r mut dyn Reflect,
) -> InnerResult<'a, &'r mut dyn Reflect> {
use ReflectMut::*;
let base_shape: TypeShape = base.reflect_ref().into();

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(Error::bad_enum_variant(TypeShape::Struct, actual)),
actual => Err(Error::InvalidEnumVariant {
expected: VariantType::Struct,
actual,
access: self.clone(),
}),
},
(&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(Error::bad_enum_variant(TypeShape::Struct, actual)),
actual => Err(Error::InvalidEnumVariant {
expected: VariantType::Struct,
actual,
access: self.clone(),
}),
},
(&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(Error::bad_enum_variant(TypeShape::Tuple, actual)),
actual => Err(Error::InvalidEnumVariant {
expected: VariantType::Tuple,
actual,
access: self.clone(),
}),
},
(&Self::ListIndex(index), List(list)) => Ok(list.get_mut(index)),
(&Self::ListIndex(index), Array(list)) => Ok(list.get_mut(index)),
(&Self::ListIndex(_), _) => Err(Error::bad_type(TypeShape::List, base_shape)),
(_, _) => Err(Error::bad_type(TypeShape::Struct, base_shape)),
(&Self::ListIndex(_), actual) => Err(Error::InvalidType {
expected: TypeKind::List,
actual: actual.into(),
access: self.clone(),
}),
(_, actual) => Err(Error::InvalidType {
expected: TypeKind::Struct,
actual: actual.into(),
access: self.clone(),
}),
}
}
}
Loading