Skip to content

Commit d40838b

Browse files
committed
Big things coming
1 parent defd052 commit d40838b

File tree

8 files changed

+671
-1
lines changed

8 files changed

+671
-1
lines changed
Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
use crate::{
2+
bindings::{access_map::ReflectAccessId, ReflectReference, WorldGuard},
3+
error::InteropError,
4+
prelude::ScriptValue,
5+
};
6+
use bevy::reflect::FromReflect;
7+
use std::{
8+
any::TypeId,
9+
ffi::OsString,
10+
ops::{Deref, DerefMut},
11+
path::PathBuf,
12+
};
13+
14+
/// Describes the procedure for constructing a value of type `T` from a [`ScriptValue`].
15+
///
16+
/// The [`FromScript::This`] associated type is used to allow for the implementation of this trait to return
17+
/// a type with the lifetime of the world guard. In 99% cases you can just use `Self` as the associated type.
18+
pub trait FromScript {
19+
type This<'w>;
20+
fn from_script(
21+
value: ScriptValue,
22+
world: WorldGuard<'_>,
23+
) -> Result<Self::This<'_>, InteropError>
24+
where
25+
Self: Sized;
26+
}
27+
28+
impl FromScript for ScriptValue {
29+
type This<'w> = Self;
30+
fn from_script(value: ScriptValue, _world: WorldGuard) -> Result<Self, InteropError> {
31+
Ok(value)
32+
}
33+
}
34+
35+
macro_rules! impl_from_with_downcast {
36+
($($ty:ty),*) => {
37+
$(
38+
impl FromScript for $ty {
39+
type This<'w> = Self;
40+
fn from_script(value: ScriptValue, world: WorldGuard) -> Result<Self, InteropError> {
41+
match value {
42+
ScriptValue::Integer(i) => Ok(i as $ty),
43+
ScriptValue::Float(i) => Ok(i as $ty),
44+
ScriptValue::Reference(r) => r.downcast::<Self>(world),
45+
_ => Err(InteropError::value_mismatch(std::any::TypeId::of::<Self>(), value)),
46+
}
47+
}
48+
}
49+
)*
50+
};
51+
}
52+
53+
impl_from_with_downcast!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, f32, f64, usize, isize);
54+
55+
macro_rules! impl_from_stringlike {
56+
($($ty:ty),*) => {
57+
$(
58+
impl FromScript for $ty {
59+
type This<'w> = Self;
60+
fn from_script(value: ScriptValue, world: WorldGuard) -> Result<Self, InteropError> {
61+
match value {
62+
ScriptValue::String(s) => Ok(s.to_string().into()),
63+
ScriptValue::Reference(r) => r.downcast::<Self>(world),
64+
_ => Err(InteropError::value_mismatch(std::any::TypeId::of::<Self>(), value)),
65+
}
66+
}
67+
}
68+
)*
69+
};
70+
}
71+
72+
impl_from_stringlike!(String, PathBuf, OsString);
73+
74+
impl FromScript for ReflectReference {
75+
type This<'w> = Self;
76+
fn from_script(value: ScriptValue, _world: WorldGuard) -> Result<Self, InteropError> {
77+
match value {
78+
ScriptValue::Reference(r) => Ok(r),
79+
_ => Err(InteropError::value_mismatch(
80+
std::any::TypeId::of::<ReflectReference>(),
81+
value,
82+
)),
83+
}
84+
}
85+
}
86+
87+
/// A wrapper around a value of type `T`.
88+
///
89+
/// This can be used to retrieve a value out of a [`ScriptValue::Reference`] corresponding to the type `T`.
90+
/// You can also use this to return values from a script function to be allocated directly as a [`ScriptValue::Reference`].
91+
pub struct Val<T>(pub T);
92+
93+
impl<T> Deref for Val<T> {
94+
type Target = T;
95+
96+
fn deref(&self) -> &Self::Target {
97+
&self.0
98+
}
99+
}
100+
101+
impl<T> DerefMut for Val<T> {
102+
fn deref_mut(&mut self) -> &mut Self::Target {
103+
&mut self.0
104+
}
105+
}
106+
107+
impl<T: FromReflect> FromScript for Val<T> {
108+
type This<'w> = Self;
109+
fn from_script(value: ScriptValue, world: WorldGuard) -> Result<Self, InteropError> {
110+
match value {
111+
ScriptValue::Reference(reflect_reference) => Ok(Val(reflect_reference.with_reflect(
112+
world.clone(),
113+
|r| {
114+
T::from_reflect(r).ok_or_else(|| {
115+
InteropError::failed_from_reflect(
116+
Some(TypeId::of::<T>()),
117+
"from reflect failed to produce output when converting to Val<T>"
118+
.to_owned(),
119+
)
120+
})
121+
},
122+
)??)),
123+
_ => Err(InteropError::value_mismatch(
124+
std::any::TypeId::of::<T>(),
125+
value,
126+
)),
127+
}
128+
}
129+
}
130+
131+
/// A wrapper around a reference to a value of type `T`.
132+
///
133+
/// This can be used to retrieve a reference out of a [`ScriptValue::Reference`] corresponding to the type `T`.
134+
/// Before downcasting the reference, it will claim write access to the object to ensure that the reference is valid.
135+
///
136+
/// However, the access is NOT released when the `Mut` is dropped. This is not unsafe but can lead to deadlocks if not released later.
137+
/// The [`ScriptFunction`] calling mechanism will take care of releasing all accesses claimed during the function call.
138+
pub struct Ref<'w, T>(pub &'w T);
139+
140+
impl<T> Deref for Ref<'_, T> {
141+
type Target = T;
142+
143+
fn deref(&self) -> &Self::Target {
144+
self.0
145+
}
146+
}
147+
148+
impl<T: FromReflect> FromScript for Ref<'_, T> {
149+
type This<'w> = Ref<'w, T>;
150+
151+
fn from_script(
152+
value: ScriptValue,
153+
world: WorldGuard<'_>,
154+
) -> Result<Self::This<'_>, InteropError> {
155+
match value {
156+
ScriptValue::Reference(reflect_reference) => {
157+
let raid = ReflectAccessId::for_reference(reflect_reference.base.base_id.clone())
158+
.ok_or_else(|| {
159+
InteropError::unregistered_base(reflect_reference.base.clone())
160+
})?;
161+
162+
if world.claim_read_access(raid) {
163+
// Safety: we just claimed access
164+
let ref_ = unsafe { reflect_reference.reflect_unsafe(world) }?;
165+
let cast = ref_.try_downcast_ref::<T>().ok_or_else(|| {
166+
InteropError::type_mismatch(
167+
std::any::TypeId::of::<T>(),
168+
ref_.get_represented_type_info().map(|i| i.type_id()),
169+
)
170+
})?;
171+
Ok(Ref(cast))
172+
} else {
173+
Err(InteropError::cannot_claim_access(reflect_reference.base))
174+
}
175+
}
176+
_ => Err(InteropError::value_mismatch(
177+
std::any::TypeId::of::<T>(),
178+
value,
179+
)),
180+
}
181+
}
182+
}
183+
184+
/// A wrapper around a mutable reference to a value of type `T`.
185+
///
186+
/// This can be used to retrieve a mutable reference out of a [`ScriptValue::Reference`] corresponding to the type `T`.
187+
/// Before downcasting the reference, it will claim write access to the object to ensure that the reference is valid.
188+
///
189+
/// However, the access is NOT released when the `Mut` is dropped. This is not unsafe but can lead to deadlocks if not released later.
190+
/// The [`ScriptFunction`] calling mechanism will take care of releasing all accesses claimed during the function call.
191+
pub struct Mut<'w, T>(pub &'w mut T);
192+
193+
impl<T> Deref for Mut<'_, T> {
194+
type Target = T;
195+
196+
fn deref(&self) -> &Self::Target {
197+
self.0
198+
}
199+
}
200+
201+
impl<T> DerefMut for Mut<'_, T> {
202+
fn deref_mut(&mut self) -> &mut Self::Target {
203+
self.0
204+
}
205+
}
206+
207+
impl<T: FromReflect> FromScript for Mut<'_, T> {
208+
type This<'w> = Mut<'w, T>;
209+
210+
fn from_script(
211+
value: ScriptValue,
212+
world: WorldGuard<'_>,
213+
) -> Result<Self::This<'_>, InteropError> {
214+
match value {
215+
ScriptValue::Reference(reflect_reference) => {
216+
let raid = ReflectAccessId::for_reference(reflect_reference.base.base_id.clone())
217+
.ok_or_else(|| {
218+
InteropError::unregistered_base(reflect_reference.base.clone())
219+
})?;
220+
221+
if world.claim_write_access(raid) {
222+
// Safety: we just claimed write access
223+
let ref_ = unsafe { reflect_reference.reflect_mut_unsafe(world) }?;
224+
let type_id = ref_.get_represented_type_info().map(|i| i.type_id());
225+
let cast = ref_.try_downcast_mut::<T>().ok_or_else(|| {
226+
InteropError::type_mismatch(std::any::TypeId::of::<T>(), type_id)
227+
})?;
228+
Ok(Mut(cast))
229+
} else {
230+
Err(InteropError::cannot_claim_access(reflect_reference.base))
231+
}
232+
}
233+
_ => Err(InteropError::value_mismatch(
234+
std::any::TypeId::of::<T>(),
235+
value,
236+
)),
237+
}
238+
}
239+
}
240+
241+
impl<T: FromScript> FromScript for Option<T>
242+
where
243+
for<'w> T::This<'w>: Into<T>,
244+
{
245+
type This<'w> = Self;
246+
247+
fn from_script(value: ScriptValue, world: WorldGuard) -> Result<Self, InteropError> {
248+
match value {
249+
ScriptValue::Unit => Ok(None),
250+
_ => Ok(Some(T::from_script(value, world)?.into())),
251+
}
252+
}
253+
}
254+
255+
impl<T: FromScript + 'static> FromScript for Vec<T>
256+
where
257+
for<'w> T::This<'w>: Into<T>,
258+
{
259+
type This<'w> = Self;
260+
261+
fn from_script(value: ScriptValue, world: WorldGuard) -> Result<Self, InteropError> {
262+
match value {
263+
ScriptValue::List(list) => {
264+
let mut vec = Vec::with_capacity(list.len());
265+
for item in list {
266+
vec.push(T::from_script(item, world.clone())?.into());
267+
}
268+
Ok(vec)
269+
}
270+
_ => Err(InteropError::value_mismatch(
271+
std::any::TypeId::of::<Vec<T>>(),
272+
value,
273+
)),
274+
}
275+
}
276+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
use std::{
2+
ffi::OsString,
3+
path::{Path, PathBuf},
4+
};
5+
6+
use bevy::reflect::{PartialReflect, ReflectRef};
7+
8+
use crate::{
9+
bindings::{ReflectReference, WorldGuard},
10+
error::InteropError,
11+
prelude::ScriptValue,
12+
};
13+
14+
use super::from::Val;
15+
16+
pub trait IntoScript {
17+
fn into_script(self, world: WorldGuard) -> Result<ScriptValue, InteropError>;
18+
}
19+
20+
impl IntoScript for ScriptValue {
21+
fn into_script(self, _world: WorldGuard) -> Result<ScriptValue, InteropError> {
22+
Ok(self)
23+
}
24+
}
25+
26+
macro_rules! impl_into_with_downcast {
27+
($variant:tt as $cast:ty [$($ty:ty),*]) => {
28+
$(
29+
impl IntoScript for $ty {
30+
fn into_script(self, _world: WorldGuard) -> Result<ScriptValue, InteropError> {
31+
Ok(ScriptValue::$variant(self as $cast))
32+
}
33+
}
34+
)*
35+
}
36+
37+
}
38+
39+
impl_into_with_downcast!(Integer as i64 [i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, usize, isize]);
40+
impl_into_with_downcast!(Float as f64 [f32, f64]);
41+
42+
macro_rules! impl_into_stringlike {
43+
($id:ident,[ $(($ty:ty => $conversion:expr)),*]) => {
44+
$(
45+
impl IntoScript for $ty {
46+
fn into_script(self, _world: WorldGuard) -> Result<ScriptValue, InteropError> {
47+
let $id = self;
48+
let converted: String = $conversion;
49+
Ok(ScriptValue::String(converted.into()))
50+
}
51+
}
52+
)*
53+
}
54+
}
55+
56+
impl_into_stringlike!(
57+
s,
58+
[
59+
(String => s),
60+
(PathBuf => s.to_string_lossy().to_string()),
61+
(OsString => s.into_string().map_err(|e| InteropError::unsupported_operation(None, Some(Box::new(e)), "Could not convert OsString to String".to_owned()))?)
62+
]
63+
);
64+
65+
impl IntoScript for ReflectReference {
66+
fn into_script(self, _world: WorldGuard) -> Result<ScriptValue, InteropError> {
67+
Ok(ScriptValue::Reference(self))
68+
}
69+
}
70+
71+
impl<T: PartialReflect> IntoScript for Val<T> {
72+
fn into_script(self, world: WorldGuard) -> Result<ScriptValue, InteropError> {
73+
let boxed: Box<dyn PartialReflect> = Box::new(self.0);
74+
let allocator = world.allocator();
75+
let mut allocator = allocator.write();
76+
77+
Ok(ScriptValue::Reference(
78+
ReflectReference::new_allocated_boxed(boxed, &mut allocator),
79+
))
80+
}
81+
}
82+
83+
impl<T: IntoScript> IntoScript for Option<T> {
84+
fn into_script(self, world: WorldGuard) -> Result<ScriptValue, InteropError> {
85+
match self {
86+
Some(val) => val.into_script(world),
87+
None => Ok(ScriptValue::Unit),
88+
}
89+
}
90+
}
91+
92+
impl<T: IntoScript> IntoScript for Vec<T> {
93+
fn into_script(self, world: WorldGuard) -> Result<ScriptValue, InteropError> {
94+
let mut values = Vec::with_capacity(self.len());
95+
for val in self {
96+
values.push(val.into_script(world.clone())?);
97+
}
98+
Ok(ScriptValue::List(values))
99+
}
100+
}
101+
102+
impl IntoScript for InteropError {
103+
fn into_script(self, _world: WorldGuard) -> Result<ScriptValue, InteropError> {
104+
Ok(ScriptValue::Error(self))
105+
}
106+
}

crates/bevy_mod_scripting_core/src/bindings/function.rs renamed to crates/bevy_mod_scripting_core/src/bindings/function/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
use std::{any::TypeId, borrow::Cow, ops::Deref, sync::Arc};
22

3+
pub mod from;
4+
pub mod into;
5+
pub mod script_function;
6+
37
use bevy::reflect::{
48
func::{
59
args::{Arg, ArgInfo, Ownership},

0 commit comments

Comments
 (0)