oxygengine_core/
scripting.rs

1use crate::{
2    app::AppBuilder,
3    ecs::pipeline::{PipelineBuilder, PipelineBuilderError},
4    prefab::PrefabValue,
5};
6use intuicio_essentials::{
7    core::{
8        function::FunctionQuery,
9        registry::Registry,
10        struct_type::{NativeStructBuilder, StructQuery},
11    },
12    data::managed::{DynamicManaged, DynamicManagedRef, DynamicManagedRefMut},
13};
14use pest::Parser;
15use serde::{de::DeserializeOwned, Deserialize, Serialize};
16use std::{collections::HashMap, sync::RwLock};
17
18pub use intuicio_essentials as intuicio;
19
20pub type ScriptValueFactory =
21    dyn FnMut(&PrefabValue) -> Result<DynamicManaged, Box<dyn std::error::Error>> + Send + Sync;
22
23lazy_static! {
24    static ref SCRIPT_VALUE_FACTORY: RwLock<HashMap<String, Box<ScriptValueFactory>>> =
25        Default::default();
26}
27
28#[allow(clippy::upper_case_acronyms)]
29mod parser {
30    #[derive(Parser)]
31    #[grammar = "scripting.pest"]
32    pub(super) struct ScriptReferenceParser;
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct ScriptStructReference {
37    #[serde(default)]
38    pub module_name: Option<String>,
39    pub name: String,
40}
41
42impl ScriptStructReference {
43    pub fn query(&self) -> StructQuery {
44        StructQuery {
45            name: Some(self.name.as_str().into()),
46            module_name: self.module_name.as_ref().map(|name| name.as_str().into()),
47            ..Default::default()
48        }
49    }
50
51    pub fn method(&self, name: &str) -> ScriptFunctionReference {
52        ScriptFunctionReference {
53            struct_type: Some(self.to_owned()),
54            module_name: self.module_name.to_owned(),
55            name: name.to_owned(),
56        }
57    }
58
59    pub fn parse(content: &str) -> Result<Self, String> {
60        match parser::ScriptReferenceParser::parse(parser::Rule::main_struct_reference, content) {
61            Ok(mut ast) => {
62                let mut ast = ast.next().unwrap().into_inner();
63                let mut ast = ast.next().unwrap().into_inner();
64                let a = ast.next().unwrap();
65                let b = ast.next();
66                let (module_name, name) = if let Some(b) = b {
67                    (Some(a.as_str().to_owned()), b.as_str().to_owned())
68                } else {
69                    (None, a.as_str().to_owned())
70                };
71                Ok(Self { module_name, name })
72            }
73            Err(error) => Err(format!("{}", error)),
74        }
75    }
76}
77
78#[derive(Debug, Clone, Serialize, Deserialize)]
79pub struct ScriptFunctionReference {
80    #[serde(default)]
81    pub struct_type: Option<ScriptStructReference>,
82    #[serde(default)]
83    pub module_name: Option<String>,
84    pub name: String,
85}
86
87impl ScriptFunctionReference {
88    pub fn query(&self) -> FunctionQuery {
89        FunctionQuery {
90            struct_query: self
91                .struct_type
92                .as_ref()
93                .map(|struct_type| struct_type.query()),
94            name: Some(self.name.as_str().into()),
95            module_name: self.module_name.as_ref().map(|name| name.as_str().into()),
96            ..Default::default()
97        }
98    }
99
100    pub fn parse(content: &str) -> Result<Self, String> {
101        match parser::ScriptReferenceParser::parse(parser::Rule::main_function_reference, content) {
102            Ok(mut ast) => {
103                let mut ast = ast.next().unwrap().into_inner();
104                let mut ast = ast.next().unwrap().into_inner();
105                let a = ast.next().unwrap();
106                let b = ast.next();
107                let c = ast.next();
108                let (struct_type, module_name, name) =
109                    if a.as_rule() == parser::Rule::struct_reference {
110                        let b = b.unwrap();
111                        let struct_type = ScriptStructReference::parse(a.as_str())?;
112                        if let Some(c) = c {
113                            (
114                                Some(struct_type),
115                                Some(b.as_str().to_owned()),
116                                c.as_str().to_owned(),
117                            )
118                        } else {
119                            (Some(struct_type), None, b.as_str().to_owned())
120                        }
121                    } else if let Some(b) = b {
122                        (None, Some(a.as_str().to_owned()), b.as_str().to_owned())
123                    } else {
124                        (None, None, a.as_str().to_owned())
125                    };
126                Ok(Self {
127                    struct_type,
128                    module_name,
129                    name,
130                })
131            }
132            Err(error) => Err(format!("{}", error)),
133        }
134    }
135}
136
137pub struct Scripting {
138    pub registry: Registry,
139}
140
141impl Scripting {
142    pub fn install(registry: &mut Registry) {
143        *registry = std::mem::take(registry).with_basic_types();
144        registry.add_struct(NativeStructBuilder::new_uninitialized::<DynamicManaged>().build());
145        registry.add_struct(NativeStructBuilder::new_uninitialized::<DynamicManagedRef>().build());
146        registry
147            .add_struct(NativeStructBuilder::new_uninitialized::<DynamicManagedRefMut>().build());
148    }
149}
150
151#[derive(Debug, Clone, Serialize, Deserialize)]
152pub struct ScriptingValue {
153    pub type_name: String,
154    #[serde(default)]
155    pub value: PrefabValue,
156}
157
158impl ScriptingValue {
159    pub fn register<T: DeserializeOwned + 'static>() {
160        Self::register_named::<T>(std::any::type_name::<T>())
161    }
162
163    pub fn register_named<T: DeserializeOwned + 'static>(type_name: impl ToString) {
164        if let Ok(mut factory) = SCRIPT_VALUE_FACTORY.write() {
165            factory.insert(
166                type_name.to_string(),
167                Box::new(
168                    |value| match serde_json::from_value::<T>(value.to_owned()) {
169                        Ok(value) => Ok(DynamicManaged::new(value)),
170                        Err(error) => Err(error.into()),
171                    },
172                ),
173            );
174        }
175    }
176
177    pub fn new<T: Serialize + 'static>(value: T) -> Result<Self, Box<dyn std::error::Error>> {
178        Ok(Self {
179            type_name: std::any::type_name::<T>().to_owned(),
180            value: serde_json::to_value(value)?,
181        })
182    }
183
184    pub fn produce(&self) -> Result<DynamicManaged, Box<dyn std::error::Error>> {
185        match SCRIPT_VALUE_FACTORY.write() {
186            Ok(mut factory) => match factory.get_mut(&self.type_name) {
187                Some(factory) => (factory)(&self.value),
188                None => Err(format!(
189                    "Script value factory not found for type: {}",
190                    self.type_name
191                )
192                .into()),
193            },
194            Err(error) => Err(error.into()),
195        }
196    }
197}
198
199pub fn bundle_installer<PB>(
200    builder: &mut AppBuilder<PB>,
201    registry: Registry,
202) -> Result<(), PipelineBuilderError>
203where
204    PB: PipelineBuilder,
205{
206    ScriptingValue::register::<()>();
207    ScriptingValue::register::<bool>();
208    ScriptingValue::register::<i8>();
209    ScriptingValue::register::<i16>();
210    ScriptingValue::register::<i32>();
211    ScriptingValue::register::<i64>();
212    ScriptingValue::register::<i128>();
213    ScriptingValue::register::<isize>();
214    ScriptingValue::register::<u8>();
215    ScriptingValue::register::<u16>();
216    ScriptingValue::register::<u32>();
217    ScriptingValue::register::<u64>();
218    ScriptingValue::register::<u128>();
219    ScriptingValue::register::<usize>();
220    ScriptingValue::register::<f32>();
221    ScriptingValue::register::<f64>();
222    ScriptingValue::register::<char>();
223    ScriptingValue::register_named::<String>("String");
224    builder.install_resource(Scripting { registry });
225    Ok(())
226}
227
228pub fn scripting_installer(registry: &mut Registry) {
229    registry.add_struct(
230        NativeStructBuilder::new_named_uninitialized::<crate::assets::database::AssetsDatabase>(
231            "AssetsDatabase",
232        )
233        .module_name("core")
234        .build(),
235    );
236}