Skip to main content

bevy_mod_scripting_bindings/function/
into.rs

1//! Implementations of the [`IntoScript`] trait for various types.
2
3use super::{DynamicScriptFunction, DynamicScriptFunctionMut, Union, V};
4use crate::{ReflectReference, ScriptValue, VariadicTuple, WorldExtensions, error::InteropError};
5use bevy_mod_scripting_world::WorldGuard;
6use bevy_platform::collections::HashMap;
7use bevy_reflect::Reflect;
8use std::{borrow::Cow, collections::VecDeque, ffi::OsString, path::PathBuf};
9
10/// Converts a value into a [`ScriptValue`].
11pub trait IntoScript {
12    /// Convert this value into a [`ScriptValue`].
13    fn into_script(self, world: WorldGuard) -> Result<ScriptValue, InteropError>;
14
15    /// Convert this value into a [`ScriptValue`], returning an error as a ScriptValue if an error occurs.
16    fn into_script_inline_error(self, world: WorldGuard) -> ScriptValue
17    where
18        Self: Sized,
19    {
20        self.into_script(world).unwrap_or_else(ScriptValue::Error)
21    }
22}
23
24impl IntoScript for ScriptValue {
25    fn into_script(self, _world: WorldGuard) -> Result<ScriptValue, InteropError> {
26        Ok(self)
27    }
28}
29
30#[profiling::all_functions]
31impl IntoScript for () {
32    fn into_script(self, _world: WorldGuard) -> Result<ScriptValue, InteropError> {
33        Ok(ScriptValue::Unit)
34    }
35}
36
37#[profiling::all_functions]
38impl IntoScript for DynamicScriptFunctionMut {
39    fn into_script(self, _world: WorldGuard) -> Result<ScriptValue, InteropError> {
40        Ok(ScriptValue::FunctionMut(self))
41    }
42}
43
44#[profiling::all_functions]
45impl IntoScript for DynamicScriptFunction {
46    fn into_script(self, _world: WorldGuard) -> Result<ScriptValue, InteropError> {
47        Ok(ScriptValue::Function(self))
48    }
49}
50
51#[profiling::all_functions]
52impl IntoScript for bool {
53    fn into_script(self, _world: WorldGuard) -> Result<ScriptValue, InteropError> {
54        Ok(ScriptValue::Bool(self))
55    }
56}
57
58macro_rules! impl_into_with_downcast {
59    ($variant:tt as $cast:ty [$($ty:ty),*]) => {
60        $(
61            #[profiling::all_functions]
62            impl IntoScript for $ty {
63                fn into_script(self, _world: WorldGuard) -> Result<ScriptValue, InteropError> {
64                    Ok(ScriptValue::$variant(self as $cast))
65                }
66            }
67        )*
68    }
69
70}
71
72impl_into_with_downcast!(Integer as i64 [i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, usize, isize]);
73impl_into_with_downcast!(Float as f64 [f32, f64]);
74
75macro_rules! impl_into_stringlike {
76    ($id:ident,[ $(($ty:ty => $conversion:expr)),*]) => {
77        $(
78            #[profiling::all_functions]
79            impl IntoScript for $ty {
80                fn into_script(self, _world: WorldGuard) -> Result<ScriptValue, InteropError> {
81                    let $id = self;
82                    let converted: String = $conversion;
83                    Ok(ScriptValue::String(converted.into()))
84                }
85            }
86        )*
87    }
88}
89
90impl_into_stringlike!(
91    s,
92    [
93        (String => s),
94        (char => s.to_string()),
95        (PathBuf => s.to_string_lossy().to_string()),
96        (OsString => s.into_string().map_err(|e| InteropError::unsupported_operation(None, Some(Box::new(e)), "Could not convert OsString to String".to_owned()))?)
97    ]
98);
99
100#[profiling::all_functions]
101impl IntoScript for &'static str {
102    fn into_script(self, _world: WorldGuard) -> Result<ScriptValue, InteropError> {
103        Ok(ScriptValue::String(Cow::Borrowed(self)))
104    }
105}
106
107#[profiling::all_functions]
108impl IntoScript for ReflectReference {
109    fn into_script(self, _world: WorldGuard) -> Result<ScriptValue, InteropError> {
110        Ok(ScriptValue::Reference(self))
111    }
112}
113
114#[profiling::all_functions]
115impl<T: Reflect> IntoScript for V<T> {
116    fn into_script(self, world: WorldGuard) -> Result<ScriptValue, InteropError> {
117        let boxed = Box::new(self.0);
118        let allocator = world.allocator();
119        let mut allocator = allocator.write();
120
121        Ok(ScriptValue::Reference(
122            ReflectReference::new_allocated_boxed(boxed, &mut allocator),
123        ))
124    }
125}
126
127#[profiling::all_functions]
128impl<T: IntoScript> IntoScript for Option<T> {
129    fn into_script(self, world: WorldGuard) -> Result<ScriptValue, InteropError> {
130        match self {
131            Some(val) => val.into_script(world),
132            None => Ok(ScriptValue::Unit),
133        }
134    }
135}
136
137#[profiling::all_functions]
138impl<T: IntoScript> IntoScript for Vec<T> {
139    fn into_script(self, world: WorldGuard) -> Result<ScriptValue, InteropError> {
140        let mut values = VecDeque::with_capacity(self.len());
141        for val in self {
142            values.push_back(val.into_script(world.clone())?);
143        }
144        Ok(ScriptValue::List(values))
145    }
146}
147
148#[profiling::all_functions]
149impl<T: IntoScript> IntoScript for VecDeque<T> {
150    fn into_script(self, world: WorldGuard) -> Result<ScriptValue, InteropError> {
151        let mut values = VecDeque::with_capacity(self.len());
152        for val in self {
153            values.push_back(val.into_script(world.clone())?);
154        }
155        Ok(ScriptValue::List(values))
156    }
157}
158
159#[profiling::all_functions]
160impl<T: IntoScript, const N: usize> IntoScript for [T; N] {
161    fn into_script(self, world: WorldGuard) -> Result<ScriptValue, InteropError> {
162        let mut values = VecDeque::with_capacity(N);
163        for val in self {
164            values.push_back(val.into_script(world.clone())?);
165        }
166        Ok(ScriptValue::List(values))
167    }
168}
169
170#[profiling::all_functions]
171impl<T1: IntoScript, T2: IntoScript> IntoScript for Union<T1, T2> {
172    fn into_script(self, world: WorldGuard) -> Result<ScriptValue, InteropError> {
173        match self.into_left() {
174            Ok(left) => left.into_script(world),
175            Err(right) => right.into_script(world),
176        }
177    }
178}
179
180#[profiling::all_functions]
181impl<V: IntoScript> IntoScript for HashMap<String, V> {
182    fn into_script(self, world: WorldGuard) -> Result<ScriptValue, InteropError> {
183        let mut map = HashMap::new();
184        for (key, value) in self {
185            map.insert(key, value.into_script(world.clone())?);
186        }
187        Ok(ScriptValue::Map(map))
188    }
189}
190
191#[profiling::all_functions]
192impl<V: IntoScript> IntoScript for std::collections::HashMap<String, V> {
193    fn into_script(self, world: WorldGuard) -> Result<ScriptValue, InteropError> {
194        let mut map = HashMap::new();
195        for (key, value) in self {
196            map.insert(key, value.into_script(world.clone())?);
197        }
198        Ok(ScriptValue::Map(map))
199    }
200}
201
202#[profiling::all_functions]
203impl IntoScript for InteropError {
204    fn into_script(self, _world: WorldGuard) -> Result<ScriptValue, InteropError> {
205        Ok(ScriptValue::Error(self))
206    }
207}
208
209impl IntoScript for VariadicTuple {
210    fn into_script(self, _world: WorldGuard) -> Result<ScriptValue, InteropError> {
211        Ok(ScriptValue::Tuple(self))
212    }
213}
214
215macro_rules! impl_into_script_tuple {
216    ($( $ty:ident ),* ) => {
217        #[allow(non_snake_case)]
218        #[profiling::all_functions]
219        impl<$($ty: IntoScript),*> IntoScript for ($($ty,)*) {
220        fn into_script(self, world: WorldGuard) -> Result<ScriptValue, InteropError> {
221            let ($($ty,)*) = self;
222            Ok(ScriptValue::Tuple(crate::script_value::VariadicTuple(VecDeque::from_iter([$($ty.into_script(world.clone())?),*].into_iter()))))
223        }
224    }
225}
226}
227
228variadics_please::all_tuples!(impl_into_script_tuple, 1, 14, T);