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}