Skip to content

Commit dbd2668

Browse files
committed
Split path, better error handling, clean element
This commit: - Splits the `path.rs` module of `bevy_reflect` in two. the `Access` structs are now defined in their own submodules - Revamps error handling - Separate Parse error from access errors - rename "index" to offset. "offset" is more commonly used to designate parsing position, and it can't be confused with the other kind of indexing done in this module - Instead of having one error variant per possible mismatching type, create a single error with a "expected" and "actual" type. - The error values now contain more details about the nature of the error. - Refactor the `read_element{_mut}` methods - They are now about 70 lines, while previously they were 188 lines - They are much more readable now. - Rename them to `element{_mut}`, since the "read" is a bit misleading. - They accept a `self` rather than `&self`, since AccessRef is Copy now. - We also rewrite Display for Access in term of write! rather than sequential write.fmt() - Rename `to_ref` to `as_ref` - Make AccessRef Copy: because it's a very small type. The downside is that it doesn't benefit from niching in the error type anymore.
1 parent befb8d9 commit dbd2668

File tree

2 files changed

+318
-320
lines changed

2 files changed

+318
-320
lines changed
Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
use std::fmt;
2+
3+
use super::ReflectPathError;
4+
use crate::{Reflect, ReflectMut, ReflectRef, VariantType};
5+
use thiserror::Error;
6+
7+
type InnerResult<T> = Result<Option<T>, AccessError<'static>>;
8+
9+
#[derive(Debug, PartialEq, Eq, Error)]
10+
pub enum AccessError<'a> {
11+
#[error(
12+
"the current {ty} doesn't have the {} {}",
13+
access.kind(),
14+
access.display_value(),
15+
)]
16+
Access { ty: Type, access: AccessRef<'a> },
17+
18+
#[error("invalid type shape: expected {expected} but found a reflect {actual}")]
19+
Type { expected: Type, actual: Type },
20+
21+
#[error("invalid enum access: expected {expected} variant but found {actual} variant")]
22+
Enum { expected: Type, actual: Type },
23+
}
24+
25+
impl<'a> AccessError<'a> {
26+
fn with_offset(self, offset: usize) -> ReflectPathError<'a> {
27+
let error = self;
28+
ReflectPathError::InvalidAccess { offset, error }
29+
}
30+
}
31+
impl AccessError<'static> {
32+
fn bad_enum(expected: Type, actual: impl Into<Type>) -> Self {
33+
let actual = actual.into();
34+
AccessError::Enum { expected, actual }
35+
}
36+
fn bad_type(expected: Type, actual: impl Into<Type>) -> Self {
37+
let actual = actual.into();
38+
AccessError::Type { expected, actual }
39+
}
40+
}
41+
42+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
43+
pub enum Type {
44+
Struct,
45+
TupleStruct,
46+
Tuple,
47+
List,
48+
Array,
49+
Map,
50+
Enum,
51+
Value,
52+
Unit,
53+
}
54+
55+
impl fmt::Display for Type {
56+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57+
let name = match self {
58+
Type::Struct => "struct",
59+
Type::TupleStruct => "tuple struct",
60+
Type::Tuple => "tuple",
61+
Type::List => "list",
62+
Type::Array => "array",
63+
Type::Map => "map",
64+
Type::Enum => "enum",
65+
Type::Value => "value",
66+
Type::Unit => "unit",
67+
};
68+
write!(f, "{name}")
69+
}
70+
}
71+
impl<'a> From<ReflectRef<'a>> for Type {
72+
fn from(value: ReflectRef<'a>) -> Self {
73+
match value {
74+
ReflectRef::Struct(_) => Type::Struct,
75+
ReflectRef::TupleStruct(_) => Type::TupleStruct,
76+
ReflectRef::Tuple(_) => Type::Tuple,
77+
ReflectRef::List(_) => Type::List,
78+
ReflectRef::Array(_) => Type::Array,
79+
ReflectRef::Map(_) => Type::Map,
80+
ReflectRef::Enum(_) => Type::Enum,
81+
ReflectRef::Value(_) => Type::Value,
82+
}
83+
}
84+
}
85+
impl From<VariantType> for Type {
86+
fn from(value: VariantType) -> Self {
87+
match value {
88+
VariantType::Struct => Type::Struct,
89+
VariantType::Tuple => Type::Tuple,
90+
VariantType::Unit => Type::Unit,
91+
}
92+
}
93+
}
94+
95+
/// A singular owned element access within a path.
96+
///
97+
/// Can be applied to a `dyn Reflect` to get a reference to the targeted element.
98+
///
99+
/// A path is composed of multiple accesses in sequence.
100+
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
101+
pub(super) enum Access {
102+
Field(Box<str>),
103+
FieldIndex(usize),
104+
TupleIndex(usize),
105+
ListIndex(usize),
106+
}
107+
108+
impl fmt::Display for Access {
109+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110+
match self {
111+
Access::Field(field) => write!(f, ".{field}"),
112+
Access::FieldIndex(index) => write!(f, "#{index}"),
113+
Access::TupleIndex(index) => write!(f, ".{index}"),
114+
Access::ListIndex(index) => write!(f, "[{index}]"),
115+
}
116+
}
117+
}
118+
119+
/// A singular borrowed element access within a path.
120+
///
121+
/// Can be applied to a `dyn Reflect` to get a reference to the targeted element.
122+
///
123+
/// Does not own the backing store it's sourced from.
124+
/// For an owned version, you can convert one to an [`Access`] with [`AccessRef::to_owned`].
125+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
126+
pub enum AccessRef<'a> {
127+
Field(&'a str),
128+
FieldIndex(usize),
129+
TupleIndex(usize),
130+
ListIndex(usize),
131+
}
132+
133+
impl Access {
134+
pub(super) fn as_ref(&self) -> AccessRef<'_> {
135+
match self {
136+
Self::Field(value) => AccessRef::Field(value),
137+
Self::FieldIndex(value) => AccessRef::FieldIndex(*value),
138+
Self::TupleIndex(value) => AccessRef::TupleIndex(*value),
139+
Self::ListIndex(value) => AccessRef::ListIndex(*value),
140+
}
141+
}
142+
}
143+
144+
impl<'a> AccessRef<'a> {
145+
pub(super) fn to_owned(self) -> Access {
146+
match self {
147+
Self::Field(value) => Access::Field(value.to_string().into_boxed_str()),
148+
Self::FieldIndex(value) => Access::FieldIndex(value),
149+
Self::TupleIndex(value) => Access::TupleIndex(value),
150+
Self::ListIndex(value) => Access::ListIndex(value),
151+
}
152+
}
153+
154+
fn display_value(&self) -> &dyn fmt::Display {
155+
match self {
156+
Self::Field(value) => value,
157+
Self::FieldIndex(value) | Self::TupleIndex(value) | Self::ListIndex(value) => value,
158+
}
159+
}
160+
fn kind(&self) -> &'static str {
161+
match self {
162+
Self::Field(_) => "field",
163+
Self::FieldIndex(_) => "field index",
164+
Self::TupleIndex(_) | Self::ListIndex(_) => "index",
165+
}
166+
}
167+
168+
pub(super) fn element(
169+
self,
170+
base: &dyn Reflect,
171+
offset: usize,
172+
) -> Result<&dyn Reflect, ReflectPathError<'a>> {
173+
let ty = base.reflect_ref().into();
174+
self.element_inner(base)
175+
.and_then(|maybe| maybe.ok_or(AccessError::Access { ty, access: self }))
176+
.map_err(|err| err.with_offset(offset))
177+
}
178+
fn element_inner(self, base: &dyn Reflect) -> InnerResult<&dyn Reflect> {
179+
use ReflectRef::*;
180+
match (self, base.reflect_ref()) {
181+
(Self::Field(field), Struct(struct_ref)) => Ok(struct_ref.field(field)),
182+
(Self::Field(field), Enum(enum_ref)) => match enum_ref.variant_type() {
183+
VariantType::Struct => Ok(enum_ref.field(field)),
184+
actual => Err(AccessError::bad_enum(Type::Struct, actual)),
185+
},
186+
(Self::FieldIndex(index), Struct(struct_ref)) => Ok(struct_ref.field_at(index)),
187+
(Self::FieldIndex(index), Enum(enum_ref)) => match enum_ref.variant_type() {
188+
VariantType::Struct => Ok(enum_ref.field_at(index)),
189+
actual => Err(AccessError::bad_enum(Type::Struct, actual)),
190+
},
191+
(Self::TupleIndex(index), TupleStruct(tuple)) => Ok(tuple.field(index)),
192+
(Self::TupleIndex(index), Tuple(tuple)) => Ok(tuple.field(index)),
193+
(Self::TupleIndex(index), Enum(enum_ref)) => match enum_ref.variant_type() {
194+
VariantType::Tuple => Ok(enum_ref.field_at(index)),
195+
actual => Err(AccessError::bad_enum(Type::Tuple, actual)),
196+
},
197+
(Self::ListIndex(index), List(list)) => Ok(list.get(index)),
198+
(Self::ListIndex(index), Array(list)) => Ok(list.get(index)),
199+
(Self::ListIndex(_), actual) => Err(AccessError::bad_type(Type::List, actual)),
200+
(_, actual) => Err(AccessError::bad_type(Type::Struct, actual)),
201+
}
202+
}
203+
204+
pub(super) fn element_mut(
205+
self,
206+
base: &mut dyn Reflect,
207+
offset: usize,
208+
) -> Result<&mut dyn Reflect, ReflectPathError<'a>> {
209+
let ty = base.reflect_ref().into();
210+
self.element_inner_mut(base)
211+
.and_then(|maybe| maybe.ok_or(AccessError::Access { ty, access: self }))
212+
.map_err(|err| err.with_offset(offset))
213+
}
214+
fn element_inner_mut(self, base: &mut dyn Reflect) -> InnerResult<&mut dyn Reflect> {
215+
use ReflectMut::*;
216+
let base_kind: Type = base.reflect_ref().into();
217+
match (self, base.reflect_mut()) {
218+
(Self::Field(field), Struct(struct_mut)) => Ok(struct_mut.field_mut(field)),
219+
(Self::Field(field), Enum(enum_mut)) => match enum_mut.variant_type() {
220+
VariantType::Struct => Ok(enum_mut.field_mut(field)),
221+
actual => Err(AccessError::bad_enum(Type::Struct, actual)),
222+
},
223+
(Self::FieldIndex(index), Struct(struct_mut)) => Ok(struct_mut.field_at_mut(index)),
224+
(Self::FieldIndex(index), Enum(enum_mut)) => match enum_mut.variant_type() {
225+
VariantType::Struct => Ok(enum_mut.field_at_mut(index)),
226+
actual => Err(AccessError::bad_enum(Type::Struct, actual)),
227+
},
228+
(Self::TupleIndex(index), TupleStruct(tuple)) => Ok(tuple.field_mut(index)),
229+
(Self::TupleIndex(index), Tuple(tuple)) => Ok(tuple.field_mut(index)),
230+
(Self::TupleIndex(index), Enum(enum_mut)) => match enum_mut.variant_type() {
231+
VariantType::Tuple => Ok(enum_mut.field_at_mut(index)),
232+
actual => Err(AccessError::bad_enum(Type::Tuple, actual)),
233+
},
234+
(Self::ListIndex(index), List(list)) => Ok(list.get_mut(index)),
235+
(Self::ListIndex(index), Array(list)) => Ok(list.get_mut(index)),
236+
(Self::ListIndex(_), _) => Err(AccessError::bad_type(Type::List, base_kind)),
237+
(_, _) => Err(AccessError::bad_type(Type::Struct, base_kind)),
238+
}
239+
}
240+
}

0 commit comments

Comments
 (0)