1pub mod ConfigLoader;
12pub mod ScriptRunner;
13pub mod EnvironmentResolver;
14
15pub use ConfigLoader::*;
17pub use ScriptRunner::*;
18pub use EnvironmentResolver::*;
19
20use rhai::Engine;
21
22pub fn create_engine() -> Engine {
28 let mut engine = Engine::new();
29
30 engine.set_max_expr_depths(0, 0);
32 engine.set_max_operations(0);
33 engine.set_allow_shadowing(true);
34
35 register_utility_functions(&mut engine);
37
38 engine
39}
40
41fn register_utility_functions(engine: &mut Engine) {
43 engine.register_fn("get_os_type", || std::env::consts::OS.to_string());
45 engine.register_fn("get_arch", || std::env::consts::ARCH.to_string());
46 engine.register_fn("get_family", || std::env::consts::FAMILY.to_string());
47
48 engine.register_fn("get_env", |name: &str| -> String {
50 std::env::var(name).unwrap_or_default()
51 });
52
53 engine.register_fn("path_exists", |path: &str| -> bool {
55 std::path::Path::new(path).exists()
56 });
57
58 engine.register_fn("timestamp", || -> i64 {
60 std::time::SystemTime::now()
61 .duration_since(std::time::UNIX_EPOCH)
62 .unwrap_or_default()
63 .as_secs() as i64
64 });
65
66 engine.register_fn("print", |s: &str| {
68 println!("[Rhai] {}", s);
69 });
70}
71
72#[cfg(test)]
77mod tests {
78 use super::*;
79
80 fn get_expected_env_vars(profile_name: &str) -> Vec<(&'static str, &'static str)> {
82 match profile_name {
83 "debug" => vec![
84 ("Debug", "true"),
85 ("Browser", "true"),
86 ("Bundle", "true"),
87 ("Clean", "true"),
88 ("Compile", "false"),
89 ("NODE_ENV", "development"),
90 ("NODE_VERSION", "22"),
91 ("NODE_OPTIONS", "--max-old-space-size=16384"),
92 ("RUST_LOG", "debug"),
93 ("AIR_LOG_JSON", "false"),
94 ("AIR_LOG_FILE", ""),
95 ("Dependency", "Microsoft/VSCode"),
96 ],
97 "production" => vec![
98 ("Debug", "false"),
99 ("Browser", "false"),
100 ("Bundle", "true"),
101 ("Clean", "true"),
102 ("Compile", "true"),
103 ("NODE_ENV", "production"),
104 ("NODE_VERSION", "22"),
105 ("NODE_OPTIONS", "--max-old-space-size=8192"),
106 ("RUST_LOG", "info"),
107 ("AIR_LOG_JSON", "false"),
108 ("Dependency", "Microsoft/VSCode"),
109 ],
110 "release" => vec![
111 ("Debug", "false"),
112 ("Browser", "false"),
113 ("Bundle", "true"),
114 ("Clean", "true"),
115 ("Compile", "true"),
116 ("NODE_ENV", "production"),
117 ("NODE_VERSION", "22"),
118 ("NODE_OPTIONS", "--max-old-space-size=8192"),
119 ("RUST_LOG", "warn"),
120 ("AIR_LOG_JSON", "false"),
121 ("Dependency", "Microsoft/VSCode"),
122 ],
123 _ => vec![],
124 }
125 }
126
127 #[test]
128 fn test_config_loader_load() {
129 let result = ConfigLoader::load(".");
130
131 assert!(result.is_ok(), "ConfigLoader::load() should succeed but got error: {:?}", result.err());
132
133 let config = result.unwrap();
134
135 assert_eq!(config.version, "1.0.0", "Configuration version should be 1.0.0");
136 assert!(!config.profiles.is_empty(), "Configuration should have at least one profile");
137 assert!(config.profiles.contains_key("debug"), "Debug profile should exist");
138 assert!(config.profiles.contains_key("production"), "Production profile should exist");
139 assert!(config.profiles.contains_key("release"), "Release profile should exist");
140 assert!(config.templates.is_some(), "Configuration should have templates defined");
141 }
142
143 #[test]
144 fn test_config_loader_get_profile_debug() {
145 let config = ConfigLoader::load(".").expect("Failed to load configuration");
146 let profile = ConfigLoader::get_profile(&config, "debug");
147
148 assert!(profile.is_some(), "Debug profile should exist in configuration");
149
150 let debug_profile = profile.unwrap();
151 assert!(debug_profile.description.is_some(), "Debug profile should have a description");
152 assert!(debug_profile.env.is_some(), "Debug profile should have environment variables defined");
153 assert!(debug_profile.rhai_script.is_some(), "Debug profile should have a Rhai script defined");
154
155 let debug_env = debug_profile.env.as_ref().unwrap();
156 assert_eq!(debug_env.get("Debug"), Some(&"true".to_string()));
157 assert_eq!(debug_env.get("NODE_ENV"), Some(&"development".to_string()));
158 assert_eq!(debug_env.get("RUST_LOG"), Some(&"debug".to_string()));
159 }
160
161 #[test]
162 fn test_resolve_profile_env_debug() {
163 let config = ConfigLoader::load(".").expect("Failed to load configuration");
164 let env_vars = ConfigLoader::resolve_profile_env(&config, "debug");
165
166 assert_eq!(env_vars.get("Debug"), Some(&"true".to_string()));
167 assert_eq!(env_vars.get("NODE_ENV"), Some(&"development".to_string()));
168 assert!(env_vars.contains_key("MOUNTAIN_DIR"), "Template variable should be present");
169 }
170
171 #[test]
172 fn test_execute_profile_script_debug() {
173 let config = ConfigLoader::load(".").expect("Failed to load configuration");
174 let profile = ConfigLoader::get_profile(&config, "debug").expect("Profile 'debug' not found");
175
176 let script_path = profile.rhai_script.as_ref().expect("No Rhai script defined for debug profile");
177 let full_script_path = std::path::Path::new(".").join(".vscode").join(script_path);
178
179 if !full_script_path.exists() {
180 panic!("Script file not found: {}", full_script_path.display());
181 }
182
183 let engine = create_engine();
184 let context = ScriptRunner::ScriptContext {
185 profile_name: "debug".to_string(),
186 cwd: ".".to_string(),
187 manifest_dir: ".".to_string(),
188 target_triple: None,
189 };
190
191 let result = ScriptRunner::execute_profile_script(&engine, full_script_path.to_str().unwrap(), &context);
192
193 assert!(
194 result.is_ok(),
195 "Script execution should succeed but got error: {:?}",
196 result.err()
197 );
198
199 let script_result = result.unwrap();
200 assert!(script_result.success, "Script execution should report success");
201 assert!(script_result.error.is_none(), "Script execution should not have errors");
202 assert!(!script_result.env_vars.is_empty(), "Script should return environment variables");
203
204 let expected = get_expected_env_vars("debug");
205 for (key, expected_val) in expected {
206 let actual_val = script_result.env_vars.get(key).map(|s| s.as_str());
207 assert_eq!(
208 actual_val,
209 Some(expected_val),
210 "Env var '{}' should be '{}', got {:?}",
211 key,
212 expected_val,
213 actual_val
214 );
215 }
216 }
217
218 #[test]
219 fn test_execute_profile_script_production() {
220 let config = ConfigLoader::load(".").expect("Failed to load configuration");
221 let profile = ConfigLoader::get_profile(&config, "production").expect("Profile 'production' not found");
222
223 let script_path = profile.rhai_script.as_ref().expect("No Rhai script defined for production profile");
224 let full_script_path = std::path::Path::new(".").join(".vscode").join(script_path);
225
226 if !full_script_path.exists() {
227 panic!("Script file not found: {}", full_script_path.display());
228 }
229
230 let engine = create_engine();
231 let context = ScriptRunner::ScriptContext {
232 profile_name: "production".to_string(),
233 cwd: ".".to_string(),
234 manifest_dir: ".".to_string(),
235 target_triple: None,
236 };
237
238 let result = ScriptRunner::execute_profile_script(&engine, full_script_path.to_str().unwrap(), &context);
239
240 assert!(
241 result.is_ok(),
242 "Script execution should succeed but got error: {:?}",
243 result.err()
244 );
245
246 let script_result = result.unwrap();
247 assert!(script_result.success, "Script execution should report success");
248 assert!(script_result.error.is_none(), "Script execution should not have errors");
249
250 let expected = get_expected_env_vars("production");
251 for (key, expected_val) in expected {
252 let actual_val = script_result.env_vars.get(key).map(|s| s.as_str());
253 assert_eq!(
254 actual_val,
255 Some(expected_val),
256 "Env var '{}' should be '{}', got {:?}",
257 key,
258 expected_val,
259 actual_val
260 );
261 }
262 }
263
264 #[test]
265 fn test_execute_profile_script_release() {
266 let config = ConfigLoader::load(".").expect("Failed to load configuration");
267 let profile = ConfigLoader::get_profile(&config, "release").expect("Profile 'release' not found");
268
269 let script_path = profile.rhai_script.as_ref().expect("No Rhai script defined for release profile");
270 let full_script_path = std::path::Path::new(".").join(".vscode").join(script_path);
271
272 if !full_script_path.exists() {
273 panic!("Script file not found: {}", full_script_path.display());
274 }
275
276 let engine = create_engine();
277 let context = ScriptRunner::ScriptContext {
278 profile_name: "release".to_string(),
279 cwd: ".".to_string(),
280 manifest_dir: ".".to_string(),
281 target_triple: None,
282 };
283
284 let result = ScriptRunner::execute_profile_script(&engine, full_script_path.to_str().unwrap(), &context);
285
286 assert!(
287 result.is_ok(),
288 "Script execution should succeed but got error: {:?}",
289 result.err()
290 );
291
292 let script_result = result.unwrap();
293 assert!(script_result.success, "Script execution should report success");
294 assert!(script_result.error.is_none(), "Script execution should not have errors");
295
296 let expected = get_expected_env_vars("release");
297 for (key, expected_val) in expected {
298 let actual_val = script_result.env_vars.get(key).map(|s| s.as_str());
299 assert_eq!(
300 actual_val,
301 Some(expected_val),
302 "Env var '{}' should be '{}', got {:?}",
303 key,
304 expected_val,
305 actual_val
306 );
307 }
308 }
309
310 #[test]
311 fn test_execute_profile_script_bundler_preparation() {
312 let config = ConfigLoader::load(".").expect("Failed to load configuration");
313 let profile = ConfigLoader::get_profile(&config, "bundler-preparation")
314 .expect("Profile 'bundler-preparation' not found in configuration");
315
316 let script_path = profile.rhai_script.as_ref()
317 .expect("No Rhai script defined for bundler-preparation profile");
318 let full_script_path = std::path::Path::new(".").join(".vscode").join(script_path);
319
320 if !full_script_path.exists() {
321 eprintln!("Skipping test - script file not found: {}", full_script_path.display());
322 return;
323 }
324
325 let engine = create_engine();
326 let context = ScriptRunner::ScriptContext {
327 profile_name: "bundler-preparation".to_string(),
328 cwd: ".".to_string(),
329 manifest_dir: ".".to_string(),
330 target_triple: None,
331 };
332
333 let result = ScriptRunner::execute_profile_script(&engine, full_script_path.to_str().unwrap(), &context);
334
335 assert!(result.is_ok(), "Script execution should succeed but got error: {:?}", result.err());
336 let script_result = result.unwrap();
337 assert!(script_result.success, "Script execution should report success");
338 assert!(!script_result.env_vars.is_empty(), "Script should return environment variables");
339
340 assert_eq!(script_result.env_vars.get("BUNDLER_TYPE"), Some(&"swc".to_string()));
342 assert_eq!(script_result.env_vars.get("SWC_TARGET"), Some(&"esnext".to_string()));
343 }
344
345 #[test]
346 fn test_execute_profile_script_swc_bundle() {
347 let config = ConfigLoader::load(".").expect("Failed to load configuration");
348 let profile = ConfigLoader::get_profile(&config, "swc-bundle")
349 .expect("Profile 'swc-bundle' not found in configuration");
350
351 let script_path = profile.rhai_script.as_ref()
352 .expect("No Rhai script defined for swc-bundle profile");
353 let full_script_path = std::path::Path::new(".").join(".vscode").join(script_path);
354
355 if !full_script_path.exists() {
356 eprintln!("Skipping test - script file not found: {}", full_script_path.display());
357 return;
358 }
359
360 let engine = create_engine();
361 let context = ScriptRunner::ScriptContext {
362 profile_name: "swc-bundle".to_string(),
363 cwd: ".".to_string(),
364 manifest_dir: ".".to_string(),
365 target_triple: None,
366 };
367
368 let result = ScriptRunner::execute_profile_script(&engine, full_script_path.to_str().unwrap(), &context);
369
370 assert!(result.is_ok(), "Script execution should succeed but got error: {:?}", result.err());
371 let script_result = result.unwrap();
372 assert!(script_result.success, "Script execution should report success");
373 assert!(!script_result.env_vars.is_empty(), "Script should return environment variables");
374
375 assert_eq!(script_result.env_vars.get("BUNDLER_TYPE"), Some(&"swc".to_string()));
376 assert_eq!(script_result.env_vars.get("NODE_ENV"), Some(&"production".to_string()));
377 }
378
379 #[test]
380 fn test_execute_profile_script_oxc_bundle() {
381 let config = ConfigLoader::load(".").expect("Failed to load configuration");
382 let profile = ConfigLoader::get_profile(&config, "oxc-bundle")
383 .expect("Profile 'oxc-bundle' not found in configuration");
384
385 let script_path = profile.rhai_script.as_ref()
386 .expect("No Rhai script defined for oxc-bundle profile");
387 let full_script_path = std::path::Path::new(".").join(".vscode").join(script_path);
388
389 if !full_script_path.exists() {
390 eprintln!("Skipping test - script file not found: {}", full_script_path.display());
391 return;
392 }
393
394 let engine = create_engine();
395 let context = ScriptRunner::ScriptContext {
396 profile_name: "oxc-bundle".to_string(),
397 cwd: ".".to_string(),
398 manifest_dir: ".".to_string(),
399 target_triple: None,
400 };
401
402 let result = ScriptRunner::execute_profile_script(&engine, full_script_path.to_str().unwrap(), &context);
403
404 assert!(result.is_ok(), "Script execution should succeed but got error: {:?}", result.err());
405 let script_result = result.unwrap();
406 assert!(script_result.success, "Script execution should report success");
407 assert!(!script_result.env_vars.is_empty(), "Script should return environment variables");
408
409 assert_eq!(script_result.env_vars.get("BUNDLER_TYPE"), Some(&"oxc".to_string()));
410 assert_eq!(script_result.env_vars.get("NODE_ENV"), Some(&"production".to_string()));
411 }
412
413 #[test]
414 fn test_env_vars_match_static_config() {
415 let config = ConfigLoader::load(".").expect("Failed to load configuration");
416
417 for profile_name in &["debug", "production", "release"] {
418 let profile = ConfigLoader::get_profile(&config, profile_name)
419 .expect(&format!("Profile '{}' not found", profile_name));
420
421 let script_path = profile.rhai_script.as_ref()
422 .expect(&format!("No Rhai script defined for {}", profile_name));
423
424 let full_script_path = std::path::Path::new(".").join(".vscode").join(script_path);
425
426 if !full_script_path.exists() {
427 continue;
428 }
429
430 let engine = create_engine();
431 let context = ScriptRunner::ScriptContext {
432 profile_name: profile_name.to_string(),
433 cwd: ".".to_string(),
434 manifest_dir: ".".to_string(),
435 target_triple: None,
436 };
437
438 let script_result = ScriptRunner::execute_profile_script(&engine, full_script_path.to_str().unwrap(), &context)
439 .expect(&format!("Failed to execute script for profile '{}'", profile_name));
440
441 let static_env = ConfigLoader::resolve_profile_env(&config, profile_name);
442
443 if let Some(static_debug) = static_env.get("Debug") {
445 let dynamic_debug = script_result.env_vars.get("Debug");
446 assert_eq!(
447 dynamic_debug,
448 Some(static_debug),
449 "Debug value should match between static config and Rhai script for profile '{}'",
450 profile_name
451 );
452 }
453 }
454 }
455}