1use std::{collections::HashMap, path::Path};
6
7use rhai::{AST, Dynamic, Engine, Scope};
8
9#[derive(Debug, Clone)]
14pub struct ScriptResult {
15 pub env_vars:HashMap<String, String>,
17
18 pub success:bool,
20
21 pub error:Option<String>,
23
24 pub pre_build_continue:bool,
26
27 pub post_build_output:Option<String>,
29
30 pub features:HashMap<String, bool>,
32
33 pub workbench:Option<String>,
35}
36
37#[derive(Debug, Clone)]
38pub struct ScriptContext {
39 pub profile_name:String,
41
42 pub cwd:String,
44
45 pub manifest_dir:String,
47
48 pub target_triple:Option<String>,
50
51 pub workbench_type:Option<String>,
53
54 pub features:HashMap<String, bool>,
56}
57
58pub fn ExecuteProfileScript(engine:&Engine, script_path:&str, context:&ScriptContext) -> Result<ScriptResult, String> {
74 let Ast = LoadScript(engine, script_path)?;
75
76 let mut Scope = CreateScope(context);
77
78 let ExecutionResult = engine.run_ast_with_scope(&mut Scope, &Ast);
80
81 let mut Result = ScriptResult {
82 env_vars:HashMap::new(),
83
84 success:ExecutionResult.is_ok(),
85
86 error:None,
87
88 pre_build_continue:true,
89
90 post_build_output:None,
91
92 features:HashMap::new(),
93
94 workbench:None,
95 };
96
97 if let Err(Error) = ExecutionResult {
98 Result.error = Some(Error.to_string());
99
100 Result.pre_build_continue = false;
101
102 return Ok(Result);
103 }
104
105 if let Ok(EnvMap) = engine.call_fn(&mut Scope, &Ast, "get_env_vars", ()) {
107 Result.env_vars = ExtractEnvMap(EnvMap);
108 }
109
110 if let Ok(FeatureMap) = engine.call_fn(&mut Scope, &Ast, "get_features", ()) {
112 Result.features = ExtractFeatureMap(FeatureMap);
113 }
114
115 if let Ok(Workbench) = engine.call_fn::<String>(&mut Scope, &Ast, "get_workbench", ()) {
117 Result.workbench = Some(Workbench);
118 }
119
120 if let Ok(ContinueResult) = engine.call_fn::<bool>(&mut Scope, &Ast, "pre_build_continue", ()) {
122 Result.pre_build_continue = ContinueResult;
123 }
124
125 if let Ok(Output) = engine.call_fn::<String>(&mut Scope, &Ast, "post_build_output", ()) {
127 Result.post_build_output = Some(Output);
128 }
129
130 Ok(Result)
131}
132
133pub fn LoadScript(engine:&Engine, script_path:&str) -> Result<AST, String> {
144 if !Path::new(script_path).exists() {
145 return Err(format!("Script file not found: {}", script_path));
146 }
147
148 let Content = std::fs::read_to_string(script_path).map_err(|Error| format!("Failed to read script: {}", Error))?;
149
150 let Ast = engine
151 .compile(&Content)
152 .map_err(|Error| format!("Failed to compile script: {}", Error))?;
153
154 Ok(Ast)
155}
156
157pub fn CreateEngine() -> Engine {
163 let mut Engine = Engine::new();
164
165 Engine.register_fn("env", |name:&str| -> String { std::env::var(name).unwrap_or_default() });
167
168 Engine.register_fn("env_or", |name:&str, default:&str| -> String {
169 std::env::var(name).unwrap_or_else(|_| default.to_string())
170 });
171
172 Engine.register_fn("set_env", |name:&str, value:&str| {
173 unsafe {
178 std::env::set_var(name, value);
179 }
180 });
181
182 Engine.register_fn("log", |message:&str| {
183 println!("[Rhai] {}", message);
184 });
185
186 Engine.register_fn("log_error", |message:&str| {
187 eprintln!("[Rhai ERROR] {}", message);
188 });
189
190 Engine.register_fn("log_warn", |message:&str| {
191 eprintln!("[Rhai WARN] {}", message);
192 });
193
194 Engine.register_fn("path_join", |base:&str, suffix:&str| -> String {
196 Path::new(base).join(suffix).to_string_lossy().to_string()
197 });
198
199 Engine.register_fn("path_exists", |path:&str| -> bool { Path::new(path).exists() });
200
201 Engine.register_fn("to_uppercase", |s:&str| -> String { s.to_uppercase() });
203
204 Engine.register_fn("to_lowercase", |s:&str| -> String { s.to_lowercase() });
205
206 Engine
207}
208
209fn CreateScope(context:&ScriptContext) -> Scope<'_> {
215 let mut Scope = Scope::new();
216
217 Scope.push("profile_name", context.profile_name.clone());
218
219 Scope.push("cwd", context.cwd.clone());
220
221 Scope.push("manifest_dir", context.manifest_dir.clone());
222
223 Scope.push("target_triple", context.target_triple.clone().unwrap_or_default());
224
225 Scope.push("workbench_type", context.workbench_type.clone().unwrap_or_default());
226
227 let mut FeaturesMap = rhai::Map::new();
229
230 for (Key, Value) in &context.features {
231 FeaturesMap.insert(Key.into(), (*Value).into());
232 }
233
234 Scope.push("features", FeaturesMap);
235
236 Scope
237}
238
239fn ExtractEnvMap(Dynamic:Dynamic) -> HashMap<String, String> {
241 let mut EnvMap = HashMap::new();
242
243 if let Some(Map) = Dynamic.try_cast::<rhai::Map>() {
244 for (Key, Value) in Map {
245 if Value.is_string() {
246 EnvMap.insert(Key.to_string(), Value.to_string());
247 } else if Value.is_int() {
248 EnvMap.insert(Key.to_string(), Value.as_int().unwrap_or(0).to_string());
249 } else if Value.is_bool() {
250 EnvMap.insert(Key.to_string(), Value.as_bool().unwrap_or(false).to_string());
251 } else {
252 EnvMap.insert(Key.to_string(), Value.to_string());
253 }
254 }
255 }
256
257 EnvMap
258}
259
260fn ExtractFeatureMap(Dynamic:Dynamic) -> HashMap<String, bool> {
262 let mut FeatureMap = HashMap::new();
263
264 if let Some(Map) = Dynamic.try_cast::<rhai::Map>() {
265 for (Key, Value) in Map {
266 if Value.is_bool() {
267 FeatureMap.insert(Key.to_string(), Value.as_bool().unwrap_or(false));
268 }
269 }
270 }
271
272 FeatureMap
273}
274
275#[cfg(test)]
280mod tests {
281
282 use super::*;
283
284 #[test]
285 fn test_create_engine() {
286 let Engine = CreateEngine();
287
288 assert!(Engine.compile("let x = 1;").is_ok());
290 }
291
292 #[test]
293 fn test_extract_env_map() {
294 let mut map = rhai::Map::new();
295
296 map.insert("KEY1".into(), "value1".into());
297
298 map.insert("KEY2".into(), 42.into());
299
300 map.insert("KEY3".into(), true.into());
301
302 let Dynamic = Dynamic::from(map);
303
304 let Env = ExtractEnvMap(Dynamic);
305
306 assert_eq!(Env.get("KEY1"), Some(&"value1".to_string()));
307
308 assert_eq!(Env.get("KEY2"), Some(&"42".to_string()));
309
310 assert_eq!(Env.get("KEY3"), Some(&"true".to_string()));
311 }
312
313 #[test]
314 fn test_extract_feature_map() {
315 let mut map = rhai::Map::new();
316
317 map.insert("feature1".into(), true.into());
318
319 map.insert("feature2".into(), false.into());
320
321 let Dynamic = Dynamic::from(map);
322
323 let Features = ExtractFeatureMap(Dynamic);
324
325 assert_eq!(Features.get("feature1"), Some(&true));
326
327 assert_eq!(Features.get("feature2"), Some(&false));
328 }
329}