Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_reflect/src/convert.rs
30635 views
1
//! The [`ReflectConvert`] type, which allows types to register conversions to
2
//! and from one another.
3
4
use alloc::boxed::Box;
5
use core::{any::TypeId, marker::PhantomData};
6
7
use bevy_utils::TypeIdMap;
8
9
use crate::{Reflect, TypePath};
10
11
/// Provides a mechanism for converting values of one type to another.
12
///
13
/// This [`crate::type_data::TypeData`] is associated with the type to be
14
/// converted *into*, not the type to be converted *from*. To convert a value,
15
/// use code like the following:
16
///
17
/// ```rust
18
/// # use bevy_reflect::{convert::ReflectConvert, GetTypeRegistration, TypeRegistry};
19
/// # use std::any::TypeId;
20
/// #
21
/// # let mut registry = TypeRegistry::default();
22
/// # registry.add_registration(i32::get_type_registration());
23
/// # registry.add_registration(String::get_type_registration());
24
/// # registry.register_type_conversion(|x: i32| Ok(x.to_string()));
25
///
26
/// let reflect_convert = registry
27
/// .get_type_data::<ReflectConvert>(TypeId::of::<String>())
28
/// .unwrap();
29
/// let converted: String = *reflect_convert
30
/// .try_convert_from(Box::new(12345i32))
31
/// .unwrap()
32
/// .downcast::<String>()
33
/// .unwrap();
34
/// ```
35
#[derive(Default)]
36
pub struct ReflectConvert {
37
/// A mapping from the type to be converted *from* to its associated
38
/// [`Converter`].
39
conversions: TypeIdMap<Box<dyn Converter>>,
40
}
41
42
/// An internal trait that wraps a conversion function in an untyped interface.
43
trait Converter: Send + Sync {
44
/// Converts the value to the appropriate type.
45
///
46
/// This returns the converted value if the conversion succeeds or the
47
/// original value if the conversion fails.
48
fn convert(&self, input: Box<dyn Reflect>) -> Result<Box<dyn Reflect>, Box<dyn Reflect>>;
49
50
/// Returns a new boxed instance wrapping the same [`Converter`].
51
fn clone_converter(&self) -> Box<dyn Converter>;
52
}
53
54
/// A wrapper that contains a conversion function and implements [`Converter`].
55
struct TypedConverter<T, U, F>
56
where
57
T: Reflect + TypePath,
58
U: Reflect + TypePath,
59
F: Fn(T) -> Result<U, T> + Clone + Send + Sync + 'static,
60
{
61
function: F,
62
phantom: PhantomData<(T, U)>,
63
}
64
65
impl ReflectConvert {
66
/// Attempts to construct an instance of this type from the provided
67
/// `input`.
68
///
69
/// If the conversion fails, either because no conversion has been
70
/// registered from the type of `input` or because the conversion function
71
/// itself returned `Err`, the `input` value is returned as an error.
72
pub fn try_convert_from(
73
&self,
74
input: Box<dyn Reflect>,
75
) -> Result<Box<dyn Reflect>, Box<dyn Reflect>> {
76
let type_id = (*input.as_any()).type_id();
77
match self.conversions.get(&type_id) {
78
Some(converter) => converter.convert(input),
79
None => Err(input),
80
}
81
}
82
83
/// Adds a conversion function from the type `T` to this type.
84
///
85
/// If the conversion succeeds, the function should return the converted
86
/// value. If the conversion fails, the function should return the original
87
/// input value.
88
pub fn register_type_conversion<T, U, F>(&mut self, function: F)
89
where
90
T: Reflect + TypePath,
91
U: Reflect + TypePath,
92
F: Fn(T) -> Result<U, T> + Clone + Send + Sync + 'static,
93
{
94
self.conversions.insert(
95
TypeId::of::<T>(),
96
Box::new(TypedConverter {
97
function,
98
phantom: PhantomData,
99
}),
100
);
101
}
102
}
103
104
impl Clone for ReflectConvert {
105
fn clone(&self) -> Self {
106
ReflectConvert {
107
conversions: self
108
.conversions
109
.iter()
110
.map(|(type_id, converter)| (*type_id, converter.clone_converter()))
111
.collect(),
112
}
113
}
114
}
115
116
impl<T, U, F> Clone for TypedConverter<T, U, F>
117
where
118
T: Reflect + TypePath,
119
U: Reflect + TypePath,
120
F: Fn(T) -> Result<U, T> + Clone + Send + Sync + 'static,
121
{
122
fn clone(&self) -> Self {
123
TypedConverter {
124
function: self.function.clone(),
125
phantom: PhantomData,
126
}
127
}
128
}
129
130
impl<T, U, F> Converter for TypedConverter<T, U, F>
131
where
132
T: Reflect + TypePath,
133
U: Reflect + TypePath,
134
F: Fn(T) -> Result<U, T> + Clone + Send + Sync + 'static,
135
{
136
fn convert(&self, input: Box<dyn Reflect>) -> Result<Box<dyn Reflect>, Box<dyn Reflect>> {
137
let mut input = input.downcast::<T>()?;
138
match (self.function)(*input) {
139
Ok(value) => Ok(Box::new(value)),
140
Err(value) => {
141
*input = value;
142
Err(input)
143
}
144
}
145
}
146
147
fn clone_converter(&self) -> Box<dyn Converter> {
148
Box::new(self.clone())
149
}
150
}
151
152
#[cfg(test)]
153
mod tests {
154
use alloc::{
155
borrow::ToOwned as _,
156
boxed::Box,
157
string::{String, ToString},
158
};
159
use core::any::TypeId;
160
161
use crate::{convert::ReflectConvert, type_registry::GetTypeRegistration, TypeRegistry};
162
163
/// Tests that `i32` can be converted to `String` if the appropriate
164
/// conversion is registered.
165
#[test]
166
fn convert_from_i32_to_string() {
167
// Register the types and the conversion.
168
let mut registry = TypeRegistry::default();
169
registry.add_registration(i32::get_type_registration());
170
registry.add_registration(String::get_type_registration());
171
registry.register_type_conversion(|x: i32| Ok(x.to_string()));
172
173
let reflect_convert = registry
174
.get_type_data::<ReflectConvert>(TypeId::of::<String>())
175
.unwrap();
176
177
// Test that a successful conversion works.
178
let converted = reflect_convert
179
.try_convert_from(Box::new(12345i32))
180
.unwrap()
181
.downcast::<String>()
182
.unwrap();
183
assert_eq!(&**converted, "12345");
184
}
185
186
/// Tests that `String` can be fallibly converted to `i32` if the
187
/// appropriate conversion is registered.
188
///
189
/// This also tests that the behavior of returning the original string on
190
/// error is correct.
191
#[test]
192
fn convert_from_string_to_i32() {
193
// Register the types and the conversion.
194
let mut registry = TypeRegistry::default();
195
registry.add_registration(i32::get_type_registration());
196
registry.add_registration(String::get_type_registration());
197
registry.register_type_conversion(|x: String| match x.parse::<i32>() {
198
Ok(value) => Ok(value),
199
Err(_) => Err(x),
200
});
201
202
let reflect_convert = registry
203
.get_type_data::<ReflectConvert>(TypeId::of::<i32>())
204
.unwrap();
205
206
// Test a successful conversion from string to integer.
207
let converted = reflect_convert
208
.try_convert_from(Box::new("12345".to_owned()))
209
.unwrap()
210
.downcast::<i32>()
211
.unwrap();
212
assert_eq!(*converted, 12345);
213
214
// Test an unsuccessful conversion from string to integer.
215
let error = reflect_convert
216
.try_convert_from(Box::new("qqqqq".to_owned()))
217
.unwrap_err()
218
.downcast::<String>()
219
.unwrap();
220
assert_eq!(&**error, "qqqqq");
221
}
222
223
/// Tests that we can register multiple conversions into the same type and
224
/// that they all work.
225
#[test]
226
fn convert_from_f32_and_u32_to_i32() {
227
let mut registry = TypeRegistry::default();
228
registry.add_registration(i32::get_type_registration());
229
registry.add_registration(f32::get_type_registration());
230
registry.add_registration(u32::get_type_registration());
231
registry.register_type_conversion::<u32, i32, _>(|n: u32| n.try_into().map_err(|_| n));
232
registry.register_type_conversion::<f32, i32, _>(|n: f32| Ok(n as i32));
233
234
let reflect_convert = registry
235
.get_type_data::<ReflectConvert>(TypeId::of::<i32>())
236
.unwrap();
237
238
// Test that we can convert `u32` and `f32` into `i32`.
239
let a = reflect_convert
240
.try_convert_from(Box::new(99u32))
241
.unwrap()
242
.downcast::<i32>()
243
.unwrap();
244
assert_eq!(*a, 99i32);
245
let b = reflect_convert
246
.try_convert_from(Box::new(99.0f32))
247
.unwrap()
248
.downcast::<i32>()
249
.unwrap();
250
assert_eq!(*b, 99i32);
251
}
252
253
/// Tests that the error-handling behavior is correct when attempting a
254
/// conversion that hasn't been registered.
255
#[test]
256
fn no_such_conversion() {
257
let mut registry = TypeRegistry::default();
258
registry.add_registration(i32::get_type_registration());
259
registry.add_registration(String::get_type_registration());
260
registry
261
.get_mut(TypeId::of::<i32>())
262
.unwrap()
263
.insert(ReflectConvert::default());
264
265
let reflect_convert = registry
266
.get_type_data::<ReflectConvert>(TypeId::of::<i32>())
267
.unwrap();
268
269
// Test that we get the original value back on error.
270
let error = reflect_convert
271
.try_convert_from(Box::new("12345".to_owned()))
272
.unwrap_err()
273
.downcast::<String>()
274
.unwrap();
275
assert_eq!(&**error, "12345");
276
}
277
}
278
279